fix: proposal threads disable the dead reply input (decide-only, INV-12) #16

Merged
benstull merged 1 commits from fix/proposal-canreply into main 2026-06-11 06:26:34 +00:00
2 changed files with 10 additions and 1 deletions
Showing only changes of commit 4c947acc43 - Show all commits
+9 -1
View File
@@ -22,6 +22,8 @@ export interface RenderedProposal {
id: string;
/** true → resolves exactly, decidable; false → stale/orphaned (INV-11). */
pending: boolean;
/** always false — proposals are decide-only, no reply input (INV-12). */
canReply: boolean;
turnId?: string;
range: { start: number; end: number };
}
@@ -252,6 +254,11 @@ export class ProposalController implements vscode.Disposable {
author: { name: proposal.author.id },
},
]);
// Proposals are decide-only (INV-12): ✓ accept / ✗ reject in the title
// bar. Without this, VS Code renders its default "Reply…" input with no
// submit command wired — a dead end. Discussion belongs in a regular
// coauthoring thread; proposal discussion trails are deferred (spec §1.7).
vsThread.canReply = false;
vsThread.label = pending
? "Pending proposal"
: "⚠ Stale proposal (target text changed or missing) — accept disabled";
@@ -315,12 +322,13 @@ export class ProposalController implements vscode.Disposable {
const state = this.docs.get(docPath);
if (!state) return [];
const out: RenderedProposal[] = [];
for (const [id] of state.vsThreads) {
for (const [id, vsThread] of state.vsThreads) {
const p = state.artifact.proposals.find((x) => x.id === id)!;
const off = state.live.get(id)!;
out.push({
id,
pending: !state.unresolved.has(id),
canReply: vsThread.canReply !== false,
turnId: p.turnId,
range: { start: off.start, end: off.end },
});
+1
View File
@@ -74,6 +74,7 @@ suite("F4 propose/accept (host E2E — programmatic ingress, no LLM)", () => {
assert.strictEqual(rendered.length, 1);
assert.strictEqual(rendered[0].pending, true);
assert.strictEqual(rendered[0].turnId, "turn-p1");
assert.strictEqual(rendered[0].canReply, false, "proposals are decide-only — no dead reply input (INV-12)");
const art = readSidecar();
assert.strictEqual(art.proposals.length, 1, "proposal persisted at propose time");
assert.strictEqual(art.anchors[art.proposals[0].anchorId].fingerprint.text, TARGET);