gsd-pi 2.79.0 → 2.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +94 -47
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto/contracts.js +1 -0
  4. package/dist/resources/extensions/gsd/auto/orchestrator.js +146 -0
  5. package/dist/resources/extensions/gsd/auto/phases.js +61 -7
  6. package/dist/resources/extensions/gsd/auto/session.js +8 -0
  7. package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +2 -0
  9. package/dist/resources/extensions/gsd/auto-prompts.js +52 -29
  10. package/dist/resources/extensions/gsd/auto-recovery.js +63 -55
  11. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -0
  12. package/dist/resources/extensions/gsd/auto-start.js +3 -2
  13. package/dist/resources/extensions/gsd/auto.js +159 -2
  14. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  15. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -2
  16. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +41 -45
  17. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +8 -8
  18. package/dist/resources/extensions/gsd/commands/context.js +1 -1
  19. package/dist/resources/extensions/gsd/gsd-db.js +34 -1
  20. package/dist/resources/extensions/gsd/guided-flow.js +40 -0
  21. package/dist/resources/extensions/gsd/paths.js +5 -1
  22. package/dist/resources/extensions/gsd/post-execution-checks.js +25 -6
  23. package/dist/resources/extensions/gsd/preferences-types.js +20 -2
  24. package/dist/resources/extensions/gsd/preferences-validation.js +3 -3
  25. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +82 -2
  26. package/dist/resources/extensions/gsd/unit-context-composer.js +32 -0
  27. package/dist/resources/extensions/gsd/unit-context-manifest.js +21 -0
  28. package/dist/resources/extensions/gsd/uok/audit.js +23 -9
  29. package/dist/resources/extensions/gsd/uok/contracts.js +69 -1
  30. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +3 -0
  31. package/dist/resources/extensions/gsd/uok/loop-adapter.js +48 -33
  32. package/dist/resources/extensions/gsd/uok/timeline.js +125 -0
  33. package/dist/resources/extensions/shared/gsd-phase-state.js +45 -3
  34. package/dist/resources/extensions/shared/interview-ui.js +15 -4
  35. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  36. package/dist/web/standalone/.next/BUILD_ID +1 -1
  37. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  38. package/dist/web/standalone/.next/build-manifest.json +2 -2
  39. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  40. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/index.html +1 -1
  56. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  63. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  65. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  66. package/package.json +1 -1
  67. package/packages/daemon/package.json +2 -2
  68. package/packages/mcp-server/dist/workflow-tools.d.ts +1 -1
  69. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  70. package/packages/mcp-server/dist/workflow-tools.js +53 -0
  71. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  72. package/packages/mcp-server/package.json +2 -2
  73. package/packages/mcp-server/src/workflow-tools.test.ts +129 -2
  74. package/packages/mcp-server/src/workflow-tools.ts +81 -0
  75. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  76. package/packages/native/package.json +1 -1
  77. package/packages/pi-agent-core/package.json +1 -1
  78. package/packages/pi-ai/package.json +1 -1
  79. package/packages/pi-coding-agent/package.json +1 -1
  80. package/packages/pi-tui/package.json +1 -1
  81. package/packages/rpc-client/package.json +1 -1
  82. package/pkg/package.json +1 -1
  83. package/src/resources/extensions/gsd/auto/contracts.ts +87 -0
  84. package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -3
  85. package/src/resources/extensions/gsd/auto/orchestrator.ts +161 -0
  86. package/src/resources/extensions/gsd/auto/phases.ts +88 -9
  87. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  88. package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
  89. package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
  90. package/src/resources/extensions/gsd/auto-prompts.ts +106 -28
  91. package/src/resources/extensions/gsd/auto-recovery.ts +59 -53
  92. package/src/resources/extensions/gsd/auto-runtime-state.ts +7 -0
  93. package/src/resources/extensions/gsd/auto-start.ts +3 -2
  94. package/src/resources/extensions/gsd/auto.ts +167 -1
  95. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +14 -1
  96. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -2
  97. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +49 -46
  98. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-shouldblock-basepath.test.ts +97 -0
  99. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +8 -4
  100. package/src/resources/extensions/gsd/commands/context.ts +1 -1
  101. package/src/resources/extensions/gsd/gsd-db.ts +35 -1
  102. package/src/resources/extensions/gsd/guided-flow.ts +47 -0
  103. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  104. package/src/resources/extensions/gsd/paths.ts +6 -1
  105. package/src/resources/extensions/gsd/post-execution-checks.ts +31 -6
  106. package/src/resources/extensions/gsd/preferences-types.ts +23 -4
  107. package/src/resources/extensions/gsd/preferences-validation.ts +3 -3
  108. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +32 -0
  109. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +353 -0
  110. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +108 -1
  111. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +39 -0
  112. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +3 -0
  113. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -2
  114. package/src/resources/extensions/gsd/tests/check-auto-start-pending-gate.test.ts +203 -0
  115. package/src/resources/extensions/gsd/tests/check-auto-start-ready-guard.test.ts +148 -0
  116. package/src/resources/extensions/gsd/tests/current-directory-root-homedir-fallback.test.ts +63 -0
  117. package/src/resources/extensions/gsd/tests/deep-planning-mode-dispatch.test.ts +42 -0
  118. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +63 -2
  119. package/src/resources/extensions/gsd/tests/execute-summary-save-empty-project.test.ts +109 -0
  120. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +95 -0
  121. package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +14 -0
  122. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +79 -0
  123. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +134 -0
  124. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +8 -0
  125. package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +2 -0
  126. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +27 -0
  127. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +46 -0
  128. package/src/resources/extensions/gsd/tests/pre-exec-gate-loop.test.ts +3 -0
  129. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +85 -0
  130. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +2 -0
  131. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +59 -0
  132. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +38 -0
  133. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +32 -0
  134. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +109 -1
  135. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +98 -0
  136. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +132 -3
  137. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +3 -0
  138. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +84 -1
  139. package/src/resources/extensions/gsd/unit-context-composer.ts +49 -0
  140. package/src/resources/extensions/gsd/unit-context-manifest.ts +34 -0
  141. package/src/resources/extensions/gsd/uok/audit.ts +25 -9
  142. package/src/resources/extensions/gsd/uok/contracts.ts +105 -0
  143. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +4 -0
  144. package/src/resources/extensions/gsd/uok/loop-adapter.ts +60 -45
  145. package/src/resources/extensions/gsd/uok/timeline.ts +158 -0
  146. package/src/resources/extensions/shared/gsd-phase-state.ts +56 -3
  147. package/src/resources/extensions/shared/interview-ui.ts +18 -5
  148. package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +43 -1
  149. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +41 -0
  150. /package/dist/web/standalone/.next/static/{J-CU-p_sp45CJHT3R9TJS → V-3Ehy4B24f9FCGiLPWIM}/_buildManifest.js +0 -0
  151. /package/dist/web/standalone/.next/static/{J-CU-p_sp45CJHT3R9TJS → V-3Ehy4B24f9FCGiLPWIM}/_ssgManifest.js +0 -0
@@ -17,6 +17,7 @@ import {
17
17
  resolveGsdRootFile, relGsdRootFile, resolveRuntimeFile,
18
18
  } from "./paths.js";
19
19
  import { resolveSkillDiscoveryMode, resolveInlineLevel, loadEffectiveGSDPreferences, resolveAllSkillReferences } from "./preferences.js";
20
+ import { isContextModeEnabled } from "./preferences-types.js";
20
21
  import { parseRoadmap } from "./parsers-legacy.js";
21
22
  import type { GSDState, InlineLevel } from "./types.js";
22
23
  import type { GSDPreferences } from "./preferences.js";
@@ -33,7 +34,7 @@ import {
33
34
  } from "./gate-registry.js";
34
35
  import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
35
36
  import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
36
- import { composeInlinedContext, type ArtifactResolver } from "./unit-context-composer.js";
37
+ import { composeContextModeInstructions, composeInlinedContext, type ArtifactResolver, type ContextModeRenderMode } from "./unit-context-composer.js";
37
38
  import { logWarning } from "./workflow-logger.js";
38
39
  import { inlineGraphSubgraph } from "./graph-context.js";
39
40
  import { buildExtractionStepsBlock } from "./commands-extract-learnings.js";
@@ -89,6 +90,30 @@ function capPreamble(preamble: string): string {
89
90
  return truncateAtSectionBoundary(preamble, budget).content;
90
91
  }
91
92
 
93
+ function renderContextModeForPrompt(
94
+ unitType: string,
95
+ base: string,
96
+ renderMode: ContextModeRenderMode = "standalone",
97
+ ): string {
98
+ const effectivePrefs = loadEffectiveGSDPreferences(base)?.preferences;
99
+ return composeContextModeInstructions(unitType, {
100
+ enabled: isContextModeEnabled(effectivePrefs),
101
+ renderMode,
102
+ });
103
+ }
104
+
105
+ function prependContextModeToBlock(
106
+ unitType: string,
107
+ base: string,
108
+ block: string,
109
+ renderMode: ContextModeRenderMode = "standalone",
110
+ ): string {
111
+ const contextMode = renderContextModeForPrompt(unitType, base, renderMode);
112
+ if (!contextMode) return block;
113
+ if (!block.trim()) return contextMode;
114
+ return `${contextMode}\n\n${block}`;
115
+ }
116
+
92
117
  // ─── Executor Constraints ─────────────────────────────────────────────────────
93
118
 
94
119
  /**
@@ -1266,6 +1291,7 @@ export async function buildDiscussMilestonePrompt(
1266
1291
  structuredQuestionsAvailable = "false",
1267
1292
  ): Promise<string> {
1268
1293
  const discussTemplates = inlineTemplate("context", "Context");
1294
+ const contextModeInstructions = renderContextModeForPrompt("discuss-milestone", base);
1269
1295
 
1270
1296
  const basePrompt = loadPrompt("guided-discuss-milestone", {
1271
1297
  workingDirectory: base,
@@ -1276,16 +1302,17 @@ export async function buildDiscussMilestonePrompt(
1276
1302
  commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1277
1303
  fastPathInstruction: "",
1278
1304
  });
1305
+ const promptWithContextMode = prependContextModeToBlock("discuss-milestone", base, basePrompt);
1279
1306
 
1280
1307
  // If a CONTEXT-DRAFT.md exists, append it as seed material
1281
1308
  const draftPath = resolveMilestoneFile(base, mid, "CONTEXT-DRAFT");
1282
1309
  const draftContent = draftPath ? await loadFile(draftPath) : null;
1283
1310
 
1284
1311
  if (draftContent) {
1285
- return `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\nThe following draft was captured from a prior multi-milestone discussion. Use it as seed material — the user has already provided this context. Start with a brief reflection on what the draft covers, then probe for any gaps or open questions before writing the full CONTEXT.md.\n\n${draftContent}`;
1312
+ return `${promptWithContextMode}\n\n## Prior Discussion (Draft Seed)\n\nThe following draft was captured from a prior multi-milestone discussion. Use it as seed material — the user has already provided this context. Start with a brief reflection on what the draft covers, then probe for any gaps or open questions before writing the full CONTEXT.md.\n\n${draftContent}`;
1286
1313
  }
1287
1314
 
1288
- return basePrompt;
1315
+ return contextModeInstructions ? promptWithContextMode : basePrompt;
1289
1316
  }
1290
1317
 
1291
1318
  /**
@@ -1298,10 +1325,10 @@ export async function buildWorkflowPreferencesPrompt(
1298
1325
  base: string,
1299
1326
  structuredQuestionsAvailable = "false",
1300
1327
  ): Promise<string> {
1301
- return loadPrompt("guided-workflow-preferences", {
1328
+ return prependContextModeToBlock("workflow-preferences", base, loadPrompt("guided-workflow-preferences", {
1302
1329
  workingDirectory: base,
1303
1330
  structuredQuestionsAvailable,
1304
- });
1331
+ }));
1305
1332
  }
1306
1333
 
1307
1334
  /**
@@ -1315,10 +1342,10 @@ export async function buildResearchProjectPrompt(
1315
1342
  base: string,
1316
1343
  structuredQuestionsAvailable = "false",
1317
1344
  ): Promise<string> {
1318
- return loadPrompt("guided-research-project", {
1345
+ return prependContextModeToBlock("research-project", base, loadPrompt("guided-research-project", {
1319
1346
  workingDirectory: base,
1320
1347
  structuredQuestionsAvailable,
1321
- });
1348
+ }));
1322
1349
  }
1323
1350
 
1324
1351
  /**
@@ -1331,10 +1358,10 @@ export async function buildResearchDecisionPrompt(
1331
1358
  base: string,
1332
1359
  structuredQuestionsAvailable = "false",
1333
1360
  ): Promise<string> {
1334
- return loadPrompt("guided-research-decision", {
1361
+ return prependContextModeToBlock("research-decision", base, loadPrompt("guided-research-decision", {
1335
1362
  workingDirectory: base,
1336
1363
  structuredQuestionsAvailable,
1337
- });
1364
+ }));
1338
1365
  }
1339
1366
 
1340
1367
  /**
@@ -1349,12 +1376,12 @@ export async function buildDiscussProjectPrompt(
1349
1376
  ): Promise<string> {
1350
1377
  const inlinedTemplates = inlineTemplate("project", "Project");
1351
1378
 
1352
- return loadPrompt("guided-discuss-project", {
1379
+ return prependContextModeToBlock("discuss-project", base, loadPrompt("guided-discuss-project", {
1353
1380
  workingDirectory: base,
1354
1381
  inlinedTemplates,
1355
1382
  structuredQuestionsAvailable,
1356
1383
  commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1357
- });
1384
+ }));
1358
1385
  }
1359
1386
 
1360
1387
  /**
@@ -1369,12 +1396,12 @@ export async function buildDiscussRequirementsPrompt(
1369
1396
  ): Promise<string> {
1370
1397
  const inlinedTemplates = inlineTemplate("requirements", "Requirements");
1371
1398
 
1372
- return loadPrompt("guided-discuss-requirements", {
1399
+ return prependContextModeToBlock("discuss-requirements", base, loadPrompt("guided-discuss-requirements", {
1373
1400
  workingDirectory: base,
1374
1401
  inlinedTemplates,
1375
1402
  structuredQuestionsAvailable,
1376
1403
  commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
1377
- });
1404
+ }));
1378
1405
  }
1379
1406
 
1380
1407
  export async function buildResearchMilestonePrompt(mid: string, midTitle: string, base: string): Promise<string> {
@@ -1428,7 +1455,11 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
1428
1455
  if (knowledgeInlineRM) parts.push(knowledgeInlineRM);
1429
1456
  }
1430
1457
 
1431
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`);
1458
+ const inlinedContext = prependContextModeToBlock(
1459
+ "research-milestone",
1460
+ base,
1461
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`),
1462
+ );
1432
1463
 
1433
1464
  const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
1434
1465
  return loadPrompt("research-milestone", {
@@ -1501,7 +1532,11 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
1501
1532
  inlined.push(inlineTemplate("task-plan", "Task Plan"));
1502
1533
  }
1503
1534
 
1504
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
1535
+ const inlinedContext = prependContextModeToBlock(
1536
+ "plan-milestone",
1537
+ base,
1538
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`),
1539
+ );
1505
1540
 
1506
1541
  const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
1507
1542
  const researchOutputPath = join(base, relMilestoneFile(base, mid, "RESEARCH"));
@@ -1530,6 +1565,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
1530
1565
 
1531
1566
  export async function buildResearchSlicePrompt(
1532
1567
  mid: string, _midTitle: string, sid: string, sTitle: string, base: string,
1568
+ options?: { contextModeRenderMode?: ContextModeRenderMode },
1533
1569
  ): Promise<string> {
1534
1570
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
1535
1571
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
@@ -1582,7 +1618,12 @@ export async function buildResearchSlicePrompt(
1582
1618
  const overridesInline = formatOverridesSection(activeOverrides);
1583
1619
  if (overridesInline) inlined.unshift(overridesInline);
1584
1620
 
1585
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
1621
+ const inlinedContext = prependContextModeToBlock(
1622
+ "research-slice",
1623
+ base,
1624
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`),
1625
+ options?.contextModeRenderMode,
1626
+ );
1586
1627
 
1587
1628
  const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
1588
1629
  return loadPrompt("research-slice", {
@@ -1629,6 +1670,7 @@ async function renderSlicePrompt(options: {
1629
1670
  sessionContextWindow?: number;
1630
1671
  modelRegistry?: MinimalModelRegistry;
1631
1672
  sessionProvider?: string;
1673
+ contextModeRenderMode?: ContextModeRenderMode;
1632
1674
  }): Promise<string> {
1633
1675
  const {
1634
1676
  mid, sid, sTitle, base, level, promptTemplate, prependBlocks = [], extraVars = {},
@@ -1684,7 +1726,12 @@ async function renderSlicePrompt(options: {
1684
1726
  const overridesInline = formatOverridesSection(await loadActiveOverrides(base));
1685
1727
  if (overridesInline) inlined.unshift(overridesInline);
1686
1728
 
1687
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
1729
+ const inlinedContext = prependContextModeToBlock(
1730
+ promptTemplate,
1731
+ base,
1732
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`),
1733
+ options.contextModeRenderMode,
1734
+ );
1688
1735
  const executorContextConstraints = formatExecutorConstraints(sessionContextWindow, modelRegistry, sessionProvider);
1689
1736
  const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
1690
1737
  const commitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
@@ -1820,6 +1867,8 @@ export interface ExecuteTaskPromptOptions {
1820
1867
  modelRegistry?: MinimalModelRegistry;
1821
1868
  /** Session model provider, used for provider-specific effective context windows. */
1822
1869
  sessionProvider?: string;
1870
+ /** Render compact Context Mode guidance when embedded inside another prompt. */
1871
+ contextModeRenderMode?: ContextModeRenderMode;
1823
1872
  }
1824
1873
 
1825
1874
  export async function buildExecuteTaskPrompt(
@@ -1962,6 +2011,7 @@ export async function buildExecuteTaskPrompt(
1962
2011
  getGatesForTurn("execute-task"),
1963
2012
  { pending: new Set(etPending.map((g) => g.gate_id)), allowOmit: true },
1964
2013
  );
2014
+ phaseAnchorSection = prependContextModeToBlock("execute-task", base, phaseAnchorSection, opts.contextModeRenderMode);
1965
2015
 
1966
2016
  return loadPrompt("execute-task", {
1967
2017
  overridesSection,
@@ -1990,6 +2040,7 @@ export async function buildExecuteTaskPrompt(
1990
2040
  taskTitle: tTitle,
1991
2041
  taskPlanContent,
1992
2042
  extraContext: [taskPlanInline, slicePlanExcerpt, finalCarryForward, resumeSection],
2043
+ unitType: "execute-task",
1993
2044
  }),
1994
2045
  });
1995
2046
  }
@@ -2088,7 +2139,11 @@ export async function buildCompleteSlicePrompt(
2088
2139
  ? `${completeOverridesInline}\n\n---\n\n${body}`
2089
2140
  : body;
2090
2141
 
2091
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${finalBody}`);
2142
+ const inlinedContext = prependContextModeToBlock(
2143
+ "complete-slice",
2144
+ base,
2145
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${finalBody}`),
2146
+ );
2092
2147
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
2093
2148
 
2094
2149
  const sliceRel = relSlicePath(base, mid, sid);
@@ -2187,7 +2242,11 @@ export async function buildCompleteMilestonePrompt(
2187
2242
  if (contextInline) inlined.push(contextInline);
2188
2243
  inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
2189
2244
 
2190
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
2245
+ const inlinedContext = prependContextModeToBlock(
2246
+ "complete-milestone",
2247
+ base,
2248
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`),
2249
+ );
2191
2250
 
2192
2251
  const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
2193
2252
 
@@ -2324,7 +2383,11 @@ export async function buildValidateMilestonePrompt(
2324
2383
  const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
2325
2384
  if (contextInline) inlined.push(contextInline);
2326
2385
 
2327
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
2386
+ const inlinedContext = prependContextModeToBlock(
2387
+ "validate-milestone",
2388
+ base,
2389
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`),
2390
+ );
2328
2391
 
2329
2392
  const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
2330
2393
  const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
@@ -2400,7 +2463,11 @@ export async function buildReplanSlicePrompt(
2400
2463
  const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
2401
2464
  if (replanOverridesInline) inlined.unshift(replanOverridesInline);
2402
2465
 
2403
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
2466
+ const inlinedContext = prependContextModeToBlock(
2467
+ "replan-slice",
2468
+ base,
2469
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`),
2470
+ );
2404
2471
 
2405
2472
  const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
2406
2473
 
@@ -2474,7 +2541,11 @@ export async function buildRunUatPrompt(
2474
2541
  };
2475
2542
 
2476
2543
  const composed = await composeInlinedContext("run-uat", resolveArtifact);
2477
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${composed}`);
2544
+ const inlinedContext = prependContextModeToBlock(
2545
+ "run-uat",
2546
+ base,
2547
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${composed}`),
2548
+ );
2478
2549
 
2479
2550
  const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "ASSESSMENT"));
2480
2551
  const uatType = getUatType(uatContent);
@@ -2548,7 +2619,11 @@ export async function buildReassessRoadmapPrompt(
2548
2619
  const knowledgeInlineRA = await inlineKnowledgeBudgeted(base, extractKeywords(midTitle));
2549
2620
  if (knowledgeInlineRA) parts.push(knowledgeInlineRA);
2550
2621
 
2551
- const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`);
2622
+ const inlinedContext = prependContextModeToBlock(
2623
+ "reassess-roadmap",
2624
+ base,
2625
+ capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${parts.join("\n\n---\n\n")}`),
2626
+ );
2552
2627
 
2553
2628
  const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
2554
2629
 
@@ -2641,6 +2716,7 @@ export async function buildReactiveExecutePrompt(
2641
2716
  sessionContextWindow: opts?.sessionContextWindow,
2642
2717
  modelRegistry: opts?.modelRegistry,
2643
2718
  sessionProvider: opts?.sessionProvider,
2719
+ contextModeRenderMode: "nested",
2644
2720
  },
2645
2721
  );
2646
2722
 
@@ -2664,7 +2740,7 @@ export async function buildReactiveExecutePrompt(
2664
2740
  milestoneTitle: midTitle,
2665
2741
  sliceId: sid,
2666
2742
  sliceTitle: sTitle,
2667
- graphContext,
2743
+ graphContext: prependContextModeToBlock("reactive-execute", base, graphContext),
2668
2744
  readyTaskCount: String(readyTaskIds.length),
2669
2745
  readyTaskList: readyTaskListLines.join("\n"),
2670
2746
  subagentPrompts: subagentSections.join("\n\n---\n\n"),
@@ -2730,7 +2806,7 @@ export async function buildParallelResearchSlicesPrompt(
2730
2806
  const subagentSections: string[] = [];
2731
2807
  const modelSuffix = subagentModel ? ` with model: "${subagentModel}"` : "";
2732
2808
  for (const slice of slices) {
2733
- const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath);
2809
+ const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath, { contextModeRenderMode: "nested" });
2734
2810
  subagentSections.push([
2735
2811
  `### ${slice.id}: ${slice.title}`,
2736
2812
  "",
@@ -2786,6 +2862,8 @@ export async function buildGateEvaluatePrompt(
2786
2862
  gateListLines.push(`- **${def.id}**: ${def.question}`);
2787
2863
 
2788
2864
  const subPrompt = [
2865
+ renderContextModeForPrompt("gate-evaluate", base, "nested"),
2866
+ "",
2789
2867
  `You are evaluating quality gate **${def.id}** for slice ${sid} (${sTitle}).`,
2790
2868
  "",
2791
2869
  `**Working directory:** \`${normalizedBase}\`. All file reads, writes, and shell commands MUST operate relative to this directory. Do NOT \`cd\` to any other directory.`,
@@ -2828,7 +2906,7 @@ export async function buildGateEvaluatePrompt(
2828
2906
  milestoneTitle: midTitle,
2829
2907
  sliceId: sid,
2830
2908
  sliceTitle: sTitle,
2831
- slicePlanContent: planContent,
2909
+ slicePlanContent: prependContextModeToBlock("gate-evaluate", base, planContent),
2832
2910
  gateCount: String(pending.length),
2833
2911
  gateList: gateListLines.join("\n"),
2834
2912
  subagentPrompts: subagentSections.join("\n\n---\n\n"),
@@ -2905,7 +2983,7 @@ export async function buildRewriteDocsPrompt(
2905
2983
 
2906
2984
  const documentList = docList.length > 0 ? docList.join("\n") : "- No active plan documents found.";
2907
2985
 
2908
- return loadPrompt("rewrite-docs", {
2986
+ return prependContextModeToBlock("rewrite-docs", base, loadPrompt("rewrite-docs", {
2909
2987
  workingDirectory: base,
2910
2988
  milestoneId: mid,
2911
2989
  milestoneTitle: midTitle,
@@ -2914,5 +2992,5 @@ export async function buildRewriteDocsPrompt(
2914
2992
  overrideContent,
2915
2993
  documentList,
2916
2994
  overridesPath: relGsdRootFile("OVERRIDES"),
2917
- });
2995
+ }));
2918
2996
  }
@@ -14,7 +14,7 @@ import { appendEvent } from "./workflow-events.js";
14
14
  import { atomicWriteSync } from "./atomic-write.js";
15
15
  import { clearParseCache } from "./files.js";
16
16
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
17
- import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone } from "./gsd-db.js";
17
+ import { isDbAvailable, getTask, getSlice, getSliceTasks, getPendingGates, updateTaskStatus, updateSliceStatus, insertSlice, getMilestone, refreshOpenDatabaseFromDisk } from "./gsd-db.js";
18
18
  import { isValidationTerminal } from "./state.js";
19
19
  import { getErrorMessage } from "./error-utils.js";
20
20
  import { logWarning, logError } from "./workflow-logger.js";
@@ -308,7 +308,7 @@ function scanGsdTaggedCommits(
308
308
  if (!commitMessageHasGsdTrailer(message)) continue;
309
309
 
310
310
  const commitFiles = getChangedFilesForCommit(basePath, hash);
311
- if (!commitMatchesMilestone(message, milestoneId, commitFiles)) continue;
311
+ if (!commitMatchesMilestone(basePath, message, milestoneId, commitFiles)) continue;
312
312
 
313
313
  matched = true;
314
314
  for (const file of commitFiles) {
@@ -336,22 +336,36 @@ function commitMessageHasGsdTrailer(message: string): boolean {
336
336
  return /^GSD-(?:Task|Unit):\s*\S+/m.test(message);
337
337
  }
338
338
 
339
- function commitMatchesMilestone(message: string, milestoneId: string, files: readonly string[]): boolean {
339
+ function commitMatchesMilestone(basePath: string, message: string, milestoneId: string, files: readonly string[]): boolean {
340
340
  if (commitTrailerStartsWithMilestone(message, milestoneId)) return true;
341
341
 
342
342
  // Meaningful execute-task commits currently store task scope as Sxx/Tyy
343
343
  // rather than Mxx/Sxx/Tyy. Bind those commits back to the milestone when
344
344
  // either the commit touched this milestone's artifacts, or — for projects
345
345
  // where .gsd/ is gitignored/external (#5033) — the message explicitly
346
- // names the milestone.
346
+ // names the milestone or local GSD state proves the task belongs here.
347
347
  if (/^GSD-Task:\s*S[^/\s]+\/T\S+/m.test(message)) {
348
348
  if (files.some((file) => isMilestoneArtifactPath(file, milestoneId))) return true;
349
349
  if (commitMessageMentionsMilestone(message, milestoneId)) return true;
350
+ if (commitTaskTrailerBelongsToMilestone(basePath, message, milestoneId)) return true;
350
351
  }
351
352
 
352
353
  return false;
353
354
  }
354
355
 
356
+ function commitTaskTrailerBelongsToMilestone(basePath: string, message: string, milestoneId: string): boolean {
357
+ const match = message.match(/^GSD-Task:\s*(S[^/\s]+)\/(T[^\s]+)/m);
358
+ if (!match) return false;
359
+ const [, sliceId, taskId] = match;
360
+
361
+ if (getTask(milestoneId, sliceId, taskId)) return true;
362
+
363
+ const tasksDir = resolveTasksDir(basePath, milestoneId, sliceId);
364
+ if (!tasksDir) return false;
365
+ return existsSync(join(tasksDir, `${taskId}-PLAN.md`))
366
+ || existsSync(join(tasksDir, `${taskId}-SUMMARY.md`));
367
+ }
368
+
355
369
  function commitMessageMentionsMilestone(message: string, milestoneId: string): boolean {
356
370
  if (!MILESTONE_ID_RE.test(milestoneId)) return false;
357
371
 
@@ -574,65 +588,32 @@ export function verifyExpectedArtifact(
574
588
  }
575
589
  }
576
590
 
577
- // plan-slice must produce a plan with actual task entries, not just a scaffold.
578
- // The plan file may exist from a prior discussion/context step with only headings
579
- // but no tasks. Without this check the artifact is considered "complete" and the
580
- // unit gets skipped — but deriveState still returns phase:"planning" because the
581
- // plan has no tasks, creating an infinite skip loop (#699).
582
- if (unitType === "plan-slice") {
583
- const planContent = readFileSync(absPath, "utf-8");
584
- // Accept checkbox-style (- [x] **T01: ...) or heading-style (### T01 -- / ### T01: / ### T01 —)
585
- const hasCheckboxTask = /^- \[[xX ]\] \*\*T\d+:/m.test(planContent);
586
- const hasHeadingTask = /^#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
587
- if (!hasCheckboxTask && !hasHeadingTask) {
588
- logWarning("recovery", `verify-fail ${unitType} ${unitId}: plan has no task checkbox/heading (len=${planContent.length}) at ${absPath}`);
589
- return false;
590
- }
591
- }
592
-
593
- // execute-task: DB status is authoritative. Fall back to checked-checkbox
594
- // detection when the DB is unavailable (unmigrated projects), or when the
595
- // disk artifacts already reflect completion but the DB replay is one beat
596
- // behind the completion write.
597
- if (unitType === "execute-task") {
598
- const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
599
- if (mid && sid && tid) {
600
- const dbTask = getTask(mid, sid, tid);
601
- if (dbTask) {
602
- if (dbTask.status !== "complete" && dbTask.status !== "done" && !hasCheckedTaskCompletionOnDisk(base, mid, sid, tid)) {
603
- return false;
604
- }
605
- } else if (!isDbAvailable()) {
606
- // LEGACY: Pre-migration fallback for projects without DB.
607
- // Require a CHECKED checkbox — a bare heading or unchecked checkbox
608
- // does not prove gsd_complete_task ran. Summary file on disk alone
609
- // is not sufficient evidence (could be a rogue write) (#3607).
610
- if (!hasCheckedTaskCompletionOnDisk(base, mid, sid, tid)) return false;
611
- } else {
612
- // DB available but task row not found — completion tool never ran (#3607)
613
- return false;
614
- }
615
- }
616
- }
617
-
618
- // plan-slice must also produce individual task plan files for every task listed
619
- // in the slice plan. Without this check, a plan-slice that wrote S{sid}-PLAN.md
620
- // but omitted T{tid}-PLAN.md files would be marked complete, causing execute-task
621
- // to dispatch with a missing task plan (see issue #739).
591
+ // plan-slice verification is DB-primary. The slice plan is a projection, so
592
+ // DB task rows prove the slice was planned even if the rendered markdown no
593
+ // longer uses legacy checkbox/heading syntax.
622
594
  if (unitType === "plan-slice") {
623
595
  const { milestone: mid, slice: sid } = parseUnitId(unitId);
624
596
  if (mid && sid) {
625
597
  try {
626
- // DB primary path — get task IDs to verify task plan files exist
627
598
  let taskIds: string[] | null = null;
628
599
  if (isDbAvailable()) {
629
- const tasks = getSliceTasks(mid, sid);
630
- if (tasks.length > 0) taskIds = tasks.map(t => t.id);
600
+ const refreshed = refreshOpenDatabaseFromDisk();
601
+ if (refreshed) {
602
+ const tasks = getSliceTasks(mid, sid);
603
+ if (tasks.length > 0) taskIds = tasks.map(t => t.id);
604
+ }
631
605
  }
632
606
 
633
607
  if (!taskIds) {
634
- // LEGACY: DB unavailable or no tasks in DB parse plan file for task IDs
608
+ // LEGACY: DB unavailable or no tasks in DB. Require actual task
609
+ // entries so an empty scaffold cannot advance the pipeline (#699).
635
610
  const planContent = readFileSync(absPath, "utf-8");
611
+ const hasCheckboxTask = /^\s*- \[[xX ]\] \*\*T\d+:/m.test(planContent);
612
+ const hasHeadingTask = /^\s*#{2,4}\s+T\d+\s*(?:--|—|:)/m.test(planContent);
613
+ if (!hasCheckboxTask && !hasHeadingTask) {
614
+ logWarning("recovery", `verify-fail ${unitType} ${unitId}: plan has no task checkbox/heading (len=${planContent.length}) at ${absPath}`);
615
+ return false;
616
+ }
636
617
  const plan = parseLegacyPlan(planContent);
637
618
  if (plan.tasks.length > 0) taskIds = plan.tasks.map((t: { id: string }) => t.id);
638
619
  }
@@ -658,6 +639,31 @@ export function verifyExpectedArtifact(
658
639
  }
659
640
  }
660
641
 
642
+ // execute-task: DB status is authoritative. Fall back to checked-checkbox
643
+ // detection when the DB is unavailable (unmigrated projects), or when the
644
+ // disk artifacts already reflect completion but the DB replay is one beat
645
+ // behind the completion write.
646
+ if (unitType === "execute-task") {
647
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
648
+ if (mid && sid && tid) {
649
+ const dbTask = getTask(mid, sid, tid);
650
+ if (dbTask) {
651
+ if (dbTask.status !== "complete" && dbTask.status !== "done" && !hasCheckedTaskCompletionOnDisk(base, mid, sid, tid)) {
652
+ return false;
653
+ }
654
+ } else if (!isDbAvailable()) {
655
+ // LEGACY: Pre-migration fallback for projects without DB.
656
+ // Require a CHECKED checkbox — a bare heading or unchecked checkbox
657
+ // does not prove gsd_complete_task ran. Summary file on disk alone
658
+ // is not sufficient evidence (could be a rogue write) (#3607).
659
+ if (!hasCheckedTaskCompletionOnDisk(base, mid, sid, tid)) return false;
660
+ } else {
661
+ // DB available but task row not found — completion tool never ran (#3607)
662
+ return false;
663
+ }
664
+ }
665
+ }
666
+
661
667
  // complete-slice: DB status is authoritative for whether the slice is done.
662
668
  // Fall back to file-based check (roadmap [x]) when DB is unavailable.
663
669
  if (unitType === "complete-slice") {
@@ -16,14 +16,21 @@ export type AutoRuntimeSnapshot = {
16
16
  paused: boolean;
17
17
  currentUnit: CurrentUnit | null;
18
18
  basePath: string;
19
+ orchestrationPhase?: "idle" | "running" | "paused" | "stopped" | "error";
20
+ orchestrationTransitionCount?: number;
21
+ orchestrationLastTransitionAt?: number;
19
22
  };
20
23
 
21
24
  export function getAutoRuntimeSnapshot(): AutoRuntimeSnapshot {
25
+ const orchestrationStatus = autoSession.orchestration?.getStatus();
22
26
  return {
23
27
  active: autoSession.active,
24
28
  paused: autoSession.paused,
25
29
  currentUnit: autoSession.currentUnit ? { ...autoSession.currentUnit } : null,
26
30
  basePath: autoSession.basePath,
31
+ orchestrationPhase: orchestrationStatus?.phase,
32
+ orchestrationTransitionCount: orchestrationStatus?.transitionCount,
33
+ orchestrationLastTransitionAt: orchestrationStatus?.lastTransitionAt,
27
34
  };
28
35
  }
29
36
 
@@ -663,9 +663,10 @@ export async function bootstrapAutoSession(
663
663
  hasSurvivorBranch = false;
664
664
  }
665
665
 
666
+ const effectivePrefs = loadEffectiveGSDPreferences(base)?.preferences;
666
667
  const deepProjectStagePending = !hasSurvivorBranch
667
668
  ? (await import("./auto-dispatch.js")).hasPendingDeepStage(
668
- loadEffectiveGSDPreferences(base)?.preferences,
669
+ effectivePrefs,
669
670
  base,
670
671
  )
671
672
  : false;
@@ -714,7 +715,7 @@ export async function bootstrapAutoSession(
714
715
  const mid = state.activeMilestone!.id;
715
716
  const contextFile = resolveMilestoneFile(base, mid, "CONTEXT");
716
717
  const hasContext = !!(contextFile && (await loadFile(contextFile)));
717
- if (!hasContext) {
718
+ if (!hasContext && effectivePrefs?.planning_depth !== "deep") {
718
719
  const { showSmartEntry } = await import("./guided-flow.js");
719
720
  await showSmartEntry(ctx, pi, base, { step: requestedStepMode });
720
721