|
|
|
@@ -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 },
|
|
|
|
|
});
|
|
|
|
|