@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
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.KNOWN_STATUS_PATTERNS = exports.KNOWN_TEMPLATE_DEFAULTS = void 0;
|
|
12
|
+
exports.toFiniteNumber = toFiniteNumber;
|
|
13
|
+
exports.isUnfilledFieldValue = isUnfilledFieldValue;
|
|
14
|
+
exports.leadingCalendarDate = leadingCalendarDate;
|
|
15
|
+
exports.isRealCalendarDate = isRealCalendarDate;
|
|
16
|
+
exports.stateFieldContinuation = stateFieldContinuation;
|
|
12
17
|
exports.stateExtractField = stateExtractField;
|
|
13
18
|
exports.stateFieldValue = stateFieldValue;
|
|
14
19
|
exports.stateCurrentPositionSlice = stateCurrentPositionSlice;
|
|
@@ -28,6 +33,15 @@ const pattern_cjs_1 = require("./pattern.cjs");
|
|
|
28
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-scope.cjs is an export= CommonJS module
|
|
29
34
|
const planningScopeMod = require("./planning-scope.cjs");
|
|
30
35
|
const { SCOPE } = planningScopeMod;
|
|
36
|
+
/**
|
|
37
|
+
* Coerce an arbitrary frontmatter scalar to a finite number, or `null` if it
|
|
38
|
+
* is not one. Exported per ADR-3473 §8.6: `state-transition.cts`'s
|
|
39
|
+
* progress-ratchet unmeasured-scan check ("is this derived total a real
|
|
40
|
+
* measurement?") must ask through the SAME coercion this module already uses
|
|
41
|
+
* for `existingProgressExceedsDerived`, rather than growing a second private
|
|
42
|
+
* copy. This matters because frontmatter scalars arrive as STRINGS
|
|
43
|
+
* (`"0"`, not `0`) — a raw `=== 0` test is wrong at both call sites.
|
|
44
|
+
*/
|
|
31
45
|
function toFiniteNumber(value) {
|
|
32
46
|
const number = Number(value);
|
|
33
47
|
return Number.isFinite(number) ? number : null;
|
|
@@ -208,6 +222,178 @@ function locateFieldRow(content, fieldName) {
|
|
|
208
222
|
}
|
|
209
223
|
return null;
|
|
210
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* True only when y/m/d name a date that actually exists on the calendar.
|
|
227
|
+
*
|
|
228
|
+
* `Date.parse` validates shape but not value: it rolls an out-of-range day
|
|
229
|
+
* FORWARD rather than rejecting it (`2026-02-30` -> `2026-03-02`,
|
|
230
|
+
* `2026-04-31` -> `2026-05-01`). Shape-only validation would therefore
|
|
231
|
+
* propagate a different, wrong instant instead of failing safe — precisely
|
|
232
|
+
* what ADR-227 ("validate shape AND value; on failure of either layer coerce
|
|
233
|
+
* to the contract's safe default, never propagate") exists to prevent. A
|
|
234
|
+
* round-trip through Date.UTC detects the rollover: any component the
|
|
235
|
+
* constructor normalised comes back changed.
|
|
236
|
+
*
|
|
237
|
+
* #3696: this predicate previously lived privately inside `smart-entry.cts`,
|
|
238
|
+
* where it gated `parseActivityTimestamp`. `state validate` needed the same
|
|
239
|
+
* answer to assert the `last_activity` invariant (S008), and a second copy is
|
|
240
|
+
* the "generative fix divergence" class outright — two surfaces that disagree
|
|
241
|
+
* about whether a STATE.md is usable is the defect #3696 opens with, so a
|
|
242
|
+
* parity test over two copies would be codifying the bug rather than fixing
|
|
243
|
+
* it. It moves here because this module is already the designated owner of
|
|
244
|
+
* STATE.md field semantics (ADR-3180 §7.7) and `smart-entry.cts` imports no
|
|
245
|
+
* peer that would make the reverse direction a cycle.
|
|
246
|
+
*/
|
|
247
|
+
/**
|
|
248
|
+
* True when a field carries no value a writer ever supplied: absent, blank, or
|
|
249
|
+
* still holding the shipped template's bracket placeholder.
|
|
250
|
+
*
|
|
251
|
+
* `templates/state.md:35` ships `Last activity: [YYYY-MM-DD] — [What happened]`,
|
|
252
|
+
* so EVERY freshly-initialized project has this exact string until something
|
|
253
|
+
* records activity. #3696's first cut only spared the ABSENT form, which made
|
|
254
|
+
* S008 fire on the shipped template itself — caught by the pre-existing
|
|
255
|
+
* "template-equivalent phase identities remain clean without disk drift" test,
|
|
256
|
+
* which is precisely what it is there for.
|
|
257
|
+
*
|
|
258
|
+
* The placeholder test is anchored at the START rather than "contains a bracket
|
|
259
|
+
* anywhere", so a real description that happens to cite one — `2026-08-19 — fixed
|
|
260
|
+
* [#123] parsing` — is still a filled-in value. That keeps the rule from
|
|
261
|
+
* silently swallowing genuine drift.
|
|
262
|
+
*
|
|
263
|
+
* Distinct from `isStateTemplateDefault`, which answers a different question
|
|
264
|
+
* ("may a later handler overwrite this?") and deliberately returns true for a
|
|
265
|
+
* bare ISO date — a perfectly valid value here.
|
|
266
|
+
*/
|
|
267
|
+
function isUnfilledFieldValue(value) {
|
|
268
|
+
if (value === null || value === undefined)
|
|
269
|
+
return true;
|
|
270
|
+
const trimmed = value.trim();
|
|
271
|
+
return trimmed === '' || trimmed.startsWith('[');
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* The `YYYY-MM-DD` prefix of `value`, but only when it names a date that
|
|
275
|
+
* actually exists. `null` for anything else — no leading date token at all, or
|
|
276
|
+
* a token that is shape-valid and calendar-impossible.
|
|
277
|
+
*
|
|
278
|
+
* #3696 review: this is deliberately a LEADING-TOKEN test, not the fully
|
|
279
|
+
* anchored prose grammar `parseProseLastActivityField` uses. That function
|
|
280
|
+
* requires the whole value to be `date` or `date <separator> description`, and
|
|
281
|
+
* returns `{date: <the entire raw string>}` when it does not match — a shape
|
|
282
|
+
* that reads like success. Asserting the S008 invariant through it therefore
|
|
283
|
+
* rejected values the real reader accepts: `smart-entry`'s
|
|
284
|
+
* `parseActivityTimestamp` needs only a leading date and reconstructs the
|
|
285
|
+
* instant even when the suffix carries no dash, so
|
|
286
|
+
* `Last activity: 2026-08-24 Shipped feature X` parses fine there while S008
|
|
287
|
+
* called it unreadable. That is the same two-surfaces-disagree defect #3696
|
|
288
|
+
* exists to close, merely pointing the other way.
|
|
289
|
+
*
|
|
290
|
+
* So the invariant asserted is the one the readers actually share: a leading
|
|
291
|
+
* ISO date token that is a real calendar date.
|
|
292
|
+
*/
|
|
293
|
+
function leadingCalendarDate(value) {
|
|
294
|
+
if (!value)
|
|
295
|
+
return null;
|
|
296
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})(?![\d-])/.exec(value.trim());
|
|
297
|
+
if (!match)
|
|
298
|
+
return null;
|
|
299
|
+
return isRealCalendarDate(Number(match[1]), Number(match[2]), Number(match[3]))
|
|
300
|
+
? `${match[1]}-${match[2]}-${match[3]}`
|
|
301
|
+
: null;
|
|
302
|
+
}
|
|
303
|
+
function isRealCalendarDate(year, month, day) {
|
|
304
|
+
if (month < 1 || month > 12 || day < 1 || day > 31)
|
|
305
|
+
return false;
|
|
306
|
+
const probe = new Date(Date.UTC(year, month - 1, day));
|
|
307
|
+
return (probe.getUTCFullYear() === year &&
|
|
308
|
+
probe.getUTCMonth() === month - 1 &&
|
|
309
|
+
probe.getUTCDate() === day);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Markdown structure that can legitimately follow a single-line field. A line
|
|
313
|
+
* matching any of these is the NEXT construct, never a continuation of the
|
|
314
|
+
* field above it.
|
|
315
|
+
*
|
|
316
|
+
* BREADTH IS THE POINT, and the failure direction is deliberate: a missed
|
|
317
|
+
* truncation costs a diagnostic nobody sees, while a false S009 reports drift on
|
|
318
|
+
* a well-formed STATE.md — a gate that fires on valid documents is worse than no
|
|
319
|
+
* gate. When a shape is ambiguous, it belongs here.
|
|
320
|
+
*
|
|
321
|
+
* #3696 review round 2 added the last three arms after all three were shown to
|
|
322
|
+
* produce false S009 fires on well-formed content: an indented code block, an
|
|
323
|
+
* HTML block, and a setext underline (`===`, which the `[-*_]{3,}` rule does not
|
|
324
|
+
* cover — it only knows `-`, `*` and `_`).
|
|
325
|
+
*/
|
|
326
|
+
const MD_STRUCTURE_LINE_RE = /^(?:#{1,6}\s|\||>|```|~~~|[-*_]{3,}\s*$|=+\s*$|[-*+]\s|\d+[.)]\s|\[[^\]]+\]:|<|(?: {4}|\t))/;
|
|
327
|
+
/**
|
|
328
|
+
* A setext heading's underline — `===` or `---` on its own line. The line ABOVE
|
|
329
|
+
* one of these is a heading TITLE, which is indistinguishable from prose on its
|
|
330
|
+
* own, so the scan must look ahead by one line rather than consume it. Without
|
|
331
|
+
* this, `Last activity: …\nMy Heading\n===` reported "My Heading ===" as dropped
|
|
332
|
+
* continuation text (#3696 review round 2).
|
|
333
|
+
*/
|
|
334
|
+
const SETEXT_UNDERLINE_RE = /^(?:=+|-+)\s*$/;
|
|
335
|
+
const STATE_SIBLING_FIELD_LINE_RE = /^\*{0,2}[A-Za-z][A-Za-z0-9 _-]*\*{0,2}:{1,2}\*{0,2}(?:\s|$)/;
|
|
336
|
+
/**
|
|
337
|
+
* Return the prose that FOLLOWS a single-line field but plainly belongs to it —
|
|
338
|
+
* i.e. the remainder `stateExtractField` silently drops when a writer emits a
|
|
339
|
+
* value long enough to wrap.
|
|
340
|
+
*
|
|
341
|
+
* `stateExtractField`'s `(.+)` is newline-excluding, so
|
|
342
|
+
*
|
|
343
|
+
* Last activity: 2026-08-19 — Project initialized from ingest; PROJECT.md,
|
|
344
|
+
* REQUIREMENTS.md, ROADMAP.md written
|
|
345
|
+
*
|
|
346
|
+
* yields only the first line and the rest is lost with no diagnostic (#3696).
|
|
347
|
+
* `templates/state.md` prescribes a single-line field, so the DOCUMENT is what
|
|
348
|
+
* is wrong here, not the reader — this function exists so `state validate` can
|
|
349
|
+
* SAY so, not so the reader can start guessing at a multi-line grammar the
|
|
350
|
+
* template does not sanction.
|
|
351
|
+
*
|
|
352
|
+
* That is also why the fix is not in `stateExtractField` itself: it has 20
|
|
353
|
+
* direct callers and a CRITICAL blast radius (ADR-3180 §7.7, Rejected #1), and
|
|
354
|
+
* joining continuations there would apply to every field — `Status:` would
|
|
355
|
+
* swallow the line beneath it.
|
|
356
|
+
*
|
|
357
|
+
* Returns `null` when the field is absent, is a pipe-table row (a table cell
|
|
358
|
+
* cannot wrap), or is followed by end-of-file, a blank line, Markdown
|
|
359
|
+
* structure, or a sibling field.
|
|
360
|
+
*/
|
|
361
|
+
function stateFieldContinuation(content, fieldName) {
|
|
362
|
+
const escaped = (0, pattern_cjs_1.escapeRegex)(fieldName);
|
|
363
|
+
// Same two single-line grammars stateExtractField uses, in the same order, so
|
|
364
|
+
// this locates exactly the line whose value it returned. The pipe-table rung
|
|
365
|
+
// is deliberately absent: a `| Field | value |` row is bounded by its closing
|
|
366
|
+
// pipe and cannot wrap.
|
|
367
|
+
const match = new RegExp(`\\*\\*${escaped}:\\*\\*[ \\t]*(.+)`, 'i').exec(content) ??
|
|
368
|
+
new RegExp(`^${escaped}:[ \\t]*(.+)`, 'im').exec(content);
|
|
369
|
+
if (!match)
|
|
370
|
+
return null;
|
|
371
|
+
// `(.+)` stops at the line terminator, so the field's line ends where the
|
|
372
|
+
// match does. JS `.` excludes \r as well as \n, so on a CRLF document the \r
|
|
373
|
+
// sits just AFTER the match rather than inside it — hence the strip below
|
|
374
|
+
// before testing for the newline.
|
|
375
|
+
const afterValue = match.index + match[0].length;
|
|
376
|
+
const rest = content.slice(afterValue).replace(/^\r/, '');
|
|
377
|
+
if (!rest.startsWith('\n'))
|
|
378
|
+
return null; // end of file: nothing follows
|
|
379
|
+
const lines = rest.slice(1).split('\n').map((line) => line.replace(/\r$/, ''));
|
|
380
|
+
const continuation = [];
|
|
381
|
+
for (let i = 0; i < lines.length; i++) {
|
|
382
|
+
const line = lines[i];
|
|
383
|
+
if (!line.trim())
|
|
384
|
+
break;
|
|
385
|
+
if (MD_STRUCTURE_LINE_RE.test(line))
|
|
386
|
+
break;
|
|
387
|
+
if (STATE_SIBLING_FIELD_LINE_RE.test(line))
|
|
388
|
+
break;
|
|
389
|
+
// Look ahead one line: a setext underline below makes THIS line a heading
|
|
390
|
+
// title, so stop before consuming it rather than after.
|
|
391
|
+
if (i + 1 < lines.length && SETEXT_UNDERLINE_RE.test(lines[i + 1]))
|
|
392
|
+
break;
|
|
393
|
+
continuation.push(line.trim());
|
|
394
|
+
}
|
|
395
|
+
return continuation.length ? continuation.join(' ') : null;
|
|
396
|
+
}
|
|
211
397
|
function stateExtractField(content, fieldName) {
|
|
212
398
|
const escaped = (0, pattern_cjs_1.escapeRegex)(fieldName);
|
|
213
399
|
// Bold inline format: **FieldName:** value
|
|
@@ -326,17 +512,42 @@ function stateCurrentPositionSlice(body) {
|
|
|
326
512
|
const section = (0, markdown_sectionizer_cjs_1.collectSection)(body, isCurrentPosition, { levelBounded: true });
|
|
327
513
|
return section ? section.body : null;
|
|
328
514
|
}
|
|
515
|
+
/**
|
|
516
|
+
* Join a matched `**Field:**`/`Field:` label prefix to its new value, inserting a
|
|
517
|
+
* single space when the prefix does not already end in same-line whitespace.
|
|
518
|
+
*
|
|
519
|
+
* On an empty field the same-line `[ \t]*` gap (below) captures nothing, so the
|
|
520
|
+
* bare `prefix + value` would glue the value to the label (`**Status:**value`);
|
|
521
|
+
* this inserts the missing separator. A non-empty field whose label-to-value
|
|
522
|
+
* separator is ordinary space/tab keeps that separator in the prefix, so `[ \t]$`
|
|
523
|
+
* is true and the output stays byte-identical to prior behaviour. The one
|
|
524
|
+
* exception is a non-empty field written with NO separator at all (a hand-edited
|
|
525
|
+
* `**Status:**value`): the narrowed gap captures nothing, `[ \t]$` is false, and a
|
|
526
|
+
* single space is inserted — an intentional normalization, not byte-identical, and
|
|
527
|
+
* with no GSD-template trigger. An empty new value inserts no separator, avoiding a
|
|
528
|
+
* dangling trailing space. See #4010.
|
|
529
|
+
*/
|
|
530
|
+
function joinFieldReplacement(prefix, newValue) {
|
|
531
|
+
const value = `${newValue}`;
|
|
532
|
+
const needsSeparator = value.length > 0 && !/[ \t]$/.test(prefix);
|
|
533
|
+
return `${prefix}${needsSeparator ? ' ' : ''}${value}`;
|
|
534
|
+
}
|
|
329
535
|
function stateReplaceField(content, fieldName, newValue) {
|
|
330
536
|
const escaped = (0, pattern_cjs_1.escapeRegex)(fieldName);
|
|
331
537
|
// Bold inline format: **FieldName:** value
|
|
332
|
-
|
|
538
|
+
// The label-to-value gap is same-line whitespace only (`[ \t]*`, mirroring the
|
|
539
|
+
// read side at stateExtractField). `\s*` here matched `\n`, so on an empty field
|
|
540
|
+
// `(.*)` captured the following line and the rebuild discarded it — the #4010
|
|
541
|
+
// data-loss. ADR-3180 §7.7 makes stateExtractField the same-line-confined owner;
|
|
542
|
+
// this aligns the writer to it.
|
|
543
|
+
const boldPattern = new RegExp(`(\\*\\*${escaped}:\\*\\*[ \\t]*)(.*)`, 'i');
|
|
333
544
|
if (boldPattern.test(content)) {
|
|
334
|
-
return content.replace(boldPattern, (_match, prefix) =>
|
|
545
|
+
return content.replace(boldPattern, (_match, prefix) => joinFieldReplacement(prefix, newValue));
|
|
335
546
|
}
|
|
336
|
-
// Plain line-start format: FieldName: value
|
|
337
|
-
const plainPattern = new RegExp(`(^${escaped}
|
|
547
|
+
// Plain line-start format: FieldName: value (same same-line confinement as above)
|
|
548
|
+
const plainPattern = new RegExp(`(^${escaped}:[ \\t]*)(.*)`, 'im');
|
|
338
549
|
if (plainPattern.test(content)) {
|
|
339
|
-
return content.replace(plainPattern, (_match, prefix) =>
|
|
550
|
+
return content.replace(plainPattern, (_match, prefix) => joinFieldReplacement(prefix, newValue));
|
|
340
551
|
}
|
|
341
552
|
// Pipe-table format: | FieldName | value |
|
|
342
553
|
// Preserve the surrounding pipe/whitespace structure; only swap the value cell.
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* STATE.md Field Schema — the one declaration (ADR-3473 §8.8, issue #3873).
|
|
4
|
+
*
|
|
5
|
+
* Phase 3 substrate. Before this module, "which STATE.md keys exist and what
|
|
6
|
+
* they carry" was declared in THREE hand-maintained places that were already
|
|
7
|
+
* observed to disagree (see the `last_activity` docstring below):
|
|
8
|
+
*
|
|
9
|
+
* - `FIELD_CLASSIFICATION` (`src/state-transition.cts`) — source/preservation/
|
|
10
|
+
* guard/mergeStrategy per frontmatter key (ADR-1769 §4 / ADR-3408).
|
|
11
|
+
* - `FRONTMATTER_BODY_SOURCE` (`src/state-transition.cts`) — which BODY field
|
|
12
|
+
* a frontmatter key derives from.
|
|
13
|
+
* - `FRONTMATTER_KEY_TO_BODY_LABEL` (`src/state.cts`) — the Title-Case label
|
|
14
|
+
* a report speaks a preserved field in (ADR-3408 §8.4/§8.5).
|
|
15
|
+
*
|
|
16
|
+
* This module is the single row-per-key declaration those three now PROJECT
|
|
17
|
+
* from at load time (`state-transition.cts` / `state.cts`), rather than
|
|
18
|
+
* hand-maintaining a fourth copy of the same knowledge. Every exported shape
|
|
19
|
+
* of the three original tables is unchanged — same keys, same key ORDER, same
|
|
20
|
+
* frozen/null-prototype-ness — so every existing consumer (the preservation
|
|
21
|
+
* dispatch loop, `getFieldClassification`, `getPreserveWhenUnchangedFields`,
|
|
22
|
+
* `bodyLabelFor`, and issue #3872's `declaredLeavesOf`) keeps working without
|
|
23
|
+
* an edit. See `.gsd/phase/feat-3873-state-md-schema/40-design.md`.
|
|
24
|
+
*
|
|
25
|
+
* LEAF MODULE, DELIBERATELY. This file imports from neither `state-transition.cts`
|
|
26
|
+
* nor `state.cts` — both of those import THIS module, and either importing
|
|
27
|
+
* back would be the exact CJS require-cycle `src/health-diagnostic-types.cts`'s
|
|
28
|
+
* own docstring describes breaking for the health-diagnostic rule tables
|
|
29
|
+
* (`module.exports` read before it is assigned, so a destructured value comes
|
|
30
|
+
* back `undefined`). `FieldSource` / `FieldPreservation` / `FieldGuard` /
|
|
31
|
+
* `FieldMergeStrategy` therefore live HERE now and are re-exported (by the same
|
|
32
|
+
* name, so no importer of `state-transition.cts` needs to change) from
|
|
33
|
+
* `state-transition.cts`.
|
|
34
|
+
*
|
|
35
|
+
* ADR-457 build-at-publish: source in `src/state-md-schema.cts`, compiled to
|
|
36
|
+
* `gsd-core/bin/lib/state-md-schema.cjs` (gitignored).
|
|
37
|
+
*
|
|
38
|
+
* Design: .gsd/phase/feat-3873-state-md-schema/40-design.md
|
|
39
|
+
* Test matrix: .gsd/phase/feat-3873-state-md-schema/50-test-matrix.md
|
|
40
|
+
*/
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.STATE_FIELD_SCHEMA = exports.STATUS_LIFECYCLE_ENUM = void 0;
|
|
43
|
+
/**
|
|
44
|
+
* The seven CANONICAL values `normalizeStateStatus` (`src/state-document.cts`)
|
|
45
|
+
* maps recognized raw status prose TO — the function's default fallback plus
|
|
46
|
+
* each branch's literal output, in the order the function tests them. This is
|
|
47
|
+
* NOT the raw body prose vocabulary `CONTEXT.md`'s "STATE.md Status Lifecycle
|
|
48
|
+
* (ADR-2207)" entry documents (`Ready to plan` → `All phases complete` →
|
|
49
|
+
* `<version> milestone complete` → `Awaiting next milestone`, plus the
|
|
50
|
+
* handler-authored strings in `KNOWN_TEMPLATE_DEFAULTS['Status']`) — that is
|
|
51
|
+
* free-form prose `normalizeStateStatus` READS.
|
|
52
|
+
*
|
|
53
|
+
* CORRECTED (#3873 phase-3 test-matrix row 26 — verified by executing
|
|
54
|
+
* `normalizeStateStatus`, not by reading this docstring's prior claim):
|
|
55
|
+
* this is NOT a closed set the `status` frontmatter key is restricted to at
|
|
56
|
+
* runtime. `normalizeStateStatus` is deliberately LENIENT: its fallback is
|
|
57
|
+
* `normalizedStatus = status || 'unknown'`, and when none of its
|
|
58
|
+
* substring-match branches recognize the raw input, that fallback — the
|
|
59
|
+
* caller's raw, UNRECOGNIZED prose — is returned unchanged. A status value
|
|
60
|
+
* outside this seven-member set is not rejected, coerced, or normalized; it
|
|
61
|
+
* passes straight through into the frontmatter. `STATUS_LIFECYCLE_ENUM` is
|
|
62
|
+
* therefore the set of values the normalizer maps recognized input ONTO, not
|
|
63
|
+
* a runtime-enforced closed vocabulary for the field.
|
|
64
|
+
*/
|
|
65
|
+
exports.STATUS_LIFECYCLE_ENUM = Object.freeze([
|
|
66
|
+
'unknown',
|
|
67
|
+
'paused',
|
|
68
|
+
'executing',
|
|
69
|
+
'planning',
|
|
70
|
+
'discussing',
|
|
71
|
+
'verifying',
|
|
72
|
+
'completed',
|
|
73
|
+
]);
|
|
74
|
+
// ─── The one declaration ────────────────────────────────────────────────────
|
|
75
|
+
//
|
|
76
|
+
// Row order below is `FIELD_CLASSIFICATION`'s (`src/state-transition.cts`,
|
|
77
|
+
// pre-#3873) ORIGINAL literal order, verified by direct read and preserved
|
|
78
|
+
// deliberately: the `FIELD_CLASSIFICATION` projection built from this table
|
|
79
|
+
// (`state-transition.cts`) walks `Object.keys(STATE_FIELD_SCHEMA)` directly,
|
|
80
|
+
// so this row order IS that projection's key order, and key order is
|
|
81
|
+
// observable (the preservation dispatch loop iterates it). The two other
|
|
82
|
+
// projections (`FRONTMATTER_BODY_SOURCE`, `FRONTMATTER_KEY_TO_BODY_LABEL`) do
|
|
83
|
+
// NOT reuse this same order — their pre-#3873 literals were independently
|
|
84
|
+
// hand-written and already disagreed with each other and with this order (see
|
|
85
|
+
// each projection's own ordering constant in its home module) — so each
|
|
86
|
+
// projection module declares its OWN explicit key-order list rather than
|
|
87
|
+
// re-deriving order from this table's iteration, which would silently change
|
|
88
|
+
// two of the three tables' observable order out from under every consumer.
|
|
89
|
+
exports.STATE_FIELD_SCHEMA = Object.freeze(Object.assign(Object.create(null), {
|
|
90
|
+
// Schema
|
|
91
|
+
gsd_state_version: {
|
|
92
|
+
type: 'string', cardinality: 'one', source: 'free', preservation: 'derive', emitted: 'always',
|
|
93
|
+
},
|
|
94
|
+
// Milestone (external — from ROADMAP.md)
|
|
95
|
+
milestone: {
|
|
96
|
+
type: 'string', cardinality: 'optional', source: 'external', preservation: 'preserve-if-placeholder', emitted: 'when-present',
|
|
97
|
+
},
|
|
98
|
+
milestone_name: {
|
|
99
|
+
type: 'string', cardinality: 'optional', source: 'external', preservation: 'preserve-if-placeholder', emitted: 'when-present',
|
|
100
|
+
},
|
|
101
|
+
// Phase / plan position (body-derived)
|
|
102
|
+
current_phase: {
|
|
103
|
+
type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
|
|
104
|
+
bodySource: Object.freeze(['Current Phase']), bodyLabel: 'Current Phase', emitted: 'when-present',
|
|
105
|
+
},
|
|
106
|
+
current_phase_name: {
|
|
107
|
+
type: 'string', cardinality: 'optional', source: 'curated', preservation: 'preserve-when-unchanged',
|
|
108
|
+
bodySource: Object.freeze(['Current Phase Name']), bodyLabel: 'Current Phase Name', emitted: 'when-present',
|
|
109
|
+
},
|
|
110
|
+
current_plan: {
|
|
111
|
+
type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
|
|
112
|
+
bodySource: Object.freeze(['Current Plan']), bodyLabel: 'Current Plan',
|
|
113
|
+
// #3873 phase-3 test-matrix row 25 (verified by executing
|
|
114
|
+
// `advancePlanCore`, `src/state-transition.cts:1306`, not by reading
|
|
115
|
+
// its docstring): TODAY the `Current Plan` body field parses in
|
|
116
|
+
// exactly ONE shape — a bare number `N`, paired with a separate
|
|
117
|
+
// `Total Plans in Phase` field. The hybrid compound `N of M` written
|
|
118
|
+
// directly into `Current Plan` (no `Total Plans in Phase` sibling)
|
|
119
|
+
// does NOT parse: `legacyTotal` is absent, `planField` reads the
|
|
120
|
+
// DIFFERENT `Plan` field (also absent), so the function falls to its
|
|
121
|
+
// NaN/NaN error branch. Feeding `Current Plan: 2 of 5` WITH a
|
|
122
|
+
// `Total Plans in Phase` sibling present does not change this — it
|
|
123
|
+
// "succeeds" only because `parseInt("2 of 5", 10)` truncates to `2`
|
|
124
|
+
// and the sibling supplies the total; the `of 5` half is silently
|
|
125
|
+
// discarded, which is `parseInt` coincidence, not shape recognition.
|
|
126
|
+
// `Plan: N of M` (a DIFFERENT field name) DOES parse the hybrid shape,
|
|
127
|
+
// but `buildStateFrontmatter` never reads `Plan` into `current_plan`
|
|
128
|
+
// (verified: it calls `stateExtractField(bodyContent, 'Current Plan')`
|
|
129
|
+
// only), so that shape is out of scope for this row regardless.
|
|
130
|
+
//
|
|
131
|
+
// #3784/#3791 WIDENED THIS ROW. The paragraph above describes the
|
|
132
|
+
// pre-#3791 parser and is kept as the record of what the shape was
|
|
133
|
+
// before, because row 25 exists to stop exactly that reading from
|
|
134
|
+
// being re-asserted by accident.
|
|
135
|
+
//
|
|
136
|
+
// `advancePlanCore` now accepts the hybrid shape, so the declared set
|
|
137
|
+
// is `['N', 'N of M']`. Two properties of the widening matter to a
|
|
138
|
+
// future reader:
|
|
139
|
+
//
|
|
140
|
+
// - It is ANCHORED. The parser matches `/^(\d+)\s+of\s+(\d+)\s*$/`
|
|
141
|
+
// against the whole value, so `4 — blocked on review of 2 PRs`
|
|
142
|
+
// is REJECTED rather than yielding a total of 2 out of prose.
|
|
143
|
+
// Declaring `'N of M'` is a claim about that grammar, not about
|
|
144
|
+
// "contains the word of".
|
|
145
|
+
// - `'N/M'` stays UNDECLARED and must keep failing. Row 23 probes
|
|
146
|
+
// the undeclared remainder of `SHAPE_EXAMPLES`, so it needs at
|
|
147
|
+
// least one member outside the declared set to stay non-vacuous.
|
|
148
|
+
//
|
|
149
|
+
// Widening this row without widening the parser (or the reverse) goes
|
|
150
|
+
// RED on rows 23/24/25. That coupling is the forcing function, and it
|
|
151
|
+
// is the reason this row is data rather than a predicate: §8.8's
|
|
152
|
+
// "parsers are checked, not generated".
|
|
153
|
+
acceptedShapes: Object.freeze(['N', 'N of M']),
|
|
154
|
+
emitted: 'when-present',
|
|
155
|
+
},
|
|
156
|
+
// Status / lifecycle (body-derived; #1230 delta heuristic applies)
|
|
157
|
+
// guard: the 'unknown' sentinel is the ONLY true executor-side guard in
|
|
158
|
+
// this table (stopped_at's `## Session` scoping is caller-side delta
|
|
159
|
+
// extraction, not an executor condition) — ADR-3408 Decision 1.
|
|
160
|
+
status: {
|
|
161
|
+
type: 'string', enum: exports.STATUS_LIFECYCLE_ENUM, cardinality: 'one', source: 'body', preservation: 'preserve-when-unchanged',
|
|
162
|
+
guard: 'non-sentinel-unknown', bodySource: Object.freeze(['Status']), bodyLabel: 'Status', emitted: 'always',
|
|
163
|
+
},
|
|
164
|
+
stopped_at: {
|
|
165
|
+
type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
|
|
166
|
+
bodySource: Object.freeze(['Stopped At', 'Stopped at']), bodyLabel: 'Stopped At', emitted: 'when-present',
|
|
167
|
+
},
|
|
168
|
+
paused_at: {
|
|
169
|
+
type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
|
|
170
|
+
bodySource: Object.freeze(['Paused At']), bodyLabel: 'Paused At', emitted: 'when-present',
|
|
171
|
+
},
|
|
172
|
+
// Activity log
|
|
173
|
+
last_updated: {
|
|
174
|
+
type: 'string', cardinality: 'one', source: 'free', preservation: 'derive', emitted: 'always',
|
|
175
|
+
}, // realClock.nowIso()
|
|
176
|
+
// #3873: THE LIVE DISAGREEMENT. Pre-schema, `FRONTMATTER_BODY_SOURCE`
|
|
177
|
+
// carried this key (`last_activity: ['Last Activity', 'Last activity']`)
|
|
178
|
+
// while `FRONTMATTER_KEY_TO_BODY_LABEL` did NOT — same field, two
|
|
179
|
+
// tables, two different answers to "does this key have a reportable
|
|
180
|
+
// body label". Resolved by DECLARATION, not by picking whichever table
|
|
181
|
+
// "looks right": `bodySource` is present below (this key IS derived from
|
|
182
|
+
// a body field and `buildStateFrontmatter` — `src/state.cts` — reads it
|
|
183
|
+
// via that exact two-case-variant fallback), and `bodyLabel` is
|
|
184
|
+
// deliberately ABSENT, because that is what ships TODAY —
|
|
185
|
+
// `last_activity`'s `preservation` is `'derive'`, never
|
|
186
|
+
// `'preserve-when-unchanged'`, so it can never reach `bodyLabelFor`'s
|
|
187
|
+
// (`src/state.cts`) `STATE_BODY_LABEL_UNWIRED_ROW` throw in the first
|
|
188
|
+
// place; the absent label is inert, not a latent bug. Pinned by
|
|
189
|
+
// `tests/state.test.cjs`'s pre-existing
|
|
190
|
+
// `lastActivityLabelResolutionMatchesShippedBehavior`. Do NOT "tidy"
|
|
191
|
+
// this by adding a label — that would be shipping a policy change
|
|
192
|
+
// disguised as a consolidation, exactly the #3427 failure this epic is
|
|
193
|
+
// named after.
|
|
194
|
+
last_activity: {
|
|
195
|
+
type: 'string', cardinality: 'optional', source: 'body', preservation: 'derive',
|
|
196
|
+
bodySource: Object.freeze(['Last Activity', 'Last activity']), emitted: 'when-present',
|
|
197
|
+
}, // always refresh on transition
|
|
198
|
+
last_activity_desc: {
|
|
199
|
+
type: 'string', cardinality: 'optional', source: 'body', preservation: 'preserve-when-unchanged',
|
|
200
|
+
bodySource: Object.freeze(['Last Activity Description']), bodyLabel: 'Last Activity Description', emitted: 'when-present',
|
|
201
|
+
},
|
|
202
|
+
// Commit provenance (#2573) — ambient git read, recomputed on every write,
|
|
203
|
+
// exactly like last_updated. Never preserved: a stale stamp would claim
|
|
204
|
+
// STATE.md was written against a commit it wasn't.
|
|
205
|
+
state_head: {
|
|
206
|
+
type: 'string', cardinality: 'optional', source: 'free', preservation: 'derive', emitted: 'when-present',
|
|
207
|
+
}, // #2573
|
|
208
|
+
// Progress block (disk-derived, except the curated progress ratchet)
|
|
209
|
+
// mergeStrategy: 'progress-ratchet' — completed_plans/completed_phases
|
|
210
|
+
// only ever ratchet UP toward the derived value (#2969); everything
|
|
211
|
+
// else in the merge is either always-derived (#2440) or always-curated.
|
|
212
|
+
progress: {
|
|
213
|
+
type: 'object', cardinality: 'optional', source: 'curated', preservation: 'preserve-always',
|
|
214
|
+
mergeStrategy: 'progress-ratchet', emitted: 'when-present',
|
|
215
|
+
}, // #3242, #1446
|
|
216
|
+
'progress.total_phases': {
|
|
217
|
+
type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
|
|
218
|
+
},
|
|
219
|
+
'progress.completed_phases': {
|
|
220
|
+
type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
|
|
221
|
+
},
|
|
222
|
+
'progress.total_plans': {
|
|
223
|
+
type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
|
|
224
|
+
},
|
|
225
|
+
'progress.completed_plans': {
|
|
226
|
+
type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
|
|
227
|
+
},
|
|
228
|
+
'progress.percent': {
|
|
229
|
+
type: 'number', cardinality: 'optional', source: 'disk', preservation: 'derive', emitted: 'when-present',
|
|
230
|
+
},
|
|
231
|
+
}));
|