@opengsd/gsd-core 1.11.0 → 1.13.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/.opencode/plugins/gsd-core.js +12 -0
- 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 +78 -42
- 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 +91 -112
- package/agents/gsd-planner.md +20 -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 +82 -7
- package/agents/gsd-ui-researcher.md +70 -3
- package/agents/gsd-verifier.md +24 -2
- package/bin/install.js +847 -200
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.md +1 -1
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/phase.md +1 -1
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/quick.md +8 -4
- package/commands/gsd/surface.md +18 -8
- package/gsd-core/bin/gsd-tools.cjs +761 -100
- 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-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +785 -144
- package/gsd-core/bin/lib/capability-state.cjs +25 -4
- package/gsd-core/bin/lib/capability-validator.cjs +321 -18
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +229 -6
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- 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-aliases.cjs +16 -0
- 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 +877 -54
- package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
- package/gsd-core/bin/lib/config-loader.cjs +121 -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 +118 -14
- package/gsd-core/bin/lib/decisions.cjs +213 -1
- package/gsd-core/bin/lib/edge-probe.cjs +23 -2
- 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/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +975 -326
- package/gsd-core/bin/lib/gap-checker.cjs +41 -8
- package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
- 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 +96 -11
- package/gsd-core/bin/lib/init-command-router.cjs +132 -21
- package/gsd-core/bin/lib/init.cjs +252 -56
- package/gsd-core/bin/lib/install-engine.cjs +252 -15
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
- package/gsd-core/bin/lib/install-profiles.cjs +100 -18
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -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 +195 -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/loop-resolver.cjs +14 -8
- package/gsd-core/bin/lib/markdown-table.cjs +175 -4
- package/gsd-core/bin/lib/milestone.cjs +112 -7
- 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-command-router.cjs +13 -6
- package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
- package/gsd-core/bin/lib/phase-id.cjs +321 -13
- 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 +1175 -115
- package/gsd-core/bin/lib/plan-document.cjs +273 -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 +165 -34
- package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
- 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/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- 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 +62 -14
- package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
- package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
- package/gsd-core/bin/lib/roadmap.cjs +248 -64
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
- 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 +75 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -31
- 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 +216 -5
- package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
- package/gsd-core/bin/lib/state-transition.cjs +850 -145
- package/gsd-core/bin/lib/state.cjs +1629 -287
- package/gsd-core/bin/lib/surface.cjs +33 -10
- 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/tdd-red-evidence.cjs +133 -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 +2542 -387
- 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/update-context.cjs +6 -2
- package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +230 -12
- 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-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -13
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +441 -56
- package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
- 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 +185 -21
- package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -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/bin/verify-reapply-patches.cjs +70 -3
- package/gsd-core/references/agent-contracts.md +6 -5
- 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 +17 -5
- package/gsd-core/references/execute-mvp-tdd.md +18 -18
- package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/execute-phase-wave-guard.md +11 -9
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- 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/mvp-concepts.md +2 -2
- 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/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- 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-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +76 -3
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +39 -9
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/tdd.md +15 -12
- 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/verifier-evidence-gate.md +160 -0
- 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 +7 -0
- package/gsd-core/templates/state.md +7 -0
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +3 -1
- package/gsd-core/workflows/add-phase.md +5 -3
- package/gsd-core/workflows/add-tests.md +4 -9
- package/gsd-core/workflows/add-todo.md +2 -2
- package/gsd-core/workflows/ai-integration-phase.md +5 -10
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +14 -3
- package/gsd-core/workflows/audit-milestone.md +11 -9
- package/gsd-core/workflows/audit-uat.md +19 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +12 -26
- package/gsd-core/workflows/check-todos.md +2 -2
- package/gsd-core/workflows/cleanup.md +3 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
- package/gsd-core/workflows/code-review-fix.md +3 -1
- package/gsd-core/workflows/code-review.md +192 -69
- package/gsd-core/workflows/complete-milestone.md +28 -14
- package/gsd-core/workflows/debug.md +6 -4
- package/gsd-core/workflows/diagnose-issues.md +17 -7
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +2 -2
- package/gsd-core/workflows/do.md +46 -19
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +3 -1
- package/gsd-core/workflows/eval-review.md +5 -10
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -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 +29 -5
- 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 +4 -2
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +68 -66
- package/gsd-core/workflows/execute-plan.md +25 -20
- package/gsd-core/workflows/explore.md +3 -1
- package/gsd-core/workflows/extract-learnings.md +3 -1
- package/gsd-core/workflows/fast.md +8 -2
- package/gsd-core/workflows/forensics.md +3 -1
- package/gsd-core/workflows/graduation.md +6 -6
- package/gsd-core/workflows/health.md +4 -7
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +2 -0
- package/gsd-core/workflows/import.md +17 -14
- package/gsd-core/workflows/inbox.md +5 -6
- package/gsd-core/workflows/ingest-docs.md +45 -12
- package/gsd-core/workflows/insert-phase.md +7 -5
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +7 -3
- package/gsd-core/workflows/list-workspaces.md +3 -1
- package/gsd-core/workflows/manager.md +15 -26
- package/gsd-core/workflows/map-codebase.md +3 -1
- package/gsd-core/workflows/milestone-summary.md +3 -1
- package/gsd-core/workflows/mvp-phase.md +3 -3
- package/gsd-core/workflows/new-milestone.md +10 -22
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +17 -29
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +4 -2
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +20 -5
- 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 +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
- package/gsd-core/workflows/plan-phase.md +251 -54
- package/gsd-core/workflows/plan-review-convergence.md +148 -19
- package/gsd-core/workflows/plant-seed.md +3 -3
- package/gsd-core/workflows/pr-branch.md +195 -51
- package/gsd-core/workflows/profile-user.md +17 -15
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +52 -15
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
- package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
- package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +33 -32
- package/gsd-core/workflows/reapply-patches.md +2 -0
- package/gsd-core/workflows/remove-phase.md +6 -4
- package/gsd-core/workflows/remove-workspace.md +3 -3
- package/gsd-core/workflows/resume-project.md +14 -14
- package/gsd-core/workflows/review.md +404 -21
- package/gsd-core/workflows/scan.md +3 -1
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +9 -9
- package/gsd-core/workflows/settings-integrations.md +66 -32
- package/gsd-core/workflows/settings.md +4 -6
- package/gsd-core/workflows/ship.md +22 -16
- package/gsd-core/workflows/sketch-wrap-up.md +13 -17
- package/gsd-core/workflows/sketch.md +13 -19
- package/gsd-core/workflows/smart-entry.md +4 -6
- package/gsd-core/workflows/spec-phase.md +31 -4
- package/gsd-core/workflows/spike-wrap-up.md +9 -11
- package/gsd-core/workflows/spike.md +21 -32
- package/gsd-core/workflows/stats.md +4 -2
- package/gsd-core/workflows/sync-skills.md +13 -5
- package/gsd-core/workflows/thread.md +13 -7
- package/gsd-core/workflows/transition.md +7 -5
- package/gsd-core/workflows/ui-phase.md +36 -21
- package/gsd-core/workflows/ui-review.md +7 -11
- package/gsd-core/workflows/ultraplan-phase.md +7 -13
- package/gsd-core/workflows/undo.md +9 -17
- package/gsd-core/workflows/update.md +47 -48
- package/gsd-core/workflows/validate-phase.md +3 -3
- 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 +106 -21
- package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +302 -22
- 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 +77 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +46 -12
- package/hooks/dist/gsd-read-guard.js +18 -7
- package/hooks/dist/gsd-read-injection-scanner.js +22 -13
- package/hooks/dist/gsd-secret-read-guard.js +1079 -0
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +222 -29
- package/hooks/dist/gsd-validate-commit.sh +523 -12
- 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 +36 -17
- 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-cmd.js +210 -1
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/managed-hooks-registry.cjs +4 -0
- package/hooks/gsd-agent-isolation-guard.js +77 -38
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +302 -22
- 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 +77 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +46 -12
- package/hooks/gsd-read-guard.js +18 -7
- package/hooks/gsd-read-injection-scanner.js +22 -13
- package/hooks/gsd-secret-read-guard.js +1079 -0
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +222 -29
- package/hooks/gsd-validate-commit.sh +523 -12
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +36 -17
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/hooks.json +6 -0
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/managed-hooks-registry.cjs +4 -0
- package/package.json +14 -9
- package/scripts/base64-scan.sh +74 -12
- package/scripts/build-hooks.js +12 -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 +52 -12
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/docs-guard-registry.cjs +406 -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 +189 -4
- 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/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- 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 +198 -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 +45 -14
- package/scripts/lint-phase-id-drift.cjs +133 -8
- package/scripts/lint-planning-prompt-drift.cjs +38 -1
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-removed-but-needed.cjs +184 -16
- package/scripts/lint-response-language-coverage.cjs +524 -0
- 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 +124 -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/lint-workflow-shellcheck-baseline.json +1027 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +83 -14
- package/scripts/require-issue-link-policy.cjs +16 -1
- 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-execute-phase/SKILL.md +1 -1
- package/skills/gsd-import/SKILL.md +1 -1
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -1
- package/skills/gsd-quick/SKILL.md +8 -4
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- 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
package/gsd-core/bin/lib/io.cjs
CHANGED
|
@@ -15,6 +15,9 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
15
15
|
const node_os_1 = __importDefault(require("node:os"));
|
|
16
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
17
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
19
|
+
const cliExitModule = require("./cli-exit.cjs");
|
|
20
|
+
const { setJsonErrorMode, getJsonErrorMode, EXIT_ENVELOPE_REASON, ExitError, setPendingOutcome, projectOutcome, getContractVersion, } = cliExitModule;
|
|
18
21
|
// ─── Temp-file helpers (needed by output()) ──────────────────────────────────
|
|
19
22
|
/**
|
|
20
23
|
* Dedicated GSD temp directory: path.join(os.tmpdir(), 'gsd').
|
|
@@ -134,7 +137,48 @@ function writeAllSync(fd, data) {
|
|
|
134
137
|
function serializeForOutput(result) {
|
|
135
138
|
return JSON.stringify(result, null, 2);
|
|
136
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* A payload-carried error, per ADR-2980's own definition (#3912, ADR-3889
|
|
142
|
+
* §4): `result` is an object carrying a SERIALIZABLE `error` property, in
|
|
143
|
+
* ANY key order — `{ found: false, error }` counts exactly the same as
|
|
144
|
+
* `{ error, found: false }`. The discriminator is a serializable error
|
|
145
|
+
* value, NOT mere key presence: `result.error`'s own truthiness is
|
|
146
|
+
* irrelevant (falsy `0`/`null`/`''` all count), and neither is `result`'s
|
|
147
|
+
* prototype (a plain object literal is all any call site here ever
|
|
148
|
+
* passes) — but `error: undefined` does NOT count, because
|
|
149
|
+
* `JSON.stringify` (the exact serializer `serializeForOutput` uses to
|
|
150
|
+
* build the payload the caller actually receives) drops an object
|
|
151
|
+
* property whose value is `undefined` entirely. A payload built as
|
|
152
|
+
* `{ found: false, error: undefined }` therefore reaches the wire as
|
|
153
|
+
* `{"found":false}` — no error at all — and recording DEGRADED for it
|
|
154
|
+
* would be a false verdict: exit 80 under v2 for output the user sees as
|
|
155
|
+
* clean. `hasOwnProperty` alone is not enough to answer "does this payload
|
|
156
|
+
* declare an error"; it must also survive `JSON.stringify`.
|
|
157
|
+
*/
|
|
158
|
+
function isPayloadCarriedError(result) {
|
|
159
|
+
return typeof result === 'object' && result !== null
|
|
160
|
+
&& Object.prototype.hasOwnProperty.call(result, 'error')
|
|
161
|
+
&& result.error !== undefined;
|
|
162
|
+
}
|
|
137
163
|
function output(result, raw, rawValue) {
|
|
164
|
+
// #3912 (ADR-3889 §4): a payload-carried error declares DEGRADED into the
|
|
165
|
+
// pending-outcome cell runMain reads. This is the ONLY new thing output()
|
|
166
|
+
// does — it still just writes fd 1 and returns; the exit code stays
|
|
167
|
+
// whatever it already was under v1 (DEGRADED projects to 0), and nothing
|
|
168
|
+
// here touches process.exitCode directly.
|
|
169
|
+
//
|
|
170
|
+
// LAST-WRITE-WINS (review fix): a clean (non-error-shaped) payload CLEARS
|
|
171
|
+
// the cell rather than leaving a prior degraded declaration in place. A
|
|
172
|
+
// handler that calls output() more than once per invocation — a
|
|
173
|
+
// diagnostic error payload followed by a clean final payload — must have
|
|
174
|
+
// its LATEST declaration win, not its first: the cell reflects "is a
|
|
175
|
+
// degraded outcome pending right now", not "was one ever declared".
|
|
176
|
+
if (isPayloadCarriedError(result)) {
|
|
177
|
+
setPendingOutcome('DEGRADED');
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
setPendingOutcome(undefined);
|
|
181
|
+
}
|
|
138
182
|
let data;
|
|
139
183
|
if (raw && rawValue !== undefined) {
|
|
140
184
|
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
@@ -161,6 +205,40 @@ function output(result, raw, rawValue) {
|
|
|
161
205
|
// and skipping process.exit() lets the event loop drain naturally.
|
|
162
206
|
writeAllSync(1, data);
|
|
163
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Single owner of the "a no-op decline reports the real condition" pairing:
|
|
210
|
+
* a `[gsd-tools] WARNING:` stderr disclosure plus the matching
|
|
211
|
+
* `{ [flagKey]: false, ...computed, reason }` JSON payload (#3957, epic
|
|
212
|
+
* #3473 B9). Before this helper, each CLI command's no-op arm hand-wrote
|
|
213
|
+
* both halves independently, and #3957's own sweep found sites where one
|
|
214
|
+
* half drifted: a decline that discarded values it had already computed, a
|
|
215
|
+
* `reason` string naming a condition that hadn't actually fired, or no
|
|
216
|
+
* stderr disclosure at all (missing the `[gsd-tools] WARNING:` convention
|
|
217
|
+
* #3217, ADR-3180 §7.6 rule 4, established for exactly this situation —
|
|
218
|
+
* a JSON `reason` field alone is easy for a caller piping stdout through
|
|
219
|
+
* `--json` to never read). Routing every no-op decline through one call
|
|
220
|
+
* site makes the convention structural — a call site, not a habit a fifth
|
|
221
|
+
* arm can independently forget — rather than leaving it to per-site
|
|
222
|
+
* discipline.
|
|
223
|
+
*
|
|
224
|
+
* `disclosure` is written to stderr VERBATIM. Like `error()`'s `message`
|
|
225
|
+
* argument (see that function's own doc comment above `formatDiagnosticToken`),
|
|
226
|
+
* this function stays a dumb, faithful writer and does not sanitize it — a
|
|
227
|
+
* caller that interpolates an UNTRUSTED substring (a caller-supplied
|
|
228
|
+
* blocker/session-field string, an argv token) into `disclosure` MUST pass
|
|
229
|
+
* that substring through `formatDiagnosticToken` first. Skipping that step
|
|
230
|
+
* lets an embedded `\n` forge a second, attacker-authored
|
|
231
|
+
* `[gsd-tools] WARNING:` line — the same risk `error()`'s doc comment
|
|
232
|
+
* documents for its own stderr write.
|
|
233
|
+
*/
|
|
234
|
+
function declineNoOp(raw, flagKey, reason, disclosure, computed = {}) {
|
|
235
|
+
// `process.stderr.write`, matching the `[gsd-tools] WARNING:` call sites
|
|
236
|
+
// this helper replaces (state.cts's stateReplaceFieldWithFallback and the
|
|
237
|
+
// cmdStateUpdateProgress decline arms) — not the `writeAllSync(2, …)`
|
|
238
|
+
// primitive `error()` uses, which is reserved for the fatal-exit path.
|
|
239
|
+
process.stderr.write(`[gsd-tools] WARNING: ${disclosure}\n`);
|
|
240
|
+
output({ [flagKey]: false, ...computed, reason }, raw, 'false');
|
|
241
|
+
}
|
|
164
242
|
/**
|
|
165
243
|
* Frozen enum of typed reason codes used by error() for structured errors.
|
|
166
244
|
* Each subcommand contributes its own codes; the enum exists so tests can
|
|
@@ -178,7 +256,7 @@ const ERROR_REASON = Object.freeze({
|
|
|
178
256
|
CONFIG_PARSE_FAILED: 'config_parse_failed',
|
|
179
257
|
CONFIG_INVALID_KEY: 'config_invalid_key',
|
|
180
258
|
// SDK / gsd-tools dispatch
|
|
181
|
-
SDK_FAIL_FAST:
|
|
259
|
+
SDK_FAIL_FAST: EXIT_ENVELOPE_REASON,
|
|
182
260
|
SDK_UNKNOWN_COMMAND: 'sdk_unknown_command',
|
|
183
261
|
SDK_MISSING_ARG: 'sdk_missing_arg',
|
|
184
262
|
// workflow / phase
|
|
@@ -195,6 +273,10 @@ const ERROR_REASON = Object.freeze({
|
|
|
195
273
|
// graphify
|
|
196
274
|
GRAPHIFY_NO_GRAPH: 'graphify_no_graph',
|
|
197
275
|
GRAPHIFY_INVALID_QUERY: 'graphify_invalid_query',
|
|
276
|
+
// estimate-calibrate (#3882, ADR-3473 §8.2): the phases directory exists
|
|
277
|
+
// but could not be read — a NON-answer, distinct from a project that
|
|
278
|
+
// genuinely has zero completed phases yet.
|
|
279
|
+
ESTIMATE_PHASES_UNREADABLE: 'estimate_phases_unreadable',
|
|
198
280
|
// hooks
|
|
199
281
|
HOOKS_OPT_OUT: 'hooks_opt_out',
|
|
200
282
|
// commit-docs-guard (#3588)
|
|
@@ -203,22 +285,17 @@ const ERROR_REASON = Object.freeze({
|
|
|
203
285
|
COMMIT_DOCS_GUARD_HOOKS_PATH_SET: 'commit_docs_guard_hooks_path_set',
|
|
204
286
|
// security-scan
|
|
205
287
|
SECURITY_SCAN_FAILED: 'security_scan_failed',
|
|
288
|
+
// --pick (#3365 / #3358, ADR-3473 §8.4): an absent field or non-JSON
|
|
289
|
+
// command output is a failure, never a demotion to an empty answer at
|
|
290
|
+
// exit 0. See .gsd/phase/feat-3884-failure-is-a-value/40-design.md.
|
|
291
|
+
PICK_FIELD_ABSENT: 'pick_field_absent',
|
|
292
|
+
PICK_OUTPUT_NOT_JSON: 'pick_output_not_json',
|
|
206
293
|
// generic
|
|
207
294
|
USAGE: 'usage',
|
|
208
295
|
UNKNOWN: 'unknown',
|
|
209
296
|
});
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
* instead of plain "Error: <message>" text. Set by gsd-tools.cjs when the
|
|
213
|
-
* CLI is invoked with `--json-errors`. Tests opt in to typed-IR error
|
|
214
|
-
* assertions by passing that flag and parsing the JSON.
|
|
215
|
-
*
|
|
216
|
-
* Default off so existing callers and human operators keep their plain-text
|
|
217
|
-
* diagnostics. The structured form is opt-in for tooling and tests (#2974).
|
|
218
|
-
*/
|
|
219
|
-
let _jsonErrorMode = false;
|
|
220
|
-
function setJsonErrorMode(v) { _jsonErrorMode = !!v; }
|
|
221
|
-
function getJsonErrorMode() { return _jsonErrorMode; }
|
|
297
|
+
// setJsonErrorMode / getJsonErrorMode now live in cli-exit.cts (imported above)
|
|
298
|
+
// and are re-exported here for the callers that already import them from io.
|
|
222
299
|
/**
|
|
223
300
|
* Emit an error and exit. When the second argument is provided it must be
|
|
224
301
|
* a value from ERROR_REASON; tests can assert on `result.reason`. When the
|
|
@@ -233,24 +310,127 @@ function getJsonErrorMode() { return _jsonErrorMode; }
|
|
|
233
310
|
* human-readable text. Ignored entirely in plain-text mode — the human
|
|
234
311
|
* message is the only thing an operator sees there.
|
|
235
312
|
*/
|
|
313
|
+
/**
|
|
314
|
+
* Render an UNTRUSTED string for embedding inside a human-readable,
|
|
315
|
+
* plain-text diagnostic (the `'Error: ' + message` line `error()` writes in
|
|
316
|
+
* non-JSON mode).
|
|
317
|
+
*
|
|
318
|
+
* WHY THIS EXISTS AND WHY `error()` DOES NOT DO IT ITSELF: `error()`
|
|
319
|
+
* deliberately writes its `message` argument verbatim — several callers in
|
|
320
|
+
* this repo intentionally emit multi-line diagnostics (e.g. the phase-gate
|
|
321
|
+
* messages), and `error()` has no way to distinguish a legitimate multi-line
|
|
322
|
+
* message from a hostile one, so it must not mangle newlines generically.
|
|
323
|
+
* That means any UNTRUSTED substring a caller interpolates into `message`
|
|
324
|
+
* (an argv token, a JSON key/value read back from a command's own output,
|
|
325
|
+
* etc.) can smuggle its own `\n` and forge a second `Error: ` line on
|
|
326
|
+
* stderr — a caller that parses stderr line-by-line would then see a second,
|
|
327
|
+
* attacker-authored error. Every call site that interpolates untrusted data
|
|
328
|
+
* into a diagnostic MUST pass that substring through this function first;
|
|
329
|
+
* `error()` itself stays a dumb, faithful writer.
|
|
330
|
+
*
|
|
331
|
+
* `JSON.stringify` is the primitive: it wraps the value in quotes and
|
|
332
|
+
* escapes control characters (`\n`, `\r`, `\t`, and the rest of the C0
|
|
333
|
+
* range, plus the quote character itself), so the result can never span
|
|
334
|
+
* more than one line or introduce an unescaped `"`. Callers embedding the
|
|
335
|
+
* result MUST NOT add their own surrounding quotes — that would
|
|
336
|
+
* double-quote it.
|
|
337
|
+
*/
|
|
338
|
+
function formatDiagnosticToken(value) {
|
|
339
|
+
return JSON.stringify(value);
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Map an ERROR_REASON wire value onto a declared outcome name (#3912,
|
|
343
|
+
* ADR-3889 §4). Closed over the 25-member enum: every reason gets an
|
|
344
|
+
* explicit entry below, so a 26th member added without a mapping falls
|
|
345
|
+
* through to the `?? 'FAIL'` default rather than silently mis-projecting —
|
|
346
|
+
* and tests/A1 iterates `Object.values(ERROR_REASON)`, so that default is
|
|
347
|
+
* exactly what makes an unmapped addition visible instead of invisible.
|
|
348
|
+
*
|
|
349
|
+
* This function's result is ONLY consulted under v2 (see `error()` below) —
|
|
350
|
+
* it is deliberately never routed through `projectOutcome` under v1, which
|
|
351
|
+
* is what keeps the v1 pin intact (`projectOutcome` treats registered names
|
|
352
|
+
* as version-invariant, so e.g. USAGE would otherwise become 64 today).
|
|
353
|
+
*
|
|
354
|
+
* Each non-FAIL choice below is justified inline; `UNKNOWN` and anything
|
|
355
|
+
* with no clearly better fit stays `FAIL` — the honest default the design
|
|
356
|
+
* calls for, not a guess dressed up as a specific outcome.
|
|
357
|
+
*/
|
|
358
|
+
const REASON_TO_OUTCOME = Object.freeze({
|
|
359
|
+
// Bad argv/subcommand/argument — the caller, not the run, is at fault.
|
|
360
|
+
[ERROR_REASON.CONFIG_INVALID_KEY]: 'USAGE',
|
|
361
|
+
[ERROR_REASON.SDK_UNKNOWN_COMMAND]: 'USAGE',
|
|
362
|
+
[ERROR_REASON.SDK_MISSING_ARG]: 'USAGE',
|
|
363
|
+
[ERROR_REASON.GRAPHIFY_INVALID_QUERY]: 'USAGE',
|
|
364
|
+
[ERROR_REASON.USAGE]: 'USAGE',
|
|
365
|
+
// A specific, named thing does not exist / nothing was there to find —
|
|
366
|
+
// genuine, known emptiness rather than a broken prerequisite.
|
|
367
|
+
[ERROR_REASON.CONFIG_KEY_NOT_FOUND]: 'NO_INPUT',
|
|
368
|
+
[ERROR_REASON.SUMMARY_NO_PLANNING]: 'NO_INPUT',
|
|
369
|
+
[ERROR_REASON.WORKSTREAM_MODE_NONE_ACTIVE]: 'NO_INPUT',
|
|
370
|
+
// A prerequisite is absent, unreadable, or otherwise not in a state the
|
|
371
|
+
// run could proceed from — distinct from NO_INPUT's genuine emptiness.
|
|
372
|
+
[ERROR_REASON.CONFIG_NO_FILE]: 'UNAVAILABLE',
|
|
373
|
+
[ERROR_REASON.CONFIG_PARSE_FAILED]: 'UNAVAILABLE',
|
|
374
|
+
[ERROR_REASON.PHASE_NOT_FOUND]: 'UNAVAILABLE',
|
|
375
|
+
[ERROR_REASON.PHASE_VERIFICATION_INCOMPLETE]: 'UNAVAILABLE',
|
|
376
|
+
[ERROR_REASON.PHASE_PLAN_COVERAGE_INCOMPLETE]: 'UNAVAILABLE',
|
|
377
|
+
// Its own docstring: "a marker exists but didn't resolve" — a broken
|
|
378
|
+
// prerequisite, not the "no marker anywhere" emptiness NONE_ACTIVE covers.
|
|
379
|
+
[ERROR_REASON.WORKSTREAM_MODE_MARKER_UNRESOLVED]: 'UNAVAILABLE',
|
|
380
|
+
[ERROR_REASON.GRAPHIFY_NO_GRAPH]: 'UNAVAILABLE',
|
|
381
|
+
// Its own docstring: "a NON-answer, distinct from a project that
|
|
382
|
+
// genuinely has zero completed phases yet" — UNAVAILABLE, not NO_INPUT.
|
|
383
|
+
[ERROR_REASON.ESTIMATE_PHASES_UNREADABLE]: 'UNAVAILABLE',
|
|
384
|
+
[ERROR_REASON.COMMIT_DOCS_GUARD_NOT_A_REPO]: 'UNAVAILABLE',
|
|
385
|
+
[ERROR_REASON.COMMIT_DOCS_GUARD_FOREIGN_HOOK]: 'UNAVAILABLE',
|
|
386
|
+
[ERROR_REASON.COMMIT_DOCS_GUARD_HOOKS_PATH_SET]: 'UNAVAILABLE',
|
|
387
|
+
// Its own docstring: "an absent field or non-JSON command output is a
|
|
388
|
+
// failure, never a demotion to an empty answer" — the field/output was
|
|
389
|
+
// supposed to be there and was not; a prerequisite of the query failed.
|
|
390
|
+
[ERROR_REASON.PICK_FIELD_ABSENT]: 'UNAVAILABLE',
|
|
391
|
+
[ERROR_REASON.PICK_OUTPUT_NOT_JSON]: 'UNAVAILABLE',
|
|
392
|
+
// Self-failure: the run itself broke, not its inputs.
|
|
393
|
+
[ERROR_REASON.SDK_FAIL_FAST]: 'INTERNAL',
|
|
394
|
+
[ERROR_REASON.SECURITY_SCAN_FAILED]: 'INTERNAL',
|
|
395
|
+
// No clearly better fit — the honest default, per design.
|
|
396
|
+
[ERROR_REASON.HOOKS_OPT_OUT]: 'FAIL',
|
|
397
|
+
[ERROR_REASON.UNKNOWN]: 'FAIL',
|
|
398
|
+
});
|
|
399
|
+
function outcomeForReason(reason) {
|
|
400
|
+
return REASON_TO_OUTCOME[reason] ?? 'FAIL';
|
|
401
|
+
}
|
|
236
402
|
function error(message, reason = ERROR_REASON.UNKNOWN, extra) {
|
|
237
|
-
if (
|
|
403
|
+
if (getJsonErrorMode()) {
|
|
238
404
|
const payload = JSON.stringify({ ok: false, reason, message, ...(extra || {}) }) + '\n';
|
|
239
405
|
writeAllSync(2, payload);
|
|
240
406
|
}
|
|
241
407
|
else {
|
|
242
408
|
writeAllSync(2, 'Error: ' + message + '\n');
|
|
243
409
|
}
|
|
244
|
-
|
|
410
|
+
// #3912 (ADR-3889 §4): the declaration is version-gated HERE, not inside
|
|
411
|
+
// projectOutcome — registered names are version-invariant there, so
|
|
412
|
+
// routing every reason through it unconditionally would change v1 exit
|
|
413
|
+
// codes today (e.g. USAGE -> 64) and break the pin. Under v1 the exit
|
|
414
|
+
// stays ExitError(1) unconditionally, byte-identical to every prior
|
|
415
|
+
// release; only v2 projects the declared outcome through the registry.
|
|
416
|
+
if (getContractVersion() === 'v2') {
|
|
417
|
+
throw new ExitError(projectOutcome(outcomeForReason(reason), 'v2'));
|
|
418
|
+
}
|
|
419
|
+
// No message passed to ExitError: the stderr write above is already done,
|
|
420
|
+
// byte-identical to the prior process.exit(1) behavior, and ExitError with
|
|
421
|
+
// no message means runMain's catch adds nothing further to stderr.
|
|
422
|
+
throw new ExitError(1);
|
|
245
423
|
}
|
|
246
424
|
module.exports = {
|
|
247
425
|
GSD_TEMP_DIR,
|
|
248
426
|
ensureGsdTempDir,
|
|
249
427
|
reapStaleTempFiles,
|
|
250
428
|
output,
|
|
429
|
+
declineNoOp,
|
|
251
430
|
serializeForOutput,
|
|
252
431
|
ERROR_REASON,
|
|
253
432
|
setJsonErrorMode,
|
|
254
433
|
getJsonErrorMode,
|
|
255
434
|
error,
|
|
435
|
+
formatDiagnosticToken,
|
|
256
436
|
};
|
|
@@ -21,6 +21,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
22
22
|
const node_path_1 = __importDefault(require("node:path"));
|
|
23
23
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
|
+
const phaseLocator = require("./phase-locator.cjs");
|
|
24
26
|
const node_os_1 = __importDefault(require("node:os"));
|
|
25
27
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
28
|
const ioMod = require("./io.cjs");
|
|
@@ -145,9 +147,55 @@ function learningsDelete(id, opts) {
|
|
|
145
147
|
node_fs_1.default.unlinkSync(filePath);
|
|
146
148
|
return true;
|
|
147
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* #3683 — resolve which learnings artifact a copy reads. The extractor writes
|
|
152
|
+
* phase-scoped `{PHASE_DIR}/{PADDED}-LEARNINGS.md`; the legacy shape is a
|
|
153
|
+
* project-root `LEARNINGS.md`. The MOST RECENT phase-scoped artifact wins (a
|
|
154
|
+
* gated completion copies the phase it just wrote); the project-root file is
|
|
155
|
+
* the fallback when no phase artifact exists. Returns null when neither shape
|
|
156
|
+
* is present.
|
|
157
|
+
*/
|
|
158
|
+
function resolveLearningsSource(planningDir) {
|
|
159
|
+
let best = null;
|
|
160
|
+
const phasesDir = node_path_1.default.join(planningDir, 'phases');
|
|
161
|
+
// Phase enumeration routes through the sanctioned seam (the #3185 drift
|
|
162
|
+
// guard rejects ad-hoc re-derivations): no cwd → every phase dir in the
|
|
163
|
+
// tree, sentinel dirs excluded, unreadable phases dir already degraded to
|
|
164
|
+
// an empty list by the locator itself.
|
|
165
|
+
const { value: phaseDirNames } = phaseLocator.listMilestonePhaseDirs(phasesDir);
|
|
166
|
+
for (const entry of phaseDirNames) {
|
|
167
|
+
const phaseDir = node_path_1.default.join(phasesDir, entry);
|
|
168
|
+
let files;
|
|
169
|
+
try {
|
|
170
|
+
files = node_fs_1.default.readdirSync(phaseDir);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// An unreadable phase dir (ACL, removal race) must not crash the copy —
|
|
174
|
+
// skip it and keep scanning.
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
for (const f of files) {
|
|
178
|
+
if (!/-LEARNINGS\.md$/i.test(f))
|
|
179
|
+
continue;
|
|
180
|
+
const p = node_path_1.default.join(phaseDir, f);
|
|
181
|
+
try {
|
|
182
|
+
const m = node_fs_1.default.statSync(p).mtimeMs;
|
|
183
|
+
if (best === null || m > best.m)
|
|
184
|
+
best = { p, m };
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
if (best !== null)
|
|
192
|
+
return best.p;
|
|
193
|
+
const rootPath = node_path_1.default.join(planningDir, 'LEARNINGS.md');
|
|
194
|
+
return node_fs_1.default.existsSync(rootPath) ? rootPath : null;
|
|
195
|
+
}
|
|
148
196
|
function learningsCopyFromProject(planningDir, opts) {
|
|
149
|
-
const learningsPath =
|
|
150
|
-
if (
|
|
197
|
+
const learningsPath = resolveLearningsSource(planningDir);
|
|
198
|
+
if (learningsPath === null) {
|
|
151
199
|
return { total: 0, created: 0, skipped: 0 };
|
|
152
200
|
}
|
|
153
201
|
const content = node_fs_1.default.readFileSync(learningsPath, 'utf-8');
|
|
@@ -167,29 +215,52 @@ function learningsCopyFromProject(planningDir, opts) {
|
|
|
167
215
|
dedupeIndex.set(existing.content_hash, existing.id);
|
|
168
216
|
}
|
|
169
217
|
}
|
|
170
|
-
// Parse markdown: split on ## headings
|
|
218
|
+
// Parse markdown: split on ## category headings.
|
|
219
|
+
// #3683: the real producer (extract-learnings.md write_learnings) writes ##
|
|
220
|
+
// categories containing ### item headings — each item is ONE learning (the
|
|
221
|
+
// store's relevance contract caps injection by count, so category-sized
|
|
222
|
+
// blobs defeat it). A bare ## section with no ### items keeps the legacy
|
|
223
|
+
// single-learning shape.
|
|
171
224
|
const sections = content.split(/^## /m).slice(1); // skip preamble before first ##
|
|
172
225
|
let created = 0;
|
|
173
226
|
let skipped = 0;
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
continue;
|
|
180
|
-
// Extract tags from title (simple: use words as tags)
|
|
181
|
-
const tags = title.toLowerCase().split(/\s+/).filter(w => w.length > 2);
|
|
227
|
+
const writeOne = (title, body, extraTags) => {
|
|
228
|
+
const tags = Array.from(new Set([
|
|
229
|
+
...extraTags,
|
|
230
|
+
...title.toLowerCase().split(/\s+/).filter(w => w.length > 2),
|
|
231
|
+
]));
|
|
182
232
|
const result = learningsWrite({
|
|
183
233
|
source_project: sourceProject,
|
|
184
234
|
learning: body,
|
|
185
235
|
context: title,
|
|
186
236
|
tags,
|
|
187
237
|
}, { ...opts, dedupeIndex });
|
|
188
|
-
if (result.created)
|
|
238
|
+
if (result.created)
|
|
189
239
|
created++;
|
|
190
|
-
|
|
191
|
-
else {
|
|
240
|
+
else
|
|
192
241
|
skipped++;
|
|
242
|
+
};
|
|
243
|
+
for (const section of sections) {
|
|
244
|
+
const lines = section.trim().split('\n');
|
|
245
|
+
const category = lines[0].trim();
|
|
246
|
+
const body = lines.slice(1).join('\n').trim();
|
|
247
|
+
if (!body)
|
|
248
|
+
continue;
|
|
249
|
+
const categoryTag = category.toLowerCase().split(/\s+/)[0] || '';
|
|
250
|
+
// Split into ### items when present; otherwise one learning per section.
|
|
251
|
+
const items = body.split(/^### /m).map(s => s.trim()).filter(s => s.length > 0);
|
|
252
|
+
const hasItemHeadings = /^### /m.test(body);
|
|
253
|
+
if (!hasItemHeadings) {
|
|
254
|
+
writeOne(category, body, categoryTag ? [categoryTag] : []);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
for (const item of items) {
|
|
258
|
+
const itemLines = item.split('\n');
|
|
259
|
+
const title = itemLines[0].trim();
|
|
260
|
+
const itemBody = itemLines.slice(1).join('\n').trim();
|
|
261
|
+
if (!itemBody && !title)
|
|
262
|
+
continue;
|
|
263
|
+
writeOne(title, itemBody || title, categoryTag ? [categoryTag] : []);
|
|
193
264
|
}
|
|
194
265
|
}
|
|
195
266
|
return { total: created + skipped, created, skipped };
|
|
@@ -262,8 +262,14 @@ function planLegacyCleanup(configDirs, opts = {}) {
|
|
|
262
262
|
}
|
|
263
263
|
}
|
|
264
264
|
|
|
265
|
-
// Legacy shared cache (fixed name from the old package)
|
|
266
|
-
|
|
265
|
+
// Legacy shared cache (fixed name from the old package). #3799: under an
|
|
266
|
+
// explicit configDirs override the cache lives under the SCOPE root(s), not
|
|
267
|
+
// the default home — the override means "clean only what this redirected
|
|
268
|
+
// install owns", and the default home's cache belongs to the live install.
|
|
269
|
+
const cacheRoot = (Array.isArray(opts.configDirs) && opts.configDirs.length > 0)
|
|
270
|
+
? opts.configDirs[0]
|
|
271
|
+
: homeDir;
|
|
272
|
+
const legacyCachePath = path.join(cacheRoot, '.cache', 'gsd', 'gsd-update-check.json');
|
|
267
273
|
try {
|
|
268
274
|
const stat = fsMod.statSync(legacyCachePath);
|
|
269
275
|
if (stat.isFile()) {
|
|
@@ -40,7 +40,7 @@ const { resolveCapabilityRuntimeState } = capabilityStateModule;
|
|
|
40
40
|
// ─── Capability-activation engine (single owner for config-key precedence) ────
|
|
41
41
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
42
42
|
const capabilityActivationModule = require("./capability-activation.cjs");
|
|
43
|
-
const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, resolveConfigKey } = capabilityActivationModule;
|
|
43
|
+
const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, _resolvePointGate, resolveConfigKey } = capabilityActivationModule;
|
|
44
44
|
// ─── Canonical points (derived from LOOP_HOST_CONTRACT — authoritative 12) ───
|
|
45
45
|
// FIX 2: Derive the authoritative canonical set from LOOP_HOST_CONTRACT so it
|
|
46
46
|
// cannot drift from the host contract. CANONICAL_POINTS_FALLBACK is kept as an
|
|
@@ -125,13 +125,15 @@ function resolveLoopHooks(input) {
|
|
|
125
125
|
// Helper: check activation using single-key precedence resolver (FIX 1 + FIX 3)
|
|
126
126
|
function isActive(hook) {
|
|
127
127
|
const when = hook['when'];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
128
|
+
if (when !== undefined && when !== null) {
|
|
129
|
+
// FIX 3: `when` present but not a non-empty string → malformed registry data → INACTIVE
|
|
130
|
+
if (typeof when !== 'string' || when.length === 0)
|
|
131
|
+
return false;
|
|
132
|
+
if (!_resolveActivationValue(when, config, cwd, registry))
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// #3661: optional point-selection gate — see capability-activation.cts.
|
|
136
|
+
return _resolvePointGate(hook['pointFrom'], point, config, cwd, registry);
|
|
135
137
|
}
|
|
136
138
|
function isCapabilityActive(capId) {
|
|
137
139
|
if (!capabilityStatesById)
|
|
@@ -555,6 +557,10 @@ module.exports = {
|
|
|
555
557
|
// Re-exported for identity parity guard (FIX 2: resolveConfigValues in this module
|
|
556
558
|
// calls resolveConfigKey; exporting it here makes the single-owner contract testable).
|
|
557
559
|
resolveConfigKey,
|
|
560
|
+
// #3661: re-exported for the same identity parity guard — isActive calls
|
|
561
|
+
// _resolvePointGate; exporting it here makes the single-owner contract testable
|
|
562
|
+
// (see tests/capability-precedence-parity.test.cjs's identity guard describe block).
|
|
563
|
+
_resolvePointGate,
|
|
558
564
|
CANONICAL_POINTS_FALLBACK,
|
|
559
565
|
CANONICAL_POINTS,
|
|
560
566
|
};
|