@opengsd/gsd-core 1.7.0 → 1.9.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 (261) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +45 -1
  4. package/README.md +2 -0
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-codebase-mapper.md +1 -1
  7. package/agents/gsd-debug-session-manager.md +78 -4
  8. package/agents/gsd-debugger.md +87 -29
  9. package/agents/gsd-executor.md +49 -9
  10. package/agents/gsd-intel-updater.md +3 -3
  11. package/agents/gsd-phase-researcher.md +4 -2
  12. package/agents/gsd-plan-checker.md +20 -0
  13. package/agents/gsd-planner.md +44 -59
  14. package/agents/gsd-project-researcher.md +2 -2
  15. package/agents/gsd-ui-auditor.md +0 -40
  16. package/agents/gsd-verifier.md +2 -2
  17. package/bin/install.js +1338 -135
  18. package/commands/gsd/ai-integration-phase.md +1 -1
  19. package/commands/gsd/mempalace-capture.md +9 -5
  20. package/commands/gsd/new-milestone.md +1 -1
  21. package/commands/gsd/plan-phase.md +5 -3
  22. package/commands/gsd/plan-review-convergence.md +7 -2
  23. package/gsd-core/bin/gsd-tools.cjs +2690 -2472
  24. package/gsd-core/bin/lib/adapter-imperative.cjs +8 -1
  25. package/gsd-core/bin/lib/agent-command-router.cjs +20 -5
  26. package/gsd-core/bin/lib/api-coverage.cjs +360 -53
  27. package/gsd-core/bin/lib/audit.cjs +8 -8
  28. package/gsd-core/bin/lib/broken-windows.cjs +716 -0
  29. package/gsd-core/bin/lib/capability-command-router.cjs +733 -0
  30. package/gsd-core/bin/lib/capability-consent.cjs +40 -1
  31. package/gsd-core/bin/lib/capability-lifecycle.cjs +58 -0
  32. package/gsd-core/bin/lib/capability-loader.cjs +23 -1
  33. package/gsd-core/bin/lib/capability-registry.cjs +1450 -160
  34. package/gsd-core/bin/lib/capability-trust.cjs +468 -33
  35. package/gsd-core/bin/lib/capability-validator.cjs +882 -6
  36. package/gsd-core/bin/lib/capability-writer.cjs +6 -1
  37. package/gsd-core/bin/lib/check-command-router.cjs +140 -27
  38. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +15 -0
  39. package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +209 -31
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +203 -25
  41. package/gsd-core/bin/lib/command-aliases.cjs +14 -0
  42. package/gsd-core/bin/lib/commands.cjs +326 -21
  43. package/gsd-core/bin/lib/config-loader.cjs +214 -30
  44. package/gsd-core/bin/lib/config.cjs +158 -22
  45. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  46. package/gsd-core/bin/lib/decisions.cjs +32 -8
  47. package/gsd-core/bin/lib/docs.cjs +6 -0
  48. package/gsd-core/bin/lib/estimate-cli.cjs +336 -0
  49. package/gsd-core/bin/lib/external-descriptor-trust.cjs +14 -2
  50. package/gsd-core/bin/lib/frontmatter.cjs +125 -15
  51. package/gsd-core/bin/lib/gap-checker.cjs +17 -2
  52. package/gsd-core/bin/lib/host-integration.cjs +215 -8
  53. package/gsd-core/bin/lib/init.cjs +155 -66
  54. package/gsd-core/bin/lib/install-engine.cjs +299 -23
  55. package/gsd-core/bin/lib/install-profiles.cjs +239 -1
  56. package/gsd-core/bin/lib/installer-migrations/005-opencode-baseline-commands-dir.cjs +146 -0
  57. package/gsd-core/bin/lib/installer-migrations/006-pi-extension-cjs-to-js.cjs +91 -0
  58. package/gsd-core/bin/lib/installer-migrations.cjs +44 -5
  59. package/gsd-core/bin/lib/markdown-sectionizer.cjs +107 -0
  60. package/gsd-core/bin/lib/milestone.cjs +248 -14
  61. package/gsd-core/bin/lib/model-catalog.cjs +69 -4
  62. package/gsd-core/bin/lib/model-resolver.cjs +189 -7
  63. package/gsd-core/bin/lib/observability/logger.cjs +7 -2
  64. package/gsd-core/bin/lib/onboard-projection.cjs +11 -8
  65. package/gsd-core/bin/lib/phase-command-router.cjs +10 -1
  66. package/gsd-core/bin/lib/phase-estimation.cjs +398 -0
  67. package/gsd-core/bin/lib/phase-id.cjs +304 -9
  68. package/gsd-core/bin/lib/phase.cjs +258 -17
  69. package/gsd-core/bin/lib/plan-drift-guard.cjs +1 -1
  70. package/gsd-core/bin/lib/plan-scan.cjs +70 -2
  71. package/gsd-core/bin/lib/planning-workspace.cjs +9 -2
  72. package/gsd-core/bin/lib/profile-output.cjs +34 -8
  73. package/gsd-core/bin/lib/review-lane-descriptor.cjs +927 -0
  74. package/gsd-core/bin/lib/review-lane-invocation.cjs +348 -0
  75. package/gsd-core/bin/lib/review-lane-runner.cjs +594 -0
  76. package/gsd-core/bin/lib/review-reviewer-selection.cjs +114 -32
  77. package/gsd-core/bin/lib/roadmap-parser.cjs +61 -10
  78. package/gsd-core/bin/lib/roadmap.cjs +23 -7
  79. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +38 -5
  80. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +23 -9
  81. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +156 -0
  82. package/gsd-core/bin/lib/runtime-name-policy.cjs +15 -2
  83. package/gsd-core/bin/lib/smart-entry.cjs +70 -5
  84. package/gsd-core/bin/lib/state-document.cjs +171 -24
  85. package/gsd-core/bin/lib/state-transition.cjs +50 -11
  86. package/gsd-core/bin/lib/state.cjs +206 -32
  87. package/gsd-core/bin/lib/surface.cjs +51 -9
  88. package/gsd-core/bin/lib/uat-predicate.cjs +6 -4
  89. package/gsd-core/bin/lib/uat.cjs +428 -11
  90. package/gsd-core/bin/lib/ui-consideration-probe.cjs +2 -2
  91. package/gsd-core/bin/lib/unusable-input.cjs +216 -0
  92. package/gsd-core/bin/lib/validate.cjs +44 -8
  93. package/gsd-core/bin/lib/verification.cjs +163 -31
  94. package/gsd-core/bin/lib/verify.cjs +348 -42
  95. package/gsd-core/bin/lib/worktree-safety.cjs +360 -15
  96. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  97. package/gsd-core/bin/shared/config-schema.manifest.json +4 -15
  98. package/gsd-core/bin/shared/model-catalog.json +5 -0
  99. package/gsd-core/bin/shared/runtime-aliases.manifest.json +5 -0
  100. package/gsd-core/references/api-coverage.md +37 -7
  101. package/gsd-core/references/checkpoints.md +1 -1
  102. package/gsd-core/references/common-bug-patterns.md +13 -0
  103. package/gsd-core/references/context-budget.md +40 -0
  104. package/gsd-core/references/debugger-bug-taxonomy.md +111 -0
  105. package/gsd-core/references/debugger-fix-acceptance.md +157 -0
  106. package/gsd-core/references/debugger-philosophy.md +1 -0
  107. package/gsd-core/references/debugger-prevention.md +98 -0
  108. package/gsd-core/references/debugger-rca-branching.md +98 -0
  109. package/gsd-core/references/debugger-repro-hardening.md +130 -0
  110. package/gsd-core/references/debugger-sbfl.md +110 -0
  111. package/gsd-core/references/debugger-semantic-recall.md +81 -0
  112. package/gsd-core/references/execute-phase-quota-recovery.md +55 -0
  113. package/gsd-core/references/execute-phase-requirement-revert.md +8 -0
  114. package/gsd-core/references/execute-phase-response-language.md +7 -0
  115. package/gsd-core/references/gate-prompts.md +6 -3
  116. package/gsd-core/references/model-profile-resolution.md +64 -13
  117. package/gsd-core/references/offer-next.md +88 -0
  118. package/gsd-core/references/planner-antipatterns.md +6 -0
  119. package/gsd-core/references/planner-mvp-mode.md +12 -13
  120. package/gsd-core/references/planner-preconditions.md +156 -0
  121. package/gsd-core/references/planner-reversibility.md +132 -0
  122. package/gsd-core/references/planning-config.md +2 -1
  123. package/gsd-core/references/reviewer-instances.md +28 -19
  124. package/gsd-core/references/runtime-aware-dispatch.md +42 -0
  125. package/gsd-core/references/skeleton-template.md +1 -1
  126. package/gsd-core/references/thinking-models-planning.md +3 -1
  127. package/gsd-core/references/ui-consideration-probe.md +2 -2
  128. package/gsd-core/references/worktree-branch-check.md +4 -4
  129. package/gsd-core/templates/DEBUG.md +5 -3
  130. package/gsd-core/templates/summary-minimal.md +4 -0
  131. package/gsd-core/templates/summary-standard.md +4 -0
  132. package/gsd-core/templates/summary.md +7 -0
  133. package/gsd-core/workflows/add-phase.md +2 -0
  134. package/gsd-core/workflows/add-tests.md +3 -1
  135. package/gsd-core/workflows/add-todo.md +32 -1
  136. package/gsd-core/workflows/ai-integration-phase.md +8 -6
  137. package/gsd-core/workflows/audit-fix.md +6 -2
  138. package/gsd-core/workflows/audit-milestone.md +8 -0
  139. package/gsd-core/workflows/autonomous.md +19 -15
  140. package/gsd-core/workflows/check-todos.md +5 -3
  141. package/gsd-core/workflows/cleanup.md +7 -1
  142. package/gsd-core/workflows/code-review-fix.md +14 -6
  143. package/gsd-core/workflows/code-review.md +93 -24
  144. package/gsd-core/workflows/complete-milestone.md +3 -0
  145. package/gsd-core/workflows/debug.md +35 -7
  146. package/gsd-core/workflows/diagnose-issues.md +5 -1
  147. package/gsd-core/workflows/discovery-phase.md +7 -0
  148. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -4
  149. package/gsd-core/workflows/discuss-phase/modes/auto.md +0 -6
  150. package/gsd-core/workflows/discuss-phase/templates/context.md +16 -2
  151. package/gsd-core/workflows/discuss-phase-assumptions.md +18 -9
  152. package/gsd-core/workflows/discuss-phase.md +2 -2
  153. package/gsd-core/workflows/do.md +7 -1
  154. package/gsd-core/workflows/docs-update.md +9 -0
  155. package/gsd-core/workflows/eval-review.md +4 -1
  156. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +4 -0
  157. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +160 -0
  158. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +4 -4
  159. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -2
  160. package/gsd-core/workflows/execute-phase.md +110 -149
  161. package/gsd-core/workflows/execute-plan.md +20 -8
  162. package/gsd-core/workflows/explore.md +4 -0
  163. package/gsd-core/workflows/extract-learnings.md +21 -0
  164. package/gsd-core/workflows/graduation.md +3 -0
  165. package/gsd-core/workflows/health.md +7 -1
  166. package/gsd-core/workflows/help/modes/full.md +9 -5
  167. package/gsd-core/workflows/import.md +11 -2
  168. package/gsd-core/workflows/inbox.md +7 -0
  169. package/gsd-core/workflows/ingest-docs.md +19 -10
  170. package/gsd-core/workflows/manager.md +3 -1
  171. package/gsd-core/workflows/map-codebase.md +17 -10
  172. package/gsd-core/workflows/mvp-phase.md +3 -0
  173. package/gsd-core/workflows/new-milestone.md +79 -23
  174. package/gsd-core/workflows/new-project.md +28 -19
  175. package/gsd-core/workflows/new-workspace.md +3 -1
  176. package/gsd-core/workflows/next.md +5 -2
  177. package/gsd-core/workflows/onboard.md +3 -0
  178. package/gsd-core/workflows/plan-phase.md +56 -51
  179. package/gsd-core/workflows/plan-review-convergence.md +61 -12
  180. package/gsd-core/workflows/plant-seed.md +3 -0
  181. package/gsd-core/workflows/profile-user.md +7 -1
  182. package/gsd-core/workflows/progress.md +31 -3
  183. package/gsd-core/workflows/quick.md +33 -10
  184. package/gsd-core/workflows/remove-workspace.md +3 -0
  185. package/gsd-core/workflows/review.md +172 -585
  186. package/gsd-core/workflows/scan.md +10 -2
  187. package/gsd-core/workflows/secure-phase.md +13 -2
  188. package/gsd-core/workflows/settings-integrations.md +3 -0
  189. package/gsd-core/workflows/settings.md +3 -0
  190. package/gsd-core/workflows/ship.md +88 -11
  191. package/gsd-core/workflows/sketch.md +3 -0
  192. package/gsd-core/workflows/smart-entry.md +4 -1
  193. package/gsd-core/workflows/spike.md +7 -1
  194. package/gsd-core/workflows/ui-phase.md +11 -2
  195. package/gsd-core/workflows/ui-review.md +11 -1
  196. package/gsd-core/workflows/undo.md +7 -0
  197. package/gsd-core/workflows/update.md +106 -5
  198. package/gsd-core/workflows/validate-phase.md +13 -2
  199. package/gsd-core/workflows/verify-phase.md +2 -2
  200. package/gsd-core/workflows/verify-work.md +15 -4
  201. package/hooks/dist/gsd-context-monitor.js +27 -9
  202. package/hooks/dist/gsd-cursor-session-start.js +6 -2
  203. package/hooks/dist/gsd-cursor-stop.js +6 -2
  204. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  205. package/hooks/dist/gsd-graphify-update.sh +9 -0
  206. package/hooks/dist/gsd-phase-boundary.sh +14 -2
  207. package/hooks/dist/gsd-prompt-guard.js +101 -2
  208. package/hooks/dist/gsd-read-guard.js +100 -2
  209. package/hooks/dist/gsd-read-injection-scanner.js +109 -2
  210. package/hooks/dist/gsd-statusline.js +97 -9
  211. package/hooks/dist/gsd-workflow-guard.js +110 -6
  212. package/hooks/dist/gsd-worktree-path-guard.js +132 -8
  213. package/hooks/dist/lib/cursor-workspace.js +74 -0
  214. package/hooks/gsd-context-monitor.js +27 -9
  215. package/hooks/gsd-cursor-session-start.js +6 -2
  216. package/hooks/gsd-cursor-stop.js +6 -2
  217. package/hooks/gsd-cursor-subagent-start.js +6 -2
  218. package/hooks/gsd-graphify-update.sh +9 -0
  219. package/hooks/gsd-phase-boundary.sh +14 -2
  220. package/hooks/gsd-prompt-guard.js +101 -2
  221. package/hooks/gsd-read-guard.js +100 -2
  222. package/hooks/gsd-read-injection-scanner.js +109 -2
  223. package/hooks/gsd-statusline.js +97 -9
  224. package/hooks/gsd-workflow-guard.js +110 -6
  225. package/hooks/gsd-worktree-path-guard.js +132 -8
  226. package/hooks/lib/cursor-workspace.js +74 -0
  227. package/package.json +10 -8
  228. package/pi/gsd.cjs +34 -3
  229. package/scripts/changeset/lint.cjs +1 -0
  230. package/scripts/changeset/parse.cjs +26 -0
  231. package/scripts/check-coverage-gate.cjs +51 -0
  232. package/scripts/check-glossary-refs.cjs +244 -0
  233. package/scripts/ci-rebase-check.cjs +48 -4
  234. package/scripts/ci-test-scope.cjs +67 -17
  235. package/scripts/gen-adr-index.cjs +528 -0
  236. package/scripts/gen-capability-matrix.cjs +26 -2
  237. package/scripts/gen-capability-registry.cjs +132 -34
  238. package/scripts/gen-emitted-baseline.cjs +145 -0
  239. package/scripts/gen-test-timings.cjs +201 -0
  240. package/scripts/lint-compiled-artifact-sync.cjs +146 -0
  241. package/scripts/lint-emitted-drift-ack.cjs +149 -0
  242. package/scripts/lint-fix-has-regression-test.cjs +131 -0
  243. package/scripts/lint-portable-timeout.cjs +140 -0
  244. package/scripts/lint-resolution-provenance.cjs +9 -0
  245. package/scripts/lint-test-file-count.allowlist.json +1 -0
  246. package/scripts/mutation-matrix.cjs +4 -0
  247. package/scripts/prompt-injection-scan.sh +6 -0
  248. package/scripts/registry-schema.cjs +57 -8
  249. package/scripts/release-notes/conventional-title.cjs +19 -1
  250. package/scripts/release-notes/format-github-release-notes.cjs +7 -3
  251. package/scripts/release-tarball-smoke.cjs +18 -11
  252. package/scripts/run-tests.cjs +420 -58
  253. package/scripts/workflow-size.cjs +16 -8
  254. package/skills/gsd-ai-integration-phase/SKILL.md +1 -1
  255. package/skills/gsd-mempalace-capture/SKILL.md +9 -5
  256. package/skills/gsd-new-milestone/SKILL.md +1 -1
  257. package/skills/gsd-plan-phase/SKILL.md +5 -3
  258. package/skills/gsd-plan-review-convergence/SKILL.md +7 -2
  259. package/vscode/package.json +1 -1
  260. package/scripts/gen-golden-install-parity-zcode.cjs +0 -77
  261. package/scripts/update-size-baseline.cjs +0 -68
@@ -399,6 +399,10 @@ function isValidConsentRecord(rec) {
399
399
  return false;
400
400
  if (typeof r['consentedAt'] !== 'string' || !r['consentedAt'])
401
401
  return false;
402
+ // Optional (see ConsentRecord.reviewerHost): absent is valid and is the common case. Present but
403
+ // non-string is a corrupt record — reject rather than silently comparing against a non-host.
404
+ if (r['reviewerHost'] !== undefined && typeof r['reviewerHost'] !== 'string')
405
+ return false;
402
406
  return true;
403
407
  }
404
408
  /**
@@ -645,7 +649,7 @@ function writeConsentStore(gsdHome, store) {
645
649
  * written. (The OLD code returned a null handle and proceeded unlocked — that is the bug.)
646
650
  */
647
651
  function recordProjectConsent(args) {
648
- const { gsdHome, projectRoot, id, integrity, disclosureSignature, contentHash } = args;
652
+ const { gsdHome, projectRoot, id, integrity, disclosureSignature, contentHash, reviewerHost } = args;
649
653
  if (isUnsafeCapabilityId(id)) {
650
654
  throw new Error(`Invalid capability id "${String(id)}": must match /^[a-z][a-z0-9-]*$/ (kebab-case, lowercase). ` +
651
655
  `Unsafe or non-kebab ids are rejected to keep the consent store prototype-pollution-safe.`);
@@ -685,6 +689,9 @@ function recordProjectConsent(args) {
685
689
  disclosureSignature,
686
690
  contentHash,
687
691
  consentedAt: new Date().toISOString(),
692
+ // Written only when the capability actually declares an egress destination, so a spawn lane
693
+ // or a non-lane capability keeps a byte-identical record shape.
694
+ ...(typeof reviewerHost === 'string' && reviewerHost ? { reviewerHost } : {}),
688
695
  };
689
696
  writeConsentStore(gsdHome, store);
690
697
  }
@@ -730,6 +737,37 @@ function revokeProjectConsent(args) {
730
737
  releaseConsentLock(lock);
731
738
  }
732
739
  }
740
+ /**
741
+ * The egress destination a capability's consent was granted against, or `undefined`.
742
+ *
743
+ * ADR-2782 D5 rule 4 (#2799) — read on the INVOCATION path, not only at install, because
744
+ * `hostConfigKey` names a key in `.planning/config.json`: the one consent-bound value that lives
745
+ * outside the SHA-pinned bundle and can be changed by an ordinary pull request with no re-install
746
+ * and no integrity check.
747
+ *
748
+ * `undefined` means "nothing to compare", and the caller MUST treat that as allow, not deny. Two
749
+ * legitimate ways to get it: the capability has no consent record at all (first-party lanes ship
750
+ * inside the SHA-pinned distribution and are never consent-gated), or the record predates this
751
+ * field. Denying on absence would break every existing local-model user on upgrade.
752
+ *
753
+ * Non-throwing: a missing, corrupt, or oversized store yields `undefined` like any other absence.
754
+ */
755
+ function readConsentedReviewerHost(args) {
756
+ const { gsdHome, projectRoot, id } = args;
757
+ if (isUnsafeCapabilityId(id))
758
+ return undefined;
759
+ try {
760
+ const store = readConsentStore(gsdHome);
761
+ const key = consentKey(realpathProject(projectRoot), id);
762
+ if (!Object.prototype.hasOwnProperty.call(store.records, key))
763
+ return undefined;
764
+ const host = store.records[key].reviewerHost;
765
+ return typeof host === 'string' && host ? host : undefined;
766
+ }
767
+ catch {
768
+ return undefined;
769
+ }
770
+ }
733
771
  /**
734
772
  * #1459 finding 2 (round 6): TEST-ONLY — override the cumulative bundle entry-count cap and return a
735
773
  * restore() that resets it to the production default. Lets a test prove the streaming walk fails closed
@@ -744,6 +782,7 @@ module.exports = {
744
782
  consentStorePath,
745
783
  bundleContentHash,
746
784
  readConsentStore,
785
+ readConsentedReviewerHost,
747
786
  hasProjectConsent,
748
787
  recordProjectConsent,
749
788
  revokeProjectConsent,
@@ -672,6 +672,59 @@ function warnIfConsentSkipped(opts, id) {
672
672
  * Best-effort: a consent-store write failure must not turn a successful install/upgrade into a
673
673
  * failure (the bundle is already committed) — it is surfaced as a warning, not a throw.
674
674
  */
675
+ /**
676
+ * Resolve an `openai-http` reviewer lane's declared `hostConfigKey` to the destination it currently
677
+ * names, or `undefined` when this capability is not such a lane.
678
+ *
679
+ * Falls back to the lane's declared `defaultHost` when the key is unset, because that is exactly
680
+ * what the invocation path will do — binding the config value while the runtime uses the default
681
+ * would guarantee a mismatch on the very first review.
682
+ *
683
+ * Non-throwing: consent binding is best-effort and must never turn a successful install into a
684
+ * failure. An unresolvable host simply records nothing, which reads as "not bound" and allows.
685
+ */
686
+ function resolveReviewerEgressHost(opts, manifest) {
687
+ try {
688
+ const reviewer = manifest['reviewer'];
689
+ if (reviewer === null || typeof reviewer !== 'object' || Array.isArray(reviewer))
690
+ return undefined;
691
+ const r = reviewer;
692
+ if (r['transport'] !== 'openai-http')
693
+ return undefined;
694
+ const invoke = r['invoke'];
695
+ if (invoke === null || typeof invoke !== 'object')
696
+ return undefined;
697
+ const inv = invoke;
698
+ const key = typeof inv['hostConfigKey'] === 'string' ? inv['hostConfigKey'] : '';
699
+ const fallback = typeof inv['defaultHost'] === 'string' ? inv['defaultHost'] : '';
700
+ let configured = '';
701
+ if (key) {
702
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
703
+ const cfgLoader = require('./config-loader.cjs');
704
+ const root = projectRootMod.consentProjectRoot(opts.runtimeDir);
705
+ const cfg = cfgLoader.loadConfigResolved ? (cfgLoader.loadConfigResolved(root).config ?? {}) : {};
706
+ let cur = cfg;
707
+ for (const part of key.split('.')) {
708
+ if (cur === null || typeof cur !== 'object') {
709
+ cur = undefined;
710
+ break;
711
+ }
712
+ cur = Object.prototype.hasOwnProperty.call(cur, part)
713
+ ? cur[part]
714
+ : undefined;
715
+ }
716
+ if (typeof cur === 'string')
717
+ configured = cur.trim();
718
+ }
719
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
720
+ const { normalizeHost } = require('./review-lane-invocation.cjs');
721
+ const resolved = normalizeHost(configured || fallback);
722
+ return resolved || undefined;
723
+ }
724
+ catch {
725
+ return undefined;
726
+ }
727
+ }
675
728
  function bindProjectConsent(opts, id, integrity, manifest) {
676
729
  // #1459 IC-07: a project-scope op WITHOUT a consent store cannot bind — warn (then nothing to do).
677
730
  if (!shouldBindConsent(opts)) {
@@ -689,6 +742,11 @@ function bindProjectConsent(opts, id, integrity, manifest) {
689
742
  integrity,
690
743
  disclosureSignature: trustMod.signatureForManifest(manifest),
691
744
  contentHash: consentMod.bundleContentHash(capDir(opts.runtimeDir, id)),
745
+ // ADR-2782 D5 rule 1 (#2799): bind the RESOLVED egress destination, not merely the config key
746
+ // that names it. The key lives in `.planning/config.json`, outside the SHA-pinned bundle, so
747
+ // without this the user consents to "wherever that key points" — a promise the bundle hash
748
+ // cannot keep. Phase 5b re-resolves and compares at invocation (rule 4).
749
+ reviewerHost: resolveReviewerEgressHost(opts, manifest),
692
750
  });
693
751
  }
694
752
  catch (err) {
@@ -426,6 +426,8 @@ function loadRegistry(options = {}) {
426
426
  // consent the CLI records is the consent the loader checks. The consent store NEVER lives in a repo.
427
427
  const gsdHome = options.gsdHome || process.env['GSD_HOME'] || os.homedir();
428
428
  const warnings = [];
429
+ // ADR-2782 D4.3 — non-fatal notices for capabilities that are ACCEPTED (see OverlayMeta).
430
+ const diagnostics = [];
429
431
  const incompatibleGateCapIds = [];
430
432
  const blockedGates = [];
431
433
  const commandRoots = {};
@@ -719,6 +721,26 @@ function loadRegistry(options = {}) {
719
721
  }
720
722
  }
721
723
  // Accepted.
724
+ //
725
+ // ADR-2782 D4.3: an unknown field inside a `reviewer` body is IGNORED WITH A
726
+ // WARNING rather than failing validation, so a lane built for a newer GSD
727
+ // degrades to accepted-but-partially-understood instead of being rejected.
728
+ // That case validates cleanly, so without this call it would surface
729
+ // nowhere at runtime — the build-time generator only ever sees first-party
730
+ // in-repo manifests, never an installed third-party overlay. Guarded on
731
+ // presence so an older built validator without the function still loads,
732
+ // and wrapped because the never-crash contract (ADR-1244 D2) outranks a
733
+ // diagnostic: a throwing collector must not cost the user a working lane.
734
+ if (typeof validator.collectReviewerWarnings === 'function') {
735
+ try {
736
+ for (const w of validator.collectReviewerWarnings(cap) || []) {
737
+ diagnostics.push(`${root.scope}:${id}: ${w}`);
738
+ }
739
+ }
740
+ catch {
741
+ // A diagnostic that cannot be produced is not worth failing an install over.
742
+ }
743
+ }
722
744
  overlayCaps.push(cap);
723
745
  acceptedIds.add(id);
724
746
  for (const s of skills)
@@ -752,7 +774,7 @@ function loadRegistry(options = {}) {
752
774
  }
753
775
  }
754
776
  }
755
- const meta = { warnings, incompatibleGateCapIds, blockedGates, commandRoots };
777
+ const meta = { warnings, diagnostics, incompatibleGateCapIds, blockedGates, commandRoots };
756
778
  if (overlayCaps.length === 0) {
757
779
  // Nothing to compose. Return the frozen registry unchanged when there is
758
780
  // also nothing to report (identity-stable); otherwise attach diagnostics.