gsd-pi 2.68.1-dev.c1497ab → 2.68.1

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 (172) hide show
  1. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +5 -1
  2. package/dist/resources/extensions/gsd/guided-flow.js +70 -25
  3. package/dist/resources/extensions/gsd/model-router.js +2 -32
  4. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  5. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  6. package/dist/resources/extensions/gsd/prompts/discuss.md +0 -2
  7. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  8. package/dist/resources/extensions/gsd/templates/context.md +2 -34
  9. package/dist/web/standalone/.next/BUILD_ID +1 -1
  10. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  11. package/dist/web/standalone/.next/build-manifest.json +3 -3
  12. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  13. package/dist/web/standalone/.next/required-server-files.json +3 -3
  14. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  15. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  25. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  35. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  36. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  37. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  38. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  39. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  41. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  53. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  73. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  74. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  83. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  89. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  109. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/index.html +1 -1
  119. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  120. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  121. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  122. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  124. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/page.js +2 -2
  126. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  128. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  129. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  130. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/middleware.js +2 -2
  132. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  134. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  135. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  136. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  137. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  138. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  139. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +1 -0
  140. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  141. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  142. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  143. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  144. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  145. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  146. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  147. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  148. package/dist/web/standalone/server.js +1 -1
  149. package/package.json +1 -1
  150. package/src/resources/extensions/gsd/auto-model-selection.ts +3 -1
  151. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +5 -1
  152. package/src/resources/extensions/gsd/guided-flow.ts +84 -22
  153. package/src/resources/extensions/gsd/model-router.ts +10 -44
  154. package/src/resources/extensions/gsd/preferences-types.ts +1 -3
  155. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  156. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  157. package/src/resources/extensions/gsd/prompts/discuss.md +0 -2
  158. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  159. package/src/resources/extensions/gsd/templates/context.md +2 -34
  160. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  161. package/src/resources/extensions/gsd/tests/capability-router.test.ts +7 -31
  162. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  163. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  164. package/src/resources/extensions/gsd/tests/model-router.test.ts +2 -2
  165. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  166. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  167. package/src/resources/extensions/gsd/tests/write-gate.test.ts +16 -13
  168. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +0 -1
  169. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  170. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  171. /package/dist/web/standalone/.next/static/{5D80IWYltFwlAJiCZ84MC → u9mQsApZYm8sVYSAaft8g}/_buildManifest.js +0 -0
  172. /package/dist/web/standalone/.next/static/{5D80IWYltFwlAJiCZ84MC → u9mQsApZYm8sVYSAaft8g}/_ssgManifest.js +0 -0
@@ -53,8 +53,25 @@ import {
53
53
  runPreparation,
54
54
  formatCodebaseBrief,
55
55
  formatPriorContextBrief,
56
+ formatEcosystemBrief,
57
+ type PreparationResult,
56
58
  } from "./preparation.js";
57
59
 
60
+ // ─── Preparation result storage ─────────────────────────────────────────────
61
+ // Stores the most recent preparation result for injection into discuss prompts.
62
+ // S02 will consume this when building the prepared discussion prompt.
63
+ let lastPreparationResult: PreparationResult | null = null;
64
+
65
+ /** Get the most recent preparation result (for S02 prompt building). */
66
+ export function getLastPreparationResult(): PreparationResult | null {
67
+ return lastPreparationResult;
68
+ }
69
+
70
+ /** Clear the preparation result (called after discussion completes). */
71
+ export function clearPreparationResult(): void {
72
+ lastPreparationResult = null;
73
+ }
74
+
58
75
  // ─── Re-exports (preserve public API for existing importers) ────────────────
59
76
  export {
60
77
  MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId,
@@ -410,7 +427,7 @@ function resolveAvailableModel<T extends { id: string; provider: string }>(
410
427
  * Build the discuss-and-plan prompt for a new milestone.
411
428
  * Used by all three "new milestone" paths (first ever, no active, all complete).
412
429
  */
413
- function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string, preparationContext?: string): string {
430
+ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string): string {
414
431
  const milestoneRel = `.gsd/milestones/${nextId}`;
415
432
  const inlinedTemplates = [
416
433
  inlineTemplate("project", "Project"),
@@ -422,7 +439,6 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string,
422
439
  return loadPrompt("discuss", {
423
440
  milestoneId: nextId,
424
441
  preamble,
425
- preparationContext: preparationContext ?? "",
426
442
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
427
443
  roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
428
444
  inlinedTemplates,
@@ -455,12 +471,59 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
455
471
  });
456
472
  }
457
473
 
474
+ /**
475
+ * Build the prepared discuss prompt with brief injection.
476
+ * Uses the discuss-prepared template which encodes the 4-layer discussion protocol.
477
+ *
478
+ * @param nextId - The milestone ID being discussed
479
+ * @param preamble - Preamble text for the discuss prompt
480
+ * @param _basePath - Root directory of the project (unused, kept for signature consistency)
481
+ * @param prepResult - Preparation result containing briefs to inject
482
+ * @returns The prepared discuss prompt string
483
+ */
484
+ function buildPreparedPrompt(
485
+ nextId: string,
486
+ preamble: string,
487
+ _basePath: string,
488
+ prepResult: PreparationResult,
489
+ ): string {
490
+ const milestoneRel = `.gsd/milestones/${nextId}`;
491
+
492
+ // Use context-enhanced instead of context for prepared discussions
493
+ const inlinedTemplates = [
494
+ inlineTemplate("project", "Project"),
495
+ inlineTemplate("requirements", "Requirements"),
496
+ inlineTemplate("context-enhanced", "Context Enhanced"),
497
+ inlineTemplate("roadmap", "Roadmap"),
498
+ inlineTemplate("decisions", "Decisions"),
499
+ ].join("\n\n---\n\n");
500
+
501
+ // Format the briefs from the preparation result
502
+ const codebaseBrief = prepResult.codebaseBrief || formatCodebaseBrief(prepResult.codebase);
503
+ const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
504
+ const ecosystemBrief = prepResult.ecosystemBrief || formatEcosystemBrief(prepResult.ecosystem);
505
+
506
+ return loadPrompt("discuss-prepared", {
507
+ milestoneId: nextId,
508
+ preamble,
509
+ codebaseBrief,
510
+ priorContextBrief,
511
+ ecosystemBrief,
512
+ contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
513
+ roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
514
+ inlinedTemplates,
515
+ commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
516
+ multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
517
+ });
518
+ }
519
+
458
520
  /**
459
521
  * Run preparation phase if enabled, then build the discuss prompt.
460
- * Preparation analyzes the codebase and prior context, injecting the results
461
- * as supplementary context into the standard discuss template. The discuss
462
- * template drives the conversation (asks "What's the vision?" first), while
463
- * the preparation briefs give the agent grounding in the existing codebase.
522
+ * This is the main entry point for new milestone discussions with preparation.
523
+ * Stores the preparation result for S02 to inject into the discuss prompt.
524
+ *
525
+ * When preparation succeeds, uses the discuss-prepared template with brief injection.
526
+ * Falls back to the standard discuss template when preparation is disabled or fails.
464
527
  *
465
528
  * @param ctx - Extension command context with UI for progress notifications
466
529
  * @param nextId - The milestone ID being discussed
@@ -474,13 +537,14 @@ async function prepareAndBuildDiscussPrompt(
474
537
  preamble: string,
475
538
  basePath: string,
476
539
  ): Promise<string> {
540
+ // Clear stale preparation result immediately to prevent cross-session/project
541
+ // state leaks. This ensures data from a prior milestone/project never leaks
542
+ // into subsequent discussions (adversarial review fix #3602).
543
+ lastPreparationResult = null;
544
+
477
545
  const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
478
546
 
479
- // Run preparation if enabled (default: true) — results are injected as
480
- // supplementary context into the standard discuss prompt, NOT as a
481
- // replacement template. The discuss prompt always leads with "What's the
482
- // vision?" so the user defines the scope, not the codebase analysis.
483
- let preparationContext = "";
547
+ // Run preparation if enabled (default: true)
484
548
  if (prefs.discuss_preparation !== false) {
485
549
  try {
486
550
  const prepResult = await runPreparation(basePath, ctx.ui, {
@@ -488,23 +552,21 @@ async function prepareAndBuildDiscussPrompt(
488
552
  discuss_web_research: prefs.discuss_web_research,
489
553
  discuss_depth: prefs.discuss_depth,
490
554
  });
555
+ lastPreparationResult = prepResult;
491
556
 
557
+ // Use prepared prompt if preparation was enabled and produced results
492
558
  if (prepResult.enabled) {
493
- const codebaseBrief = prepResult.codebaseBrief || formatCodebaseBrief(prepResult.codebase);
494
- const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
495
- const parts: string[] = [];
496
- if (codebaseBrief) parts.push(`### Codebase Brief\n\n${codebaseBrief}`);
497
- if (priorContextBrief) parts.push(`### Prior Context Brief\n\n${priorContextBrief}`);
498
- if (parts.length > 0) {
499
- preparationContext = `\n\n## Preparation Context\n\nThe system analyzed the codebase before this discussion. Use these findings as background context — they describe what already exists, NOT what the user wants to build. Always ask the user what they want to build first.\n\n${parts.join("\n\n")}`;
500
- }
559
+ return buildPreparedPrompt(nextId, preamble, basePath, prepResult);
501
560
  }
502
- } catch (err) {
503
- logWarning("guided", `preparation failed, proceeding without context: ${(err as Error).message}`);
561
+ } catch {
562
+ // If preparation throws, ensure stale data doesn't persist
563
+ lastPreparationResult = null;
504
564
  }
505
565
  }
506
566
 
507
- return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
567
+ // Fall back to standard discuss prompt for backward compatibility
568
+ // lastPreparationResult is already null (cleared at entry or on error)
569
+ return buildDiscussPrompt(nextId, preamble, basePath);
508
570
  }
509
571
 
510
572
  /**
@@ -58,7 +58,7 @@ export interface ModelCapabilities {
58
58
  // Maps known model IDs to their capability tier. Used when tier_models is not
59
59
  // explicitly configured to pick the best available model for each tier.
60
60
 
61
- export const MODEL_CAPABILITY_TIER: Record<string, ComplexityTier> = {
61
+ const MODEL_CAPABILITY_TIER: Record<string, ComplexityTier> = {
62
62
  // Light-tier models (cheapest)
63
63
  "claude-haiku-4-5": "light",
64
64
  "claude-3-5-haiku-latest": "light",
@@ -139,49 +139,15 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
139
139
  // model selection within an eligible tier set.
140
140
 
141
141
  export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
142
- // ── Anthropic ──────────────────────────────────────────────────────────────
143
- "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
144
- "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
145
- "claude-sonnet-4-5-20250514": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
146
- "claude-3-5-sonnet-latest": { coding: 82, debugging: 78, research: 72, reasoning: 78, speed: 62, longContext: 70, instruction: 82 },
147
- "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
148
- "claude-3-5-haiku-latest": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
149
- "claude-3-haiku-20240307": { coding: 50, debugging: 40, research: 35, reasoning: 40, speed: 95, longContext: 40, instruction: 65 },
150
- "claude-3-opus-latest": { coding: 90, debugging: 85, research: 82, reasoning: 90, speed: 35, longContext: 75, instruction: 88 },
151
-
152
- // ── OpenAI GPT ─────────────────────────────────────────────────────────────
153
- "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
154
- "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
155
- "gpt-4-turbo": { coding: 78, debugging: 72, research: 68, reasoning: 72, speed: 50, longContext: 65, instruction: 78 },
156
- "gpt-4.1": { coding: 82, debugging: 78, research: 72, reasoning: 78, speed: 62, longContext: 72, instruction: 82 },
157
- "gpt-4.1-mini": { coding: 58, debugging: 48, research: 42, reasoning: 48, speed: 88, longContext: 48, instruction: 72 },
158
- "gpt-4.1-nano": { coding: 40, debugging: 30, research: 25, reasoning: 30, speed: 95, longContext: 30, instruction: 60 },
159
- "gpt-5": { coding: 92, debugging: 88, research: 85, reasoning: 92, speed: 40, longContext: 85, instruction: 90 },
160
- "gpt-5-mini": { coding: 62, debugging: 52, research: 48, reasoning: 52, speed: 88, longContext: 52, instruction: 74 },
161
- "gpt-5-nano": { coding: 42, debugging: 32, research: 28, reasoning: 32, speed: 95, longContext: 32, instruction: 62 },
162
- "gpt-5-pro": { coding: 94, debugging: 90, research: 88, reasoning: 94, speed: 35, longContext: 88, instruction: 92 },
163
- "gpt-5.1": { coding: 93, debugging: 89, research: 86, reasoning: 93, speed: 42, longContext: 86, instruction: 91 },
164
- "gpt-5.1-codex-max": { coding: 90, debugging: 85, research: 70, reasoning: 85, speed: 55, longContext: 75, instruction: 85 },
165
- "gpt-5.1-codex-mini": { coding: 65, debugging: 55, research: 40, reasoning: 50, speed: 88, longContext: 48, instruction: 72 },
166
- "gpt-5.2": { coding: 93, debugging: 90, research: 87, reasoning: 93, speed: 42, longContext: 87, instruction: 91 },
167
- "gpt-5.2-codex": { coding: 93, debugging: 90, research: 72, reasoning: 88, speed: 50, longContext: 78, instruction: 88 },
168
- "gpt-5.3-codex": { coding: 94, debugging: 91, research: 74, reasoning: 89, speed: 50, longContext: 80, instruction: 89 },
169
- "gpt-5.3-codex-spark": { coding: 68, debugging: 58, research: 42, reasoning: 52, speed: 90, longContext: 50, instruction: 74 },
170
- "gpt-5.4": { coding: 95, debugging: 92, research: 88, reasoning: 94, speed: 42, longContext: 88, instruction: 92 },
171
-
172
- // ── OpenAI o-series (reasoning-first) ──────────────────────────────────────
173
- "o1": { coding: 78, debugging: 82, research: 78, reasoning: 90, speed: 20, longContext: 65, instruction: 82 },
174
- "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
175
- "o4-mini": { coding: 75, debugging: 80, research: 72, reasoning: 88, speed: 60, longContext: 65, instruction: 80 },
176
- "o4-mini-deep-research": { coding: 75, debugging: 80, research: 85, reasoning: 88, speed: 30, longContext: 80, instruction: 80 },
177
-
178
- // ── Google ─────────────────────────────────────────────────────────────────
179
- "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
180
- "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
181
- "gemini-flash-2.0": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
182
-
183
- // ── DeepSeek ───────────────────────────────────────────────────────────────
184
- "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
142
+ "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
143
+ "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
144
+ "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
145
+ "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
146
+ "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
147
+ "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
148
+ "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
149
+ "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
150
+ "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
185
151
  };
186
152
 
187
153
  // ─── Base Task Requirements Data Table ───────────────────────────────────────
@@ -20,7 +20,7 @@ import type {
20
20
  ReactiveExecutionConfig,
21
21
  GateEvaluationConfig,
22
22
  } from "./types.js";
23
- import type { DynamicRoutingConfig, ModelCapabilities } from "./model-router.js";
23
+ import type { DynamicRoutingConfig } from "./model-router.js";
24
24
 
25
25
  export interface ContextManagementConfig {
26
26
  observation_masking?: boolean; // default: true
@@ -255,8 +255,6 @@ export interface GSDPreferences {
255
255
  post_unit_hooks?: PostUnitHookConfig[];
256
256
  pre_dispatch_hooks?: PreDispatchHookConfig[];
257
257
  dynamic_routing?: DynamicRoutingConfig;
258
- /** Per-model capability overrides. Deep-merged with built-in profiles for capability-aware routing (ADR-004). */
259
- modelOverrides?: Record<string, { capabilities?: Partial<ModelCapabilities> }>;
260
258
  context_management?: ContextManagementConfig;
261
259
  token_profile?: TokenProfile;
262
260
  phases?: PhaseSkipPreferences;
@@ -0,0 +1,88 @@
1
+ /**
2
+ * GSD Prompt Validation — Validates enhanced context output before writing.
3
+ *
4
+ * Implements R109 validation requirement: CONTEXT.md must have required sections
5
+ * before being written to disk.
6
+ */
7
+
8
+ /**
9
+ * Result of validating enhanced context output.
10
+ */
11
+ export interface ValidationResult {
12
+ /** Whether all required sections are present. */
13
+ valid: boolean;
14
+ /** List of missing required sections. */
15
+ missing: string[];
16
+ }
17
+
18
+ /**
19
+ * Validate that enhanced context content has all required sections.
20
+ *
21
+ * Required sections per R109:
22
+ * - Scope section (## Scope, ## Milestone Scope, or ## Why This Milestone)
23
+ * - Architectural Decisions section (## Architectural Decisions)
24
+ * - Acceptance Criteria section (## Acceptance Criteria or ## Final Integrated Acceptance)
25
+ *
26
+ * Additionally validates that the Architectural Decisions section contains
27
+ * at least one decision entry (### heading or **Decision marker).
28
+ *
29
+ * @param content - The enhanced context markdown content
30
+ * @returns ValidationResult with valid flag and list of missing sections
31
+ */
32
+ export function validateEnhancedContext(content: string): ValidationResult {
33
+ const missing: string[] = [];
34
+
35
+ // Required section 1: Scope (multiple acceptable header variants)
36
+ const hasScopeSection =
37
+ /^## Scope\b/m.test(content) ||
38
+ /^## Milestone Scope\b/m.test(content) ||
39
+ /^## Why This Milestone\b/m.test(content);
40
+
41
+ if (!hasScopeSection) {
42
+ missing.push("Milestone Scope or Why This Milestone");
43
+ }
44
+
45
+ // Required section 2: Architectural Decisions
46
+ const hasArchitecturalDecisions = /^## Architectural Decisions\b/m.test(content);
47
+ if (!hasArchitecturalDecisions) {
48
+ missing.push("Architectural Decisions");
49
+ }
50
+
51
+ // Required section 3: Acceptance Criteria (multiple acceptable header variants)
52
+ const hasAcceptanceCriteria =
53
+ /^## Acceptance Criteria\b/m.test(content) ||
54
+ /^## Final Integrated Acceptance\b/m.test(content);
55
+
56
+ if (!hasAcceptanceCriteria) {
57
+ missing.push("Acceptance Criteria");
58
+ }
59
+
60
+ // Additional validation: Architectural Decisions must have at least one entry
61
+ if (hasArchitecturalDecisions) {
62
+ // Extract the section content between ## Architectural Decisions and the next ## heading.
63
+ // Uses indexOf-based extraction instead of regex with \z (which is invalid in JavaScript
64
+ // regex — it's PCRE/Ruby syntax and JS treats it as literal 'z').
65
+ const sectionStart = content.indexOf("## Architectural Decisions");
66
+ if (sectionStart === -1) {
67
+ missing.push("Architectural Decisions");
68
+ } else {
69
+ const afterHeading = content.slice(sectionStart + "## Architectural Decisions".length);
70
+ const nextSection = afterHeading.search(/^## /m);
71
+ const sectionContent = nextSection === -1 ? afterHeading : afterHeading.slice(0, nextSection);
72
+
73
+ // Check for actual decision entries:
74
+ // - ### heading (subsection per decision)
75
+ // - **Decision marker (inline decision format)
76
+ const hasDecisionEntry = /^### /m.test(sectionContent) || /^\*\*Decision/m.test(sectionContent);
77
+
78
+ if (!hasDecisionEntry) {
79
+ missing.push("At least one architectural decision entry");
80
+ }
81
+ }
82
+ }
83
+
84
+ return {
85
+ valid: missing.length === 0,
86
+ missing,
87
+ };
88
+ }