fix: proposal threads disable the reply input — decide-only, INV-12

Playing with F4, the operator hit VS Code's default "Reply…" box on a
proposal thread: no submit command is wired for cowriting.proposals, so
typed text had nowhere to go. Proposals are decide-only (accept/reject in
the title bar); discussion belongs in a coauthoring thread. Set
canReply=false so the dead input never renders, and expose canReply on the
test-facing RenderedProposal with an E2E assertion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-06-10 23:26:11 -07:00
parent 4651410136
commit 4c947acc43
2 changed files with 10 additions and 1 deletions
+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 },
});