@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
|
@@ -75,6 +75,215 @@ const bulletTitledColonRe = /^\s*-\s+\*\*D-([A-Za-z0-9][A-Za-z0-9_-]*)(?:\s*\[([
|
|
|
75
75
|
* shape without hardcoding the `D`.
|
|
76
76
|
*/
|
|
77
77
|
const boldLeadInBulletRe = /^\s*-\s+\*\*[A-Z]+[0-9]*-[A-Za-z0-9]/m;
|
|
78
|
+
/**
|
|
79
|
+
* #3939: a decision bullet's DECLARATION line — the `- **D-NN … **` bold lead-in
|
|
80
|
+
* the three grammars above anchor on — may wrap across a line break. Physical
|
|
81
|
+
* line breaks inside a bullet are markdown-insignificant, and GSD's own
|
|
82
|
+
* discuss-phase writer emits the wrapped shape whenever a decision title runs
|
|
83
|
+
* past the wrap column. All three grammars require the closing `**` in the same
|
|
84
|
+
* string as the `- **D-` anchor, so a wrapped declaration matched none of them
|
|
85
|
+
* and fell to the #1365 parse-miss guard, forcing `could-not-parse` (which
|
|
86
|
+
* hard-blocks `check.decision-coverage-plan`) on a well-formed CONTEXT.md.
|
|
87
|
+
*
|
|
88
|
+
* The repair is confined to how the LOGICAL bullet is assembled — the grammars
|
|
89
|
+
* themselves are untouched, so every single-line form parses exactly as before.
|
|
90
|
+
*/
|
|
91
|
+
const decisionBulletStartRe = /^\s*-\s+\*\*D-/;
|
|
92
|
+
/**
|
|
93
|
+
* A line that opens a new BLOCK-LEVEL construct, and therefore terminates the
|
|
94
|
+
* bullet above it: a list marker of any family (`-`, `*`, `+`, `1.`, `1)`), an
|
|
95
|
+
* ATX heading, a blockquote, or a table row. Joining never reaches across one of
|
|
96
|
+
* these (nor across a blank/whitespace-only line, checked separately), so a
|
|
97
|
+
* declaration whose bold run genuinely never closes cannot absorb the block
|
|
98
|
+
* below it and get "closed" by an unrelated inline `**` — it stays a parse-miss
|
|
99
|
+
* and still fails loud, which #1365's contract requires.
|
|
100
|
+
*
|
|
101
|
+
* The four MARKER families demand trailing whitespace so that a continuation
|
|
102
|
+
* line opening with emphasis (`*in* the header.** …`) is text, not a bullet.
|
|
103
|
+
* The table-row alternative deliberately does not: CommonMark tables may open
|
|
104
|
+
* flush (`|Col1|Col2|`), and a leading `|` is never ordinary decision prose.
|
|
105
|
+
* A `- ` line at ANY indent stops the join: a deeper one is #3169 nested
|
|
106
|
+
* elaboration, which the main loop folds into the open decision itself.
|
|
107
|
+
*
|
|
108
|
+
* DELIBERATE DIVERGENCE from the sectionizer seam (ADR-1372): `iterateBullets`
|
|
109
|
+
* recognises only the `N. ` ordered-list form (`numberedRe`,
|
|
110
|
+
* src/markdown-sectionizer.cts), while this set also stops at the `N) ` form.
|
|
111
|
+
* That is intentional and one-directional — this regex answers "may the join
|
|
112
|
+
* cross this line?", where recognising MORE block openers is the conservative
|
|
113
|
+
* answer (a missed terminator can manufacture a decision; a spare one can only
|
|
114
|
+
* make a malformed bullet fail loud, which #1365 already wants). `N)` is a
|
|
115
|
+
* CommonMark ordered-list marker, so a join must not reach across it whether or
|
|
116
|
+
* not the seam's own bullet iterator yields it. Both forms are pinned by tests,
|
|
117
|
+
* and a drift test asserts the seam still does NOT treat `N)` as a bullet, so
|
|
118
|
+
* this divergence stays visible if either side moves.
|
|
119
|
+
*
|
|
120
|
+
* Accepted over-termination: continuation prose that happens to open with
|
|
121
|
+
* digits-then-`.`/`)` ("10. really keeps going") or a literal `|` stops the join
|
|
122
|
+
* early, so such a bullet fails loud rather than parsing. That is the same
|
|
123
|
+
* markdown ambiguity every line-oriented reader carries, and this direction of
|
|
124
|
+
* the trade is the one #1365 asks for — fail loud, never guess.
|
|
125
|
+
*/
|
|
126
|
+
const blockConstructRe = /^(?:[-*+]\s|\d+[.)]\s|#{1,6}\s|>\s|\|)/;
|
|
127
|
+
/**
|
|
128
|
+
* The id-adjacent `[tags]` REGION of a logical bullet, as far as it has been
|
|
129
|
+
* assembled. Matching means the region is still unsettled, in one of two ways:
|
|
130
|
+
* capture group 1 is present when the bracket is open (group 1 is the content
|
|
131
|
+
* seen so far), and absent when the id has been read but no `[` has followed
|
|
132
|
+
* yet — so a bracket may still open on the next absorbed line.
|
|
133
|
+
*
|
|
134
|
+
* A NON-match means the region is settled for good: the bracket closed, or
|
|
135
|
+
* something other than `[` followed the id. Either way the join no longer has
|
|
136
|
+
* to watch for a splice.
|
|
137
|
+
*
|
|
138
|
+
* The id character class is deliberately looser than the grammars' (it admits
|
|
139
|
+
* an empty id, so a bare `- **D-` still counts as unsettled). This regex only
|
|
140
|
+
* answers "may an id-adjacent bracket still open here?", where recognising MORE
|
|
141
|
+
* shapes is the conservative direction: an over-broad match can only make a
|
|
142
|
+
* malformed bullet fail loud, while a missed one silently re-classifies.
|
|
143
|
+
*
|
|
144
|
+
* Only the ID-ADJACENT bracket matters: that is the one the three grammars turn
|
|
145
|
+
* into `tags` (and therefore into `trackable`). A `[` further along the title is
|
|
146
|
+
* ordinary text and does not restrict the join.
|
|
147
|
+
*/
|
|
148
|
+
const tagRegionRe = /^\s*-\s+\*\*D-[A-Za-z0-9_-]*\s*(?:\[([^\]]*))?$/;
|
|
149
|
+
/**
|
|
150
|
+
* #3939 (review): would folding `next` onto a lead-in whose `[tags]` bracket is
|
|
151
|
+
* still open splice the inserted space INTO a tag token?
|
|
152
|
+
*
|
|
153
|
+
* Tags are comma-split and trimmed, so a space landing next to a delimiter
|
|
154
|
+
* (`[`, `,`, `]`) changes nothing — `[informational,` + `deferred]` is still
|
|
155
|
+
* exactly `[informational, deferred]`. A space landing anywhere else splits one
|
|
156
|
+
* token into two (`[defer` + `red]` → `defer red`), which would not fail; it
|
|
157
|
+
* would parse to a DIFFERENT tag, silently flipping `trackable` on a gate that
|
|
158
|
+
* decides whether a decision must be covered. Refusing to join there leaves the
|
|
159
|
+
* bullet unchanged, so it reaches the #1365 parse-miss guard and fails loud —
|
|
160
|
+
* a wrong answer about coverage is worse than a blocked gate.
|
|
161
|
+
*
|
|
162
|
+
* `tail` is the bracket content accumulated so far, `next` the trimmed
|
|
163
|
+
* continuation line.
|
|
164
|
+
*/
|
|
165
|
+
function wouldSpliceTagToken(tail, next) {
|
|
166
|
+
const before = tail.trimEnd();
|
|
167
|
+
if (before === '' || before.endsWith(',') || before.endsWith('['))
|
|
168
|
+
return false;
|
|
169
|
+
return !(next.startsWith(',') || next.startsWith(']'));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* True when the bullet's own bold lead-in — the FIRST bold run on the line —
|
|
173
|
+
* is still open at end-of-line. Deliberately asks only about that first run
|
|
174
|
+
* (not `**`-parity over the whole string), because that is the run the three
|
|
175
|
+
* grammars anchor on: a balanced inline `**bold**` later in the body must not
|
|
176
|
+
* make a terminated lead-in look open.
|
|
177
|
+
*/
|
|
178
|
+
function boldLeadInIsUnterminated(text) {
|
|
179
|
+
const open = text.indexOf('**');
|
|
180
|
+
if (open === -1)
|
|
181
|
+
return false;
|
|
182
|
+
return text.indexOf('**', open + 2) === -1;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Fold a decision bullet whose bold lead-in wraps into ONE logical line, so the
|
|
186
|
+
* declaration grammars see the whole lead-in (#3939).
|
|
187
|
+
*
|
|
188
|
+
* Bounded and fail-loud-preserving: a wrapped declaration absorbs following
|
|
189
|
+
* lines only until its lead-in closes, and a blank/whitespace-only line, a new
|
|
190
|
+
* block-level construct (`blockConstructRe`), or the end of the block stops it.
|
|
191
|
+
* If the lead-in never closes, the original line is emitted UNCHANGED — a
|
|
192
|
+
* genuinely malformed bullet (e.g. an unterminated bold run) still reaches the
|
|
193
|
+
* parse-miss guard and still fails loud, exactly as #1365 requires. Non-decision
|
|
194
|
+
* lines pass through untouched, so continuation lines (#1372 FIX) and nested
|
|
195
|
+
* cross-reference bullets (#3169) are handled by the main loop as before.
|
|
196
|
+
*
|
|
197
|
+
* The joined line keeps the FIRST physical line's leading whitespace, so the
|
|
198
|
+
* `indentWidth` signal #3169 depends on is unchanged. Absorbed lines are
|
|
199
|
+
* trimmed and re-joined with a single space, which is what a soft line break
|
|
200
|
+
* means in markdown — so the join reproduces the rendered one-line text rather
|
|
201
|
+
* than concatenating the raw bytes.
|
|
202
|
+
*
|
|
203
|
+
* One place that equivalence does not hold is inside the id-adjacent `[tags]`
|
|
204
|
+
* bracket, where an inserted space can split a tag token and silently flip
|
|
205
|
+
* `trackable`. The join stops there instead (`wouldSpliceTagToken`), leaving the
|
|
206
|
+
* bullet to fail loud.
|
|
207
|
+
*
|
|
208
|
+
* Absorption stops at the first `**` on a continuation line, so an inline
|
|
209
|
+
* `**bold**` INSIDE a wrapped title closes the run early. That is deliberate:
|
|
210
|
+
* the result is byte-identical to what the same bullet written on one physical
|
|
211
|
+
* line parses to (the text past the early close re-attaches through the main
|
|
212
|
+
* loop's continuation folding), which is the whole contract here — wrapping is
|
|
213
|
+
* markdown-insignificant, never a second grammar.
|
|
214
|
+
*/
|
|
215
|
+
function joinWrappedBoldLeadIns(lines) {
|
|
216
|
+
const joined = [];
|
|
217
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
218
|
+
const line = lines[i];
|
|
219
|
+
if (!decisionBulletStartRe.test(line) || !boldLeadInIsUnterminated(line)) {
|
|
220
|
+
joined.push(line);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// Absorbed lines accumulate as SEGMENTS joined by a single space, and each
|
|
224
|
+
// new segment is searched on its own: the lead-in is known to be open at the
|
|
225
|
+
// end of the declaration line, and the inserted space means a closing `**`
|
|
226
|
+
// can never straddle a segment boundary, so the first `**` in any later
|
|
227
|
+
// segment is the close. Scanning per segment (rather than re-searching the
|
|
228
|
+
// accumulated string, which forces a rope flatten every iteration) keeps a
|
|
229
|
+
// long unterminated run linear on the plan gate's hot path.
|
|
230
|
+
const segments = [line];
|
|
231
|
+
// Bracket content accumulated while the id-adjacent `[tags]` bracket is
|
|
232
|
+
// still open; null when it is not open. O(1) per segment.
|
|
233
|
+
let tagTail = null;
|
|
234
|
+
// The logical text assembled so far, kept ONLY while the id-adjacent
|
|
235
|
+
// bracket has yet to open, so a bracket that opens on ANY absorbed line
|
|
236
|
+
// arms the splice guard — not just one that opens on the declaration line.
|
|
237
|
+
// Null once the region settles (the bracket opened and `tagTail` took over,
|
|
238
|
+
// or the id was followed by something else), so this never re-walks a long
|
|
239
|
+
// absorption: a non-empty segment that is not a bracket-open settles the
|
|
240
|
+
// region immediately, which bounds the string to a single extra join.
|
|
241
|
+
let tagRegion = null;
|
|
242
|
+
const declRegion = tagRegionRe.exec(line);
|
|
243
|
+
if (declRegion !== null) {
|
|
244
|
+
if (declRegion[1] === undefined)
|
|
245
|
+
tagRegion = line;
|
|
246
|
+
else
|
|
247
|
+
tagTail = declRegion[1];
|
|
248
|
+
}
|
|
249
|
+
let scan = i + 1;
|
|
250
|
+
let closed = false;
|
|
251
|
+
while (scan < lines.length) {
|
|
252
|
+
const trimmed = lines[scan].trim();
|
|
253
|
+
if (trimmed === '' || blockConstructRe.test(trimmed))
|
|
254
|
+
break;
|
|
255
|
+
if (tagTail !== null && wouldSpliceTagToken(tagTail, trimmed))
|
|
256
|
+
break;
|
|
257
|
+
segments.push(trimmed);
|
|
258
|
+
scan += 1;
|
|
259
|
+
if (tagTail !== null) {
|
|
260
|
+
tagTail = trimmed.indexOf(']') === -1 ? trimmed : null;
|
|
261
|
+
}
|
|
262
|
+
else if (tagRegion !== null) {
|
|
263
|
+
tagRegion = `${tagRegion} ${trimmed}`;
|
|
264
|
+
const opened = tagRegionRe.exec(tagRegion);
|
|
265
|
+
if (opened === null)
|
|
266
|
+
tagRegion = null;
|
|
267
|
+
else if (opened[1] !== undefined) {
|
|
268
|
+
tagTail = opened[1];
|
|
269
|
+
tagRegion = null;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (trimmed.indexOf('**') !== -1) {
|
|
273
|
+
closed = true;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (closed) {
|
|
278
|
+
joined.push(segments.join(' '));
|
|
279
|
+
i = scan - 1;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
joined.push(line);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return joined;
|
|
286
|
+
}
|
|
78
287
|
/**
|
|
79
288
|
* Parse decision lines from a block of text (the inner text of a <decisions>
|
|
80
289
|
* or markdown-header section body). Returns the extracted decisions and a count
|
|
@@ -83,9 +292,12 @@ const boldLeadInBulletRe = /^\s*-\s+\*\*[A-Z]+[0-9]*-[A-Za-z0-9]/m;
|
|
|
83
292
|
* FIX B (#1365): parseMisses > 0 means the caller must treat the result as
|
|
84
293
|
* could-not-parse even when some decisions were extracted — a silent drop is
|
|
85
294
|
* worse than a fail-loud signal.
|
|
295
|
+
*
|
|
296
|
+
* #3939: physical lines are folded into logical bullets first, so a declaration
|
|
297
|
+
* whose bold lead-in wraps is matched as the one bullet it is.
|
|
86
298
|
*/
|
|
87
299
|
function parseDecisionLines(block) {
|
|
88
|
-
const lines = block.split(/\r?\n/);
|
|
300
|
+
const lines = joinWrappedBoldLeadIns(block.split(/\r?\n/));
|
|
89
301
|
const out = [];
|
|
90
302
|
let category = '';
|
|
91
303
|
let inDiscretion = false;
|
|
@@ -27,6 +27,9 @@ exports.validateResolution = validateResolution;
|
|
|
27
27
|
exports.proposeEdges = proposeEdges;
|
|
28
28
|
exports.analyzeCoverage = analyzeCoverage;
|
|
29
29
|
const probe_core_cjs_1 = require("./probe-core.cjs");
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
|
+
const cliExitModule = require("./cli-exit.cjs");
|
|
32
|
+
const { runMain } = cliExitModule;
|
|
30
33
|
/**
|
|
31
34
|
* Word-boundary cues mapping requirement prose -> data/behavior shape.
|
|
32
35
|
* Heuristic and intentionally lossy; an authored `shapes` array overrides it.
|
|
@@ -107,6 +110,15 @@ function validateRequirement(requirement) {
|
|
|
107
110
|
if (r.shapes == null && !(typeof r.text === 'string' && r.text.trim())) {
|
|
108
111
|
throw new Error(`requirement ${requirement.id} text must be a non-empty string when no shapes override is provided`);
|
|
109
112
|
}
|
|
113
|
+
// text_en (#3717) is optional, but when present it must be a non-empty string. An empty
|
|
114
|
+
// string is NOT caught by `??` (only null/undefined are), so an unvalidated `text_en: ''`
|
|
115
|
+
// would silently win `text_en ?? text` and classify against '' — the same fail-open shape
|
|
116
|
+
// #1110/#2773 already exist to eliminate, just moved one field over. Validated
|
|
117
|
+
// unconditionally (not gated on whether `shapes` will make it unused) so bad data fails
|
|
118
|
+
// closed even when it happens to be dead for this particular call.
|
|
119
|
+
if (r.text_en != null && !(typeof r.text_en === 'string' && r.text_en.trim())) {
|
|
120
|
+
throw new Error(`requirement ${requirement.id} text_en must be a non-empty string when present`);
|
|
121
|
+
}
|
|
110
122
|
}
|
|
111
123
|
/** Validate an edge resolution against the edge verification vocabulary. */
|
|
112
124
|
function validateResolution(resolution) {
|
|
@@ -132,7 +144,11 @@ function proposeEdges(requirement) {
|
|
|
132
144
|
shapes = requirement.shapes;
|
|
133
145
|
}
|
|
134
146
|
else {
|
|
135
|
-
|
|
147
|
+
// #3717: prefer the English translation when present — SHAPE_CUES are English-only
|
|
148
|
+
// word-boundary patterns, so a non-English `text` (e.g. response_language projects)
|
|
149
|
+
// would otherwise classify to zero shapes. validateRequirement (called above) has
|
|
150
|
+
// already guaranteed text_en, if present, is a non-empty string.
|
|
151
|
+
shapes = classifyShape(requirement.text_en ?? requirement.text);
|
|
136
152
|
if (shapes.length === 0) {
|
|
137
153
|
// Prose present but no shape cue matched. Do NOT silently drop it (#1110): an
|
|
138
154
|
// edge-relevant requirement whose phrasing missed every cue would otherwise vanish from
|
|
@@ -192,5 +208,10 @@ function analyzeCoverage(requirements, resolutions = []) {
|
|
|
192
208
|
* `require.main === module` so it runs only when the compiled `.cjs` is executed directly.
|
|
193
209
|
*/
|
|
194
210
|
if (require.main === module) {
|
|
195
|
-
|
|
211
|
+
// runProbeCli's default `exit` now throws ExitError (src/probe-core.cts) rather
|
|
212
|
+
// than calling process.exit directly, so this entry point must run under
|
|
213
|
+
// runMain to translate that throw into process.exitCode.
|
|
214
|
+
runMain(() => {
|
|
215
|
+
(0, probe_core_cjs_1.runProbeCli)((requirements, resolutions) => analyzeCoverage(requirements, resolutions), { usage: 'edge-probe.cjs <requirements.json> [resolutions.json]' });
|
|
216
|
+
});
|
|
196
217
|
}
|
|
@@ -21,7 +21,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
21
21
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
22
|
};
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.CALIBRATION_FILENAME = void 0;
|
|
24
|
+
exports.PhasesUnreadableError = exports.CALIBRATION_FILENAME = void 0;
|
|
25
25
|
exports.readSmartZoneBudget = readSmartZoneBudget;
|
|
26
26
|
exports.readCalibrationSamples = readCalibrationSamples;
|
|
27
27
|
exports.parseTokensFlag = parseTokensFlag;
|
|
@@ -39,9 +39,15 @@ const estimation = require("./phase-estimation.cjs");
|
|
|
39
39
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
40
40
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- config-loader.cjs is an export= CommonJS module
|
|
41
41
|
const configLoader = require("./config-loader.cjs");
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
|
|
43
|
+
const phaseLocator = require("./phase-locator.cjs");
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-scope.cjs is an export= CommonJS module
|
|
45
|
+
const planningScopeMod = require("./planning-scope.cjs");
|
|
42
46
|
const { output, error, ERROR_REASON } = io;
|
|
43
47
|
const { planningDir } = planningWorkspace;
|
|
44
48
|
const { CONFIG_DEFAULTS } = configLoader;
|
|
49
|
+
const { listMilestonePhaseDirs } = phaseLocator;
|
|
50
|
+
const { SCOPE } = planningScopeMod;
|
|
45
51
|
// WIN-1 parity (DEFECT.WINDOWS-FS-OPS): on Windows a concurrent reader, indexer,
|
|
46
52
|
// or AV scanner can transiently hold the rename target open. Retry the transient
|
|
47
53
|
// errnos with backoff, matching the writeLedger / writeConsentStore idiom.
|
|
@@ -192,6 +198,24 @@ function cmdEstimateCheck(cwd, args, raw) {
|
|
|
192
198
|
sample_count: calibration.sampleCount,
|
|
193
199
|
}, raw);
|
|
194
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Thrown by `collectCalibrationSamples` when the phases directory EXISTS but
|
|
203
|
+
* could not be read (EACCES/EIO, `scope: SCOPE.UNREADABLE`). #3882/ADR-3473
|
|
204
|
+
* §8.5: that is a NON-answer, not "zero completed phases" — silently
|
|
205
|
+
* returning `[]` here would make an unreadable directory output-identical to
|
|
206
|
+
* a genuinely empty one, which is exactly the defect class §8.5 exists to
|
|
207
|
+
* end. `cmdEstimateCalibrate` catches this and refuses the rebuild instead
|
|
208
|
+
* of persisting a phantom empty calibration.
|
|
209
|
+
*/
|
|
210
|
+
class PhasesUnreadableError extends Error {
|
|
211
|
+
phasesRoot;
|
|
212
|
+
constructor(phasesRoot) {
|
|
213
|
+
super(`phases directory exists but could not be read: ${phasesRoot}`);
|
|
214
|
+
this.phasesRoot = phasesRoot;
|
|
215
|
+
this.name = 'PhasesUnreadableError';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
exports.PhasesUnreadableError = PhasesUnreadableError;
|
|
195
219
|
/**
|
|
196
220
|
* Pair each completed phase's PLAN estimate with its SUMMARY actuals.
|
|
197
221
|
*
|
|
@@ -199,18 +223,25 @@ function cmdEstimateCheck(cwd, args, raw) {
|
|
|
199
223
|
* A plan with no `estimate` block, a summary with no `actuals`, or a malformed
|
|
200
224
|
* value is skipped rather than guessed — a fabricated sample would silently
|
|
201
225
|
* steer every future estimate.
|
|
226
|
+
*
|
|
227
|
+
* #3882 (ADR-3473 §8.2): this used to hand-roll a raw `readdirSync` over the
|
|
228
|
+
* phases directory, treating every directory (including sentinel phases —
|
|
229
|
+
* milestone 0 / 999, SENTINEL_RANGES) as a completed phase. A sentinel's
|
|
230
|
+
* PLAN/SUMMARY pair then silently contributed a phantom sample to the
|
|
231
|
+
* CALIBRATION FACTOR applied to every future estimate. Routed through the
|
|
232
|
+
* canonical owner (`listMilestonePhaseDirs`, `src/phase-locator.cts`) called
|
|
233
|
+
* with NO `cwd` — that combination is already "all milestone windows,
|
|
234
|
+
* sentinels excluded", exactly what this caller needs; no new API was
|
|
235
|
+
* required for this half (see the design doc's §3, corrected after
|
|
236
|
+
* measurement). It also inherits the `scope` discriminator for free, so an
|
|
237
|
+
* unreadable phases directory is no longer indistinguishable from a real
|
|
238
|
+
* empty one.
|
|
202
239
|
*/
|
|
203
240
|
function collectCalibrationSamples(cwd) {
|
|
204
241
|
const phasesRoot = node_path_1.default.join(planningDir(cwd), 'phases');
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
.filter((d) => d.isDirectory())
|
|
209
|
-
.map((d) => d.name)
|
|
210
|
-
.sort();
|
|
211
|
-
}
|
|
212
|
-
catch {
|
|
213
|
-
return [];
|
|
242
|
+
const { value: phases, scope } = listMilestonePhaseDirs(phasesRoot);
|
|
243
|
+
if (scope === SCOPE.UNREADABLE) {
|
|
244
|
+
throw new PhasesUnreadableError(phasesRoot);
|
|
214
245
|
}
|
|
215
246
|
const readBlock = (file, key) => {
|
|
216
247
|
let text;
|
|
@@ -277,7 +308,20 @@ function collectCalibrationSamples(cwd) {
|
|
|
277
308
|
* next estimate reads the result.
|
|
278
309
|
*/
|
|
279
310
|
function cmdEstimateCalibrate(cwd, _args, raw) {
|
|
280
|
-
|
|
311
|
+
let samples;
|
|
312
|
+
try {
|
|
313
|
+
samples = collectCalibrationSamples(cwd);
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
if (err instanceof PhasesUnreadableError) {
|
|
317
|
+
// #3882/ADR-3473 §8.5: refuse rather than silently rebuild the
|
|
318
|
+
// calibration document from a phantom empty sample set — an
|
|
319
|
+
// unreadable phases directory must never look output-identical to a
|
|
320
|
+
// project with genuinely zero completed phases.
|
|
321
|
+
error(err.message, ERROR_REASON.ESTIMATE_PHASES_UNREADABLE);
|
|
322
|
+
}
|
|
323
|
+
throw err;
|
|
324
|
+
}
|
|
281
325
|
const calibration = estimation.computeCalibration(samples);
|
|
282
326
|
const target = node_path_1.default.join(planningDir(cwd), exports.CALIBRATION_FILENAME);
|
|
283
327
|
let written = true;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// GENERATED FILE — DO NOT EDIT BY HAND.
|
|
4
|
+
// Source of truth: gsd-core/bin/shared/exit-codes.json. Regenerate with:
|
|
5
|
+
// node scripts/gen-exit-code-registry.cjs --write
|
|
6
|
+
// This exact content is emitted to THREE locations — gsd-core/bin/lib/exit-code-registry.cjs,
|
|
7
|
+
// scripts/lib/exit-code-registry.cjs, and hooks/lib/exit-code-registry.js (the latter two
|
|
8
|
+
// committed so scripts/ and hooks/ consumers work on an unbuilt clone) — all byte-compared by
|
|
9
|
+
// `npm run lint:generated-sync` (#3905 ADR-3889 Phase 1; #3906 Phase 2 added the second copy;
|
|
10
|
+
// #3911 ADR-3889 Phase 7 added the hooks/lib/ copy).
|
|
11
|
+
//
|
|
12
|
+
// exitCodeFor(name) / nameForExitCode(code) are pure and total over this
|
|
13
|
+
// closed table — each throws for anything not registered here.
|
|
14
|
+
|
|
15
|
+
const EXIT_CODES = Object.freeze([
|
|
16
|
+
Object.freeze({
|
|
17
|
+
code: 2,
|
|
18
|
+
name: "HOOK_DENY",
|
|
19
|
+
meaning: "Hook protocol deny — the harness blocks the tool call",
|
|
20
|
+
owner: "hook-adapter",
|
|
21
|
+
authorizedBy: "ADR-3889",
|
|
22
|
+
}),
|
|
23
|
+
Object.freeze({
|
|
24
|
+
code: 64,
|
|
25
|
+
name: "USAGE",
|
|
26
|
+
meaning: "Caller error — bad argv, unknown subcommand, missing argument",
|
|
27
|
+
owner: "generic",
|
|
28
|
+
authorizedBy: "ADR-3889",
|
|
29
|
+
}),
|
|
30
|
+
Object.freeze({
|
|
31
|
+
code: 66,
|
|
32
|
+
name: "NO_INPUT",
|
|
33
|
+
meaning: "Ran; zero units were in scope, and that emptiness is known to be genuine",
|
|
34
|
+
owner: "generic",
|
|
35
|
+
authorizedBy: "ADR-3889",
|
|
36
|
+
}),
|
|
37
|
+
Object.freeze({
|
|
38
|
+
code: 69,
|
|
39
|
+
name: "UNAVAILABLE",
|
|
40
|
+
meaning: "Could not run — prerequisite absent, input unreadable, scope unestablished",
|
|
41
|
+
owner: "generic",
|
|
42
|
+
authorizedBy: "ADR-3889",
|
|
43
|
+
}),
|
|
44
|
+
Object.freeze({
|
|
45
|
+
code: 70,
|
|
46
|
+
name: "INTERNAL",
|
|
47
|
+
meaning: "Self-failure — crash, timeout, killed subprocess",
|
|
48
|
+
owner: "generic",
|
|
49
|
+
authorizedBy: "ADR-3889",
|
|
50
|
+
}),
|
|
51
|
+
Object.freeze({
|
|
52
|
+
code: 80,
|
|
53
|
+
name: "DEGRADED",
|
|
54
|
+
meaning: "Ran to completion and is reporting a condition through its result payload rather than as a process failure",
|
|
55
|
+
owner: "gsd-tools",
|
|
56
|
+
authorizedBy: "ADR-3889 + ADR-2980",
|
|
57
|
+
})
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const NAME_TO_CODE = new Map(EXIT_CODES.map((entry) => [entry.name, entry.code]));
|
|
61
|
+
const CODE_TO_NAME = new Map(EXIT_CODES.map((entry) => [entry.code, entry.name]));
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the registered exit code for a symbolic name. Pure, total: throws
|
|
65
|
+
* for anything not an exact, registered, exact-case key — including
|
|
66
|
+
* non-strings, the empty string, untrimmed strings, wrong case, and
|
|
67
|
+
* prototype-chain names like `__proto__`/`constructor`/`toString` (a Map
|
|
68
|
+
* lookup never touches the prototype chain, so these are indistinguishable
|
|
69
|
+
* from any other unregistered name).
|
|
70
|
+
*
|
|
71
|
+
* @param {string} name
|
|
72
|
+
* @returns {number}
|
|
73
|
+
*/
|
|
74
|
+
function exitCodeFor(name) {
|
|
75
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
76
|
+
throw new Error(`exitCodeFor: name must be a non-empty string, received ${JSON.stringify(name)}`);
|
|
77
|
+
}
|
|
78
|
+
if (!NAME_TO_CODE.has(name)) {
|
|
79
|
+
throw new Error(`exitCodeFor: unregistered exit code name: ${JSON.stringify(name)}`);
|
|
80
|
+
}
|
|
81
|
+
return NAME_TO_CODE.get(name);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Reverse of exitCodeFor: resolve the symbolic name for a registered exit
|
|
86
|
+
* code. Pure, total: throws for anything not an exact, registered code.
|
|
87
|
+
*
|
|
88
|
+
* @param {number} code
|
|
89
|
+
* @returns {string}
|
|
90
|
+
*/
|
|
91
|
+
function nameForExitCode(code) {
|
|
92
|
+
if (!CODE_TO_NAME.has(code)) {
|
|
93
|
+
throw new Error(`nameForExitCode: unregistered exit code: ${JSON.stringify(code)}`);
|
|
94
|
+
}
|
|
95
|
+
return CODE_TO_NAME.get(code);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { EXIT_CODES, exitCodeFor, nameForExitCode };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File-overlap partitioner — shared greedy first-fit stage assignment (#3674).
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `claude-orchestration.cts`'s `partitionStages` (#1143), which
|
|
6
|
+
* emits sequential Workflow `parallel()` stage barriers so that no two plans
|
|
7
|
+
* sharing a `files_modified` entry ever cohabit a stage. This module is the
|
|
8
|
+
* SAME algorithm, generalized: it depends on no `Plan`/`Wave` interface from
|
|
9
|
+
* `claude-orchestration.cts` (or any other caller-specific shape), so a future
|
|
10
|
+
* consumer (quick-batch, #3675 / ADR-1239 "Quick-batch binding") can partition
|
|
11
|
+
* its own planned-path items without pulling in orchestration internals.
|
|
12
|
+
*
|
|
13
|
+
* This is a pure, behavior-preserving extraction (#3674's acceptance bar is
|
|
14
|
+
* byte-identical output for `claude-orchestration.cts`'s existing callers —
|
|
15
|
+
* not an improvement). Explicitly OUT of scope, per the #3674 design lock:
|
|
16
|
+
* - dependency-DAG ordering — this module only ever sees a flat item list
|
|
17
|
+
* and file-overlap; a caller resolves dependency order before calling in
|
|
18
|
+
* (same contract `partitionStages` already had);
|
|
19
|
+
* - path normalization — file entries are compared by exact string equality
|
|
20
|
+
* only. `Foo.ts` vs `foo.ts`, or `src/a.ts` vs `src\a.ts`, are treated as
|
|
21
|
+
* DISTINCT files. This is deliberate, not a gap to "fix" during extraction;
|
|
22
|
+
* - filesystem access — this module never reads a path off disk.
|
|
23
|
+
*
|
|
24
|
+
* Zero external dependencies. Pure function. Never throws on well-typed input.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Partition `items` into a near-minimal number of sequential stages (via
|
|
28
|
+
* greedy first-fit — not guaranteed optimal for arbitrary overlap graphs, but
|
|
29
|
+
* correct: no two items sharing a file ever cohabit a stage) such that no two
|
|
30
|
+
* items in the same stage share a file. Each item goes into the earliest
|
|
31
|
+
* stage where it does not overlap any item already there, in input order.
|
|
32
|
+
*
|
|
33
|
+
* An item with an EMPTY `files` array declares no files; it overlaps nothing
|
|
34
|
+
* and coalesces into stage 0 (mirrors `partitionStages`' original behavior —
|
|
35
|
+
* this module cannot guard against undeclared concurrent writes; a caller
|
|
36
|
+
* must declare `files` accurately).
|
|
37
|
+
*
|
|
38
|
+
* File comparison is EXACT STRING EQUALITY — no path normalization, no
|
|
39
|
+
* case-folding, no separator canonicalization. Duplicate `id`s in the input
|
|
40
|
+
* are NOT deduplicated; each item is placed independently, in input order.
|
|
41
|
+
*
|
|
42
|
+
* Deterministic: identical input (including input order) always yields an
|
|
43
|
+
* identical partition.
|
|
44
|
+
*/
|
|
45
|
+
function partitionByFileOverlap(items) {
|
|
46
|
+
const stages = [];
|
|
47
|
+
for (const item of items) {
|
|
48
|
+
const fileSet = new Set(item.files);
|
|
49
|
+
let placed = false;
|
|
50
|
+
for (const stage of stages) {
|
|
51
|
+
let overlap = false;
|
|
52
|
+
for (const f of fileSet) {
|
|
53
|
+
if (stage.files.has(f)) {
|
|
54
|
+
overlap = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!overlap) {
|
|
59
|
+
stage.items.push(item);
|
|
60
|
+
for (const f of fileSet)
|
|
61
|
+
stage.files.add(f);
|
|
62
|
+
placed = true;
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!placed) {
|
|
67
|
+
stages.push({ items: [item], files: new Set(fileSet) });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return stages.map((s) => s.items.map((i) => i.id));
|
|
71
|
+
}
|
|
72
|
+
module.exports = {
|
|
73
|
+
partitionByFileOverlap,
|
|
74
|
+
};
|