@opengsd/gsd-core 1.11.0 → 1.12.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 +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -9
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +0 -1
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +3 -1
- package/agents/gsd-plan-checker.md +15 -55
- package/agents/gsd-planner.md +6 -4
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +609 -134
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/quick.md +8 -4
- package/gsd-core/bin/gsd-tools.cjs +567 -51
- package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
- package/gsd-core/bin/lib/api-coverage.cjs +30 -9
- package/gsd-core/bin/lib/artifacts.cjs +2 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
- package/gsd-core/bin/lib/audit.cjs +163 -41
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +336 -95
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-validator.cjs +205 -18
- package/gsd-core/bin/lib/check-command-router.cjs +145 -5
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +543 -44
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +118 -29
- package/gsd-core/bin/lib/config.cjs +92 -2
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +84 -7
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/frontmatter.cjs +840 -305
- package/gsd-core/bin/lib/gap-checker.cjs +27 -3
- package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
- package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +120 -41
- package/gsd-core/bin/lib/install-engine.cjs +68 -3
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
- package/gsd-core/bin/lib/install-profiles.cjs +78 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +160 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-table.cjs +52 -4
- package/gsd-core/bin/lib/milestone.cjs +90 -5
- package/gsd-core/bin/lib/model-catalog.cjs +177 -19
- package/gsd-core/bin/lib/model-resolver.cjs +10 -28
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +70 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
- package/gsd-core/bin/lib/phase-locator.cjs +138 -17
- package/gsd-core/bin/lib/phase.cjs +405 -84
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-scan.cjs +13 -2
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
- package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
- package/gsd-core/bin/lib/probe-core.cjs +4 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
- package/gsd-core/bin/lib/roadmap.cjs +74 -19
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
- package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
- package/gsd-core/bin/lib/smart-entry.cjs +12 -22
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +47 -18
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +186 -0
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +517 -101
- package/gsd-core/bin/lib/state.cjs +946 -163
- package/gsd-core/bin/lib/surface.cjs +10 -2
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
- package/gsd-core/bin/lib/uat.cjs +1376 -125
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
- package/gsd-core/bin/lib/unusable-input.cjs +13 -0
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/vendor/README.md +43 -5
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
- package/gsd-core/bin/lib/verification.cjs +14 -1
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +95 -40
- package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +3 -2
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +37 -19
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +1 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +3 -3
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +22 -0
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +37 -8
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +2 -2
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +11 -11
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/phase-prompt.md +3 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +4 -9
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +10 -26
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
- package/gsd-core/workflows/code-review-fix.md +1 -1
- package/gsd-core/workflows/code-review.md +121 -40
- package/gsd-core/workflows/complete-milestone.md +15 -10
- package/gsd-core/workflows/debug.md +5 -3
- package/gsd-core/workflows/diagnose-issues.md +12 -6
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +4 -9
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +38 -54
- package/gsd-core/workflows/execute-plan.md +17 -12
- package/gsd-core/workflows/explore.md +1 -1
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +2 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +3 -6
- package/gsd-core/workflows/import.md +14 -11
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +44 -11
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +2 -2
- package/gsd-core/workflows/new-milestone.md +9 -21
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +12 -26
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +121 -42
- package/gsd-core/workflows/plan-review-convergence.md +46 -9
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +27 -12
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +20 -29
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +193 -15
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +2 -2
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +3 -5
- package/gsd-core/workflows/ship.md +12 -6
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +23 -1
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +5 -5
- package/gsd-core/workflows/ui-phase.md +10 -16
- package/gsd-core/workflows/ui-review.md +6 -10
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +6 -10
- package/gsd-core/workflows/validate-phase.md +2 -2
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +57 -18
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +16 -7
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +17 -8
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +215 -26
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +34 -16
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +3 -1
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +28 -23
- package/hooks/gsd-cursor-subagent-stop.js +3 -1
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +16 -7
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +17 -8
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +215 -26
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +34 -16
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +12 -7
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +5 -0
- package/scripts/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +45 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-capability-registry.cjs +8 -6
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-loop-host-contract.cjs +134 -1
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +32 -2
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +21 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-write-path-drift.cjs +337 -432
- package/scripts/lint-test-file-count.allowlist.json +122 -4
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +51 -64
- package/scripts/lint-vendored-deps.cjs +208 -35
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/prompt-injection-scan.sh +75 -14
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +22 -3
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/vscode/package.json +1 -1
- package/bin/lib/ui-safety-gate.cjs +0 -109
- package/scripts/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/state-write-path-drift-baseline.json +0 -19
|
@@ -45,7 +45,7 @@ const configLoaderMod = require("./config-loader.cjs");
|
|
|
45
45
|
const { loadConfig } = configLoaderMod;
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
47
47
|
const installProfilesMod = require("./install-profiles.cjs");
|
|
48
|
-
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents } = installProfilesMod;
|
|
48
|
+
const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents, workflowAgentRefs } = installProfilesMod;
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
50
|
const surfaceMod = require("./surface.cjs");
|
|
51
51
|
const { resolveSurface } = surfaceMod;
|
|
@@ -299,7 +299,17 @@ function _loadInstalledSkillsManifest(configDir) {
|
|
|
299
299
|
* no gsd-*.md files (so _resolveManifest can use size>0 as the "flat layout
|
|
300
300
|
* present" signal and fall through to the installed-skills branch otherwise).
|
|
301
301
|
*/
|
|
302
|
-
function _loadFlatCommandsGsdManifest(commandsParentDir) {
|
|
302
|
+
function _loadFlatCommandsGsdManifest(commandsParentDir, workflowsDir) {
|
|
303
|
+
// #3798: derive agents from the command body PLUS the workflow files it
|
|
304
|
+
// references, exactly like loadSkillsManifest does for the nested layout —
|
|
305
|
+
// the flat (#1858) layout retained the original defect otherwise, and the
|
|
306
|
+
// parity test in tests/capability-state.test.cjs asserts the two loaders
|
|
307
|
+
// produce identical _calls_agents_* sets. Default: <pkg>/gsd-core/workflows
|
|
308
|
+
// one level above the commands dir — the shape of both the repo checkout
|
|
309
|
+
// (<repo>/commands/) and a flat runtime install (the runtime config dir's
|
|
310
|
+
// commands/ with the package's gsd-core/ beside it).
|
|
311
|
+
const flatWorkflowsDir = workflowsDir
|
|
312
|
+
|| node_path_1.default.resolve(commandsParentDir, '..', 'gsd-core', 'workflows');
|
|
303
313
|
const manifest = new Map();
|
|
304
314
|
let entries;
|
|
305
315
|
try {
|
|
@@ -327,7 +337,12 @@ function _loadFlatCommandsGsdManifest(commandsParentDir) {
|
|
|
327
337
|
try {
|
|
328
338
|
const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsParentDir, entry.name), 'utf8');
|
|
329
339
|
manifest.set(stem, parseRequires(content));
|
|
330
|
-
manifest.set(`_calls_agents_${stem}`,
|
|
340
|
+
manifest.set(`_calls_agents_${stem}`, [
|
|
341
|
+
...new Set([
|
|
342
|
+
...parseCallsAgents(content),
|
|
343
|
+
...workflowAgentRefs(content, flatWorkflowsDir),
|
|
344
|
+
]),
|
|
345
|
+
]);
|
|
331
346
|
}
|
|
332
347
|
catch {
|
|
333
348
|
manifest.set(stem, []);
|
|
@@ -396,6 +396,11 @@ function validateCapability(cap, folderId) {
|
|
|
396
396
|
errors.push(...validateRuntimeBody(cap));
|
|
397
397
|
// A host that is ALSO a reviewer keeps exactly one manifest (ADR-2782 D1).
|
|
398
398
|
errors.push(...validateReviewerBody(cap));
|
|
399
|
+
// ADR-3646: a runtime capability installs a host CLI, it does not resolve
|
|
400
|
+
// task content — taskContentResolver is feature-only.
|
|
401
|
+
if (cap.taskContentResolver !== undefined) {
|
|
402
|
+
errors.push('role:runtime capability must not have a "taskContentResolver" body (feature-only field)');
|
|
403
|
+
}
|
|
399
404
|
} else if (cap.role === 'reviewer') {
|
|
400
405
|
// ADR-2782 D3 — a lane that is not an install target. No runtime body, no
|
|
401
406
|
// install surface, no runtimeCompat (it surfaces through no host runtime).
|
|
@@ -718,6 +723,118 @@ function validateFeatureBody(cap) {
|
|
|
718
723
|
}
|
|
719
724
|
}
|
|
720
725
|
|
|
726
|
+
// ADR-3646: optional per-task external-tracker content-resolution seam.
|
|
727
|
+
errors.push(...validateTaskContentResolver(cap));
|
|
728
|
+
|
|
729
|
+
return errors;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* ADR-3646 — validate an OPTIONAL `taskContentResolver` body on a `role:
|
|
734
|
+
* "feature"` capability. Absence is never an error (most manifests won't
|
|
735
|
+
* have one); presence is strictly validated.
|
|
736
|
+
*
|
|
737
|
+
* Wrapped in try/catch to degrade any unexpected throw (a hostile Proxy, a
|
|
738
|
+
* throwing getter, etc.) to a single validation error rather than crashing
|
|
739
|
+
* every consumer of loadRegistry, per the #1461 OVL-1 discipline that
|
|
740
|
+
* `validateReviewerBody` follows.
|
|
741
|
+
*
|
|
742
|
+
* @param {object} cap The parsed capability manifest.
|
|
743
|
+
* @returns {string[]} Array of error strings; empty = valid or absent.
|
|
744
|
+
*/
|
|
745
|
+
function validateTaskContentResolver(cap) {
|
|
746
|
+
try {
|
|
747
|
+
return validateTaskContentResolverFields(cap);
|
|
748
|
+
} catch (err) {
|
|
749
|
+
return ['capability taskContentResolver body could not be validated: ' + safeErrorMessage(err)];
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Upper bound for `taskContentResolver.invoke.timeoutMs`, specific to this
|
|
755
|
+
* field only. `isPositiveIntegerMs()` has no ceiling and stays that way — it
|
|
756
|
+
* is shared with the reviewer lane's `timeoutFloorMs` and probe `timeoutMs`,
|
|
757
|
+
* which may legitimately need a longer or unbounded value. Without a ceiling
|
|
758
|
+
* here, a manifest could declare `Number.MAX_SAFE_INTEGER` and let
|
|
759
|
+
* `resolve-content` hang near-indefinitely on a stuck/malicious resolver,
|
|
760
|
+
* defeating the feature's "bounded subprocess" design intent.
|
|
761
|
+
*/
|
|
762
|
+
const TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS = 120000;
|
|
763
|
+
|
|
764
|
+
function validateTaskContentResolverFields(cap) {
|
|
765
|
+
const errors = [];
|
|
766
|
+
if (typeof cap !== 'object' || cap === null || Array.isArray(cap)) return errors;
|
|
767
|
+
|
|
768
|
+
const tcr = cap.taskContentResolver;
|
|
769
|
+
if (tcr === undefined) return errors; // optional — never an error to omit
|
|
770
|
+
|
|
771
|
+
const ctx = 'capability "' + (typeof cap.id === 'string' ? cap.id : '(unknown)') + '"';
|
|
772
|
+
|
|
773
|
+
if (typeof tcr !== 'object' || tcr === null || Array.isArray(tcr)) {
|
|
774
|
+
const got = tcr === null ? 'null' : Array.isArray(tcr) ? 'array' : typeof tcr;
|
|
775
|
+
errors.push(
|
|
776
|
+
ctx + ' taskContentResolver must be an object (got: ' + got + '). ' +
|
|
777
|
+
'Omit the key entirely to declare no resolver — an explicit null is not an omission.',
|
|
778
|
+
);
|
|
779
|
+
return errors; // cannot validate fields of a non-object
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
// ── trackerPrefix — same grammar as a capability id ─────────────────────
|
|
783
|
+
if (typeof tcr.trackerPrefix !== 'string' || tcr.trackerPrefix.length === 0 || !KEBAB_RE.test(tcr.trackerPrefix)) {
|
|
784
|
+
errors.push(
|
|
785
|
+
ctx + ' taskContentResolver.trackerPrefix must be a non-empty kebab-case string matching ' +
|
|
786
|
+
String(KEBAB_RE) + ' (got: ' + describeValue(tcr.trackerPrefix) + ')',
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// ── invoke ────────────────────────────────────────────────────────────
|
|
791
|
+
const inv = tcr.invoke;
|
|
792
|
+
if (typeof inv !== 'object' || inv === null || Array.isArray(inv)) {
|
|
793
|
+
const got = inv === null ? 'null' : Array.isArray(inv) ? 'array' : typeof inv;
|
|
794
|
+
errors.push(ctx + ' taskContentResolver.invoke must be an object (got: ' + got + ')');
|
|
795
|
+
return errors; // cannot validate sub-fields of a non-object
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (typeof inv.binary !== 'string' || inv.binary.length === 0) {
|
|
799
|
+
errors.push(ctx + ' taskContentResolver.invoke.binary must be a non-empty string');
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (!Array.isArray(inv.args)) {
|
|
803
|
+
errors.push(ctx + ' taskContentResolver.invoke.args must be an array of strings');
|
|
804
|
+
} else {
|
|
805
|
+
let hasPlaceholder = false;
|
|
806
|
+
for (const a of inv.args) {
|
|
807
|
+
if (typeof a !== 'string') {
|
|
808
|
+
errors.push(ctx + ' taskContentResolver.invoke.args entries must be strings (got: ' + describeValue(a) + ')');
|
|
809
|
+
} else if (a === '{{id}}') {
|
|
810
|
+
hasPlaceholder = true;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (!hasPlaceholder) {
|
|
814
|
+
errors.push(
|
|
815
|
+
ctx + ' taskContentResolver.invoke.args must contain a "{{id}}" placeholder — ' +
|
|
816
|
+
'without it the resolved tracker id could never reach the resolver subprocess',
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (!isPositiveIntegerMs(inv.timeoutMs)) {
|
|
822
|
+
errors.push(
|
|
823
|
+
ctx + ' taskContentResolver.invoke.timeoutMs must be a positive integer of milliseconds — ' +
|
|
824
|
+
'an unbounded resolver call could hang task execution indefinitely ' +
|
|
825
|
+
'(got: ' + describeValue(inv.timeoutMs) + ')',
|
|
826
|
+
);
|
|
827
|
+
} else if (inv.timeoutMs > TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS) {
|
|
828
|
+
// Ceiling specific to this field — `isPositiveIntegerMs()` itself stays
|
|
829
|
+
// unbounded because it is shared with the reviewer lane's
|
|
830
|
+
// `timeoutFloorMs`/probe `timeoutMs`, which have no such ceiling.
|
|
831
|
+
errors.push(
|
|
832
|
+
ctx + ' taskContentResolver.invoke.timeoutMs must not exceed ' +
|
|
833
|
+
TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS + 'ms — an unbounded-in-practice value defeats the ' +
|
|
834
|
+
'"bounded subprocess" design intent (got: ' + inv.timeoutMs + ')',
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
|
|
721
838
|
return errors;
|
|
722
839
|
}
|
|
723
840
|
|
|
@@ -943,7 +1060,7 @@ const HTTP_ONLY_INVOKE_FIELDS = ['hostConfigKey', 'defaultHost', 'path', 'model
|
|
|
943
1060
|
|
|
944
1061
|
// Feature-only fields are as forbidden on a lane-only capability as on a runtime
|
|
945
1062
|
// one; a `role: "reviewer"` capability owns no artefacts and wires no loop point.
|
|
946
|
-
const FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey'];
|
|
1063
|
+
const FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey', 'taskContentResolver'];
|
|
947
1064
|
|
|
948
1065
|
// GATE A: installSurface → allowed hooksSurface values (DEFECT.GENERATIVE-FIX: parity invariant)
|
|
949
1066
|
// Derived from the actual pairings in the 16 real runtime descriptors.
|
|
@@ -1588,6 +1705,16 @@ function validateRuntimeBody(cap) {
|
|
|
1588
1705
|
'runtime.orchestratorExec.promptFlag must be a string or null (got: ' + JSON.stringify(oe.promptFlag) + ')',
|
|
1589
1706
|
);
|
|
1590
1707
|
}
|
|
1708
|
+
|
|
1709
|
+
// modelFlag — optional; string or null (#3714). `null`/absent means the
|
|
1710
|
+
// host offers no per-invocation model override on this exec path; a
|
|
1711
|
+
// string names the flag that pins the executor's model (codex: --model).
|
|
1712
|
+
// Deliberately asymmetric: only codex declares this today.
|
|
1713
|
+
if (oe.modelFlag !== undefined && oe.modelFlag !== null && typeof oe.modelFlag !== 'string') {
|
|
1714
|
+
errors.push(
|
|
1715
|
+
'runtime.orchestratorExec.modelFlag must be a string or null (got: ' + JSON.stringify(oe.modelFlag) + ')',
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1591
1718
|
}
|
|
1592
1719
|
}
|
|
1593
1720
|
|
|
@@ -3174,6 +3301,11 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
|
|
|
3174
3301
|
const laneSlugClaims = new Map(); // slug → capId[]
|
|
3175
3302
|
const laneFlagClaims = new Map(); // flag → capId[]
|
|
3176
3303
|
const laneSectionClaims = new Map(); // reviewsSection → capId[]
|
|
3304
|
+
// ADR-3646: task-content resolver tracker-prefix uniqueness across the
|
|
3305
|
+
// MERGED first-party ∪ overlay set, mirroring the reviewer-lane collision
|
|
3306
|
+
// pattern above — two resolvers claiming the same prefix would make
|
|
3307
|
+
// `execute:task` dispatch ambiguous (which capability's resolver runs?).
|
|
3308
|
+
const trackerPrefixClaims = new Map(); // trackerPrefix → capId[]
|
|
3177
3309
|
|
|
3178
3310
|
// Claims are ACCUMULATED and reported after the sweep, never reported on the
|
|
3179
3311
|
// second claimant. Reporting pairwise-on-collision looks equivalent and is not:
|
|
@@ -3194,6 +3326,15 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
|
|
|
3194
3326
|
};
|
|
3195
3327
|
|
|
3196
3328
|
for (const [capId, cap] of capMap) {
|
|
3329
|
+
// ADR-3646: a MALFORMED taskContentResolver body was already reported by
|
|
3330
|
+
// validateCapability — do not double-report; only claim well-shaped
|
|
3331
|
+
// bodies. Independent of the reviewer-lane checks below, so it runs even
|
|
3332
|
+
// for capabilities that carry no `reviewer` body at all.
|
|
3333
|
+
const tcr = cap.taskContentResolver;
|
|
3334
|
+
if (typeof tcr === 'object' && tcr !== null && !Array.isArray(tcr)) {
|
|
3335
|
+
claim(trackerPrefixClaims, tcr.trackerPrefix, capId);
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3197
3338
|
const r = cap.reviewer;
|
|
3198
3339
|
// A capability with no lane contributes to no uniqueness set. A MALFORMED
|
|
3199
3340
|
// body was already reported by validateCapability — do not double-report.
|
|
@@ -3215,6 +3356,7 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
|
|
|
3215
3356
|
[laneSlugClaims, 'slug'],
|
|
3216
3357
|
[laneFlagClaims, 'flag'],
|
|
3217
3358
|
[laneSectionClaims, 'reviewsSection'],
|
|
3359
|
+
[trackerPrefixClaims, 'taskContentResolver.trackerPrefix'],
|
|
3218
3360
|
]) {
|
|
3219
3361
|
for (const [key, claimants] of claims) {
|
|
3220
3362
|
if (claimants.length < 2) continue;
|
|
@@ -3435,26 +3577,55 @@ function topoSortContributions(entries) {
|
|
|
3435
3577
|
|
|
3436
3578
|
// ─── Gen-time wired guard ─────────────────────────────────────────────────────
|
|
3437
3579
|
|
|
3580
|
+
/**
|
|
3581
|
+
* Hook group (capability.json array name) → hook kind (dispatch discriminator).
|
|
3582
|
+
* Single source of truth for both validateHooksWired and the scanner-parity
|
|
3583
|
+
* test in tests/capability-registry.test.cjs (#3606).
|
|
3584
|
+
*/
|
|
3585
|
+
const HOOK_GROUP_KINDS = Object.freeze({
|
|
3586
|
+
steps: 'step',
|
|
3587
|
+
contributions: 'contribution',
|
|
3588
|
+
gates: 'gate',
|
|
3589
|
+
});
|
|
3590
|
+
|
|
3438
3591
|
/**
|
|
3439
3592
|
* Validate that every hook point declared by a capability has a corresponding
|
|
3440
|
-
* `loop render-hooks <point>` call site in one of the host-loop workflow files
|
|
3593
|
+
* `loop render-hooks <point>` call site in one of the host-loop workflow files,
|
|
3594
|
+
* AND — #3606 — that the call site's dispatch text covers the hook's KIND.
|
|
3595
|
+
*
|
|
3596
|
+
* A call site proves hooks are rendered, not dispatched: a consumer that
|
|
3597
|
+
* iterates only `kind == "gate"` (or narrows `kind == "step"` to one
|
|
3598
|
+
* `ref.skill`) silently drops every other registered kind — a capability can
|
|
3599
|
+
* be wired, enabled, resolved active, and never run. See
|
|
3600
|
+
* gsd-core/references/loop-hook-dispatch.md ("A point whose workflow
|
|
3601
|
+
* hand-rolls one kind does not implement this contract").
|
|
3441
3602
|
*
|
|
3442
3603
|
* Only valid loop points (in VALID_LOOP_POINTS) are checked here. Invalid points
|
|
3443
3604
|
* are already caught by validateStep/validateContribution/validateGate — do not
|
|
3444
3605
|
* double-report.
|
|
3445
3606
|
*
|
|
3607
|
+
* KNOWN LIMITATION (#3606): coverage is the UNION across all call sites for a
|
|
3608
|
+
* point in the five STEP_WORKFLOWS host files. Consumers outside that universe
|
|
3609
|
+
* (quick.md, autonomous.md, code-review*.md, audit-milestone.md,
|
|
3610
|
+
* secure-phase.md, validate-phase.md) are not per-file checked — a narrowed
|
|
3611
|
+
* consumer there passes as long as one host file covers the point. Per-file
|
|
3612
|
+
* coverage maps are the tightening path.
|
|
3613
|
+
*
|
|
3446
3614
|
* @param {object} cap Validated capability object.
|
|
3447
|
-
* @param {Set<string
|
|
3448
|
-
*
|
|
3615
|
+
* @param {Map<string, Set<string>>} wiredKinds Per point, the hook kinds the
|
|
3616
|
+
* host workflows' call-site dispatch text covers (getWiredKinds). A point
|
|
3617
|
+
* absent from the map is unwired.
|
|
3618
|
+
* @returns {string[]} Array of error strings; empty means all points
|
|
3619
|
+
* are wired and every registered kind is covered.
|
|
3449
3620
|
*/
|
|
3450
|
-
function validateHooksWired(cap,
|
|
3621
|
+
function validateHooksWired(cap, wiredKinds) {
|
|
3451
3622
|
const errors = [];
|
|
3452
3623
|
const capId = cap.id || '(unknown)';
|
|
3453
3624
|
|
|
3454
|
-
function checkPoint(point, groupName, idx) {
|
|
3625
|
+
function checkPoint(point, groupName, kind, idx) {
|
|
3455
3626
|
// Only flag valid points that are unwired — invalid points are schema-validator's job.
|
|
3456
3627
|
if (!VALID_LOOP_POINTS.has(point)) return;
|
|
3457
|
-
if (!
|
|
3628
|
+
if (!wiredKinds.has(point)) {
|
|
3458
3629
|
errors.push(
|
|
3459
3630
|
'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
|
|
3460
3631
|
'" is declared but not wired in any host-loop workflow ' +
|
|
@@ -3462,20 +3633,34 @@ function validateHooksWired(cap, wiredSet) {
|
|
|
3462
3633
|
'Wire the call site in the host workflow ' +
|
|
3463
3634
|
'(see scripts/gen-loop-host-contract.cjs STEP_WORKFLOWS) or remove the hook.',
|
|
3464
3635
|
);
|
|
3636
|
+
return;
|
|
3637
|
+
}
|
|
3638
|
+
const covered = wiredKinds.get(point);
|
|
3639
|
+
if (covered.size === 0) {
|
|
3640
|
+
errors.push(
|
|
3641
|
+
'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
|
|
3642
|
+
'" has `loop render-hooks ' + point + '` call site(s), but their dispatch text covers NO ' +
|
|
3643
|
+
'hook kind — every consumer is narrowed to specific hooks. Dispatch every registered kind ' +
|
|
3644
|
+
'per gsd-core/references/loop-hook-dispatch.md.',
|
|
3645
|
+
);
|
|
3646
|
+
return;
|
|
3647
|
+
}
|
|
3648
|
+
if (!covered.has(kind)) {
|
|
3649
|
+
errors.push(
|
|
3650
|
+
'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
|
|
3651
|
+
'" registers a ' + kind + ' hook, but the host call site\'s dispatch text never ' +
|
|
3652
|
+
'covers `kind == "' + kind + '"` (it covers: ' + [...covered].sort().join(', ') + '). ' +
|
|
3653
|
+
'A hand-rolled single-kind consumer silently never dispatches the other kinds — ' +
|
|
3654
|
+
'dispatch every registered kind per gsd-core/references/loop-hook-dispatch.md.',
|
|
3655
|
+
);
|
|
3465
3656
|
}
|
|
3466
3657
|
}
|
|
3467
3658
|
|
|
3468
|
-
for (
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
const hook = cap.contributions[i];
|
|
3474
|
-
if (hook.point !== undefined) checkPoint(hook.point, 'contributions', i);
|
|
3475
|
-
}
|
|
3476
|
-
for (let i = 0; i < (cap.gates || []).length; i++) {
|
|
3477
|
-
const hook = cap.gates[i];
|
|
3478
|
-
if (hook.point !== undefined) checkPoint(hook.point, 'gates', i);
|
|
3659
|
+
for (const [group, kind] of Object.entries(HOOK_GROUP_KINDS)) {
|
|
3660
|
+
for (let i = 0; i < (cap[group] || []).length; i++) {
|
|
3661
|
+
const hook = cap[group][i];
|
|
3662
|
+
if (hook.point !== undefined) checkPoint(hook.point, group, kind, i);
|
|
3663
|
+
}
|
|
3479
3664
|
}
|
|
3480
3665
|
|
|
3481
3666
|
return errors;
|
|
@@ -3681,6 +3866,7 @@ module.exports = {
|
|
|
3681
3866
|
validateCommandEntry,
|
|
3682
3867
|
validateRuntimeCompat,
|
|
3683
3868
|
validateFeatureBody,
|
|
3869
|
+
validateTaskContentResolver,
|
|
3684
3870
|
validateConfigHome,
|
|
3685
3871
|
validateArtifactKindEntry,
|
|
3686
3872
|
validateArtifactLayout,
|
|
@@ -3699,6 +3885,7 @@ module.exports = {
|
|
|
3699
3885
|
topoSortSteps,
|
|
3700
3886
|
topoSortContributions,
|
|
3701
3887
|
validateHooksWired,
|
|
3888
|
+
HOOK_GROUP_KINDS,
|
|
3702
3889
|
validateConfigSliceEntry,
|
|
3703
3890
|
classifyCrossErrors,
|
|
3704
3891
|
runConfigFormatParityGate,
|
|
@@ -52,6 +52,9 @@ const { scanPhasePlans } = planScanMod;
|
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
53
53
|
const planningScopeMod = require("./planning-scope.cjs");
|
|
54
54
|
const { SCOPE } = planningScopeMod;
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
56
|
+
const verifyCommandGroundingMod = require("./verify-command-grounding.cjs");
|
|
57
|
+
const { probePhaseVerifyCommands, probePhaseFailingDirections } = verifyCommandGroundingMod;
|
|
55
58
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
56
59
|
function normalizePhrase(text) {
|
|
57
60
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
@@ -783,14 +786,11 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
|
|
|
783
786
|
}
|
|
784
787
|
const violations = rows.filter(r => r.status === 'FAIL').length;
|
|
785
788
|
// Build review table
|
|
786
|
-
const sep = '━'.repeat(53);
|
|
787
789
|
const tableHeader = '| Plan | RED | GREEN | REFACTOR | Status |';
|
|
788
790
|
const tableDivider = '|------|-----|-------|----------|--------|';
|
|
789
791
|
const tableRows = rows.map(r => `| ${r.planId.padEnd(4)} | ${r.red ? ' ✓ ' : ' ✗ '} | ${r.green ? ' ✓ ' : ' ✗ '} | ${r.refactor ? ' ✓ ' : ' — '} | ${r.status.padEnd(6)} |`);
|
|
790
792
|
let table = [
|
|
791
|
-
|
|
792
|
-
` TDD REVIEW — Phase ${phase}`,
|
|
793
|
-
sep,
|
|
793
|
+
`### TDD REVIEW — Phase ${phase}`,
|
|
794
794
|
'',
|
|
795
795
|
`TDD Plans: ${tddPlanFiles.length} | Gate violations: ${violations}`,
|
|
796
796
|
'',
|
|
@@ -825,6 +825,111 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
|
|
|
825
825
|
// the host-loop dispatch's advisory branch can surface it.
|
|
826
826
|
output(result, raw, undefined);
|
|
827
827
|
}
|
|
828
|
+
/**
|
|
829
|
+
* Resolve a phase argument to an absolute phase directory, or '' when it
|
|
830
|
+
* cannot be resolved. Shared by every `check` arm that probes a phase's
|
|
831
|
+
* PLAN.md files, so the two never drift (DEFECT.GENERATIVE-FIX-DIVERGENCE).
|
|
832
|
+
* Never throws — the callers emit a degraded JSON payload instead, because
|
|
833
|
+
* a consumer must be able to tell "nothing to report" from "could not look".
|
|
834
|
+
*/
|
|
835
|
+
function resolvePhaseDirOrEmpty(projectDir, phase) {
|
|
836
|
+
try {
|
|
837
|
+
const result = findPhaseInternal(projectDir, phase);
|
|
838
|
+
if (result && typeof result === 'object') {
|
|
839
|
+
// findPhaseInternal returns { directory: '<relative-posix-path>', ... }
|
|
840
|
+
// directory is relative to cwd — resolve it to absolute.
|
|
841
|
+
const relDir = typeof result['directory'] === 'string' ? result['directory'] : '';
|
|
842
|
+
if (relDir) {
|
|
843
|
+
return node_path_1.default.resolve(projectDir, relDir);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
else if (typeof result === 'string') {
|
|
847
|
+
return result;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
catch { /* phase dir lookup failure → caller emits degraded payload */ }
|
|
851
|
+
return '';
|
|
852
|
+
}
|
|
853
|
+
// ─── verify-command-paths (#2401) ──────────────────────────────────────────────
|
|
854
|
+
/**
|
|
855
|
+
* verify-command-paths: probes every `<automated>` verify command declared in a
|
|
856
|
+
* phase's `-PLAN.md` files against the filesystem WITHOUT executing anything —
|
|
857
|
+
* see verify-command-grounding.cjs for the recognizer contract.
|
|
858
|
+
*
|
|
859
|
+
* Args: check verify-command-paths <phase>
|
|
860
|
+
* Invocable as: gsd_run check verify-command-paths <phase>
|
|
861
|
+
*
|
|
862
|
+
* When the phase cannot be resolved to a directory, this emits a non-throwing
|
|
863
|
+
* degraded JSON payload (status/commands/counts all zeroed, `readError`
|
|
864
|
+
* populated) rather than calling `error()` — the plan-checker parses this
|
|
865
|
+
* result and must be able to distinguish "nothing to report" from "could not
|
|
866
|
+
* look", which a non-zero exit / thrown error would collapse.
|
|
867
|
+
*/
|
|
868
|
+
function cmdVerifyCommandPaths(projectDir, args, raw) {
|
|
869
|
+
// args[0] = 'check', args[1] = 'verify-command-paths', args[2] = phase
|
|
870
|
+
const phase = args[2] || '';
|
|
871
|
+
if (!phase) {
|
|
872
|
+
output({
|
|
873
|
+
status: 'unresolvable',
|
|
874
|
+
commands: [],
|
|
875
|
+
counts: { blocker: 0, warning: 0, total: 0 },
|
|
876
|
+
readError: 'verify-command-paths requires a phase argument: check verify-command-paths <phase>',
|
|
877
|
+
}, raw, undefined);
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
const phaseDir = resolvePhaseDirOrEmpty(projectDir, phase);
|
|
881
|
+
if (!phaseDir) {
|
|
882
|
+
output({
|
|
883
|
+
status: 'unresolvable',
|
|
884
|
+
commands: [],
|
|
885
|
+
counts: { blocker: 0, warning: 0, total: 0 },
|
|
886
|
+
readError: `could not resolve phase directory for phase ${phase}`,
|
|
887
|
+
}, raw, undefined);
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
const probed = probePhaseVerifyCommands({ phaseDir, projectRoot: projectDir });
|
|
891
|
+
output(probed, raw, undefined);
|
|
892
|
+
}
|
|
893
|
+
// ─── verify-failure-directions (#3172) ─────────────────────────────────────────
|
|
894
|
+
/**
|
|
895
|
+
* verify-failure-directions: probes every `<automated>` verify command
|
|
896
|
+
* declared in a phase's `-PLAN.md` files for a stated `<fails_when>` failing
|
|
897
|
+
* direction — see verify-command-grounding.cjs for the recognizer contract.
|
|
898
|
+
*
|
|
899
|
+
* Args: check verify-failure-directions <phase>
|
|
900
|
+
* Invocable as: gsd_run check verify-failure-directions <phase>
|
|
901
|
+
*
|
|
902
|
+
* When the phase cannot be resolved to a directory, this emits a non-throwing
|
|
903
|
+
* degraded JSON payload (status/commands/counts all zeroed, `readError`
|
|
904
|
+
* populated) rather than calling `error()` — the plan-checker parses this
|
|
905
|
+
* result and must be able to distinguish "nothing to report" from "could not
|
|
906
|
+
* look", which a non-zero exit / thrown error would collapse.
|
|
907
|
+
*/
|
|
908
|
+
function cmdVerifyFailureDirections(projectDir, args, raw) {
|
|
909
|
+
// args[0] = 'check', args[1] = 'verify-failure-directions', args[2] = phase
|
|
910
|
+
const phase = args[2] || '';
|
|
911
|
+
if (!phase) {
|
|
912
|
+
output({
|
|
913
|
+
status: 'unresolvable',
|
|
914
|
+
commands: [],
|
|
915
|
+
counts: { blocker: 0, warning: 0, total: 0 },
|
|
916
|
+
readError: 'verify-failure-directions requires a phase argument: check verify-failure-directions <phase>',
|
|
917
|
+
}, raw, undefined);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const phaseDir = resolvePhaseDirOrEmpty(projectDir, phase);
|
|
921
|
+
if (!phaseDir) {
|
|
922
|
+
output({
|
|
923
|
+
status: 'unresolvable',
|
|
924
|
+
commands: [],
|
|
925
|
+
counts: { blocker: 0, warning: 0, total: 0 },
|
|
926
|
+
readError: `could not resolve phase directory for phase ${phase}`,
|
|
927
|
+
}, raw, undefined);
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
const result = probePhaseFailingDirections({ phaseDir });
|
|
931
|
+
output(result, raw, undefined);
|
|
932
|
+
}
|
|
828
933
|
// ─── gap-analysis-plan-post ───────────────────────────────────────────────────
|
|
829
934
|
/**
|
|
830
935
|
* gap-analysis-plan-post: non-blocking advisory check that runs the post-planning
|
|
@@ -1203,6 +1308,27 @@ function cmdApiCoverageVerifyPre(projectDir, args, raw) {
|
|
|
1203
1308
|
}, raw, undefined);
|
|
1204
1309
|
return;
|
|
1205
1310
|
}
|
|
1311
|
+
// An EMPTY scope is not a negative verdict. This gate's neighbouring arms
|
|
1312
|
+
// already fail closed (unresolvable phase → block; unreadable plan → block),
|
|
1313
|
+
// but a phase with no plan body AND no roadmap section fell through to
|
|
1314
|
+
// detection over zero bytes and CERTIFIED "no external-API integration" —
|
|
1315
|
+
// clearing a blocking seal gate on a probe that examined nothing
|
|
1316
|
+
// (ADR-3889 failure class (c), #3909). The discriminator is BYTES EXAMINED,
|
|
1317
|
+
// never SIGNALS FOUND: a phase with real plans and no API vocabulary still
|
|
1318
|
+
// reaches the pass below unchanged.
|
|
1319
|
+
if (scope.text.trim() === '') {
|
|
1320
|
+
output({
|
|
1321
|
+
block: true,
|
|
1322
|
+
passed: false,
|
|
1323
|
+
coverage_present: false,
|
|
1324
|
+
detected: false,
|
|
1325
|
+
scope_unavailable: true,
|
|
1326
|
+
message: 'api-coverage: the phase scope is empty — no plan body and no roadmap section were ' +
|
|
1327
|
+
'found, so nothing was examined. Refusing to certify no external-API integration ' +
|
|
1328
|
+
'from an unestablished scope. Add the phase plan, or add a COVERAGE.md declaration.',
|
|
1329
|
+
}, raw, undefined);
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1206
1332
|
const detection = detectApiIntegration(scope.text);
|
|
1207
1333
|
if (detection.detected) {
|
|
1208
1334
|
// Surface only verb/noun (typed, bounded) — NOT raw prose snippets — so the
|
|
@@ -1324,6 +1450,18 @@ function routeCheckCommand({ args, cwd, raw }) {
|
|
|
1324
1450
|
cmdGapAnalysisPlanPost(cwd, args, raw);
|
|
1325
1451
|
return;
|
|
1326
1452
|
}
|
|
1453
|
+
if (subcommand === 'verify-command-paths') {
|
|
1454
|
+
// Deterministic filesystem probe for <automated> verify commands (#2401) —
|
|
1455
|
+
// never executes anything; see verify-command-grounding.cjs.
|
|
1456
|
+
cmdVerifyCommandPaths(cwd, args, raw);
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (subcommand === 'verify-failure-directions') {
|
|
1460
|
+
// Presence probe for a stated <fails_when> per <automated> command
|
|
1461
|
+
// (#3172) — never executes anything; see verify-command-grounding.cjs.
|
|
1462
|
+
cmdVerifyFailureDirections(cwd, args, raw);
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1327
1465
|
if (subcommand === 'api-coverage-verify-pre') {
|
|
1328
1466
|
// ai-integration capability blocking gate at verify:pre (#1562). Dot-to-
|
|
1329
1467
|
// hyphen normalization means query "api-coverage.verify-pre" routes here.
|
|
@@ -1370,7 +1508,7 @@ function routeCheckCommand({ args, cwd, raw }) {
|
|
|
1370
1508
|
(0, prohibition_enforcement_cjs_1.routeProhibitionEnforcement)(args, raw);
|
|
1371
1509
|
return;
|
|
1372
1510
|
}
|
|
1373
|
-
error('Unknown check subcommand. Available: api-coverage-verify-pre, auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, predicate, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
1511
|
+
error('Unknown check subcommand. Available: api-coverage-verify-pre, auto-mode, decision-coverage-plan, decision-coverage-verify, gap-analysis-plan-post, predicate, prohibition-enforcement, tdd-review-checkpoint, ui-plan-gate, ui-safety-gate, verify-command-paths, verify-failure-directions, verify-schema-drift, verify-codebase-drift', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
1374
1512
|
}
|
|
1375
1513
|
module.exports = {
|
|
1376
1514
|
routeCheckCommand,
|
|
@@ -1379,6 +1517,8 @@ module.exports = {
|
|
|
1379
1517
|
computeUiPlanGate,
|
|
1380
1518
|
computeUiSafetyGate,
|
|
1381
1519
|
cmdGapAnalysisPlanPost,
|
|
1520
|
+
cmdVerifyCommandPaths,
|
|
1521
|
+
cmdVerifyFailureDirections,
|
|
1382
1522
|
cmdTddReviewCheckpoint,
|
|
1383
1523
|
cmdCheckPredicate,
|
|
1384
1524
|
buildPredicateDeps,
|