feat: share the proposal board across chats

The unfinished-proposal board is now visible to every conversation,
with entries marked scope=own/scope=other (chat type only, no IDs), so
anyone can see what the single worker is busy with. All actions remain
owner-only, and pending reminders still target only the owner.
This commit is contained in:
zenord
2026-08-19 17:08:48 +08:00
parent f17fee7383
commit 91327dc632
7 changed files with 67 additions and 25 deletions
+11 -1
View File
@@ -397,18 +397,28 @@ test("proposals are owned per chat+user: a second user in the same chat cannot c
assert.equal(harness.proposals.get(proposal.id)!.status, "proposed");
assert.equal(await harness.manager.finish("webhook", "chat-1", "user-b"), false);
assert.equal(await harness.manager.stop("webhook", "chat-1", "user-b"), false);
assert.equal(harness.manager.listProposals("webhook", "chat-1", "user-b").length, 0);
// The board is globally visible: both users see the same proposal, tagged by scope.
assert.equal(harness.manager.listProposals("webhook", "chat-1", "user-b").length, 1);
assert.equal(harness.manager.listProposals("webhook", "chat-1", "user-a").length, 1);
assert.equal(await harness.manager.confirm("webhook", "chat-1", "user-a"), true);
assert.equal(harness.proposals.get(proposal.id)!.status, "working");
await harness.manager.prompt(request("hello owner", "user-a"));
const aPrompt = readLog(harness.logFile).find((entry) => entry.method === "session/prompt" && entry.text?.startsWith("[User message]\nhello owner"));
assert.ok(aPrompt?.text);
assert.match(aPrompt.text!, /scope=own/);
await harness.manager.prompt(request("hello again", "user-b"));
const bPrompt = readLog(harness.logFile).find((entry) => entry.method === "session/prompt" && entry.text?.startsWith("[User message]\nhello again"));
assert.ok(bPrompt?.text);
// Debugging phase: non-owners also see the live card, marked as another user's proposal.
assert.match(bPrompt.text!, /Test proposal/);
assert.match(bPrompt.text!, /another user's proposal/);
// The shared board tags the entry scope=other with a chat-type label; no raw user ID leaks.
assert.match(bPrompt.text!, /scope=other chat=direct/);
assert.doesNotMatch(bPrompt.text!, /user-a/);
assert.equal(await harness.manager.stop("webhook", "chat-1", "user-b"), false);
assert.equal(harness.proposals.get(proposal.id)!.status, "working");