From 947ff2ba0a714600ddf0371fcf28cd30d75d2e9f Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 19:36:01 -0700 Subject: [PATCH 1/2] fix(gsd): default insertMilestone status to queued instead of active Hallucinated tool calls could auto-create phantom milestones as "active", hijacking the state machine. Observed: complete-slice issued a stray gsd_task_complete for M000, which was auto-created as active and promoted over the real M028. Changing the default from "active" to "queued" means phantom milestones from stray tool calls won't be promoted as active. Combined with the ghost detection fix (#3645), they'll be cleaned up automatically. Legitimate callers (plan-milestone, state reconciliation, md-importer) already pass status explicitly. Closes #3380 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/gsd-db.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/gsd/gsd-db.ts b/src/resources/extensions/gsd/gsd-db.ts index fdb53492a..933b3d32e 100644 --- a/src/resources/extensions/gsd/gsd-db.ts +++ b/src/resources/extensions/gsd/gsd-db.ts @@ -1119,7 +1119,9 @@ export function insertMilestone(m: { ).run({ ":id": m.id, ":title": m.title ?? "", - ":status": m.status ?? "active", + // Default to "queued" — never auto-create milestones as "active" (#3380). + // Callers that need "active" must pass it explicitly. + ":status": m.status ?? "queued", ":depends_on": JSON.stringify(m.depends_on ?? []), ":created_at": new Date().toISOString(), ":vision": m.planning?.vision ?? "", From 9ea3beab221c15bbda3e934c905557712777b49c Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Mon, 6 Apr 2026 22:25:44 -0700 Subject: [PATCH 2/2] test: add regression test for phantom milestone default queued status Co-Authored-By: Claude Opus 4.6 (1M context) --- .../phantom-milestone-default-queued.test.ts | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts diff --git a/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts b/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts new file mode 100644 index 000000000..97c12b4a3 --- /dev/null +++ b/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts @@ -0,0 +1,39 @@ +/** + * Regression test for #3695 — insertMilestone defaults status to "queued" + * + * Milestones were being auto-created with status "active", causing phantom + * milestones to appear as active work. The fix defaults to "queued" so + * new milestones must be explicitly activated. + */ + +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 dbSrc = readFileSync( + join(__dirname, '..', 'gsd-db.ts'), + 'utf-8', +); + +describe('insertMilestone defaults status to queued (#3695)', () => { + test('insertMilestone function exists', () => { + assert.match(dbSrc, /export function insertMilestone\(/, + 'insertMilestone should be exported from gsd-db.ts'); + }); + + test('default status is "queued" not "active"', () => { + // The status parameter should default to "queued" via nullish coalescing + assert.match(dbSrc, /m\.status\s*\?\?\s*"queued"/, + 'insertMilestone should default status to "queued"'); + }); + + test('comment explains the rationale', () => { + assert.match(dbSrc, /never auto-create milestones as "active"/i, + 'should have a comment explaining why default is queued'); + }); +});