@opengsd/gsd-pi 1.10.0 → 1.11.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 (218) hide show
  1. package/README.md +10 -9
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto-model-selection.js +1 -0
  4. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  5. package/dist/resources/extensions/gsd/auto-verification.js +25 -12
  6. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -3
  7. package/dist/resources/extensions/gsd/db/engine.js +8 -2
  8. package/dist/resources/extensions/gsd/db-canonical-foundation-schema.js +120 -0
  9. package/dist/resources/extensions/gsd/db-migration-steps.js +4 -0
  10. package/dist/resources/extensions/gsd/docs/preferences-reference.md +12 -3
  11. package/dist/resources/extensions/gsd/doctor-engine-checks.js +16 -4
  12. package/dist/resources/extensions/gsd/model-cost-table.js +6 -3
  13. package/dist/resources/extensions/gsd/model-router.js +10 -5
  14. package/dist/resources/extensions/gsd/planning-subagent-registry.js +12 -4
  15. package/dist/resources/extensions/gsd/preferences-models.js +50 -10
  16. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  17. package/dist/resources/extensions/gsd/preferences-validation.js +41 -6
  18. package/dist/resources/extensions/gsd/preferences.js +7 -0
  19. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
  20. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -1
  21. package/dist/resources/extensions/gsd/verification-evidence.js +5 -3
  22. package/dist/resources/extensions/ollama/ollama-chat-provider.js +2 -2
  23. package/dist/resources/extensions/subagent/index.js +1 -1
  24. package/dist/startup-model-validation.d.ts +1 -1
  25. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  26. package/dist/web/standalone/.next/BUILD_ID +1 -1
  27. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  28. package/dist/web/standalone/.next/build-manifest.json +2 -2
  29. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  30. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  33. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  38. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  40. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  41. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/index.html +1 -1
  46. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  53. package/dist/web/standalone/.next/server/chunks/8357.js +2 -2
  54. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  56. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  57. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  58. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  59. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  60. package/integrations/hermes/pyproject.toml +1 -1
  61. package/package.json +7 -6
  62. package/packages/cloud-mcp-gateway/package.json +2 -2
  63. package/packages/contracts/dist/rpc.d.ts +1 -1
  64. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  65. package/packages/contracts/dist/rpc.js +1 -1
  66. package/packages/contracts/dist/rpc.js.map +1 -1
  67. package/packages/contracts/dist/rpc.test.js +1 -1
  68. package/packages/contracts/dist/rpc.test.js.map +1 -1
  69. package/packages/contracts/package.json +1 -1
  70. package/packages/daemon/package.json +4 -4
  71. package/packages/gsd-agent-core/package.json +5 -5
  72. package/packages/gsd-agent-modes/dist/cli/args.js +2 -2
  73. package/packages/gsd-agent-modes/dist/cli/args.js.map +1 -1
  74. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  75. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +1 -0
  76. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  77. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  78. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js +1 -0
  79. package/packages/gsd-agent-modes/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  80. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  81. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js +1 -0
  82. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  83. package/packages/gsd-agent-modes/package.json +7 -7
  84. package/packages/gsd-cloud/README.md +12 -2
  85. package/packages/gsd-cloud/dist/background-cli.test.js +157 -80
  86. package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -1
  87. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
  88. package/packages/gsd-cloud/dist/cli.js +36 -12
  89. package/packages/gsd-cloud/dist/cli.js.map +1 -1
  90. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +6 -1
  91. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
  92. package/packages/gsd-cloud/dist/cloud-runtime.js +106 -24
  93. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
  94. package/packages/gsd-cloud/dist/cloud-runtime.test.js +224 -59
  95. package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -1
  96. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts +3 -0
  97. package/packages/gsd-cloud/dist/runtime-artifacts.d.ts.map +1 -0
  98. package/packages/gsd-cloud/dist/runtime-artifacts.js +36 -0
  99. package/packages/gsd-cloud/dist/runtime-artifacts.js.map +1 -0
  100. package/packages/gsd-cloud/dist/runtime-process.d.ts +5 -0
  101. package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -1
  102. package/packages/gsd-cloud/dist/runtime-process.js +429 -61
  103. package/packages/gsd-cloud/dist/runtime-process.js.map +1 -1
  104. package/packages/gsd-cloud/dist/runtime-process.test.js +493 -47
  105. package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -1
  106. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts +117 -0
  107. package/packages/gsd-cloud/dist/runtime-telemetry.d.ts.map +1 -0
  108. package/packages/gsd-cloud/dist/runtime-telemetry.js +291 -0
  109. package/packages/gsd-cloud/dist/runtime-telemetry.js.map +1 -0
  110. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts +2 -0
  111. package/packages/gsd-cloud/dist/runtime-telemetry.test.d.ts.map +1 -0
  112. package/packages/gsd-cloud/dist/runtime-telemetry.test.js +434 -0
  113. package/packages/gsd-cloud/dist/runtime-telemetry.test.js.map +1 -0
  114. package/packages/gsd-cloud/package.json +2 -2
  115. package/packages/mcp-server/package.json +4 -4
  116. package/packages/native/package.json +1 -1
  117. package/packages/pi-agent-core/dist/types.d.ts +2 -2
  118. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  119. package/packages/pi-agent-core/dist/types.js.map +1 -1
  120. package/packages/pi-agent-core/package.json +1 -1
  121. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/models.generated.d.ts +113 -2
  123. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  124. package/packages/pi-ai/dist/models.generated.js +84 -9
  125. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  126. package/packages/pi-ai/dist/models.js +18 -6
  127. package/packages/pi-ai/dist/models.js.map +1 -1
  128. package/packages/pi-ai/dist/providers/amazon-bedrock.js +3 -2
  129. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  130. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts +1 -1
  131. package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
  133. package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
  134. package/packages/pi-ai/dist/providers/google.js.map +1 -1
  135. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts +1 -1
  136. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  137. package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
  138. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  139. package/packages/pi-ai/dist/providers/openai-completions.d.ts +1 -1
  140. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  141. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  142. package/packages/pi-ai/dist/providers/openai-responses.d.ts +1 -1
  143. package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
  144. package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
  145. package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
  146. package/packages/pi-ai/dist/providers/simple-options.d.ts +1 -1
  147. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  148. package/packages/pi-ai/dist/providers/simple-options.js +1 -1
  149. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  150. package/packages/pi-ai/dist/types.d.ts +9 -1
  151. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  152. package/packages/pi-ai/dist/types.js.map +1 -1
  153. package/packages/pi-ai/package.json +1 -1
  154. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/model-registry.js +1 -0
  156. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
  158. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -3
  160. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  162. package/packages/pi-coding-agent/dist/theme/theme.d.ts +1 -1
  163. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  164. package/packages/pi-coding-agent/dist/theme/theme.js +1 -0
  165. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  166. package/packages/pi-coding-agent/package.json +7 -7
  167. package/packages/pi-tui/package.json +2 -2
  168. package/packages/rpc-client/package.json +2 -2
  169. package/pkg/dist/theme/theme.d.ts +1 -1
  170. package/pkg/dist/theme/theme.d.ts.map +1 -1
  171. package/pkg/dist/theme/theme.js +1 -0
  172. package/pkg/dist/theme/theme.js.map +1 -1
  173. package/pkg/package.json +1 -1
  174. package/src/resources/extensions/gsd/auto-model-selection.ts +1 -0
  175. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  176. package/src/resources/extensions/gsd/auto-verification.ts +46 -12
  177. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +4 -3
  178. package/src/resources/extensions/gsd/db/engine.ts +9 -1
  179. package/src/resources/extensions/gsd/db-canonical-foundation-schema.ts +129 -0
  180. package/src/resources/extensions/gsd/db-migration-steps.ts +5 -0
  181. package/src/resources/extensions/gsd/docs/preferences-reference.md +12 -3
  182. package/src/resources/extensions/gsd/doctor-engine-checks.ts +15 -4
  183. package/src/resources/extensions/gsd/model-cost-table.ts +14 -3
  184. package/src/resources/extensions/gsd/model-router.ts +10 -5
  185. package/src/resources/extensions/gsd/planning-subagent-registry.ts +18 -4
  186. package/src/resources/extensions/gsd/preferences-models.ts +54 -10
  187. package/src/resources/extensions/gsd/preferences-types.ts +10 -2
  188. package/src/resources/extensions/gsd/preferences-validation.ts +49 -6
  189. package/src/resources/extensions/gsd/preferences.ts +13 -0
  190. package/src/resources/extensions/gsd/templates/PREFERENCES.md +3 -0
  191. package/src/resources/extensions/gsd/tests/db-canonical-foundation.test.ts +459 -0
  192. package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +3 -3
  193. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +64 -0
  194. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +22 -7
  195. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +55 -0
  196. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  197. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +24 -0
  198. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +38 -1
  199. package/src/resources/extensions/gsd/tests/preferences.test.ts +71 -4
  200. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +3 -0
  201. package/src/resources/extensions/gsd/tests/service-tier.test.ts +2 -2
  202. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +1 -0
  203. package/src/resources/extensions/gsd/tests/thinking-level-resolution.test.ts +1 -1
  204. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +12 -0
  205. package/src/resources/extensions/gsd/tests/workflow-authority-faults.test.ts +238 -0
  206. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.test.ts +48 -0
  207. package/src/resources/extensions/gsd/tests/workflow-authority-fixture.ts +141 -0
  208. package/src/resources/extensions/gsd/tests/workflow-authority-projection-conflict.test.ts +177 -0
  209. package/src/resources/extensions/gsd/tests/workflow-fault-harness.test.ts +39 -0
  210. package/src/resources/extensions/gsd/tests/workflow-fault-harness.ts +72 -0
  211. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +40 -0
  212. package/src/resources/extensions/gsd/tools/plan-slice.ts +5 -1
  213. package/src/resources/extensions/gsd/verification-evidence.ts +5 -2
  214. package/src/resources/extensions/ollama/ollama-chat-provider.ts +2 -2
  215. package/src/resources/extensions/ollama/tests/ollama-chat-provider-think.test.ts +4 -0
  216. package/src/resources/extensions/subagent/index.ts +1 -1
  217. /package/dist/web/standalone/.next/static/{EAhnhYMt5UVOiG9-iPDjc → 8ub6oiEp2YnlOHQU20LJr}/_buildManifest.js +0 -0
  218. /package/dist/web/standalone/.next/static/{EAhnhYMt5UVOiG9-iPDjc → 8ub6oiEp2YnlOHQU20LJr}/_ssgManifest.js +0 -0
package/README.md CHANGED
@@ -28,16 +28,16 @@ See [CHANGELOG.md](./CHANGELOG.md) for release-by-release fixes and [Legacy Rele
28
28
  ## Latest Release Highlights
29
29
 
30
30
  <!-- release-highlights:start -->
31
- Latest release: **v1.10.0**
31
+ Latest release: **v1.11.0**
32
32
 
33
- - **pi-ai:** Add Meta Muse Spark 1.1 to the model catalog.
34
- - **gsd:** Surface cacheRetention as a settable prompt-cache TTL preference.
35
- - **models:** Add GPT-5.6 as a first-class model.
36
- - **models:** Add Grok 4.5 as a first-class xAI model.
37
- - **agent-core:** Pin compaction summarization reasoning to low, bound degenerate-chunk retry.
38
- - **gsd:** Filter superseded context injections from provider payload.
39
- - **gsd:** Stop advertising alias tool schemas on model-facing surfaces.
40
- - **gsd:** Cap execute-task and discuss-slice inlined context; provider-aware budgets.
33
+ - **gsd:** Add canonical database foundation (#1426).
34
+ - **gsd-cloud:** Add macOS cloud monitor app and runtime telemetry (#1399).
35
+ - **Added:** Add workflow authority baseline gate (#1422).
36
+ - **issue:** [Feature]: Support using pi-llama-cpp for local models (#1425).
37
+ - **issue:** [Bug]: GPT-5.6 ships as the nonexistent bare `gpt-5.6` slug — the real routable models are gpt-5.6-sol/terra/luna (#1424).
38
+ - **issue:** Fix(doctor): artifact_file_missing false positive for active-worktree artifacts; checkbox_db_status_divergence regression on plan-slice re-dispatch (#1420).
39
+ - **issue:** Planning_subagents should support user-defined planning agents.
40
+ - **issue:** Auto-verification.js writes VERIFY.json to legacy tasks/ subdir in flat-phase projects.
41
41
 
42
42
  <!-- release-highlights:end -->
43
43
 
@@ -194,6 +194,7 @@ Then use slash commands inside the GSD session:
194
194
  | --- | --- |
195
195
  | `src/` | Core runtime resources and bundled extensions |
196
196
  | `packages/` | Workspace packages used by the CLI, agent, TUI, RPC, and native bridge |
197
+ | `apps/` | Standalone native apps, such as the GSD Cloud Monitor menu bar app |
197
198
  | `native/` | Native engine packaging and platform binaries |
198
199
  | `studio/` | Desktop studio app |
199
200
  | `web/` | Web UI and API surface |
@@ -1 +1 @@
1
- 5f0f1c788a126951
1
+ 2b84d39575210a02
@@ -314,6 +314,7 @@ const THINKING_LEVEL_ORDER = [
314
314
  "medium",
315
315
  "high",
316
316
  "xhigh",
317
+ "max",
317
318
  ];
318
319
  /**
319
320
  * Minimum reasoning level for code-writing units.
@@ -821,7 +821,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
821
821
  // provider the user is not logged into and surface as "Not logged in · Please
822
822
  // run /login" before pausing and resetting to claude-code/claude-sonnet-4-6.
823
823
  const manualSessionOverride = getSessionModelOverride(ctx.sessionManager.getSessionId());
824
- const sessionProviderIsCustom = isCustomProvider(ctx.model?.provider);
824
+ const sessionProviderIsCustom = isCustomProvider(ctx.model?.provider, ctx.modelRegistry);
825
825
  const profileModelIds = modelIdsForProfileResolution(ctx.modelRegistry, resolveProfileAnchorProvider(ctx.model?.provider), resolveDisabledModelProvidersFromPreferences());
826
826
  const preferredModel = sessionProviderIsCustom
827
827
  ? null
@@ -1,7 +1,7 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Post-unit verification gate for GSD auto-mode units.
3
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
4
- import { relMilestoneFile, resolveSlicePath } from "./paths.js";
4
+ import { legacyMilestonesDir, relMilestoneFile, resolveMilestonePath, resolveSlicePath } from "./paths.js";
5
5
  import { resolveMilestoneValidationVerdict } from "./milestone-validation-verdict.js";
6
6
  import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
7
7
  import { parseUnitId } from "./unit-id.js";
@@ -24,6 +24,20 @@ import { getLedger } from "./metrics.js";
24
24
  import { getUnitCostSpikeAction, resolveUnitCostSpikeMultiplier } from "./auto-budget.js";
25
25
  import { formatPostUnitStatusCard } from "./auto-status-message.js";
26
26
  import { detectWebApp } from "./web-app-uat.js";
27
+ function resolveVerificationEvidenceLocation(basePath, milestoneId, sliceId) {
28
+ const mDir = resolveMilestonePath(basePath, milestoneId);
29
+ if (!mDir)
30
+ return null;
31
+ const legacyBase = legacyMilestonesDir(basePath);
32
+ const isLegacy = mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\");
33
+ if (!isLegacy) {
34
+ return { dir: mDir, fileSliceId: sliceId };
35
+ }
36
+ const sDir = resolveSlicePath(basePath, milestoneId, sliceId);
37
+ if (!sDir)
38
+ return null;
39
+ return { dir: join(sDir, "tasks") };
40
+ }
27
41
  function getCurrentUnitCostStats(unitId) {
28
42
  const ledger = getLedger();
29
43
  if (!ledger || !Array.isArray(ledger.units) || ledger.units.length === 0) {
@@ -431,11 +445,10 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
431
445
  const taskAlreadyComplete = taskRow?.status === "complete" || taskRow?.status === "done";
432
446
  if (mid && sid && tid) {
433
447
  try {
434
- const sDir = resolveSlicePath(s.basePath, mid, sid);
435
- if (sDir) {
436
- const tasksDir = join(sDir, "tasks");
448
+ const evidenceLocation = resolveVerificationEvidenceLocation(s.basePath, mid, sid);
449
+ if (evidenceLocation) {
437
450
  if (result.passed) {
438
- writeVerificationJSON(result, tasksDir, tid, s.currentUnit.id);
451
+ writeVerificationJSON(result, evidenceLocation.dir, tid, s.currentUnit.id, undefined, undefined, evidenceLocation.fileSliceId);
439
452
  }
440
453
  else {
441
454
  const nextAttempt = attempt + 1;
@@ -444,7 +457,7 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
444
457
  verdict.retryable &&
445
458
  autoFixEnabled &&
446
459
  nextAttempt <= maxRetries;
447
- writeVerificationJSON(result, tasksDir, tid, s.currentUnit.id, includeRetryMetadata ? nextAttempt : undefined, includeRetryMetadata ? maxRetries : undefined);
460
+ writeVerificationJSON(result, evidenceLocation.dir, tid, s.currentUnit.id, includeRetryMetadata ? nextAttempt : undefined, includeRetryMetadata ? maxRetries : undefined, evidenceLocation.fileSliceId);
448
461
  }
449
462
  }
450
463
  }
@@ -560,9 +573,8 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
560
573
  // Re-write verification evidence JSON with post-execution checks
561
574
  if (postExecChecks && postExecChecks.length > 0 && mid && sid && tid) {
562
575
  try {
563
- const sDir = resolveSlicePath(s.basePath, mid, sid);
564
- if (sDir) {
565
- const tasksDir = join(sDir, "tasks");
576
+ const evidenceLocation = resolveVerificationEvidenceLocation(s.basePath, mid, sid);
577
+ if (evidenceLocation) {
566
578
  // Add postExecutionChecks to the result for the JSON write
567
579
  const resultWithPostExec = {
568
580
  ...result,
@@ -570,7 +582,7 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
570
582
  passed: result.passed && !postExecBlockingFailure,
571
583
  };
572
584
  // Manually write with postExecutionChecks field
573
- writeVerificationJSONWithPostExec(resultWithPostExec, tasksDir, tid, s.currentUnit.id, postExecChecks, postExecBlockingFailure ? attempt + 1 : undefined, postExecBlockingFailure ? maxRetries : undefined);
585
+ writeVerificationJSONWithPostExec(resultWithPostExec, evidenceLocation.dir, tid, s.currentUnit.id, postExecChecks, postExecBlockingFailure ? attempt + 1 : undefined, postExecBlockingFailure ? maxRetries : undefined, evidenceLocation.fileSliceId);
574
586
  }
575
587
  }
576
588
  catch (evidenceErr) {
@@ -738,7 +750,7 @@ export async function runPostUnitVerification(vctx, pauseAuto) {
738
750
  * Write verification evidence JSON with post-execution checks included.
739
751
  * This is a variant of writeVerificationJSON that adds the postExecutionChecks field.
740
752
  */
741
- function writeVerificationJSONWithPostExec(result, tasksDir, taskId, unitId, postExecutionChecks, retryAttempt, maxRetries) {
753
+ function writeVerificationJSONWithPostExec(result, tasksDir, taskId, unitId, postExecutionChecks, retryAttempt, maxRetries, sliceId) {
742
754
  mkdirSync(tasksDir, { recursive: true });
743
755
  const evidence = {
744
756
  schemaVersion: 1,
@@ -774,6 +786,7 @@ function writeVerificationJSONWithPostExec(result, tasksDir, taskId, unitId, pos
774
786
  fixAvailable: w.fixAvailable,
775
787
  }));
776
788
  }
777
- const filePath = join(tasksDir, `${taskId}-VERIFY.json`);
789
+ const fileName = sliceId ? `${sliceId}-${taskId}-VERIFY.json` : `${taskId}-VERIFY.json`;
790
+ const filePath = join(tasksDir, fileName);
778
791
  writeFileSync(filePath, JSON.stringify(evidence, null, 2) + "\n", "utf-8");
779
792
  }
@@ -5,7 +5,7 @@ import { minimatch } from "minimatch";
5
5
  import { GSD_PHASE_SCOPE_DISPLAY_REASON, shouldBlockAutoUnitToolCall } from "../auto-unit-tool-scope.js";
6
6
  import { canonicalToolName } from "../engine-hook-contract.js";
7
7
  import { loadJsonFileOrNull } from "../json-persistence.js";
8
- import { getIsolationMode } from "../preferences.js";
8
+ import { getIsolationMode, loadEffectiveGSDPreferences } from "../preferences.js";
9
9
  import { compileSubagentPermissionContract } from "../unit-context-manifest.js";
10
10
  import { allowedPlanningDispatchAgentsList, isReadOnlyPlanningDispatchAgent, } from "../planning-subagent-registry.js";
11
11
  import { logWarning } from "../workflow-logger.js";
@@ -1118,6 +1118,7 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
1118
1118
  const dispatchContract = compileSubagentPermissionContract(policy);
1119
1119
  const allowedSubagents = dispatchContract.allowedSubagents;
1120
1120
  const allowed = new Set(allowedSubagents);
1121
+ const planningSubagentRegistry = loadEffectiveGSDPreferences(basePath)?.preferences.planning_subagent_registry;
1121
1122
  // When agentClasses is undefined, the caller has not been updated to extract
1122
1123
  // agent identities yet. Block and warn so stale callers surface in telemetry
1123
1124
  // instead of silently bypassing the gate.
@@ -1131,9 +1132,9 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
1131
1132
  if (requested.length === 0) {
1132
1133
  return { block: false };
1133
1134
  }
1134
- const globallyDisallowed = requested.find(a => !isReadOnlyPlanningDispatchAgent(a));
1135
+ const globallyDisallowed = requested.find(a => !isReadOnlyPlanningDispatchAgent(a, planningSubagentRegistry));
1135
1136
  if (globallyDisallowed) {
1136
- return planningBlock(unitType, policy.mode, `subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList()}) may be dispatched from ${policy.mode} units`);
1137
+ return planningBlock(unitType, policy.mode, `subagent dispatch of "${globallyDisallowed}" not permitted; only read-only specialists (${allowedPlanningDispatchAgentsList(planningSubagentRegistry)}) may be dispatched from ${policy.mode} units`);
1137
1138
  }
1138
1139
  const disallowedByPolicy = requested.find(a => !allowed.has(a));
1139
1140
  if (disallowedByPolicy) {
@@ -18,7 +18,8 @@ import { createBaseSchemaObjects } from "../db-base-schema.js";
18
18
  import { createCoordinationTablesV24 } from "../db-coordination-schema.js";
19
19
  import { createDbConnectionCache } from "../db-connection-cache.js";
20
20
  import { backupDatabaseBeforeMigration, isMigrationBackupError } from "../db-migration-backup.js";
21
- import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, applyMigrationV26MilestoneCommitAttributions, applyMigrationV27ArtifactHash, applyMigrationV28MemoryLastHitAt, applyMigrationV29RepositoryTargets, applyMigrationV30ReworkBriefs, } from "../db-migration-steps.js";
21
+ import { applyMigrationV2Artifacts, applyMigrationV3Memories, applyMigrationV4DecisionMadeBy, applyMigrationV5HierarchyTables, applyMigrationV6SliceSummaries, applyMigrationV7Dependencies, applyMigrationV8PlanningFields, applyMigrationV9Ordering, applyMigrationV10ReplanTrigger, applyMigrationV11TaskPlanning, applyMigrationV12QualityGates, applyMigrationV13HotPathIndexes, applyMigrationV14SliceDependencies, applyMigrationV15AuditTables, applyMigrationV16EscalationSource, applyMigrationV17TaskEscalation, applyMigrationV18MemorySources, applyMigrationV19MemoryFts, applyMigrationV20MemoryRelations, applyMigrationV21StructuredMemories, applyMigrationV22QualityGateRepair, applyMigrationV23MilestoneQueue, applyMigrationV26MilestoneCommitAttributions, applyMigrationV27ArtifactHash, applyMigrationV28MemoryLastHitAt, applyMigrationV29RepositoryTargets, applyMigrationV30ReworkBriefs, applyMigrationV31CanonicalFoundation, } from "../db-migration-steps.js";
22
+ import { createCanonicalFoundationSchemaV31 } from "../db-canonical-foundation-schema.js";
22
23
  import { isMemoriesFtsAvailableSchema, rebuildMemoriesFtsSchemaOnce, tryCreateMemoriesFtsSchema, } from "../db-memory-fts-schema.js";
23
24
  import { createDbOpenState } from "../db-open-state.js";
24
25
  import { createRuntimeKvTableV25 } from "../db-runtime-kv-schema.js";
@@ -58,7 +59,7 @@ const providerLoader = createSqliteProviderLoader({
58
59
  nodeVersion: process.versions.node,
59
60
  writeStderr: (message) => process.stderr.write(message),
60
61
  });
61
- export const SCHEMA_VERSION = 30;
62
+ export const SCHEMA_VERSION = 31;
62
63
  function initSchema(db, fileBacked, dbPath) {
63
64
  const conservativeFilePragmas = fileBacked && _isLikelyWslDrvFsPathForTest(dbPath);
64
65
  if (fileBacked)
@@ -104,6 +105,7 @@ function initSchema(db, fileBacked, dbPath) {
104
105
  else {
105
106
  createCoordinationTablesV24(db);
106
107
  createRuntimeKvTableV25(db);
108
+ createCanonicalFoundationSchemaV31(db);
107
109
  // Fresh install — all tables are created above with the full current schema,
108
110
  // so it is safe to create all migration-specific indexes here. For existing
109
111
  // databases these indexes are created inside the individual migration guards
@@ -318,6 +320,10 @@ function migrateSchema(db, dbPath) {
318
320
  applyMigrationV30ReworkBriefs(db);
319
321
  recordSchemaVersion(db, 30);
320
322
  }
323
+ if (currentVersion < 31) {
324
+ applyMigrationV31CanonicalFoundation(db);
325
+ recordSchemaVersion(db, 31);
326
+ }
321
327
  if (_migrationFaultForTest)
322
328
  throw new Error("migration fault injected for test");
323
329
  db.exec("COMMIT");
@@ -0,0 +1,120 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Additive v31 authority, operation provenance, domain event, and outbox schema.
3
+ /**
4
+ * v31 mapping:
5
+ * - schema_version remains DDL compatibility, not the domain revision.
6
+ * - audit_events remains optional operational telemetry, not domain history.
7
+ * - milestone_commit_attributions remains Git-specific, not operation provenance.
8
+ * - command_queue and runtime_kv remain coordination/cache surfaces, not an outbox.
9
+ */
10
+ export function createCanonicalFoundationSchemaV31(db) {
11
+ db.exec(`
12
+ CREATE TABLE IF NOT EXISTS project_authority (
13
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
14
+ project_id TEXT NOT NULL UNIQUE,
15
+ project_root_realpath TEXT NOT NULL DEFAULT '',
16
+ revision INTEGER NOT NULL DEFAULT 0 CHECK (revision >= 0),
17
+ authority_epoch INTEGER NOT NULL DEFAULT 0 CHECK (authority_epoch >= 0),
18
+ created_at TEXT NOT NULL DEFAULT '',
19
+ updated_at TEXT NOT NULL DEFAULT ''
20
+ )
21
+ `);
22
+ db.exec(`
23
+ CREATE TABLE IF NOT EXISTS workflow_operations (
24
+ operation_id TEXT PRIMARY KEY,
25
+ project_id TEXT NOT NULL,
26
+ operation_type TEXT NOT NULL,
27
+ idempotency_key TEXT NOT NULL,
28
+ expected_revision INTEGER NOT NULL CHECK (expected_revision >= 0),
29
+ resulting_revision INTEGER NOT NULL CHECK (resulting_revision = expected_revision + 1),
30
+ expected_authority_epoch INTEGER NOT NULL CHECK (expected_authority_epoch >= 0),
31
+ resulting_authority_epoch INTEGER NOT NULL CHECK (
32
+ resulting_authority_epoch = expected_authority_epoch OR
33
+ resulting_authority_epoch = expected_authority_epoch + 1
34
+ ),
35
+ actor_type TEXT NOT NULL,
36
+ actor_id TEXT DEFAULT NULL,
37
+ source_transport TEXT NOT NULL,
38
+ trace_id TEXT DEFAULT NULL,
39
+ turn_id TEXT DEFAULT NULL,
40
+ request_hash TEXT NOT NULL,
41
+ created_at TEXT NOT NULL,
42
+ UNIQUE (project_id, idempotency_key),
43
+ UNIQUE (project_id, resulting_revision),
44
+ UNIQUE (operation_id, project_id, resulting_revision, resulting_authority_epoch),
45
+ FOREIGN KEY (project_id) REFERENCES project_authority(project_id)
46
+ )
47
+ `);
48
+ db.exec(`
49
+ CREATE TABLE IF NOT EXISTS workflow_domain_events (
50
+ event_id TEXT PRIMARY KEY,
51
+ operation_id TEXT NOT NULL,
52
+ event_index INTEGER NOT NULL DEFAULT 0 CHECK (event_index >= 0),
53
+ project_id TEXT NOT NULL,
54
+ project_revision INTEGER NOT NULL CHECK (project_revision > 0),
55
+ authority_epoch INTEGER NOT NULL CHECK (authority_epoch >= 0),
56
+ event_type TEXT NOT NULL,
57
+ entity_type TEXT NOT NULL,
58
+ entity_id TEXT NOT NULL,
59
+ caused_by_event_id TEXT DEFAULT NULL,
60
+ payload_json TEXT NOT NULL DEFAULT '{}',
61
+ created_at TEXT NOT NULL,
62
+ UNIQUE (operation_id, event_index),
63
+ FOREIGN KEY (operation_id, project_id, project_revision, authority_epoch)
64
+ REFERENCES workflow_operations(
65
+ operation_id, project_id, resulting_revision, resulting_authority_epoch
66
+ ),
67
+ FOREIGN KEY (caused_by_event_id) REFERENCES workflow_domain_events(event_id)
68
+ )
69
+ `);
70
+ db.exec(`
71
+ CREATE TRIGGER IF NOT EXISTS trg_workflow_domain_events_immutable_update
72
+ BEFORE UPDATE ON workflow_domain_events
73
+ BEGIN
74
+ SELECT RAISE(ABORT, 'workflow domain events are immutable');
75
+ END
76
+ `);
77
+ db.exec(`
78
+ CREATE TRIGGER IF NOT EXISTS trg_workflow_domain_events_immutable_delete
79
+ BEFORE DELETE ON workflow_domain_events
80
+ BEGIN
81
+ SELECT RAISE(ABORT, 'workflow domain events are immutable');
82
+ END
83
+ `);
84
+ db.exec(`
85
+ CREATE TABLE IF NOT EXISTS workflow_outbox (
86
+ outbox_id INTEGER PRIMARY KEY AUTOINCREMENT,
87
+ event_id TEXT NOT NULL,
88
+ destination TEXT NOT NULL,
89
+ available_at TEXT NOT NULL DEFAULT '',
90
+ attempt_count INTEGER NOT NULL DEFAULT 0 CHECK (attempt_count >= 0),
91
+ claimed_by TEXT DEFAULT NULL,
92
+ claim_expires_at TEXT DEFAULT NULL,
93
+ delivered_at TEXT DEFAULT NULL,
94
+ last_error TEXT DEFAULT NULL,
95
+ UNIQUE (event_id, destination),
96
+ FOREIGN KEY (event_id) REFERENCES workflow_domain_events(event_id)
97
+ )
98
+ `);
99
+ db.exec(`
100
+ CREATE INDEX IF NOT EXISTS idx_workflow_operations_created
101
+ ON workflow_operations(project_id, created_at, operation_id)
102
+ `);
103
+ db.exec(`
104
+ CREATE INDEX IF NOT EXISTS idx_workflow_domain_events_entity
105
+ ON workflow_domain_events(project_id, entity_type, entity_id, project_revision, event_index)
106
+ `);
107
+ db.exec(`
108
+ CREATE INDEX IF NOT EXISTS idx_workflow_outbox_pending
109
+ ON workflow_outbox(delivered_at, available_at, outbox_id)
110
+ `);
111
+ db.exec(`
112
+ INSERT OR IGNORE INTO project_authority (
113
+ singleton, project_id, project_root_realpath,
114
+ revision, authority_epoch, created_at, updated_at
115
+ ) VALUES (
116
+ 1, lower(hex(randomblob(16))), '',
117
+ 0, 0, '', ''
118
+ )
119
+ `);
120
+ }
@@ -1,5 +1,6 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Schema migration DDL steps for the GSD database facade.
3
+ import { createCanonicalFoundationSchemaV31 } from "./db-canonical-foundation-schema.js";
3
4
  import { ensureColumn } from "./db-schema-metadata.js";
4
5
  export function applyMigrationV2Artifacts(db) {
5
6
  db.exec(`
@@ -448,3 +449,6 @@ export function applyMigrationV30ReworkBriefs(db) {
448
449
  db.exec("CREATE INDEX IF NOT EXISTS idx_rework_briefs_task ON rework_briefs(milestone_id, slice_id, task_id)");
449
450
  db.exec("CREATE INDEX IF NOT EXISTS idx_rework_findings_status ON rework_brief_findings(brief_id, severity, status)");
450
451
  }
452
+ export function applyMigrationV31CanonicalFoundation(db) {
453
+ createCanonicalFoundationSchemaV31(db);
454
+ }
@@ -124,11 +124,11 @@ Diagnostics record the file path, scope (global/project), severity (error/warnin
124
124
  - `validation` — used for gate evaluation, roadmap reassessment, milestone validation, and doc rewrites. Falls back to `planning` if unset.
125
125
  - `uat` — used for UAT runs. Falls back to `completion` if unset.
126
126
 
127
- - `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
127
+ - `thinking`: per-phase reasoning effort (ADR-026), separate from `models`. Same phase keys as `models`. Values: `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`. Thinking travels with the model — model choice and reasoning effort are independent controls, so you can run one model across phases at different reasoning levels.
128
128
  - Two equivalent ways to set it: inline as `models.<phase>.thinking`, or as a separate `thinking:` block keyed by phase. For the same phase, the inline value wins over the block; project preferences win over global; a phase that's unset inherits via the same sibling chain as `models` (e.g. `discuss → planning`).
129
129
  - If no thinking is configured for a phase, the session level (set via `/model`) is used, exactly as before — this is fully backward-compatible.
130
130
  - `execute-task` (code-writing) has a measured reasoning floor of `medium`: at lower levels models stop planning edits and thrash on file re-reads. The floor applies to the session/default path. An **explicit** `execution` thinking level bypasses the floor and is honored verbatim (with a one-time advisory) — set `execution: low` deliberately if you want it.
131
- - Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` support is model-dependent; current examples include OpenAI Codex Max models and Claude Sonnet 5 on Amazon Bedrock.
131
+ - Levels a model can't support are clamped to the nearest supported level at dispatch and never sent to the provider, so a model/level mismatch never fails a unit mid-run. `xhigh` and `max` support are model-dependent; current examples include `gpt-5.6-sol`, `gpt-5.6-terra`, and `gpt-5.6-luna`, which advertise both.
132
132
 
133
133
  - `skill_staleness_days`: number — skills unused for this many days get deprioritized during discovery. Set to `0` to disable staleness tracking. Default: `60`.
134
134
 
@@ -357,24 +357,33 @@ In `"parent"` mode, slice/task `targetRepositories` default to the declared chil
357
357
  - `"replace"` requires `prompt`.
358
358
  - `"skip"` is valid with no additional fields.
359
359
 
360
+ - `planning_subagent_registry`: object — optional classification for custom planning agents. Each key is an agent id and must set:
361
+ - `read_only_specialist`: boolean — set to `true` to classify the custom agent as safe for planning dispatch.
362
+
360
363
  - `planning_subagents`: object — project-local widening for read-only planning subagent dispatch. Supported unit keys are `plan-milestone` and `plan-slice`; each has:
361
364
  - `allowed`: string[] — additional planning-safe agents for that unit.
362
365
 
363
- Agents must already be classified by GSD as read-only planning specialists: `mnemo`, `scout`, `planner`, `reviewer`, `security`, or `tester`. The write gate still blocks source writes outside `.gsd/**`, unrestricted bash, stale subagent calls without agent identities, agents outside the global read-only registry, and agents not listed for the active unit.
366
+ Agents must be built-in read-only planning specialists (`mnemo`, `scout`, `planner`, `reviewer`, `security`, or `tester`) or registered in `planning_subagent_registry` with `read_only_specialist: true`. The write gate still blocks source writes outside `.gsd/**`, unrestricted bash, stale subagent calls without agent identities, agents outside the read-only registry, and agents not listed for the active unit.
364
367
 
365
368
  Example:
366
369
 
367
370
  ```yaml
371
+ planning_subagent_registry:
372
+ my-custom-planner:
373
+ read_only_specialist: true
374
+
368
375
  planning_subagents:
369
376
  plan-milestone:
370
377
  allowed:
371
378
  - scout
372
379
  - planner
380
+ - my-custom-planner
373
381
  - security
374
382
  plan-slice:
375
383
  allowed:
376
384
  - scout
377
385
  - planner
386
+ - my-custom-planner
378
387
  - reviewer
379
388
  - security
380
389
  ```
@@ -13,6 +13,7 @@ import { parseRoadmapSlices } from "./roadmap-slices.js";
13
13
  import { parsePlan } from "./parsers-legacy.js";
14
14
  import { parseSummary } from "./files.js";
15
15
  import { LAYOUT_SEGMENTS } from "./layout-policy.js";
16
+ import { resolveCanonicalMilestoneRoot } from "./worktree-manager.js";
16
17
  const USER_AUTHORED_ARTIFACT_TYPES = new Set(["CONTEXT", "RESEARCH"]);
17
18
  function escapeRegExp(value) {
18
19
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -77,7 +78,8 @@ function checkboxDbStatusMilestoneIds(basePath, milestoneIds) {
77
78
  }
78
79
  function checkProjectionCheckboxDbStatus(basePath, milestoneIds, issues) {
79
80
  for (const milestoneId of milestoneIds) {
80
- const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
81
+ const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, milestoneId);
82
+ const roadmapPath = resolveMilestoneFile(artifactBasePath, milestoneId, "ROADMAP");
81
83
  const slices = getMilestoneSlices(milestoneId);
82
84
  if (roadmapPath && existsSync(roadmapPath)) {
83
85
  try {
@@ -95,7 +97,7 @@ function checkProjectionCheckboxDbStatus(basePath, milestoneIds, issues) {
95
97
  }
96
98
  }
97
99
  for (const slice of slices) {
98
- const planPath = resolveSliceFile(basePath, milestoneId, slice.id, "PLAN");
100
+ const planPath = resolveSliceFile(artifactBasePath, milestoneId, slice.id, "PLAN");
99
101
  if (!planPath || !existsSync(planPath))
100
102
  continue;
101
103
  try {
@@ -134,7 +136,7 @@ function isClearedByMilestoneShellProjectionFlush(basePath, issue, reRenderedMil
134
136
  function artifactExistsOnDisk(basePath, artifactPath, row) {
135
137
  return resolveArtifactDiskPath(basePath, artifactPath, row) !== null;
136
138
  }
137
- function resolveLiteralArtifactDiskPath(basePath, artifactPath) {
139
+ function resolveLiteralArtifactDiskPath(basePath, artifactPath, row) {
138
140
  const relativeArtifactPath = artifactPathRelativeToGsd(artifactPath);
139
141
  if (isAbsolute(relativeArtifactPath)) {
140
142
  return existsSync(relativeArtifactPath) ? relativeArtifactPath : null;
@@ -144,6 +146,16 @@ function resolveLiteralArtifactDiskPath(basePath, artifactPath) {
144
146
  if (isPathInside(root, candidate) && existsSync(candidate))
145
147
  return candidate;
146
148
  }
149
+ if (row?.milestone_id) {
150
+ const artifactBasePath = resolveCanonicalMilestoneRoot(basePath, row.milestone_id);
151
+ if (artifactBasePath !== basePath) {
152
+ for (const root of [gsdProjectionRoot(artifactBasePath), gsdRoot(artifactBasePath)]) {
153
+ const candidate = join(root, relativeArtifactPath);
154
+ if (isPathInside(root, candidate) && existsSync(candidate))
155
+ return candidate;
156
+ }
157
+ }
158
+ }
147
159
  return null;
148
160
  }
149
161
  function relativeToGsdRoot(basePath, diskPath) {
@@ -183,7 +195,7 @@ function resolveArtifactDiskPath(basePath, artifactPath, row) {
183
195
  if (flatPath)
184
196
  return flatPath;
185
197
  }
186
- return resolveLiteralArtifactDiskPath(basePath, artifactPath);
198
+ return resolveLiteralArtifactDiskPath(basePath, artifactPath, row);
187
199
  }
188
200
  function taskExistsInPlan(basePath, milestoneId, sliceId, taskId) {
189
201
  const planPath = resolveSliceFile(basePath, milestoneId, sliceId, "PLAN");
@@ -48,8 +48,9 @@ export const BUNDLED_COST_TABLE = [
48
48
  // GPT-5.5 API list price, also used for live Codex OAuth routing.
49
49
  // Source: https://openai.com/api/pricing/
50
50
  { id: "gpt-5.5", inputPer1k: 0.005, outputPer1k: 0.03, updatedAt: "2026-04-23" },
51
- // Mirrors gpt-5.5 list pricing until OpenAI publishes official gpt-5.6 pricing.
52
- { id: "gpt-5.6", inputPer1k: 0.005, outputPer1k: 0.03, updatedAt: "2026-07-08" },
51
+ { id: "gpt-5.6-sol", inputPer1k: 0.005, outputPer1k: 0.03, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.01, outputPer1k: 0.045 }], updatedAt: "2026-07-11" },
52
+ { id: "gpt-5.6-terra", inputPer1k: 0.0025, outputPer1k: 0.015, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.005, outputPer1k: 0.0225 }], updatedAt: "2026-07-11" },
53
+ { id: "gpt-5.6-luna", inputPer1k: 0.001, outputPer1k: 0.006, tiers: [{ inputTokensAbove: 272000, inputPer1k: 0.002, outputPer1k: 0.009 }], updatedAt: "2026-07-11" },
53
54
  // Google
54
55
  { id: "gemini-2.0-flash", inputPer1k: 0.0001, outputPer1k: 0.0004, updatedAt: "2025-03-15" },
55
56
  { id: "gemini-flash-2.0", inputPer1k: 0.0001, outputPer1k: 0.0004, updatedAt: "2025-03-15" },
@@ -63,7 +64,9 @@ export const BUNDLED_COST_TABLE = [
63
64
  export function lookupModelCost(modelId) {
64
65
  const bareId = modelId.includes("/") ? modelId.split("/").pop() : modelId;
65
66
  return BUNDLED_COST_TABLE.find(e => e.id === bareId)
66
- ?? BUNDLED_COST_TABLE.find(e => bareId.includes(e.id) || e.id.includes(bareId));
67
+ ?? BUNDLED_COST_TABLE.find(e => bareId.startsWith(`${e.id}-`) ||
68
+ bareId.startsWith(`${e.id}:`) ||
69
+ bareId.startsWith(`${e.id}@`));
67
70
  }
68
71
  /**
69
72
  * Compare two models by input cost. Returns negative if a is cheaper.
@@ -46,7 +46,9 @@ export const MODEL_CAPABILITY_TIER = {
46
46
  "gpt-5.3-codex": "heavy",
47
47
  "gpt-5.4": "heavy",
48
48
  "gpt-5.5": "heavy",
49
- "gpt-5.6": "heavy",
49
+ "gpt-5.6-sol": "heavy",
50
+ "gpt-5.6-terra": "heavy",
51
+ "gpt-5.6-luna": "heavy",
50
52
  "o1": "heavy",
51
53
  "o3": "heavy",
52
54
  "o4-mini": "heavy",
@@ -84,7 +86,9 @@ const MODEL_COST_PER_1K_INPUT = {
84
86
  "gpt-5.3-codex-spark": 0.0003,
85
87
  "gpt-5.4": 0.005,
86
88
  "gpt-5.5": 0.005,
87
- "gpt-5.6": 0.005,
89
+ "gpt-5.6-sol": 0.005,
90
+ "gpt-5.6-terra": 0.0025,
91
+ "gpt-5.6-luna": 0.001,
88
92
  "o4-mini": 0.005,
89
93
  "o4-mini-deep-research": 0.005,
90
94
  "gemini-2.0-flash": 0.0001,
@@ -132,9 +136,10 @@ export const MODEL_CAPABILITY_PROFILES = {
132
136
  // OpenAI's 2026-04-23 published eval deltas across coding, tool use, and long context.
133
137
  // Source: https://openai.com/index/introducing-gpt-5-5/
134
138
  "gpt-5.5": { coding: 96, debugging: 93, research: 89, reasoning: 95, speed: 42, longContext: 90, instruction: 93 },
135
- // GPT-5.6 profile is provisional: a small uniform step over gpt-5.5, to be
136
- // recalibrated once OpenAI publishes official eval results.
137
- "gpt-5.6": { coding: 97, debugging: 94, research: 90, reasoning: 96, speed: 42, longContext: 91, instruction: 94 },
139
+ // GPT-5.6 profiles are provisional until official eval results are available.
140
+ "gpt-5.6-sol": { coding: 97, debugging: 94, research: 90, reasoning: 96, speed: 42, longContext: 91, instruction: 94 },
141
+ "gpt-5.6-terra": { coding: 96, debugging: 93, research: 89, reasoning: 95, speed: 48, longContext: 91, instruction: 93 },
142
+ "gpt-5.6-luna": { coding: 92, debugging: 88, research: 84, reasoning: 91, speed: 72, longContext: 91, instruction: 90 },
138
143
  // ── OpenAI o-series (reasoning-first) ──────────────────────────────────────
139
144
  "o1": { coding: 78, debugging: 82, research: 78, reasoning: 90, speed: 20, longContext: 65, instruction: 82 },
140
145
  "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
@@ -16,10 +16,18 @@ const PLANNING_DISPATCH_AGENT_REGISTRY = {
16
16
  export const ALLOWED_PLANNING_DISPATCH_AGENTS = new Set(Object.entries(PLANNING_DISPATCH_AGENT_REGISTRY)
17
17
  .filter(([, metadata]) => metadata.readOnlySpecialist)
18
18
  .map(([agentId]) => agentId));
19
- export function isReadOnlyPlanningDispatchAgent(agentId) {
19
+ function configuredPlanningDispatchAgents(registry) {
20
+ return Object.entries(registry ?? {})
21
+ .filter(([, metadata]) => metadata.read_only_specialist === true)
22
+ .map(([agentId]) => agentId);
23
+ }
24
+ export function isReadOnlyPlanningDispatchAgent(agentId, registry) {
20
25
  const metadata = PLANNING_DISPATCH_AGENT_REGISTRY[agentId];
21
- return metadata?.readOnlySpecialist === true;
26
+ return metadata?.readOnlySpecialist === true || registry?.[agentId]?.read_only_specialist === true;
22
27
  }
23
- export function allowedPlanningDispatchAgentsList() {
24
- return [...ALLOWED_PLANNING_DISPATCH_AGENTS].join(", ");
28
+ export function allowedPlanningDispatchAgentsList(registry) {
29
+ return Array.from(new Set([
30
+ ...ALLOWED_PLANNING_DISPATCH_AGENTS,
31
+ ...configuredPlanningDispatchAgents(registry),
32
+ ])).join(", ");
25
33
  }