From a3d0b6683603b3f4f46d55c4c5495766a15d9a74 Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 19:14:18 -0700 Subject: [PATCH 1/3] fix(gsd): mark note captures as executed in executeTriageResolutions Note captures (informational-only, no action needed) were never marked as Executed because loadActionableCaptures excluded them and there was no dedicated handler. They stayed in "resolved but not executed" limbo. Now marks note captures as executed immediately since the triage classification is their complete lifecycle. Closes #3578 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/triage-resolution.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/resources/extensions/gsd/triage-resolution.ts b/src/resources/extensions/gsd/triage-resolution.ts index 256091edf..791b7f1ad 100644 --- a/src/resources/extensions/gsd/triage-resolution.ts +++ b/src/resources/extensions/gsd/triage-resolution.ts @@ -510,6 +510,16 @@ export function executeTriageResolutions( } } + // Mark note captures as executed — they're informational only, no action + // needed. Without this they stay in "resolved but not executed" limbo (#3578). + const notes = loadAllCaptures(basePath).filter( + c => c.status === "resolved" && !c.executed && c.classification === "note", + ); + for (const cap of notes) { + markCaptureExecuted(basePath, cap.id); + result.actions.push(`Note acknowledged: ${cap.id} — "${cap.text}"`); + } + if (actionable.length === 0) return result; for (const capture of actionable) { From ad21e0de76269a745e20b74b8749864760d92c1e Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 22:24:43 -0700 Subject: [PATCH 2/3] test: add regression test for note captures execution in triage resolution Structural verification that classification === "note" filter and markCaptureExecuted call exist in executeTriageResolutions (#3578). Co-Authored-By: Claude Opus 4.6 (1M context) --- .../gsd/tests/note-captures-executed.test.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 src/resources/extensions/gsd/tests/note-captures-executed.test.ts diff --git a/src/resources/extensions/gsd/tests/note-captures-executed.test.ts b/src/resources/extensions/gsd/tests/note-captures-executed.test.ts new file mode 100644 index 000000000..60c0a7a65 --- /dev/null +++ b/src/resources/extensions/gsd/tests/note-captures-executed.test.ts @@ -0,0 +1,46 @@ +/** + * Regression test for #3578 — note captures marked as executed + * + * Note-classified captures were stuck in "resolved but not executed" limbo + * because executeTriageResolutions only handled inject/replan/defer. The fix + * adds a filter for classification === "note" and calls markCaptureExecuted + * for each matching capture. + * + * Structural verification test — reads source to confirm the note filter + * and markCaptureExecuted call exist. + */ + +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import { fileURLToPath } from 'node:url'; +import { dirname, join } from 'node:path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const source = readFileSync(join(__dirname, '..', 'triage-resolution.ts'), 'utf-8'); + +describe('note captures executed in triage resolution (#3578)', () => { + test('markCaptureExecuted is imported', () => { + assert.match(source, /markCaptureExecuted/, + 'markCaptureExecuted should be imported'); + }); + + test('note classification filter exists', () => { + assert.match(source, /classification\s*===\s*"note"/, + 'filter should check classification === "note"'); + }); + + test('note filter checks resolved status and not-executed', () => { + assert.match(source, /status\s*===\s*"resolved"\s*&&\s*!c\.executed\s*&&\s*c\.classification\s*===\s*"note"/, + 'filter should check resolved + not-executed + note classification'); + }); + + test('markCaptureExecuted is called for note captures', () => { + // The source should call markCaptureExecuted for note captures + const noteSection = source.slice(source.indexOf('classification === "note"')); + assert.match(noteSection, /markCaptureExecuted\(basePath,\s*cap\.id\)/, + 'markCaptureExecuted should be called for note captures'); + }); +}); From 267c89082369833a97e5d5f23b11843a2db68a59 Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 22:51:15 -0700 Subject: [PATCH 3/3] fix(test): update action count for note captures now included in results The fix marks note captures as executed and adds them to the actions list. Update the mixed classifications test to expect 3 actions instead of 2, since notes are no longer excluded. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/tests/triage-resolution.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/gsd/tests/triage-resolution.test.ts b/src/resources/extensions/gsd/tests/triage-resolution.test.ts index deb924347..0decf9e6f 100644 --- a/src/resources/extensions/gsd/tests/triage-resolution.test.ts +++ b/src/resources/extensions/gsd/tests/triage-resolution.test.ts @@ -387,7 +387,8 @@ test("resolution: executeTriageResolutions handles mixed classifications", () => assert.strictEqual(result.injected, 1, "should inject 1 task"); assert.strictEqual(result.replanned, 0); assert.strictEqual(result.quickTasks.length, 1, "should queue 1 quick-task"); - assert.strictEqual(result.actions.length, 2, "should have 2 action entries (note/defer excluded)"); + // inject + quick-task + note acknowledged = 3 actions (defer still excluded) + assert.strictEqual(result.actions.length, 3, "should have 3 action entries (defer excluded, note now included)"); } finally { rmSync(tmp, { recursive: true, force: true }); }