@opengsd/gsd-core 1.9.1 → 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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +27 -3
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +12 -10
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +46 -0
- package/agents/gsd-planner.md +49 -54
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +26 -73
- package/bin/install.js +1272 -1238
- package/bin/lib/ui-safety-gate.cjs +2 -0
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +2 -2
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +2 -2
- package/commands/gsd/plan-phase.md +1 -1
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1009 -115
- package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
- package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
- package/gsd-core/bin/lib/api-coverage.cjs +123 -5
- package/gsd-core/bin/lib/artifacts.cjs +3 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +926 -202
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-registry.cjs +608 -148
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +507 -24
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +114 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
- package/gsd-core/bin/lib/command-aliases.cjs +94 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +665 -99
- package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +76 -0
- package/gsd-core/bin/lib/config.cjs +22 -2
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +217 -40
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +239 -32
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1325 -169
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +805 -264
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
- package/gsd-core/bin/lib/install-profiles.cjs +160 -57
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
- package/gsd-core/bin/lib/io.cjs +38 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +821 -109
- package/gsd-core/bin/lib/model-catalog.cjs +59 -1
- package/gsd-core/bin/lib/model-resolver.cjs +183 -40
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
- package/gsd-core/bin/lib/phase-id.cjs +507 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +258 -58
- package/gsd-core/bin/lib/phase.cjs +891 -156
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +86 -2
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
- package/gsd-core/bin/lib/probe-core.cjs +1 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +405 -84
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
- package/gsd-core/bin/lib/smart-entry.cjs +154 -22
- package/gsd-core/bin/lib/state-command-router.cjs +5 -1
- package/gsd-core/bin/lib/state-document.cjs +152 -8
- package/gsd-core/bin/lib/state-transition.cjs +424 -105
- package/gsd-core/bin/lib/state.cjs +1927 -401
- package/gsd-core/bin/lib/surface.cjs +35 -10
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +20 -4
- package/gsd-core/bin/lib/uat.cjs +706 -64
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
- package/gsd-core/bin/lib/unusable-input.cjs +33 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +37 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -20
- package/gsd-core/bin/lib/verify.cjs +368 -880
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
- package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/execute-mvp-tdd.md +3 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +6 -2
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-planning-commit.md +2 -1
- package/gsd-core/references/loop-hook-dispatch.md +39 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planning-config.md +8 -6
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +8 -7
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +2 -4
- package/gsd-core/templates/state.md +4 -4
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +33 -70
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +74 -166
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +160 -95
- package/gsd-core/workflows/debug.md +16 -17
- package/gsd-core/workflows/diagnose-issues.md +56 -8
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/edit-phase.md +26 -1
- package/gsd-core/workflows/eval-review.md +3 -5
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +103 -187
- package/gsd-core/workflows/execute-plan.md +36 -4
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/health.md +73 -4
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +7 -6
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +35 -47
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +27 -240
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +89 -209
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +45 -159
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +55 -405
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +41 -13
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings.md +66 -2
- package/gsd-core/workflows/ship.md +104 -44
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +41 -20
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +106 -13
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +53 -31
- package/gsd-core/workflows/ui-phase.md +13 -12
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +19 -8
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +17 -65
- package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
- package/hooks/dist/gsd-check-update-worker.js +64 -12
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
- package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/dist/gsd-prompt-guard.js +21 -20
- package/hooks/dist/gsd-read-injection-scanner.js +45 -24
- package/hooks/dist/gsd-statusline.js +90 -6
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +277 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +517 -0
- package/hooks/gsd-check-update-worker.js +64 -12
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +607 -26
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +45 -24
- package/hooks/gsd-statusline.js +90 -6
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +277 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +31 -10
- package/pi/gsd.cjs +71 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +9 -0
- package/scripts/changeset/lint.cjs +68 -6
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/ci-test-scope.cjs +19 -2
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +439 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +150 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/drift-scan.cjs +278 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-compiled-artifact-sync.cjs +6 -1
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-phase-enumeration-drift.cjs +479 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +434 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +320 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +1045 -0
- package/scripts/lint-test-file-count.allowlist.json +40 -3
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +52 -6
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/state-write-path-drift-baseline.json +19 -0
- package/scripts/sync-runtime-launcher.cjs +2 -4
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +2 -2
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +2 -2
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +1 -2
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +1 -1
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -577
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/gen-emitted-baseline.cjs +0 -145
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1050
|
@@ -210,10 +210,35 @@ const SHA512_INTEGRITY_RE = /^sha512-[A-Za-z0-9+/]{86}==$/;
|
|
|
210
210
|
// hard validation error — fail closed so the capability install/load is rejected loudly.
|
|
211
211
|
const SAFE_HOOK_SCRIPT_RE = /^[A-Za-z0-9._/-]+$/;
|
|
212
212
|
|
|
213
|
+
// #3631 (defense-in-depth, mirrors capability-lifecycle.cts — KEEP BOTH IN SYNC): a declared script
|
|
214
|
+
// path must not point into the space bundleContentHash (capability-consent.cts) excludes from the
|
|
215
|
+
// consent-binding digest. A file whose basename ends `.pyc`/`.pyo` can contain perfectly valid
|
|
216
|
+
// JavaScript and would be executed by `node` regardless of extension, and a `__pycache__`/
|
|
217
|
+
// `.pytest_cache` segment marks a directory whose digest marker is suppressed — so a MANIFEST-DECLARED
|
|
218
|
+
// executable surface must never be able to reach either, or the exclusion becomes reachable from a
|
|
219
|
+
// path an attacker fully controls at declare-time rather than only via post-consent tamper.
|
|
220
|
+
// Regex asymmetry is DELIBERATE, KEEP BOTH RULES IN SYNC WITH capability-lifecycle.cts (byte-identical
|
|
221
|
+
// text, verified by the isSafeHookScriptPath parity test in tests/capability-registry.test.cjs):
|
|
222
|
+
// (i) PYCACHE_SUFFIX_RE is case-INSENSITIVE (`/i`) on purpose — a validator should be STRICTER than
|
|
223
|
+
// the digest it defends, so it rejects `x.PYC` too even though bundleContentHash's own suffix
|
|
224
|
+
// match (hasPycacheFileSuffix, capability-consent.cts) is byte-exact and would still hash it.
|
|
225
|
+
// (ii) The __pycache__/.pytest_cache SEGMENT match is case-SENSITIVE to match the digest's own
|
|
226
|
+
// byte-exact, case-sensitive directory-basename comparison (CPython always writes a lowercase
|
|
227
|
+
// `__pycache__`) — a validator segment match looser than the digest here would reject paths the
|
|
228
|
+
// digest would still hash, which is over-strict in the wrong direction for a defense-in-depth
|
|
229
|
+
// check layered on top of an already-correct digest.
|
|
230
|
+
// (iii) The `[/\\]` backslash alternations in both regexes are defensive/UNREACHABLE in practice:
|
|
231
|
+
// SAFE_HOOK_SCRIPT_RE (above) already rejects any backslash character outright, so a script
|
|
232
|
+
// string containing `\` never reaches either PYCACHE_*_RE check.
|
|
233
|
+
const PYCACHE_SEGMENT_RE = /(?:^|[/\\])(__pycache__|\.pytest_cache)(?:[/\\]|$)/;
|
|
234
|
+
const PYCACHE_SUFFIX_RE = /\.(pyc|pyo)$/i;
|
|
235
|
+
|
|
213
236
|
/**
|
|
214
237
|
* #1460 (R): true when a relative hook-script path is shell-safe (see SAFE_HOOK_SCRIPT_RE).
|
|
215
238
|
* Rejects absolute paths, `..` segments, a leading `-` on any path segment, and any char
|
|
216
|
-
* outside the allowlist (whitespace / shell metacharacters / control / NUL).
|
|
239
|
+
* outside the allowlist (whitespace / shell metacharacters / control / NUL). #3631: also rejects a
|
|
240
|
+
* path with a `__pycache__`/`.pytest_cache` segment or a `.pyc`/`.pyo` basename suffix (defense in
|
|
241
|
+
* depth — keeps a declared executable surface out of the digest-excluded space).
|
|
217
242
|
*/
|
|
218
243
|
function isSafeHookScriptPath(script) {
|
|
219
244
|
if (typeof script !== 'string' || script.length === 0) return false;
|
|
@@ -225,6 +250,8 @@ function isSafeHookScriptPath(script) {
|
|
|
225
250
|
for (const seg of segments) {
|
|
226
251
|
if (seg.startsWith('-')) return false;
|
|
227
252
|
}
|
|
253
|
+
if (PYCACHE_SEGMENT_RE.test(script)) return false;
|
|
254
|
+
if (PYCACHE_SUFFIX_RE.test(path.basename(script))) return false;
|
|
228
255
|
return true;
|
|
229
256
|
}
|
|
230
257
|
|
|
@@ -707,7 +734,6 @@ const VALID_CONVERTER_NAMES = new Set([
|
|
|
707
734
|
'convertClaudeCommandToCodebuddySkill',
|
|
708
735
|
'convertClaudeCommandToCodexSkill',
|
|
709
736
|
'convertClaudeCommandToCopilotSkill',
|
|
710
|
-
'convertClaudeCommandToCursorCommand',
|
|
711
737
|
'convertClaudeCommandToCursorSkill',
|
|
712
738
|
'convertClaudeCommandToKiloSkill',
|
|
713
739
|
'convertClaudeCommandToKimiSkill',
|
|
@@ -728,6 +754,16 @@ const VALID_CONVERTER_NAMES = new Set([
|
|
|
728
754
|
'convertClaudeAgentToCodexAgent',
|
|
729
755
|
// ADR-1239 / #2092 Phase B Upgrade 1 — native .qwen/agents/*.md subagent projection.
|
|
730
756
|
'convertClaudeAgentToQwenAgent',
|
|
757
|
+
// #3384 — ZCode agents are Claude-shaped but its dispatcher treats mcp__* tools
|
|
758
|
+
// grants as required MCP servers; this converter strips them at install time.
|
|
759
|
+
'convertClaudeAgentToZcodeAgent',
|
|
760
|
+
// #2875 Part 2 (the agents-bypass closure) — data-driven Hermes branding
|
|
761
|
+
// converter (reads hostBehaviors.brandingRewrites rather than a hardcode),
|
|
762
|
+
// and the kilo/opencode agent converters (shared with those runtimes'
|
|
763
|
+
// commands-kind entries, options-bag signature `(content, {isAgent, modelOverride})`).
|
|
764
|
+
'convertClaudeAgentToHermesAgent',
|
|
765
|
+
'convertClaudeToKiloFrontmatter',
|
|
766
|
+
'convertClaudeToOpencodeFrontmatter',
|
|
731
767
|
]);
|
|
732
768
|
|
|
733
769
|
// C3: Validate role:runtime body
|
|
@@ -746,6 +782,23 @@ const VALID_EXTENSION_EVENTS = new Set(['opencode', 'pi', 'hermes', 'kilo', 'non
|
|
|
746
782
|
const VALID_SANDBOX_TIERS = new Set(['none', 'codex-agent-sandbox']);
|
|
747
783
|
const VALID_ARTIFACT_KIND_NAMES = new Set(['commands', 'agents', 'skills', 'kimi-agents']);
|
|
748
784
|
const VALID_ARTIFACT_NESTINGS = new Set(['flat', 'nested']);
|
|
785
|
+
// #2871 Phase 2 — only `commands` and `skills` are trigger-bearing (a `/gsd-<name>`
|
|
786
|
+
// the USER types). `agents` and `kimi-agents` are a separate dispatch interface
|
|
787
|
+
// point (subagent invocation via `subagent_type`/named dispatch), never a trigger —
|
|
788
|
+
// see 40-design.md's "agents are not trigger-bearing" correction. A narrower set
|
|
789
|
+
// than VALID_ARTIFACT_KIND_NAMES on purpose: an artifact KIND can be agents; a
|
|
790
|
+
// trigger-precedence MEMBER never can.
|
|
791
|
+
const VALID_TRIGGER_PRECEDENCE_KINDS = new Set(['commands', 'skills']);
|
|
792
|
+
// The default runtime.triggerPrecedence (highest priority first) applied when a
|
|
793
|
+
// descriptor omits the axis (see validateRuntimeBody's required-with-default
|
|
794
|
+
// handling below). Not invented: `['skills', 'commands']` is the ordering every
|
|
795
|
+
// in-tree runtime that emits both kinds (from the same trigger stems) wants —
|
|
796
|
+
// claude's local/global collision and the same-scope collision (codebuddy, kilo,
|
|
797
|
+
// opencode, zcode) all resolve to skills winning. claude's shipped descriptor
|
|
798
|
+
// declares this SAME value explicitly, and
|
|
799
|
+
// tests/runtime-artifact-layout-trigger-surface.test.cjs asserts the two agree
|
|
800
|
+
// (a parity assertion — two surfaces reading one rule must not silently drift).
|
|
801
|
+
const DEFAULT_TRIGGER_PRECEDENCE = Object.freeze(['skills', 'commands']);
|
|
749
802
|
const FEATURE_FIELDS_FORBIDDEN_ON_RUNTIME = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey'];
|
|
750
803
|
// 'none' added #2103 — Marketplace/VSIX-distributed hosts (e.g. VS Code) that
|
|
751
804
|
// are never CLI-installed (no allRuntimes membership, no install flag).
|
|
@@ -852,7 +905,36 @@ const VALID_LANE_HANDLERS = new Set(['antigravity', 'openai-compatible', 'op
|
|
|
852
905
|
// BOTH sub-shapes, or from NEITHER, has undefined meaning and fails validation.
|
|
853
906
|
// The discriminator is explicit rather than inferred from field presence, which
|
|
854
907
|
// is precisely the ambiguity these two sets exist to detect.
|
|
855
|
-
|
|
908
|
+
// `env` added by #2483. It belongs in THIS set and not merely in validateSpawnInvoke: an environment
|
|
909
|
+
// pair has no meaning for a transport that issues an HTTP POST, so a manifest declaring it alongside
|
|
910
|
+
// `openai-http` fields is the undefined-meaning case the comment above describes. Registering it here
|
|
911
|
+
// is what makes that case reportable — the openai-http arm rejects exactly the members of this list,
|
|
912
|
+
// so a field absent from it is silently accepted on the wrong transport. Note `effortChannel` is
|
|
913
|
+
// deliberately in NEITHER set: D2 defines it for both transports, so it is shared, not spawn-only.
|
|
914
|
+
const SPAWN_ONLY_INVOKE_FIELDS = ['binary', 'args', 'promptChannel', 'outputChannel', 'outputArg', 'modelArg', 'env'];
|
|
915
|
+
// Environment names refused outright on a reviewer lane (#2483): each one turns a declared pair into
|
|
916
|
+
// arbitrary code execution in the spawned child. DEFENCE IN DEPTH, NOT THE BOUNDARY — say so plainly,
|
|
917
|
+
// because a future reader who mistakes this for the control will under-invest in the one that is.
|
|
918
|
+
// The boundary is install-time consent: `capability-trust` discloses every declared env pair, shows
|
|
919
|
+
// its value, and binds it to the consent signature, so an unlisted name is still SEEN before it runs.
|
|
920
|
+
//
|
|
921
|
+
// Deliberately incomplete, and it cannot be otherwise: the spawned binary is arbitrary third-party
|
|
922
|
+
// code, so the exhaustive set is every interpreter's injection variables. What this list buys is that
|
|
923
|
+
// the highest-confidence, lowest-legitimacy routes cannot be taken quietly. `PATH` is included — it is
|
|
924
|
+
// the most complete primitive of the set (repoint it at a directory holding a fake binary) and no
|
|
925
|
+
// shipped reviewer manifest declares it; a lane that needs a specific executable declares an absolute
|
|
926
|
+
// `invoke.binary` rather than reshaping the child's `PATH`.
|
|
927
|
+
//
|
|
928
|
+
// Matched CASE-INSENSITIVELY (members stored uppercase) — Windows environment lookup is
|
|
929
|
+
// case-insensitive, so an exact-case set is bypassed by declaring `Path` or `node_options`.
|
|
930
|
+
const DENIED_LANE_ENV_KEYS = new Set([
|
|
931
|
+
'PATH', 'NODE_OPTIONS', 'NODE_REPL_EXTERNAL_MODULE', 'NODE_PATH',
|
|
932
|
+
'LD_PRELOAD', 'LD_AUDIT', 'LD_LIBRARY_PATH',
|
|
933
|
+
'DYLD_INSERT_LIBRARIES', 'DYLD_LIBRARY_PATH',
|
|
934
|
+
'PYTHONSTARTUP', 'PYTHONPATH', 'BASH_ENV', 'ENV',
|
|
935
|
+
'PERL5OPT', 'RUBYOPT', 'JAVA_TOOL_OPTIONS', '_JAVA_OPTIONS', 'CLASSPATH',
|
|
936
|
+
'GIT_SSH_COMMAND', 'GIT_EXTERNAL_DIFF',
|
|
937
|
+
]);
|
|
856
938
|
// `defaultHost` / `fallbackModel` added by Phase 5b (#2799). Phase 4 federated every
|
|
857
939
|
// `review.*_host` key with a default of `""`, so the REAL fallback destination and model
|
|
858
940
|
// (`http://localhost:11434` / `llama3` and friends) existed only inside the bash leg. Once the
|
|
@@ -1075,7 +1157,18 @@ function validateArtifactLayout(capId, layout) {
|
|
|
1075
1157
|
errors.push(ctx + '.' + scope + ' must be an array');
|
|
1076
1158
|
} else {
|
|
1077
1159
|
for (let i = 0; i < arr.length; i++) {
|
|
1078
|
-
|
|
1160
|
+
const entry = arr[i];
|
|
1161
|
+
errors.push(...validateArtifactKindEntry(capId, entry, 'artifactLayout.' + scope + '[' + i + ']'));
|
|
1162
|
+
if (
|
|
1163
|
+
scope === 'local' &&
|
|
1164
|
+
typeof entry === 'object' && entry !== null && !Array.isArray(entry) &&
|
|
1165
|
+
Object.prototype.hasOwnProperty.call(entry, 'home')
|
|
1166
|
+
) {
|
|
1167
|
+
errors.push(
|
|
1168
|
+
ctx + '.local[' + i + '].home is not allowed; ' +
|
|
1169
|
+
'local artifact layout entries must remain project-scoped',
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1079
1172
|
}
|
|
1080
1173
|
}
|
|
1081
1174
|
}
|
|
@@ -1547,6 +1640,45 @@ function validateRuntimeBody(cap) {
|
|
|
1547
1640
|
}
|
|
1548
1641
|
}
|
|
1549
1642
|
|
|
1643
|
+
// triggerPrecedence — #2871 Phase 2 amendment to ADR-1016's runtime body.
|
|
1644
|
+
// REQUIRED-WITH-DEFAULT: no other axis in this validator uses this shape —
|
|
1645
|
+
// every axis above either hard-requires the field (throws when absent) or
|
|
1646
|
+
// treats absence as fully unconstrained (effortSurface/isolation: "nothing to
|
|
1647
|
+
// validate" when undefined). This axis does neither: an ABSENT value is
|
|
1648
|
+
// substituted with DEFAULT_TRIGGER_PRECEDENCE and then validated exactly as
|
|
1649
|
+
// if it HAD been supplied, so a third-party capability.json authored before
|
|
1650
|
+
// this phase keeps validating (ADR-894 additive-only / Hyrum's Law, ADR-1244)
|
|
1651
|
+
// while a PRESENT-but-malformed value still fails loudly instead of silently
|
|
1652
|
+
// passing through unchecked.
|
|
1653
|
+
const triggerPrecedenceValue = Object.prototype.hasOwnProperty.call(r, 'triggerPrecedence')
|
|
1654
|
+
? r.triggerPrecedence
|
|
1655
|
+
: DEFAULT_TRIGGER_PRECEDENCE;
|
|
1656
|
+
if (!Array.isArray(triggerPrecedenceValue)) {
|
|
1657
|
+
errors.push(
|
|
1658
|
+
'runtime.triggerPrecedence must be an array of trigger-bearing kind names (' +
|
|
1659
|
+
[...VALID_TRIGGER_PRECEDENCE_KINDS].join(', ') + ') (got: ' + JSON.stringify(triggerPrecedenceValue) + ')',
|
|
1660
|
+
);
|
|
1661
|
+
} else if (triggerPrecedenceValue.length === 0) {
|
|
1662
|
+
errors.push('runtime.triggerPrecedence must not be empty');
|
|
1663
|
+
} else {
|
|
1664
|
+
const seenKinds = new Set();
|
|
1665
|
+
for (let i = 0; i < triggerPrecedenceValue.length; i++) {
|
|
1666
|
+
const k = triggerPrecedenceValue[i];
|
|
1667
|
+
if (k === '__proto__' || k === 'constructor' || k === 'prototype') {
|
|
1668
|
+
errors.push('runtime.triggerPrecedence[' + i + '] "' + k + '" is a reserved name');
|
|
1669
|
+
} else if (typeof k !== 'string' || !VALID_TRIGGER_PRECEDENCE_KINDS.has(k)) {
|
|
1670
|
+
errors.push(
|
|
1671
|
+
'runtime.triggerPrecedence[' + i + '] must be one of: ' + [...VALID_TRIGGER_PRECEDENCE_KINDS].join(', ') +
|
|
1672
|
+
' (got: ' + JSON.stringify(k) + ')',
|
|
1673
|
+
);
|
|
1674
|
+
} else if (seenKinds.has(k)) {
|
|
1675
|
+
errors.push('runtime.triggerPrecedence contains a duplicate kind: ' + JSON.stringify(k));
|
|
1676
|
+
} else {
|
|
1677
|
+
seenKinds.add(k);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1550
1682
|
return errors;
|
|
1551
1683
|
}
|
|
1552
1684
|
|
|
@@ -1568,6 +1700,122 @@ const KNOWN_REVIEWER_FIELDS = new Set([
|
|
|
1568
1700
|
'handler',
|
|
1569
1701
|
]);
|
|
1570
1702
|
|
|
1703
|
+
/**
|
|
1704
|
+
* The closed `runtime.hostBehaviors` vocabulary (ADR-1016, closed via #2801).
|
|
1705
|
+
*
|
|
1706
|
+
* ADR-1016's core principle is that every per-runtime difference is a value over
|
|
1707
|
+
* a closed primitive vocabulary, and that a host needing a new shape gets a
|
|
1708
|
+
* reviewed first-party primitive rather than "an open escape hatch in the
|
|
1709
|
+
* descriptor" (§Alternatives #2 rejects exactly that). `hostBehaviors` was the
|
|
1710
|
+
* one hole left in that closure: 59 keys across 18 manifests, 39 of them set by
|
|
1711
|
+
* a single capability, validated by nothing. It was described in the reference
|
|
1712
|
+
* docs as a deliberate open seam sanctioned by ADR-1016 — ADR-1016 does not
|
|
1713
|
+
* mention `hostBehaviors` at all, and its stated principle is the opposite.
|
|
1714
|
+
*
|
|
1715
|
+
* Adding a key here is deliberate and reviewed. That friction IS the decision
|
|
1716
|
+
* (ADR-1016 §Consequences: "the closed vocabulary must grow (reviewed) when a
|
|
1717
|
+
* genuinely new host shape appears — intentional friction, the trust boundary").
|
|
1718
|
+
*
|
|
1719
|
+
* WARNING, never error. Two reasons:
|
|
1720
|
+
* 1. It matches the ADR-2782 D4.3 treatment of an unknown `reviewer` field, so
|
|
1721
|
+
* a manifest built against a newer GSD degrades visibly instead of failing
|
|
1722
|
+
* the build of a repo that merely reads it.
|
|
1723
|
+
* 2. An error would hard-break an out-of-tree descriptor carrying a bespoke
|
|
1724
|
+
* key, with no deprecation window — the exact mistake #2801's own alias
|
|
1725
|
+
* removal spent a full release avoiding. Escalating to an error is a later
|
|
1726
|
+
* step and needs its own window.
|
|
1727
|
+
*
|
|
1728
|
+
* Kept in sorted order, and `tests/reviewer-manifest-body.test.cjs` asserts this
|
|
1729
|
+
* set equals the keys the shipped manifests actually declare, so the list cannot
|
|
1730
|
+
* silently rot away from reality (DEFECT.GENERATIVE-FIX).
|
|
1731
|
+
*/
|
|
1732
|
+
const KNOWN_HOST_BEHAVIORS = new Set([
|
|
1733
|
+
'agentFileExtension',
|
|
1734
|
+
'agentFrontmatterExtensions',
|
|
1735
|
+
'agentManifestStyle',
|
|
1736
|
+
'agentTomlFiles',
|
|
1737
|
+
'attributionConfigResolver',
|
|
1738
|
+
'attributionSource',
|
|
1739
|
+
'authorsCanonicalWorkflow',
|
|
1740
|
+
'brandingRewrites',
|
|
1741
|
+
'cleanupSkillSidecars',
|
|
1742
|
+
'clineRulesSurface',
|
|
1743
|
+
'combinedFamilyInstall',
|
|
1744
|
+
'commandBodyConverter',
|
|
1745
|
+
'doneBannerStyle',
|
|
1746
|
+
'flatCommandDir',
|
|
1747
|
+
'frontmatterDialect',
|
|
1748
|
+
'globalDirResolver',
|
|
1749
|
+
'hookPathStyle',
|
|
1750
|
+
'hooksJsonSurface',
|
|
1751
|
+
'hyphenNameAgentBody',
|
|
1752
|
+
'installsCommandBodiesForWorkflowDelegation',
|
|
1753
|
+
'legacyCommandsGsdCleanup',
|
|
1754
|
+
'legacyCommandsGsdInstallMigration',
|
|
1755
|
+
'legacyCommandsGsdUninstall',
|
|
1756
|
+
'legacyDevinSkillsCleanup',
|
|
1757
|
+
'localCommandsViaRules',
|
|
1758
|
+
'localInstallDeferred',
|
|
1759
|
+
'localInstallStyle',
|
|
1760
|
+
'localTargetIsProjectRoot',
|
|
1761
|
+
'managedHookEvents',
|
|
1762
|
+
'mcpCompanion',
|
|
1763
|
+
'namedSubagentsSupported',
|
|
1764
|
+
'nativeModelAliases',
|
|
1765
|
+
'nativePlugin',
|
|
1766
|
+
'noPathRewrite',
|
|
1767
|
+
'ownsClaudePaths',
|
|
1768
|
+
'permissionsSchema',
|
|
1769
|
+
'pluginOnlyInstall',
|
|
1770
|
+
'projectInstructionFile',
|
|
1771
|
+
'reapplyCommand',
|
|
1772
|
+
'reportCommandsDir',
|
|
1773
|
+
'reportSkillsCount',
|
|
1774
|
+
'retiredArtifacts',
|
|
1775
|
+
'settingsFileByScope',
|
|
1776
|
+
'sharedHooksDirName',
|
|
1777
|
+
'skillFrontmatterVersion',
|
|
1778
|
+
'skillPriorityFrontmatter',
|
|
1779
|
+
'skillsGlobalOnboarding',
|
|
1780
|
+
'skillsManifestPrefix',
|
|
1781
|
+
'skipCodexSkillsManifest',
|
|
1782
|
+
'skipHomePrefixSubstitution',
|
|
1783
|
+
'skipSettingsUi',
|
|
1784
|
+
'skipSharedHooksInstall',
|
|
1785
|
+
'skipUpdateBannerCommand',
|
|
1786
|
+
'soloStageMetadata',
|
|
1787
|
+
'sourceMarkerFile',
|
|
1788
|
+
'tomlConfigInstall',
|
|
1789
|
+
'trackCategoryDescription',
|
|
1790
|
+
'verificationStyle',
|
|
1791
|
+
'writeCategoryDescription',
|
|
1792
|
+
]);
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* Frozen reason codes for the non-fatal reviewer diagnostics (ADR-2782 D4.3).
|
|
1796
|
+
*
|
|
1797
|
+
* The IR behind `collectReviewerWarnings`' rendered strings. Tests assert on
|
|
1798
|
+
* these codes; the rendered `message` is operator console output and tests must
|
|
1799
|
+
* not depend on it (CONTRIBUTING.md, "Prohibited: Raw Text Matching on Test
|
|
1800
|
+
* Outputs"), which is the same split `bin/verify-reapply-patches.cjs` uses.
|
|
1801
|
+
*
|
|
1802
|
+
* Adding a code is THREE coordinated changes: this enum, the emitting site in
|
|
1803
|
+
* `collectReviewerWarningRecordFields`, and the test that locks
|
|
1804
|
+
* `Object.keys(REVIEWER_WARNING).sort()`. That coupling is the point — it stops
|
|
1805
|
+
* the code surface drifting from the test surface.
|
|
1806
|
+
*/
|
|
1807
|
+
const REVIEWER_WARNING = Object.freeze({
|
|
1808
|
+
/** A key inside a `reviewer` body that this GSD version does not know. */
|
|
1809
|
+
UNKNOWN_REVIEWER_FIELD: 'unknown_reviewer_field',
|
|
1810
|
+
/** A `runtime.hostBehaviors` key that was removed from the vocabulary. */
|
|
1811
|
+
REMOVED_HOST_BEHAVIOR: 'removed_host_behavior',
|
|
1812
|
+
/** A `runtime.hostBehaviors` key outside the closed vocabulary. */
|
|
1813
|
+
UNKNOWN_HOST_BEHAVIOR: 'unknown_host_behavior',
|
|
1814
|
+
});
|
|
1815
|
+
|
|
1816
|
+
/** Dotted path of the field removed by ADR-2782 D9 / #2801. */
|
|
1817
|
+
const REMOVED_REVIEWER_CLI_FIELD = 'runtime.hostBehaviors.reviewerCli';
|
|
1818
|
+
|
|
1571
1819
|
const KNOWN_PROBE_FIELDS = new Set(['kind', 'binary', 'needle', 'timeoutMs', 'hostConfigKey', 'path']);
|
|
1572
1820
|
|
|
1573
1821
|
/** A bounded probe timeout must be a finite, positive INTEGER of milliseconds. */
|
|
@@ -1605,6 +1853,41 @@ function describeValue(v) {
|
|
|
1605
1853
|
}
|
|
1606
1854
|
}
|
|
1607
1855
|
|
|
1856
|
+
/**
|
|
1857
|
+
* Ceiling on how many undeclared-key diagnostics one capability may produce.
|
|
1858
|
+
*
|
|
1859
|
+
* The loops below iterate MANIFEST-SUPPLIED keys, and an installed third-party
|
|
1860
|
+
* manifest is bounded only by MANIFEST_MAX_BYTES (8MB). Unbounded, one manifest
|
|
1861
|
+
* of 800k keys yields 800k records and ~139MB of message text, retained for the
|
|
1862
|
+
* registry's lifetime in OverlayMeta.diagnostics. Ten is enough to act on; the
|
|
1863
|
+
* rest are summarized. Mirrors the existing truncation idiom in
|
|
1864
|
+
* `capability-loader.cts` (`crossErrs.slice(0, 3)`).
|
|
1865
|
+
*/
|
|
1866
|
+
const MAX_REPORTED_UNKNOWN_KEYS = 10;
|
|
1867
|
+
|
|
1868
|
+
/** Ceiling on how much of one manifest-supplied key name a diagnostic repeats. */
|
|
1869
|
+
const MAX_REPORTED_KEY_CHARS = 80;
|
|
1870
|
+
|
|
1871
|
+
/**
|
|
1872
|
+
* Render a manifest-supplied KEY for a diagnostic: control-safe and bounded.
|
|
1873
|
+
*
|
|
1874
|
+
* Key names carry no grammar anywhere — unlike `cap.id`, which `validateCapability`
|
|
1875
|
+
* gates on KEBAB_RE before these diagnostics run — so a key is fully
|
|
1876
|
+
* attacker-controlled text heading for stderr and OverlayMeta.warnings. C0/C1
|
|
1877
|
+
* controls (ESC, CR, LF) become U+FFFD so a key cannot emit terminal escapes or
|
|
1878
|
+
* forge a log line, and the result is clipped so one key cannot carry megabytes
|
|
1879
|
+
* into a retained diagnostic.
|
|
1880
|
+
*
|
|
1881
|
+
* @param {*} key
|
|
1882
|
+
* @returns {string}
|
|
1883
|
+
*/
|
|
1884
|
+
function describeKey(key) {
|
|
1885
|
+
const raw = typeof key === 'string' ? key : String(key);
|
|
1886
|
+
// eslint-disable-next-line no-control-regex
|
|
1887
|
+
const safe = raw.replace(/[\x00-\x1f\x7f-\x9f]/g, '�');
|
|
1888
|
+
return safe.length > MAX_REPORTED_KEY_CHARS ? safe.slice(0, MAX_REPORTED_KEY_CHARS) + '…' : safe;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1608
1891
|
/** Extract a message from an unknown thrown value without throwing again. */
|
|
1609
1892
|
function safeErrorMessage(err) {
|
|
1610
1893
|
try {
|
|
@@ -1651,7 +1934,39 @@ function validateEnumField(ctx, label, value, validSet) {
|
|
|
1651
1934
|
}
|
|
1652
1935
|
|
|
1653
1936
|
/**
|
|
1654
|
-
* Collect NON-FATAL diagnostics for a
|
|
1937
|
+
* Collect NON-FATAL reviewer diagnostics for a capability manifest as TYPED
|
|
1938
|
+
* RECORDS (ADR-2782 D4.3, plus the D9 `hostBehaviors.reviewerCli` removal
|
|
1939
|
+
* notice, #2801).
|
|
1940
|
+
*
|
|
1941
|
+
* This is the IR. `collectReviewerWarnings` below renders it to strings for the
|
|
1942
|
+
* two production consumers; tests assert on these records instead of matching
|
|
1943
|
+
* the rendered prose (CONTRIBUTING.md, "Prohibited: Raw Text Matching on Test
|
|
1944
|
+
* Outputs").
|
|
1945
|
+
*
|
|
1946
|
+
* Record shape — `code` and `capId` are always present; the rest is per-code:
|
|
1947
|
+
* { code: REVIEWER_WARNING.UNKNOWN_REVIEWER_FIELD,
|
|
1948
|
+
* capId, field: 'reviewer.<key>', knownFields: string[], message }
|
|
1949
|
+
* { code: REVIEWER_WARNING.REMOVED_HOST_BEHAVIOR,
|
|
1950
|
+
* capId, field: 'runtime.hostBehaviors.reviewerCli',
|
|
1951
|
+
* replacement: 'reviewer', docs: '<how-to path>', message }
|
|
1952
|
+
*
|
|
1953
|
+
* TOTAL: returns an array for ANY input and never throws. It runs on
|
|
1954
|
+
* loadRegistry's ACCEPT path, so a diagnostic that throws would cost the user a
|
|
1955
|
+
* lane that is otherwise perfectly valid (#1461 OVL-1).
|
|
1956
|
+
*
|
|
1957
|
+
* @param {object} cap A capability manifest.
|
|
1958
|
+
* @returns {Array<object>} Warning records; empty when there is nothing to say.
|
|
1959
|
+
*/
|
|
1960
|
+
function collectReviewerWarningRecords(cap) {
|
|
1961
|
+
try {
|
|
1962
|
+
return collectReviewerWarningRecordFields(cap);
|
|
1963
|
+
} catch {
|
|
1964
|
+
return [];
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
|
|
1968
|
+
/**
|
|
1969
|
+
* Render the IR for operator console output.
|
|
1655
1970
|
*
|
|
1656
1971
|
* Kept separate from validateReviewerBody so validateCapability's contract
|
|
1657
1972
|
* (`=> string[]` of ERRORS) is unchanged for its two existing callers. The
|
|
@@ -1659,35 +1974,135 @@ function validateEnumField(ctx, label, value, validSet) {
|
|
|
1659
1974
|
* through OverlayMeta.warnings. A warning written only to a build log nobody
|
|
1660
1975
|
* reads is not a warning (ADR-2782 D4, "Where warnings surface").
|
|
1661
1976
|
*
|
|
1977
|
+
* The `string[]` shape is load-bearing for both consumers and does not change.
|
|
1978
|
+
*
|
|
1662
1979
|
* @param {object} cap A capability manifest.
|
|
1663
1980
|
* @returns {string[]} Warning strings; empty when there is nothing to say.
|
|
1664
1981
|
*/
|
|
1665
1982
|
function collectReviewerWarnings(cap) {
|
|
1666
|
-
|
|
1667
|
-
// called from loadRegistry's accept path, and a diagnostic that throws must
|
|
1668
|
-
// never cost the user a working lane.
|
|
1669
|
-
try {
|
|
1670
|
-
return collectReviewerWarningFields(cap);
|
|
1671
|
-
} catch {
|
|
1672
|
-
return [];
|
|
1673
|
-
}
|
|
1983
|
+
return collectReviewerWarningRecords(cap).map((record) => record.message);
|
|
1674
1984
|
}
|
|
1675
1985
|
|
|
1676
|
-
function
|
|
1677
|
-
const
|
|
1678
|
-
if (typeof cap !== 'object' || cap === null || Array.isArray(cap)) return
|
|
1679
|
-
const r = cap.reviewer;
|
|
1680
|
-
if (typeof r !== 'object' || r === null || Array.isArray(r)) return warnings;
|
|
1986
|
+
function collectReviewerWarningRecordFields(cap) {
|
|
1987
|
+
const records = [];
|
|
1988
|
+
if (typeof cap !== 'object' || cap === null || Array.isArray(cap)) return records;
|
|
1681
1989
|
|
|
1682
1990
|
const capId = typeof cap.id === 'string' ? cap.id : '(unknown)';
|
|
1991
|
+
|
|
1992
|
+
// ADR-2782 D9 / #2801 — the REMOVED `runtime.hostBehaviors.reviewerCli` alias.
|
|
1993
|
+
//
|
|
1994
|
+
// Emitted BEFORE the `reviewer`-body early-return below, deliberately. The
|
|
1995
|
+
// manifest this notice exists for is the ALIAS-ONLY one, which by definition
|
|
1996
|
+
// carries no body; after that guard the check would fire only for
|
|
1997
|
+
// capabilities that already declare a lane — exactly the set that does not
|
|
1998
|
+
// need telling.
|
|
1999
|
+
//
|
|
2000
|
+
// Presence-based, not `=== true`: the key is unknown at ANY value now, which
|
|
2001
|
+
// is the same rule the unknown-`reviewer.*`-field loop below applies. Own-key
|
|
2002
|
+
// read, so a polluted prototype cannot manufacture this warning on every
|
|
2003
|
+
// otherwise-innocent manifest. This is ONE keyed removal notice, not general
|
|
2004
|
+
// `hostBehaviors` validation — the closed vocabulary below handles that (#2801).
|
|
2005
|
+
const runtimeBody = cap.runtime;
|
|
2006
|
+
if (typeof runtimeBody === 'object' && runtimeBody !== null && !Array.isArray(runtimeBody)) {
|
|
2007
|
+
const hostBehaviors = runtimeBody.hostBehaviors;
|
|
2008
|
+
if (
|
|
2009
|
+
typeof hostBehaviors === 'object'
|
|
2010
|
+
&& hostBehaviors !== null
|
|
2011
|
+
&& !Array.isArray(hostBehaviors)
|
|
2012
|
+
&& Object.prototype.hasOwnProperty.call(hostBehaviors, 'reviewerCli')
|
|
2013
|
+
) {
|
|
2014
|
+
records.push({
|
|
2015
|
+
code: REVIEWER_WARNING.REMOVED_HOST_BEHAVIOR,
|
|
2016
|
+
capId,
|
|
2017
|
+
field: REMOVED_REVIEWER_CLI_FIELD,
|
|
2018
|
+
replacement: 'reviewer',
|
|
2019
|
+
docs: 'docs/how-to/ship-a-reviewer-lane.md',
|
|
2020
|
+
message:
|
|
2021
|
+
'⚠ capability "' + capId + '" ' + REMOVED_REVIEWER_CLI_FIELD + ' was removed (ADR-2782 D9) ' +
|
|
2022
|
+
'— ignored, and it contributes no reviewer lane. Declare a `reviewer` body instead; see ' +
|
|
2023
|
+
'docs/how-to/ship-a-reviewer-lane.md',
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// #2801 — the closed `hostBehaviors` vocabulary (ADR-1016). `reviewerCli` is
|
|
2028
|
+
// excluded here: it already drew its own removal notice above, and a second,
|
|
2029
|
+
// generic "unknown key" record for the same key would be noise, not signal.
|
|
2030
|
+
if (typeof hostBehaviors === 'object' && hostBehaviors !== null && !Array.isArray(hostBehaviors)) {
|
|
2031
|
+
let reported = 0;
|
|
2032
|
+
let omitted = 0;
|
|
2033
|
+
for (const key of Object.keys(hostBehaviors)) {
|
|
2034
|
+
if (isReservedName(key) || key === 'reviewerCli' || KNOWN_HOST_BEHAVIORS.has(key)) continue;
|
|
2035
|
+
if (reported >= MAX_REPORTED_UNKNOWN_KEYS) {
|
|
2036
|
+
omitted += 1;
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
reported += 1;
|
|
2040
|
+
const safeKey = describeKey(key);
|
|
2041
|
+
records.push({
|
|
2042
|
+
code: REVIEWER_WARNING.UNKNOWN_HOST_BEHAVIOR,
|
|
2043
|
+
capId,
|
|
2044
|
+
field: 'runtime.hostBehaviors.' + safeKey,
|
|
2045
|
+
message:
|
|
2046
|
+
'⚠ capability "' + capId + '" runtime.hostBehaviors.' + safeKey + ' is not a known host behavior ' +
|
|
2047
|
+
'in this GSD version — ignored. Adding one is a reviewed first-party change (ADR-1016).',
|
|
2048
|
+
});
|
|
2049
|
+
}
|
|
2050
|
+
if (omitted > 0) {
|
|
2051
|
+
records.push({
|
|
2052
|
+
code: REVIEWER_WARNING.UNKNOWN_HOST_BEHAVIOR,
|
|
2053
|
+
capId,
|
|
2054
|
+
field: 'runtime.hostBehaviors',
|
|
2055
|
+
truncated: true,
|
|
2056
|
+
omittedCount: omitted,
|
|
2057
|
+
message:
|
|
2058
|
+
'⚠ capability "' + capId + '" declares ' + omitted + ' further unknown runtime.hostBehaviors ' +
|
|
2059
|
+
'key(s), not listed. A manifest this far outside the vocabulary is likely built for a ' +
|
|
2060
|
+
'different GSD version.',
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
const r = cap.reviewer;
|
|
2067
|
+
if (typeof r !== 'object' || r === null || Array.isArray(r)) return records;
|
|
2068
|
+
|
|
2069
|
+
// Same ceiling and the same key sanitization as the hostBehaviors sweep above.
|
|
2070
|
+
// This loop predates #2801 and carried both defects; fixing only the new copy
|
|
2071
|
+
// would leave the identical defect one screen away from its own fix.
|
|
2072
|
+
let reportedFields = 0;
|
|
2073
|
+
let omittedFields = 0;
|
|
1683
2074
|
for (const key of Object.keys(r)) {
|
|
1684
2075
|
if (isReservedName(key) || KNOWN_REVIEWER_FIELDS.has(key)) continue;
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
2076
|
+
if (reportedFields >= MAX_REPORTED_UNKNOWN_KEYS) {
|
|
2077
|
+
omittedFields += 1;
|
|
2078
|
+
continue;
|
|
2079
|
+
}
|
|
2080
|
+
reportedFields += 1;
|
|
2081
|
+
const safeKey = describeKey(key);
|
|
2082
|
+
records.push({
|
|
2083
|
+
code: REVIEWER_WARNING.UNKNOWN_REVIEWER_FIELD,
|
|
2084
|
+
capId,
|
|
2085
|
+
field: 'reviewer.' + safeKey,
|
|
2086
|
+
knownFields: [...KNOWN_REVIEWER_FIELDS],
|
|
2087
|
+
message:
|
|
2088
|
+
'⚠ capability "' + capId + '" reviewer.' + safeKey + ' is not a known reviewer field ' +
|
|
2089
|
+
'in this GSD version — ignored. Known fields: ' + [...KNOWN_REVIEWER_FIELDS].join(', '),
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2092
|
+
if (omittedFields > 0) {
|
|
2093
|
+
records.push({
|
|
2094
|
+
code: REVIEWER_WARNING.UNKNOWN_REVIEWER_FIELD,
|
|
2095
|
+
capId,
|
|
2096
|
+
field: 'reviewer',
|
|
2097
|
+
knownFields: [...KNOWN_REVIEWER_FIELDS],
|
|
2098
|
+
truncated: true,
|
|
2099
|
+
omittedCount: omittedFields,
|
|
2100
|
+
message:
|
|
2101
|
+
'⚠ capability "' + capId + '" declares ' + omittedFields + ' further unknown reviewer field(s), ' +
|
|
2102
|
+
'not listed.',
|
|
2103
|
+
});
|
|
2104
|
+
}
|
|
2105
|
+
return records;
|
|
1691
2106
|
}
|
|
1692
2107
|
|
|
1693
2108
|
/**
|
|
@@ -2036,6 +2451,66 @@ function validateSpawnInvoke(ctx, invoke) {
|
|
|
2036
2451
|
|
|
2037
2452
|
errors.push(...validateEnumField(ctx, 'reviewer.invoke.effortChannel', invoke.effortChannel, VALID_LANE_EFFORT_CHANNELS));
|
|
2038
2453
|
|
|
2454
|
+
// `env` — per-invocation environment pairs (#2483). OPTIONAL, unlike every field above: only a lane
|
|
2455
|
+
// that needs to shape its child's environment declares it, and absent is the common case. Validated
|
|
2456
|
+
// when present, because `resolveLanePlan` DROPS a non-string value rather than coercing it — so an
|
|
2457
|
+
// unvalidated manifest declares a pair that silently never reaches the spawn, which is the failure
|
|
2458
|
+
// mode a shipped env-guard can least afford. Keys are held to the portable POSIX
|
|
2459
|
+
// environment-name grammar, which is a POLICY — not a claim about what an environment can physically
|
|
2460
|
+
// hold. Measured: of the names refused below only NUL is actually rejected by `spawnSync`; `=`, a
|
|
2461
|
+
// leading digit, a dash and a space are all carried through to the child (an `A=B` key arrives as
|
|
2462
|
+
// the raw entry `A=B=value`, and reads back via `process.env['A=B']`). They are refused because a
|
|
2463
|
+
// name outside the grammar is not portably addressable by the program meant to read it.
|
|
2464
|
+
if (invoke.env !== undefined) {
|
|
2465
|
+
if (typeof invoke.env !== 'object' || invoke.env === null || Array.isArray(invoke.env)) {
|
|
2466
|
+
errors.push(
|
|
2467
|
+
ctx + ' reviewer.invoke.env must be an object of environment name/value pairs ' +
|
|
2468
|
+
'(got: ' + describeValue(invoke.env) + ')',
|
|
2469
|
+
);
|
|
2470
|
+
} else {
|
|
2471
|
+
for (const key of Object.keys(invoke.env)) {
|
|
2472
|
+
// `__proto__` passes the grammar below and IS a real own key once a manifest is JSON-parsed,
|
|
2473
|
+
// but assigning it onto a plain accumulator goes through the inherited `__proto__` SETTER
|
|
2474
|
+
// instead of creating an own property. For the string values this field permits the setter is
|
|
2475
|
+
// a no-op — the prototype is not even changed — so the pair would validate and then simply
|
|
2476
|
+
// vanish before the spawn: the declared-but-never-delivered failure this block exists to catch.
|
|
2477
|
+
// Refused by name, because the grammar cannot see it.
|
|
2478
|
+
//
|
|
2479
|
+
// Only `__proto__` needs this. Sibling reserved-name guards in this file reject
|
|
2480
|
+
// `constructor`/`prototype` alongside it, but those guard bracket LOOKUPS that resolve
|
|
2481
|
+
// prototype members; here the read is `Object.keys` + an own-value read, and `constructor`
|
|
2482
|
+
// assigns as an ordinary own key. Refusing it too would reject a name the spawn could carry.
|
|
2483
|
+
// CASE-INSENSITIVE, and that is not pedantry: Windows environment lookups are
|
|
2484
|
+
// case-insensitive, so `Path` / `node_options` reach the child as `PATH` / `NODE_OPTIONS`
|
|
2485
|
+
// and an exact-case set is bypassed by changing one letter. The grammar above already
|
|
2486
|
+
// constrains keys to ASCII, so a plain uppercase fold is sufficient here.
|
|
2487
|
+
if (DENIED_LANE_ENV_KEYS.has(key.toUpperCase())) {
|
|
2488
|
+
errors.push(
|
|
2489
|
+
ctx + ' reviewer.invoke.env key "' + key + '" is not permitted ' +
|
|
2490
|
+
'(it makes the spawned reviewer run code of the manifest\'s choosing; ' +
|
|
2491
|
+
'declare an absolute `invoke.binary` instead of reshaping the child\'s environment)',
|
|
2492
|
+
);
|
|
2493
|
+
} else if (key === '__proto__') {
|
|
2494
|
+
errors.push(
|
|
2495
|
+
ctx + ' reviewer.invoke.env key "__proto__" is not permitted ' +
|
|
2496
|
+
'(it is silently dropped when the spawn plan is assembled, so it would never reach the child)',
|
|
2497
|
+
);
|
|
2498
|
+
} else if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
|
|
2499
|
+
errors.push(
|
|
2500
|
+
ctx + ' reviewer.invoke.env key ' + describeValue(key) +
|
|
2501
|
+
' is not a valid environment variable name',
|
|
2502
|
+
);
|
|
2503
|
+
}
|
|
2504
|
+
if (typeof invoke.env[key] !== 'string') {
|
|
2505
|
+
errors.push(
|
|
2506
|
+
ctx + ' reviewer.invoke.env.' + key + ' must be a string ' +
|
|
2507
|
+
'(got: ' + describeValue(invoke.env[key]) + ')',
|
|
2508
|
+
);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2039
2514
|
return errors;
|
|
2040
2515
|
}
|
|
2041
2516
|
|
|
@@ -3145,6 +3620,8 @@ module.exports = {
|
|
|
3145
3620
|
VALID_SANDBOX_TIERS,
|
|
3146
3621
|
VALID_ARTIFACT_KIND_NAMES,
|
|
3147
3622
|
VALID_ARTIFACT_NESTINGS,
|
|
3623
|
+
VALID_TRIGGER_PRECEDENCE_KINDS,
|
|
3624
|
+
DEFAULT_TRIGGER_PRECEDENCE,
|
|
3148
3625
|
FEATURE_FIELDS_FORBIDDEN_ON_RUNTIME,
|
|
3149
3626
|
// ADR-2782 D1/D2/D3/D6/D7/D8 — reviewer lane body
|
|
3150
3627
|
FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER,
|
|
@@ -3160,8 +3637,14 @@ module.exports = {
|
|
|
3160
3637
|
VALID_EVIDENCE_CLASSES,
|
|
3161
3638
|
VALID_LANE_HANDLERS,
|
|
3162
3639
|
KNOWN_REVIEWER_FIELDS,
|
|
3640
|
+
KNOWN_HOST_BEHAVIORS,
|
|
3641
|
+
MAX_REPORTED_UNKNOWN_KEYS,
|
|
3642
|
+
MAX_REPORTED_KEY_CHARS,
|
|
3163
3643
|
validateReviewerBody,
|
|
3164
3644
|
collectReviewerWarnings,
|
|
3645
|
+
collectReviewerWarningRecords,
|
|
3646
|
+
REVIEWER_WARNING,
|
|
3647
|
+
REMOVED_REVIEWER_CLI_FIELD,
|
|
3165
3648
|
VALID_INSTALL_SURFACES,
|
|
3166
3649
|
VALID_PERMISSION_WRITERS,
|
|
3167
3650
|
VALID_EXTENDED_HOOK_EVENTS,
|
|
@@ -270,8 +270,9 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
|
|
|
270
270
|
// access to getCommitAttribution (which lives in bin/install.js). Until that
|
|
271
271
|
// is refactored into a shared module, surface-path agents for descriptor-
|
|
272
272
|
// driven runtimes will lack the Co-Authored-By trailer that install adds.
|
|
273
|
-
// Parity is proven when resolveAttribution IS provided (see
|
|
274
|
-
//
|
|
273
|
+
// Parity is proven when resolveAttribution IS provided (see the
|
|
274
|
+
// folded:issue-1575-agent-descriptor-parity describe block in
|
|
275
|
+
// tests/golden-parity-single-source.test.cjs).
|
|
275
276
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
276
277
|
applySurface(resolvedConfigDir, layout, manifest, undefined, registry, opts?.materialize?.resolveAttribution
|
|
277
278
|
? { resolveAttribution: opts.materialize.resolveAttribution }
|