gsd-pi 2.68.1-dev.362687a → 2.68.1-dev.c1497ab

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 (61) hide show
  1. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -5
  2. package/dist/resources/extensions/gsd/guided-flow.js +25 -70
  3. package/dist/resources/extensions/gsd/model-router.js +32 -2
  4. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  5. package/dist/resources/extensions/gsd/templates/context.md +34 -2
  6. package/dist/web/standalone/.next/BUILD_ID +1 -1
  7. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  8. package/dist/web/standalone/.next/build-manifest.json +2 -2
  9. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  10. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  12. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/index.html +1 -1
  27. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  34. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  36. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  37. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  38. package/package.json +1 -1
  39. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -3
  40. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -5
  41. package/src/resources/extensions/gsd/guided-flow.ts +22 -84
  42. package/src/resources/extensions/gsd/model-router.ts +44 -10
  43. package/src/resources/extensions/gsd/preferences-types.ts +3 -1
  44. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  45. package/src/resources/extensions/gsd/templates/context.md +34 -2
  46. package/src/resources/extensions/gsd/tests/capability-router.test.ts +31 -7
  47. package/src/resources/extensions/gsd/tests/model-router.test.ts +2 -2
  48. package/src/resources/extensions/gsd/tests/write-gate.test.ts +13 -16
  49. package/dist/resources/extensions/gsd/prompt-validation.js +0 -67
  50. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +0 -424
  51. package/dist/resources/extensions/gsd/templates/context-enhanced.md +0 -138
  52. package/src/resources/extensions/gsd/prompt-validation.ts +0 -88
  53. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +0 -424
  54. package/src/resources/extensions/gsd/templates/context-enhanced.md +0 -138
  55. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +0 -223
  56. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +0 -53
  57. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +0 -525
  58. package/src/resources/extensions/gsd/tests/preparation.test.ts +0 -1211
  59. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +0 -669
  60. /package/dist/web/standalone/.next/static/{VkiZZ5UjK7EfSjrWWd5RC → 5D80IWYltFwlAJiCZ84MC}/_buildManifest.js +0 -0
  61. /package/dist/web/standalone/.next/static/{VkiZZ5UjK7EfSjrWWd5RC → 5D80IWYltFwlAJiCZ84MC}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- <!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-6e4d7e9a4f57bed4.js"/><script src="/_next/static/chunks/4bd1b696-e356ca5ba0218e27.js" async=""></script><script src="/_next/static/chunks/3794-42fdce068d44fa4f.js" async=""></script><script src="/_next/static/chunks/main-app-fdab67f7802d7832.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-6e4d7e9a4f57bed4.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[57121,[],\"\"]\n3:I[74581,[],\"\"]\n4:I[90484,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[90484,[],\"ViewportBoundary\"]\na:I[90484,[],\"MetadataBoundary\"]\nc:I[27123,[],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"VkiZZ5UjK7EfSjrWWd5RC\"}\n"])</script><script>self.__next_f.push([1,"d:[]\n7:\"$Wd\"\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\nb:[]\n"])</script></body></html>
1
+ <!DOCTYPE html><html id="__next_error__"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-6e4d7e9a4f57bed4.js"/><script src="/_next/static/chunks/4bd1b696-e356ca5ba0218e27.js" async=""></script><script src="/_next/static/chunks/3794-42fdce068d44fa4f.js" async=""></script><script src="/_next/static/chunks/main-app-fdab67f7802d7832.js" async=""></script><meta name="next-size-adjust" content=""/><title>500: This page couldn’t load</title><style>:root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }</style><script src="/_next/static/chunks/polyfills-42372ed130431b0a.js" noModule=""></script></head><body><div hidden=""><!--$--><!--/$--></div><div style="font-family:system-ui,&quot;Segoe UI&quot;,Roboto,Helvetica,Arial,sans-serif,&quot;Apple Color Emoji&quot;,&quot;Segoe UI Emoji&quot;;height:100vh;display:flex;align-items:center;justify-content:center"><div style="margin-top:-32px;max-width:325px;padding:32px 28px;text-align:left"><svg width="32" height="32" viewBox="-0.2 -1.5 32 32" fill="none" style="margin-bottom:24px"><path d="M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z" fill="var(--next-error-title)"></path></svg><h1 style="font-size:24px;font-weight:500;letter-spacing:-0.02em;line-height:32px;margin:0 0 12px 0;color:var(--next-error-title)">This page couldn’t load</h1><p style="font-size:14px;font-weight:400;line-height:21px;margin:0 0 20px 0;color:var(--next-error-message)">A server error occurred. Reload to try again.</p><form style="margin:0"><button type="submit" style="display:inline-flex;align-items:center;justify-content:center;height:32px;padding:0 12px;font-size:14px;font-weight:500;line-height:20px;border-radius:6px;cursor:pointer;color:var(--next-error-btn-text);background:var(--next-error-btn-bg);border:var(--next-error-btn-border)">Reload</button></form></div></div><!--$--><!--/$--><script src="/_next/static/chunks/webpack-6e4d7e9a4f57bed4.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[57121,[],\"\"]\n3:I[74581,[],\"\"]\n4:I[90484,[],\"OutletBoundary\"]\n5:\"$Sreact.suspense\"\n8:I[90484,[],\"ViewportBoundary\"]\na:I[90484,[],\"MetadataBoundary\"]\nc:I[27123,[],\"default\",1]\n"])</script><script>self.__next_f.push([1,"0:{\"P\":null,\"c\":[\"\",\"_global-error\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"_global-error\",{\"children\":[\"__PAGE__\",{}]}]}],[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L2\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L3\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}],{\"children\":[[\"$\",\"$1\",\"c\",{\"children\":[[\"$\",\"html\",null,{\"id\":\"__next_error__\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"title\",null,{\"children\":\"500: This page couldn’t load\"}],[\"$\",\"style\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\":root {--next-error-bg: #fff;--next-error-text: #171717;--next-error-title: #171717;--next-error-message: #171717;--next-error-digest: #666666;--next-error-btn-text: #fff;--next-error-btn-bg: #171717;--next-error-btn-border: none;--next-error-btn-secondary-text: #171717;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(0,0,0,0.08);}@media (prefers-color-scheme: dark) {:root {--next-error-bg: #0a0a0a;--next-error-text: #ededed;--next-error-title: #ededed;--next-error-message: #ededed;--next-error-digest: #a0a0a0;--next-error-btn-text: #0a0a0a;--next-error-btn-bg: #ededed;--next-error-btn-border: none;--next-error-btn-secondary-text: #ededed;--next-error-btn-secondary-bg: transparent;--next-error-btn-secondary-border: 1px solid rgba(255,255,255,0.14);}}body { margin: 0; color: var(--next-error-text); background: var(--next-error-bg); }\"}}]]}],[\"$\",\"body\",null,{\"children\":[\"$\",\"div\",null,{\"style\":{\"fontFamily\":\"system-ui,\\\"Segoe UI\\\",Roboto,Helvetica,Arial,sans-serif,\\\"Apple Color Emoji\\\",\\\"Segoe UI Emoji\\\"\",\"height\":\"100vh\",\"display\":\"flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\"},\"children\":[\"$\",\"div\",null,{\"style\":{\"marginTop\":\"-32px\",\"maxWidth\":\"325px\",\"padding\":\"32px 28px\",\"textAlign\":\"left\"},\"children\":[[\"$\",\"svg\",null,{\"width\":\"32\",\"height\":\"32\",\"viewBox\":\"-0.2 -1.5 32 32\",\"fill\":\"none\",\"style\":{\"marginBottom\":\"24px\"},\"children\":[\"$\",\"path\",null,{\"d\":\"M16.9328 0C18.0839 0.000116771 19.1334 0.658832 19.634 1.69531L31.4299 26.1309C32.0708 27.4588 31.1036 28.9999 29.6291 29H2.00215C0.527541 29 -0.439628 27.4588 0.201371 26.1309L11.9973 1.69531C12.4979 0.658823 13.5474 7.75066e-05 14.6984 0H16.9328ZM3.59493 26H28.0363L16.9328 3H14.6984L3.59493 26ZM15.8156 19C16.9202 19.0001 17.8156 19.8955 17.8156 21C17.8156 22.1045 16.9202 22.9999 15.8156 23C14.7111 23 13.8156 22.1046 13.8156 21C13.8156 19.8954 14.7111 19 15.8156 19ZM17.3156 16.5H14.3156V8.5H17.3156V16.5Z\",\"fill\":\"var(--next-error-title)\"}]}],[\"$\",\"h1\",null,{\"style\":{\"fontSize\":\"24px\",\"fontWeight\":500,\"letterSpacing\":\"-0.02em\",\"lineHeight\":\"32px\",\"margin\":\"0 0 12px 0\",\"color\":\"var(--next-error-title)\"},\"children\":\"This page couldn’t load\"}],[\"$\",\"p\",null,{\"style\":{\"fontSize\":\"14px\",\"fontWeight\":400,\"lineHeight\":\"21px\",\"margin\":\"0 0 20px 0\",\"color\":\"var(--next-error-message)\"},\"children\":\"A server error occurred. Reload to try again.\"}],[\"$\",\"form\",null,{\"style\":{\"margin\":0},\"children\":[\"$\",\"button\",null,{\"type\":\"submit\",\"style\":{\"display\":\"inline-flex\",\"alignItems\":\"center\",\"justifyContent\":\"center\",\"height\":\"32px\",\"padding\":\"0 12px\",\"fontSize\":\"14px\",\"fontWeight\":500,\"lineHeight\":\"20px\",\"borderRadius\":\"6px\",\"cursor\":\"pointer\",\"color\":\"var(--next-error-btn-text)\",\"background\":\"var(--next-error-btn-bg)\",\"border\":\"var(--next-error-btn-border)\"},\"children\":\"Reload\"}]}]]}]}]}]]}],null,[\"$\",\"$L4\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@6\"}]}]]}],{},null,false,null]},null,false,\"$@7\"]},null,false,\"$@7\"],[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L8\",null,{\"children\":\"$L9\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$La\",null,{\"children\":[\"$\",\"$5\",null,{\"name\":\"Next.Metadata\",\"children\":\"$Lb\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}],false]],\"m\":\"$undefined\",\"G\":[\"$c\",[]],\"S\":true,\"h\":null,\"s\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"5D80IWYltFwlAJiCZ84MC\"}\n"])</script><script>self.__next_f.push([1,"d:[]\n7:\"$Wd\"\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}]]\n"])</script><script>self.__next_f.push([1,"6:null\nb:[]\n"])</script></body></html>
@@ -1 +1 @@
1
- {"node":{},"edge":{},"encryptionKey":"zjsh/XNsxewvOmGZvHmAmQHhk0hq49Y5N+EyeqPc4HE="}
1
+ {"node":{},"edge":{},"encryptionKey":"ImH7aNfUO2RRkkY1URhjN33kUDO+hFzWdB/jMCBiNZI="}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.68.1-dev.362687a",
3
+ "version": "2.68.1-dev.c1497ab",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -132,9 +132,7 @@ export async function selectAndApplyModel(
132
132
  }
133
133
 
134
134
  // Load user capability overrides from preferences (D-17: deep-merged with built-in profiles)
135
- const capabilityOverrides = loadCapabilityOverrides(
136
- (prefs as { modelOverrides?: Record<string, { capabilities?: Record<string, number> }> } | undefined) ?? {},
137
- );
135
+ const capabilityOverrides = loadCapabilityOverrides(prefs ?? {});
138
136
 
139
137
  // Fire before_model_select hook (ADR-004, D-03)
140
138
  // Hook can override model selection entirely by returning { modelId }
@@ -47,13 +47,9 @@ let pendingGateId: string | null = null;
47
47
 
48
48
  /**
49
49
  * Recognized gate question ID patterns.
50
- * These appear in both discuss-prepared.md (4-layer) and discuss.md (depth/requirements/roadmap).
50
+ * These appear in discuss.md (depth/requirements/roadmap).
51
51
  */
52
52
  const GATE_QUESTION_PATTERNS = [
53
- "layer1_scope_gate",
54
- "layer2_architecture_gate",
55
- "layer3_error_gate",
56
- "layer4_quality_gate",
57
53
  "depth_verification",
58
54
  ] as const;
59
55
 
@@ -53,25 +53,8 @@ import {
53
53
  runPreparation,
54
54
  formatCodebaseBrief,
55
55
  formatPriorContextBrief,
56
- formatEcosystemBrief,
57
- type PreparationResult,
58
56
  } from "./preparation.js";
59
57
 
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
-
75
58
  // ─── Re-exports (preserve public API for existing importers) ────────────────
76
59
  export {
77
60
  MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId,
@@ -427,7 +410,7 @@ function resolveAvailableModel<T extends { id: string; provider: string }>(
427
410
  * Build the discuss-and-plan prompt for a new milestone.
428
411
  * Used by all three "new milestone" paths (first ever, no active, all complete).
429
412
  */
430
- function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string): string {
413
+ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string, preparationContext?: string): string {
431
414
  const milestoneRel = `.gsd/milestones/${nextId}`;
432
415
  const inlinedTemplates = [
433
416
  inlineTemplate("project", "Project"),
@@ -439,6 +422,7 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string)
439
422
  return loadPrompt("discuss", {
440
423
  milestoneId: nextId,
441
424
  preamble,
425
+ preparationContext: preparationContext ?? "",
442
426
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
443
427
  roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
444
428
  inlinedTemplates,
@@ -471,59 +455,12 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
471
455
  });
472
456
  }
473
457
 
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
-
520
458
  /**
521
459
  * Run preparation phase if enabled, then build the discuss prompt.
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.
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.
527
464
  *
528
465
  * @param ctx - Extension command context with UI for progress notifications
529
466
  * @param nextId - The milestone ID being discussed
@@ -537,14 +474,13 @@ async function prepareAndBuildDiscussPrompt(
537
474
  preamble: string,
538
475
  basePath: string,
539
476
  ): 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
-
545
477
  const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
546
478
 
547
- // Run preparation if enabled (default: true)
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 = "";
548
484
  if (prefs.discuss_preparation !== false) {
549
485
  try {
550
486
  const prepResult = await runPreparation(basePath, ctx.ui, {
@@ -552,21 +488,23 @@ async function prepareAndBuildDiscussPrompt(
552
488
  discuss_web_research: prefs.discuss_web_research,
553
489
  discuss_depth: prefs.discuss_depth,
554
490
  });
555
- lastPreparationResult = prepResult;
556
491
 
557
- // Use prepared prompt if preparation was enabled and produced results
558
492
  if (prepResult.enabled) {
559
- return buildPreparedPrompt(nextId, preamble, basePath, prepResult);
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
+ }
560
501
  }
561
- } catch {
562
- // If preparation throws, ensure stale data doesn't persist
563
- lastPreparationResult = null;
502
+ } catch (err) {
503
+ logWarning("guided", `preparation failed, proceeding without context: ${(err as Error).message}`);
564
504
  }
565
505
  }
566
506
 
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);
507
+ return buildDiscussPrompt(nextId, preamble, basePath, preparationContext);
570
508
  }
571
509
 
572
510
  /**
@@ -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
- const MODEL_CAPABILITY_TIER: Record<string, ComplexityTier> = {
61
+ export 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,15 +139,49 @@ 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
- "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 },
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 },
151
185
  };
152
186
 
153
187
  // ─── 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 } from "./model-router.js";
23
+ import type { DynamicRoutingConfig, ModelCapabilities } from "./model-router.js";
24
24
 
25
25
  export interface ContextManagementConfig {
26
26
  observation_masking?: boolean; // default: true
@@ -255,6 +255,8 @@ 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> }>;
258
260
  context_management?: ContextManagementConfig;
259
261
  token_profile?: TokenProfile;
260
262
  phases?: PhaseSkipPreferences;
@@ -28,6 +28,8 @@ After reflection is confirmed, decide the approach based on the actual scope —
28
28
 
29
29
  **Anti-reduction rule:** If the user describes a big vision, plan the big vision. Do not ask "what's the minimum viable version?" or try to reduce scope unless the user explicitly asks for an MVP or minimal version. When something is complex or risky, phase it into a later milestone — do not cut it. The user's ambition is the target, and your job is to sequence it intelligently, not shrink it.
30
30
 
31
+ {{preparationContext}}
32
+
31
33
  ## Mandatory Investigation Before First Question Round
32
34
 
33
35
  Before asking your first question, do a mandatory investigation pass. This is not optional.
@@ -38,6 +38,28 @@ To call this milestone complete, we must prove:
38
38
  - {{one real end-to-end scenario}}
39
39
  - {{what cannot be simulated if this milestone is to be considered truly done}}
40
40
 
41
+ ## Architectural Decisions
42
+
43
+ ### {{decisionTitle}}
44
+
45
+ **Decision:** {{decisionStatement}}
46
+
47
+ **Rationale:** {{rationale}}
48
+
49
+ **Alternatives Considered:**
50
+ - {{alternative}} — {{whyNotChosen}}
51
+
52
+ ---
53
+
54
+ > Add additional decisions as separate `### Decision Title` blocks following the same structure above.
55
+ > See `.gsd/DECISIONS.md` for the full append-only register of all project decisions.
56
+
57
+ ## Error Handling Strategy
58
+
59
+ {{errorHandlingStrategy}}
60
+
61
+ > Describe the approach for handling failures, edge cases, and error propagation. Include retry policies, fallback behaviors, and user-facing error messages where relevant.
62
+
41
63
  ## Risks and Unknowns
42
64
 
43
65
  - {{riskOrUnknown}} — {{whyItMatters}}
@@ -47,8 +69,6 @@ To call this milestone complete, we must prove:
47
69
  - `{{fileOrModule}}` — {{howItRelates}}
48
70
  - `{{fileOrModule}}` — {{howItRelates}}
49
71
 
50
- > See `.gsd/DECISIONS.md` for all architectural and pattern decisions — it is an append-only register; read it during planning, append to it during execution.
51
-
52
72
  ## Relevant Requirements
53
73
 
54
74
  - {{requirementId}} — {{howThisMilestoneAdvancesIt}}
@@ -71,6 +91,18 @@ To call this milestone complete, we must prove:
71
91
 
72
92
  - {{systemOrService}} — {{howThisMilestoneInteractsWithIt}}
73
93
 
94
+ ## Testing Requirements
95
+
96
+ {{testingRequirements}}
97
+
98
+ > Specify test types (unit, integration, e2e), coverage expectations, and specific test scenarios that must pass.
99
+
100
+ ## Acceptance Criteria
101
+
102
+ {{acceptanceCriteria}}
103
+
104
+ > Per-slice acceptance criteria gathered during discussion. Each slice should have clear, testable criteria.
105
+
74
106
  ## Open Questions
75
107
 
76
108
  - {{question}} — {{currentThinking}}
@@ -11,6 +11,7 @@ import {
11
11
  getEligibleModels,
12
12
  resolveModelForComplexity,
13
13
  MODEL_CAPABILITY_PROFILES,
14
+ MODEL_CAPABILITY_TIER,
14
15
  BASE_REQUIREMENTS,
15
16
  defaultRoutingConfig,
16
17
  } from "../model-router.js";
@@ -125,13 +126,9 @@ describe("computeTaskRequirements", () => {
125
126
  // ─── MODEL_CAPABILITY_PROFILES ───────────────────────────────────────────────
126
127
 
127
128
  describe("MODEL_CAPABILITY_PROFILES", () => {
128
- test("contains all 9 required models", () => {
129
- const required = [
130
- "claude-opus-4-6", "claude-sonnet-4-6", "claude-haiku-4-5",
131
- "gpt-4o", "gpt-4o-mini", "gemini-2.5-pro", "gemini-2.0-flash",
132
- "deepseek-chat", "o3",
133
- ];
134
- for (const model of required) {
129
+ test("contains profiles for all tier-mapped models", () => {
130
+ const tierModels = Object.keys(MODEL_CAPABILITY_TIER);
131
+ for (const model of tierModels) {
135
132
  assert.ok(MODEL_CAPABILITY_PROFILES[model], `Missing profile for ${model}`);
136
133
  }
137
134
  });
@@ -345,3 +342,30 @@ describe("RoutingDecision.selectionMethod", () => {
345
342
  assert.equal(result.selectionMethod, "tier-only");
346
343
  });
347
344
  });
345
+
346
+ // ─── ADR-004: Profile Completeness Lint ─────────────────────────────────────
347
+ // Every model in MODEL_CAPABILITY_TIER must have an entry in
348
+ // MODEL_CAPABILITY_PROFILES. This prevents profile staleness as new models
349
+ // are added to the tier map without corresponding capability data.
350
+
351
+ describe("profile completeness (ADR-004 lint)", () => {
352
+ test("every model in MODEL_CAPABILITY_TIER has a MODEL_CAPABILITY_PROFILES entry", () => {
353
+ const tierModels = Object.keys(MODEL_CAPABILITY_TIER);
354
+ const missing = tierModels.filter(id => !MODEL_CAPABILITY_PROFILES[id]);
355
+ assert.equal(
356
+ missing.length,
357
+ 0,
358
+ `Models in MODEL_CAPABILITY_TIER but missing from MODEL_CAPABILITY_PROFILES:\n ${missing.join("\n ")}\n\nAdd capability profiles for these models in model-router.ts.`,
359
+ );
360
+ });
361
+
362
+ test("MODEL_CAPABILITY_PROFILES does not contain models absent from MODEL_CAPABILITY_TIER", () => {
363
+ const profileModels = Object.keys(MODEL_CAPABILITY_PROFILES);
364
+ const orphaned = profileModels.filter(id => !MODEL_CAPABILITY_TIER[id]);
365
+ assert.equal(
366
+ orphaned.length,
367
+ 0,
368
+ `Models in MODEL_CAPABILITY_PROFILES but not in MODEL_CAPABILITY_TIER:\n ${orphaned.join("\n ")}\n\nEither add these to MODEL_CAPABILITY_TIER or remove stale profiles.`,
369
+ );
370
+ });
371
+ });
@@ -287,9 +287,9 @@ test("resolveModelForComplexity falls back to tier-only when capability_routing
287
287
  assert.ok(!result.selectionMethod || result.selectionMethod === "tier-only");
288
288
  });
289
289
 
290
- test("MODEL_CAPABILITY_PROFILES has entries for core models", () => {
290
+ test("MODEL_CAPABILITY_PROFILES has entries for all tier-mapped models", () => {
291
291
  const profiledModels = Object.keys(MODEL_CAPABILITY_PROFILES);
292
- assert.ok(profiledModels.length >= 9, `Expected ≥9 profiles, got ${profiledModels.length}`);
292
+ assert.ok(profiledModels.length >= 30, `Expected ≥30 profiles, got ${profiledModels.length}`);
293
293
  assert.ok(MODEL_CAPABILITY_PROFILES["claude-opus-4-6"]);
294
294
  assert.ok(MODEL_CAPABILITY_PROFILES["claude-haiku-4-5"]);
295
295
  });
@@ -230,16 +230,13 @@ import {
230
230
  // ─── Scenario 19: isGateQuestionId recognizes all gate patterns ──
231
231
 
232
232
  test('write-gate: isGateQuestionId recognizes all gate patterns', () => {
233
- assert.strictEqual(isGateQuestionId('layer1_scope_gate'), true);
234
- assert.strictEqual(isGateQuestionId('layer2_architecture_gate'), true);
235
- assert.strictEqual(isGateQuestionId('layer3_error_gate'), true);
236
- assert.strictEqual(isGateQuestionId('layer4_quality_gate'), true);
237
233
  assert.strictEqual(isGateQuestionId('depth_verification'), true);
238
234
  assert.strictEqual(isGateQuestionId('depth_verification_M002'), true);
239
- assert.strictEqual(isGateQuestionId('my_layer1_scope_gate_question'), true);
235
+ assert.strictEqual(isGateQuestionId('depth_verification_confirm'), true);
240
236
  // Non-gate question IDs
241
237
  assert.strictEqual(isGateQuestionId('project_intent'), false);
242
238
  assert.strictEqual(isGateQuestionId('feature_priority'), false);
239
+ assert.strictEqual(isGateQuestionId('layer1_scope_gate'), false);
243
240
  assert.strictEqual(isGateQuestionId(''), false);
244
241
  });
245
242
 
@@ -249,14 +246,14 @@ test('write-gate: pending gate lifecycle (set, get, clear)', () => {
249
246
  clearDiscussionFlowState();
250
247
  assert.strictEqual(getPendingGate(), null, 'starts null');
251
248
 
252
- setPendingGate('layer1_scope_gate');
253
- assert.strictEqual(getPendingGate(), 'layer1_scope_gate', 'set correctly');
249
+ setPendingGate('depth_verification');
250
+ assert.strictEqual(getPendingGate(), 'depth_verification', 'set correctly');
254
251
 
255
252
  clearPendingGate();
256
253
  assert.strictEqual(getPendingGate(), null, 'cleared correctly');
257
254
 
258
255
  // clearDiscussionFlowState also clears pending gate
259
- setPendingGate('layer2_architecture_gate');
256
+ setPendingGate('depth_verification_M002');
260
257
  clearDiscussionFlowState();
261
258
  assert.strictEqual(getPendingGate(), null, 'clearDiscussionFlowState clears pending gate');
262
259
  });
@@ -265,12 +262,12 @@ test('write-gate: pending gate lifecycle (set, get, clear)', () => {
265
262
 
266
263
  test('write-gate: shouldBlockPendingGate blocks write/edit during pending gate', () => {
267
264
  clearDiscussionFlowState();
268
- setPendingGate('layer1_scope_gate');
265
+ setPendingGate('depth_verification');
269
266
 
270
267
  // write should be blocked during discussion
271
268
  const writeResult = shouldBlockPendingGate('write', 'M001', false);
272
269
  assert.strictEqual(writeResult.block, true, 'write should be blocked');
273
- assert.ok(writeResult.reason!.includes('layer1_scope_gate'), 'reason mentions the gate');
270
+ assert.ok(writeResult.reason!.includes('depth_verification'), 'reason mentions the gate');
274
271
 
275
272
  // edit should be blocked
276
273
  const editResult = shouldBlockPendingGate('edit', 'M001', false);
@@ -287,7 +284,7 @@ test('write-gate: shouldBlockPendingGate blocks write/edit during pending gate',
287
284
 
288
285
  test('write-gate: shouldBlockPendingGate allows read-only and ask_user_questions during pending gate', () => {
289
286
  clearDiscussionFlowState();
290
- setPendingGate('layer1_scope_gate');
287
+ setPendingGate('depth_verification');
291
288
 
292
289
  // ask_user_questions is always safe (model needs to re-ask)
293
290
  assert.strictEqual(shouldBlockPendingGate('ask_user_questions', 'M001').block, false);
@@ -304,7 +301,7 @@ test('write-gate: shouldBlockPendingGate allows read-only and ask_user_questions
304
301
 
305
302
  test('write-gate: shouldBlockPendingGate blocks outside discussion when a gate is pending', () => {
306
303
  clearDiscussionFlowState();
307
- setPendingGate('layer1_scope_gate');
304
+ setPendingGate('depth_verification');
308
305
 
309
306
  // No milestoneId and no queue phase — still block because the gate is pending
310
307
  const result = shouldBlockPendingGate('write', null, false);
@@ -330,7 +327,7 @@ test('write-gate: shouldBlockPendingGate blocks in queue mode when gate is pendi
330
327
 
331
328
  test('write-gate: shouldBlockPendingGateBash allows read-only commands during pending gate', () => {
332
329
  clearDiscussionFlowState();
333
- setPendingGate('layer2_architecture_gate');
330
+ setPendingGate('depth_verification');
334
331
 
335
332
  assert.strictEqual(shouldBlockPendingGateBash('cat file.txt', 'M001').block, false);
336
333
  assert.strictEqual(shouldBlockPendingGateBash('git log --oneline', 'M001').block, false);
@@ -344,11 +341,11 @@ test('write-gate: shouldBlockPendingGateBash allows read-only commands during pe
344
341
 
345
342
  test('write-gate: shouldBlockPendingGateBash blocks mutating commands during pending gate', () => {
346
343
  clearDiscussionFlowState();
347
- setPendingGate('layer2_architecture_gate');
344
+ setPendingGate('depth_verification');
348
345
 
349
346
  const result = shouldBlockPendingGateBash('npm run build', 'M001');
350
347
  assert.strictEqual(result.block, true, 'mutating bash should be blocked');
351
- assert.ok(result.reason!.includes('layer2_architecture_gate'));
348
+ assert.ok(result.reason!.includes('depth_verification'));
352
349
 
353
350
  clearDiscussionFlowState();
354
351
  });
@@ -365,7 +362,7 @@ test('write-gate: no pending gate means no blocking', () => {
365
362
  // ─── Scenario 28: resetWriteGateState clears pending gate ──
366
363
 
367
364
  test('write-gate: resetWriteGateState clears pending gate', () => {
368
- setPendingGate('layer3_error_gate');
365
+ setPendingGate('depth_verification');
369
366
  resetWriteGateState();
370
367
  assert.strictEqual(getPendingGate(), null);
371
368
  });