@opengsd/gsd-core 1.15.0 → 1.16.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 +85 -5
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +7 -2
- package/agents/gsd-planner.md +3 -3
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +42 -51
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +31 -9
- package/bin/install.js +126 -196
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +5 -0
- package/commands/gsd/capture.md +8 -4
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +5 -1
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +5 -1
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +6 -1
- package/commands/gsd/quick-batch.md +20 -8
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +5 -1
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +142 -56
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +78 -44
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +183 -103
- package/gsd-core/bin/lib/capability-validator.cjs +16 -0
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +173 -170
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config.cjs +43 -12
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +413 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/init.cjs +162 -96
- package/gsd-core/bin/lib/install-engine.cjs +19 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +19 -1
- package/gsd-core/bin/lib/model-resolver.cjs +18 -18
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id.cjs +1 -1
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +280 -80
- package/gsd-core/bin/lib/plan-document.cjs +93 -19
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +273 -40
- package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
- package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
- package/gsd-core/bin/lib/roadmap.cjs +169 -72
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state.cjs +214 -105
- package/gsd-core/bin/lib/surface.cjs +2 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
- package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
- package/gsd-core/bin/lib/uat.cjs +424 -7
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1165 -314
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +286 -567
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +10 -4
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +3 -2
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +3 -2
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous.md +28 -18
- package/gsd-core/workflows/check-todos.md +6 -5
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +5 -4
- package/gsd-core/workflows/code-review.md +84 -90
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +12 -7
- package/gsd-core/workflows/debug.md +30 -5
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +1 -1
- package/gsd-core/workflows/docs-update.md +4 -3
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +5 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- 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 +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase.md +78 -170
- package/gsd-core/workflows/execute-plan.md +13 -19
- package/gsd-core/workflows/explore.md +2 -2
- package/gsd-core/workflows/extract-learnings.md +3 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +3 -2
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +5 -3
- package/gsd-core/workflows/map-codebase.md +4 -3
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +10 -10
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
- package/gsd-core/workflows/plan-phase.md +27 -18
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +42 -31
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +3 -2
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +11 -12
- package/gsd-core/workflows/settings-advanced.md +1 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +12 -12
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +1 -1
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +1 -1
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +5 -4
- package/gsd-core/workflows/ui-review.md +4 -3
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/validate-phase.md +10 -12
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +68 -59
- package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +147 -16
- package/hooks/dist/gsd-statusline.js +15 -7
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +8 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +147 -16
- package/hooks/gsd-statusline.js +15 -7
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +21 -4
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +12 -8
- package/scripts/docs-guard-registry.cjs +6 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +10 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +20 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +34 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- 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-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +29 -0
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-test-file-count.allowlist.json +11 -1
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +5 -0
- package/skills/gsd-capture/SKILL.md +8 -4
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +5 -1
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +6 -1
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +20 -8
- package/skills/gsd-review/SKILL.md +5 -1
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
|
@@ -223,6 +223,8 @@
|
|
|
223
223
|
* Loop Extension Point Queries (ADR-857 phase 3c):
|
|
224
224
|
* loop render-hooks <point> Resolve + render active Capability hooks at a loop point
|
|
225
225
|
* [--config-dir <path>] [--runtime <r>] [--active-cap <capId>]
|
|
226
|
+
* [--after-fingerprint <phaseDir>] (#5105: skip verify:post
|
|
227
|
+
* steps whose declared artifact already exists in phaseDir)
|
|
226
228
|
* Returns JSON envelope { point, activeHooks, rendered }
|
|
227
229
|
* Valid points: discuss:pre/post, plan:pre/post,
|
|
228
230
|
* execute:pre/wave:pre/wave:post/post, verify:pre/post, ship:pre/post
|
|
@@ -245,6 +247,14 @@
|
|
|
245
247
|
* Import a GSD-2 (.gsd/) project back to GSD v1 (.planning/) format
|
|
246
248
|
*/
|
|
247
249
|
|
|
250
|
+
// #5183: Node's on-disk compile cache, so later runs reuse V8's compiled code
|
|
251
|
+
// for unchanged modules. Only as the entry (a test require() is unaffected),
|
|
252
|
+
// skipped under V8 coverage, optional (Bun stubs it), and never fatal.
|
|
253
|
+
// NODE_DISABLE_COMPILE_CACHE (any value) turns it off.
|
|
254
|
+
if (require.main === module && !process.env.NODE_V8_COVERAGE) {
|
|
255
|
+
try { require('node:module').enableCompileCache?.(); } catch { /* optimization only */ }
|
|
256
|
+
}
|
|
257
|
+
|
|
248
258
|
const fs = require('fs');
|
|
249
259
|
const path = require('path');
|
|
250
260
|
|
|
@@ -276,7 +286,7 @@ try {
|
|
|
276
286
|
|
|
277
287
|
const { ExitError, runMain, resolveContractVersion } = require('./lib/cli-exit.cjs');
|
|
278
288
|
const io = require('./lib/io.cjs');
|
|
279
|
-
const { error, ERROR_REASON, setJsonErrorMode, output, formatDiagnosticToken } = io;
|
|
289
|
+
const { error, ERROR_REASON, setJsonErrorMode, output, formatDiagnosticToken, captureStdoutSyncWrites, resolveAtFileOutput } = io;
|
|
280
290
|
const projectRoot = require('./lib/project-root.cjs');
|
|
281
291
|
// Resolve findProjectRoot lazily at call time rather than binding it at module
|
|
282
292
|
// load. It is sourced from project-root.cjs; a call-time lookup is robust
|
|
@@ -491,6 +501,7 @@ function dispatchCapabilityCommand({ command, args, cwd, raw, error, registry, r
|
|
|
491
501
|
_result = fn({ args, cwd, raw, error });
|
|
492
502
|
} catch (e) {
|
|
493
503
|
if (e instanceof ExitError) throw e; // intentional structured error from the router (honors --json-errors) — propagate untouched
|
|
504
|
+
if (isVerificationStatusError(e)) throw e; // #5118: translated once, centrally, by main()
|
|
494
505
|
error(
|
|
495
506
|
'capability command "' + command + '" router "' + entry.router + '" in module "' + entry.module + '" threw: ' + (e && e.message ? e.message : String(e)),
|
|
496
507
|
ERROR_REASON.SDK_FAIL_FAST,
|
|
@@ -606,6 +617,7 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
606
617
|
_result = fn({ args, cwd, raw, error });
|
|
607
618
|
} catch (e) {
|
|
608
619
|
if (e instanceof ExitError) throw e;
|
|
620
|
+
if (isVerificationStatusError(e)) throw e; // #5118: translated once, centrally, by main()
|
|
609
621
|
error(
|
|
610
622
|
'capability command "' + command + '" router "' + entry.router + '" in module "' + entry.module + '" threw: ' + (e && e.message ? e.message : String(e)),
|
|
611
623
|
ERROR_REASON.SDK_FAIL_FAST,
|
|
@@ -1512,7 +1524,10 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
1512
1524
|
error(`quick-tasks-append: STATE.md not found at ${statePath}`, ERROR_REASON.USAGE);
|
|
1513
1525
|
}
|
|
1514
1526
|
|
|
1515
|
-
|
|
1527
|
+
// #4905: an operator-facing date, so the local calendar day (#2136)
|
|
1528
|
+
// through the clock seam, which honors the GSD_NOW_MS pin (#474).
|
|
1529
|
+
const { realClock } = require('./lib/clock.cjs');
|
|
1530
|
+
const date = realClock.localToday();
|
|
1516
1531
|
const { execGit } = require('./lib/shell-command-projection.cjs');
|
|
1517
1532
|
const hashResult = execGit(['rev-parse', '--short', 'HEAD'], { cwd });
|
|
1518
1533
|
const commit = hashResult.exitCode === 0 && hashResult.stdout ? hashResult.stdout : '—';
|
|
@@ -2766,6 +2781,63 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
2766
2781
|
};
|
|
2767
2782
|
try {
|
|
2768
2783
|
nodeFs.mkdirSync(sentinelDir, { recursive: true });
|
|
2784
|
+
// Never write through a project-supplied .gsd symlink. Resolve the
|
|
2785
|
+
// directory as well so an ancestor redirect cannot escape this project.
|
|
2786
|
+
if (nodeFs.lstatSync(sentinelDir).isSymbolicLink() ||
|
|
2787
|
+
nodePath.relative(nodeFs.realpathSync(cwd), nodeFs.realpathSync(sentinelDir)) !== '.gsd') {
|
|
2788
|
+
throw new Error('sentinel directory is a symlink or resolves outside the project');
|
|
2789
|
+
}
|
|
2790
|
+
// User projects do not inherit gsd-core's root .gitignore. Ignore this
|
|
2791
|
+
// tool-owned directory locally before writing the first sentinel. An
|
|
2792
|
+
// ordinary ignore-file IO failure must not prevent recording isolation.
|
|
2793
|
+
const ignorePath = nodePath.join(sentinelDir, '.gitignore');
|
|
2794
|
+
try {
|
|
2795
|
+
nodeFs.writeFileSync(ignorePath, '*\n', { flag: 'wx' });
|
|
2796
|
+
} catch (ignoreError) {
|
|
2797
|
+
if (ignoreError.code === 'EEXIST') {
|
|
2798
|
+
// Keep a pre-existing ignore file. A bare '*' already ignores the
|
|
2799
|
+
// whole directory, including this file and the sentinel temp file.
|
|
2800
|
+
// Read through an fd opened without following links where supported;
|
|
2801
|
+
// replace by rename rather than append through the path, so a link
|
|
2802
|
+
// swapped in after the check cannot redirect a write elsewhere.
|
|
2803
|
+
let fd;
|
|
2804
|
+
let ignoreTmpPath;
|
|
2805
|
+
try {
|
|
2806
|
+
if (nodeFs.lstatSync(ignorePath).isSymbolicLink()) {
|
|
2807
|
+
throw new Error('sentinel ignore file is a symlink');
|
|
2808
|
+
}
|
|
2809
|
+
fd = nodeFs.openSync(ignorePath, nodeFs.constants.O_RDONLY | (nodeFs.constants.O_NOFOLLOW || 0));
|
|
2810
|
+
if (!nodeFs.fstatSync(fd).isFile()) throw new Error('sentinel ignore file is not a regular file');
|
|
2811
|
+
const current = nodeFs.readFileSync(fd, 'utf8');
|
|
2812
|
+
nodeFs.closeSync(fd);
|
|
2813
|
+
fd = undefined;
|
|
2814
|
+
if (!current.includes('# gsd-core dispatch sentinel') && !/^\*\r?$/m.test(current)) {
|
|
2815
|
+
const additionalRules = `${current.endsWith('\n') ? '' : '\n'}# gsd-core dispatch sentinel\n` +
|
|
2816
|
+
'/dispatch-isolation-sentinel.json\n' +
|
|
2817
|
+
'/dispatch-isolation-sentinel.json.tmp-*\n' +
|
|
2818
|
+
'/.gitignore\n';
|
|
2819
|
+
ignoreTmpPath = `${ignorePath}.tmp-${process.pid}-${require('crypto').randomBytes(6).toString('hex')}`;
|
|
2820
|
+
nodeFs.writeFileSync(ignoreTmpPath, current + additionalRules, { flag: 'wx' });
|
|
2821
|
+
if (nodeFs.lstatSync(ignorePath).isSymbolicLink()) {
|
|
2822
|
+
throw new Error('sentinel ignore file is a symlink');
|
|
2823
|
+
}
|
|
2824
|
+
nodeFs.renameSync(ignoreTmpPath, ignorePath);
|
|
2825
|
+
ignoreTmpPath = null;
|
|
2826
|
+
}
|
|
2827
|
+
} catch (existingIgnoreError) {
|
|
2828
|
+
if (existingIgnoreError.code === 'ELOOP' || existingIgnoreError.message === 'sentinel ignore file is a symlink') {
|
|
2829
|
+
throw new Error('sentinel ignore file is a symlink');
|
|
2830
|
+
}
|
|
2831
|
+
// Ignore seeding is best-effort; the sentinel is still useful if
|
|
2832
|
+
// this path cannot be read or updated.
|
|
2833
|
+
} finally {
|
|
2834
|
+
if (fd !== undefined) nodeFs.closeSync(fd);
|
|
2835
|
+
if (ignoreTmpPath) {
|
|
2836
|
+
try { nodeFs.unlinkSync(ignoreTmpPath); } catch (_) { /* best-effort cleanup */ }
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2769
2841
|
// Atomic write: unique temp file + rename, so a concurrent reader (a
|
|
2770
2842
|
// guard hook firing mid-write) never observes a partially-written
|
|
2771
2843
|
// sentinel. Unique per-process+time so concurrent orchestrator-worktree
|
|
@@ -3076,8 +3148,13 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
3076
3148
|
const coverage = require('./lib/coverage.cjs');
|
|
3077
3149
|
const options = parseNamedArgsOrExit(args, { valueFlags: ['summary', 'file'], positionals: 2 }, error);
|
|
3078
3150
|
coverage.cmdClassify(cwd, options, raw);
|
|
3151
|
+
} else if (subcommand === 'complete-session') {
|
|
3152
|
+
const uat = require('./lib/uat.cjs');
|
|
3153
|
+
const uatPath = args[2];
|
|
3154
|
+
const options = parseNamedArgsOrExit(args, { valueFlags: ['message'], positionals: 3 }, error);
|
|
3155
|
+
return uat.cmdUatCompleteSession(cwd, uatPath, { message: options.message }, raw);
|
|
3079
3156
|
} else {
|
|
3080
|
-
error('Unknown uat subcommand. Available: render-checkpoint, classify-coverage', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
3157
|
+
error('Unknown uat subcommand. Available: render-checkpoint, classify-coverage, complete-session', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
3081
3158
|
}
|
|
3082
3159
|
}
|
|
3083
3160
|
|
|
@@ -3175,10 +3252,27 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
3175
3252
|
}
|
|
3176
3253
|
loopRuntime = value;
|
|
3177
3254
|
}
|
|
3255
|
+
// --after-fingerprint <phaseDir> (#5105 R2): gate out verify:post
|
|
3256
|
+
// steps whose declared artifact already exists in phaseDir.
|
|
3257
|
+
let loopAfterFingerprint = undefined;
|
|
3258
|
+
const afterFpEqArg = args.find(arg => arg.startsWith('--after-fingerprint='));
|
|
3259
|
+
const afterFpIdx = args.indexOf('--after-fingerprint');
|
|
3260
|
+
if (afterFpEqArg) {
|
|
3261
|
+
const value = afterFpEqArg.slice('--after-fingerprint='.length).trim();
|
|
3262
|
+
if (!value) error('Missing value for --after-fingerprint', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
3263
|
+
loopAfterFingerprint = value;
|
|
3264
|
+
} else if (afterFpIdx !== -1) {
|
|
3265
|
+
const value = args[afterFpIdx + 1];
|
|
3266
|
+
if (!value || value.startsWith('--')) {
|
|
3267
|
+
error('Missing value for --after-fingerprint', ERROR_REASON ? ERROR_REASON.USAGE : undefined);
|
|
3268
|
+
}
|
|
3269
|
+
loopAfterFingerprint = value;
|
|
3270
|
+
}
|
|
3178
3271
|
loopResolver.cmdLoopRenderHooks(cwd, args[2], raw, {
|
|
3179
3272
|
configDir: loopConfigDir ? path.resolve(loopConfigDir) : undefined,
|
|
3180
3273
|
activeCap: loopActiveCap,
|
|
3181
3274
|
runtime: loopRuntime,
|
|
3275
|
+
afterFingerprint: loopAfterFingerprint,
|
|
3182
3276
|
});
|
|
3183
3277
|
} else {
|
|
3184
3278
|
error(
|
|
@@ -3720,13 +3814,14 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
3720
3814
|
|| relPath.startsWith('agents/')
|
|
3721
3815
|
|| relPath.startsWith('commands/');
|
|
3722
3816
|
if (isFrontmatterSurface) {
|
|
3723
|
-
|
|
3817
|
+
// The block is the one the one fence owner finds (found while implementing #5105).
|
|
3818
|
+
const found = frontmatter.frontmatterRegion(content);
|
|
3724
3819
|
const missingFields = [];
|
|
3725
|
-
if (!
|
|
3820
|
+
if (!found || !found.terminated) {
|
|
3726
3821
|
missingFields.push('name', 'description');
|
|
3727
3822
|
} else {
|
|
3728
|
-
if (!/^name:\s*\S/m.test(
|
|
3729
|
-
if (!/^description:\s*\S/m.test(
|
|
3823
|
+
if (!/^name:\s*\S/m.test(found.region)) missingFields.push('name');
|
|
3824
|
+
if (!/^description:\s*\S/m.test(found.region)) missingFields.push('description');
|
|
3730
3825
|
}
|
|
3731
3826
|
if (missingFields.length > 0) {
|
|
3732
3827
|
warnings.push({
|
|
@@ -4510,7 +4605,19 @@ function dispatchOverlayCapabilityCommand({ command, args, cwd, raw, error, load
|
|
|
4510
4605
|
}
|
|
4511
4606
|
|
|
4512
4607
|
const roadmapStatus = matchedRow.Status;
|
|
4513
|
-
|
|
4608
|
+
// #5060: normalize the raw Status cell through the Phase Status
|
|
4609
|
+
// Module's `matchRoadmapStatusCell` owner before comparing ranks —
|
|
4610
|
+
// this recognizes a cell with trailing operator prose after its
|
|
4611
|
+
// leading token (e.g. "Complete — shipped") the same way the
|
|
4612
|
+
// ROADMAP writers/readers already do, rather than requiring the
|
|
4613
|
+
// WHOLE cell to equal a bare token. The raw `roadmapStatus` in the
|
|
4614
|
+
// output stays untouched — only the comparison input changes.
|
|
4615
|
+
const { matchRoadmapStatusCell } = require('./lib/phase-status.cjs');
|
|
4616
|
+
const matched = matchRoadmapStatusCell(roadmapStatus);
|
|
4617
|
+
const result = comparePhaseStatus({
|
|
4618
|
+
stateStatus,
|
|
4619
|
+
roadmapStatus: matched ? matched.token.toLowerCase() : roadmapStatus,
|
|
4620
|
+
});
|
|
4514
4621
|
output({
|
|
4515
4622
|
verdict: result.verdict,
|
|
4516
4623
|
phase,
|
|
@@ -5092,6 +5199,28 @@ function resolveMainWorktreeCwd(cwd, deps = {}) {
|
|
|
5092
5199
|
return worktreeRoot;
|
|
5093
5200
|
}
|
|
5094
5201
|
|
|
5202
|
+
// ─── #5118: an out-of-set verification status thrown past a command ─────────
|
|
5203
|
+
// ADR-5057 Phase 4 closed the verification-status vocabulary: a
|
|
5204
|
+
// *-VERIFICATION.md whose frontmatter `status` is outside `passed |
|
|
5205
|
+
// gaps_found | human_needed` is a hard error (verification.cjs's
|
|
5206
|
+
// VerificationStatusError). A command that reads one report directly
|
|
5207
|
+
// (`verification status`, `phase uat-passed`, `phase complete`) lets it throw;
|
|
5208
|
+
// aggregates carry it in their own results and fail themselves. Either way
|
|
5209
|
+
// the CLI fails through the owner's `failOnVerificationStatusError` — the
|
|
5210
|
+
// error's own message and its own `.reason`; nothing here restates either.
|
|
5211
|
+
function isVerificationStatusError(err) {
|
|
5212
|
+
return err instanceof verification.VerificationStatusError;
|
|
5213
|
+
}
|
|
5214
|
+
|
|
5215
|
+
async function captureTranslatingVerificationStatus(run) {
|
|
5216
|
+
try {
|
|
5217
|
+
return await captureStdoutSyncWrites(run);
|
|
5218
|
+
} catch (err) {
|
|
5219
|
+
if (isVerificationStatusError(err)) verification.failOnVerificationStatusError(err);
|
|
5220
|
+
throw err;
|
|
5221
|
+
}
|
|
5222
|
+
}
|
|
5223
|
+
|
|
5095
5224
|
async function main() {
|
|
5096
5225
|
let args = process.argv.slice(2);
|
|
5097
5226
|
|
|
@@ -5357,7 +5486,7 @@ async function main() {
|
|
|
5357
5486
|
// themselves JSON text, so resolving late would make every large result a
|
|
5358
5487
|
// false "output was not JSON" (negative space N8).
|
|
5359
5488
|
if (pickField) {
|
|
5360
|
-
const captured = await
|
|
5489
|
+
const captured = await captureTranslatingVerificationStatus(async () => {
|
|
5361
5490
|
await runCommand(command, args, cwd, raw, defaultValue, originalCommand, workstreamContext, preWorktreeRemapCwd);
|
|
5362
5491
|
});
|
|
5363
5492
|
const resolved = resolveAtFileOutput(captured);
|
|
@@ -5389,58 +5518,15 @@ async function main() {
|
|
|
5389
5518
|
// already resolves this, but the normal path wrote @file: to stdout, forcing
|
|
5390
5519
|
// every workflow to have a bash-specific `if [[ "$INIT" == @file:* ]]` check
|
|
5391
5520
|
// that breaks on PowerShell and other non-bash shells.
|
|
5392
|
-
const captured = await
|
|
5521
|
+
const captured = await captureTranslatingVerificationStatus(async () => {
|
|
5393
5522
|
await runCommand(command, args, cwd, raw, defaultValue, originalCommand, workstreamContext, preWorktreeRemapCwd);
|
|
5394
5523
|
});
|
|
5395
5524
|
fs.writeSync(1, resolveAtFileOutput(captured));
|
|
5396
5525
|
}
|
|
5397
5526
|
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
fs.writeSync = function patchedWriteSync(fd, data, ...rest) {
|
|
5403
|
-
if (fd === 1) {
|
|
5404
|
-
if (Buffer.isBuffer(data)) {
|
|
5405
|
-
captured += data.toString('utf-8');
|
|
5406
|
-
return data.length;
|
|
5407
|
-
}
|
|
5408
|
-
const text = String(data);
|
|
5409
|
-
captured += text;
|
|
5410
|
-
let encoding = 'utf-8';
|
|
5411
|
-
if (typeof rest[1] === 'string') encoding = rest[1];
|
|
5412
|
-
return Buffer.byteLength(text, encoding);
|
|
5413
|
-
}
|
|
5414
|
-
return originalWriteSync.call(fs, fd, data, ...rest);
|
|
5415
|
-
};
|
|
5416
|
-
|
|
5417
|
-
const restore = () => {
|
|
5418
|
-
fs.writeSync = originalWriteSync;
|
|
5419
|
-
};
|
|
5420
|
-
|
|
5421
|
-
return Promise.resolve()
|
|
5422
|
-
.then(() => run())
|
|
5423
|
-
.then(() => {
|
|
5424
|
-
restore();
|
|
5425
|
-
return captured;
|
|
5426
|
-
}, (err) => {
|
|
5427
|
-
restore();
|
|
5428
|
-
// The wrapped command may have written to stdout BEFORE it threw — e.g. a --raw
|
|
5429
|
-
// command that emits a JSON result/error envelope and THEN throws ExitError to set a
|
|
5430
|
-
// non-zero exit code (capability set/disable on an unknown id). Without this flush that
|
|
5431
|
-
// captured output is silently discarded (the success-path flush at the call site never
|
|
5432
|
-
// runs on a throw). Emit it now; the error still propagates so the exit code is preserved.
|
|
5433
|
-
if (captured) {
|
|
5434
|
-
try { originalWriteSync.call(fs, 1, resolveAtFileOutput(captured)); } catch { /* best-effort flush */ }
|
|
5435
|
-
}
|
|
5436
|
-
throw err;
|
|
5437
|
-
});
|
|
5438
|
-
}
|
|
5439
|
-
|
|
5440
|
-
function resolveAtFileOutput(captured) {
|
|
5441
|
-
if (!captured.startsWith('@file:')) return captured;
|
|
5442
|
-
return fs.readFileSync(captured.slice(6), 'utf-8');
|
|
5443
|
-
}
|
|
5527
|
+
// captureStdoutSyncWrites and resolveAtFileOutput moved to src/io.cts
|
|
5528
|
+
// (gsd-core/bin/lib/io.cjs) — the ONE shared pair (#5105 S9, review finding
|
|
5529
|
+
// 7), also used by uat.cts's cmdUatCompleteSession.
|
|
5444
5530
|
|
|
5445
5531
|
// A plain object root/intermediate value — everything else (null, an array,
|
|
5446
5532
|
// a number, a string, a boolean) is treated as non-object for NAMED-key
|
|
@@ -395,6 +395,14 @@ function parseCliWorkstream(args) {
|
|
|
395
395
|
args: args.slice(),
|
|
396
396
|
};
|
|
397
397
|
}
|
|
398
|
+
function workstreamDirectoryExists(cwd, name) {
|
|
399
|
+
try {
|
|
400
|
+
return node_fs_1.default.readdirSync(node_path_1.default.join(planningRoot(cwd), 'workstreams')).includes(name);
|
|
401
|
+
}
|
|
402
|
+
catch {
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
398
406
|
function resolveActiveWorkstream(cwd, args, env = process.env, deps = {}) {
|
|
399
407
|
const parsed = parseCliWorkstream(args);
|
|
400
408
|
const getStored = deps.getStored || ((dir) => getActiveWorkstream(dir, deps));
|
|
@@ -415,6 +423,13 @@ function resolveActiveWorkstream(cwd, args, env = process.env, deps = {}) {
|
|
|
415
423
|
if (ws && !validateWorkstreamName(ws)) {
|
|
416
424
|
throw new Error('Invalid workstream name: must be alphanumeric, hyphens, underscores, or dots');
|
|
417
425
|
}
|
|
426
|
+
// #4772: a reserved name (`none`) is rejected from every source (--ws, env,
|
|
427
|
+
// pointer) unless a directory of that name already exists (grandfathered).
|
|
428
|
+
// Grandfathering compares the directory listing exactly, so a case-insensitive
|
|
429
|
+
// filesystem (macOS, Windows) cannot let `NONE` through on the strength of `none`.
|
|
430
|
+
if (ws && (0, workstream_name_policy_cjs_1.isReservedWorkstreamName)(ws) && !workstreamDirectoryExists(cwd, ws)) {
|
|
431
|
+
throw new Error((0, workstream_name_policy_cjs_1.reservedWorkstreamNameMessage)(ws, source));
|
|
432
|
+
}
|
|
418
433
|
return {
|
|
419
434
|
ws,
|
|
420
435
|
source,
|
|
@@ -483,7 +483,10 @@ function agentFileExists(agentsDir, name, runtime) {
|
|
|
483
483
|
// kimi requires BOTH the persona yaml and the prompt md (same as checkAgentsInstalled).
|
|
484
484
|
const kimiYaml = node_path_1.default.join(agentsDir, 'subagents', `${name}.yaml`);
|
|
485
485
|
const kimiPrompt = node_path_1.default.join(agentsDir, 'subagents', `${name}.md`);
|
|
486
|
-
|
|
486
|
+
// #5169: descriptor-declared (`hostBehaviors.requiresSubagentPair`); a label
|
|
487
|
+
// that is not a registered runtime declares nothing, so it never requires the pair.
|
|
488
|
+
return (0, runtime_name_policy_cjs_1.hostBehaviorsFor)(runtime).requiresSubagentPair === true
|
|
489
|
+
&& node_fs_1.default.existsSync(kimiYaml) && node_fs_1.default.existsSync(kimiPrompt);
|
|
487
490
|
}
|
|
488
491
|
/**
|
|
489
492
|
* Resolve a per-plan `agent_hint` specialist name to a dispatchable subagent
|
|
@@ -25,7 +25,7 @@ const coreUtils = require("./core-utils.cjs");
|
|
|
25
25
|
const { normalizeLineEndings } = coreUtils;
|
|
26
26
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
27
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
28
|
-
const { planningDir, quickDirFrom, todosDir } = planningWorkspace;
|
|
28
|
+
const { planningDir, quickDirFrom, debugDir, todosDir } = planningWorkspace;
|
|
29
29
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
30
30
|
const frontmatter = require("./frontmatter.cjs");
|
|
31
31
|
// #4378 (roll-in): scanSeeds publishes the SAME canonical seed identity the
|
|
@@ -33,7 +33,7 @@ const frontmatter = require("./frontmatter.cjs");
|
|
|
33
33
|
// does not require this module, so the edge is acyclic.
|
|
34
34
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
35
35
|
const commandsModule = require("./commands.cjs");
|
|
36
|
-
const { extractFrontmatter,
|
|
36
|
+
const { extractFrontmatter, frontmatterBlock, spliceFrontmatter, isFrontmatterWriteRefusal } = frontmatter;
|
|
37
37
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
38
|
const phaseIdMod = require("./phase-id.cjs");
|
|
39
39
|
const { PHASE_NUMBER_TOKEN_SOURCE, scopeToPhase } = phaseIdMod;
|
|
@@ -46,6 +46,11 @@ const shell_command_projection_cjs_2 = require("./shell-command-projection.cjs")
|
|
|
46
46
|
const io = require("./io.cjs");
|
|
47
47
|
const { output, error: ioError } = io;
|
|
48
48
|
const command_arg_projection_cjs_1 = require("./command-arg-projection.cjs");
|
|
49
|
+
// #5118: the verification-status owner's report reader and closed enum.
|
|
50
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
51
|
+
const verificationMod = require("./verification.cjs");
|
|
52
|
+
const { reportStatusOf, VERIFICATION_STATUS, VerificationStatusError } = verificationMod;
|
|
53
|
+
const clock_cjs_1 = require("./clock.cjs");
|
|
49
54
|
// The SCOPE BOUNDARY convention's filename (`agents/gsd-executor.md`), shared
|
|
50
55
|
// verbatim with the #2287 phase-boundary reader in `uat.cts`.
|
|
51
56
|
const DEFERRED_ITEMS_FILENAME = 'deferred-items.md';
|
|
@@ -257,19 +262,27 @@ function deriveOpenQuestionsDigest(questions) {
|
|
|
257
262
|
}
|
|
258
263
|
// ─── scanDebugSessions ────────────────────────────────────────────────────────
|
|
259
264
|
/**
|
|
260
|
-
*
|
|
265
|
+
* #4869: gsd-debugger's archive_session step appends every resolved session to
|
|
266
|
+
* this file in the debug directory. It is a document, not a session: it has no
|
|
267
|
+
* frontmatter, so its status would derive `unknown` and read as open forever.
|
|
268
|
+
* Excluded by this fixed name only. Any other file with missing or unparseable
|
|
269
|
+
* frontmatter is still an open session. Every shipped debug-directory reader
|
|
270
|
+
* excludes the same name from active sessions (#5011).
|
|
271
|
+
*/
|
|
272
|
+
const DEBUG_KNOWLEDGE_BASE_FILENAME = 'knowledge-base.md';
|
|
273
|
+
/**
|
|
274
|
+
* Scan the root-scoped .planning/debug/ directory supplied by the caller for open sessions.
|
|
261
275
|
* Open = status NOT in ['resolved', 'complete'].
|
|
262
|
-
* Ignores the resolved/ subdirectory.
|
|
276
|
+
* Ignores the resolved/ subdirectory and the debugger's knowledge base.
|
|
263
277
|
*/
|
|
264
|
-
function scanDebugSessions(
|
|
265
|
-
|
|
266
|
-
if (!node_fs_1.default.existsSync(debugDir))
|
|
278
|
+
function scanDebugSessions(debugPath) {
|
|
279
|
+
if (!node_fs_1.default.existsSync(debugPath))
|
|
267
280
|
return { items: [], acknowledged: 0 };
|
|
268
281
|
const results = [];
|
|
269
282
|
let acknowledged = 0;
|
|
270
283
|
let files;
|
|
271
284
|
try {
|
|
272
|
-
files = node_fs_1.default.readdirSync(
|
|
285
|
+
files = node_fs_1.default.readdirSync(debugPath, { withFileTypes: true });
|
|
273
286
|
}
|
|
274
287
|
catch {
|
|
275
288
|
return { items: [{ scan_error: true, slug: '', status: '', updated: '', hypothesis: '' }], acknowledged: 0 };
|
|
@@ -279,10 +292,12 @@ function scanDebugSessions(planDir) {
|
|
|
279
292
|
continue;
|
|
280
293
|
if (!entry.name.endsWith('.md'))
|
|
281
294
|
continue;
|
|
282
|
-
|
|
295
|
+
if (entry.name === DEBUG_KNOWLEDGE_BASE_FILENAME)
|
|
296
|
+
continue;
|
|
297
|
+
const filePath = node_path_1.default.join(debugPath, entry.name);
|
|
283
298
|
let safeFilePath;
|
|
284
299
|
try {
|
|
285
|
-
safeFilePath = (0, security_cjs_1.requireSafePath)(filePath,
|
|
300
|
+
safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, node_path_1.default.dirname(debugPath), 'debug session file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
|
|
286
301
|
}
|
|
287
302
|
catch {
|
|
288
303
|
continue;
|
|
@@ -291,9 +306,9 @@ function scanDebugSessions(planDir) {
|
|
|
291
306
|
// document at this read boundary, same seam as `src/uat.cts`'s
|
|
292
307
|
// `readNormalizedDocument` — `platformReadSync` performs no line-ending
|
|
293
308
|
// normalization itself, and extractFrontmatter/status-derivation below
|
|
294
|
-
// degrade a lone-CR file's frontmatter to `unknown`, which
|
|
295
|
-
//
|
|
296
|
-
//
|
|
309
|
+
// degrade a lone-CR file's frontmatter to `unknown`, which this scan
|
|
310
|
+
// reports as an open session (#4869) — so an un-normalized resolved
|
|
311
|
+
// session would resurface as open.
|
|
297
312
|
const rawContent = (0, shell_command_projection_cjs_1.platformReadSync)(safeFilePath);
|
|
298
313
|
if (rawContent === null)
|
|
299
314
|
continue;
|
|
@@ -591,9 +606,11 @@ function scanTodos(todosBase) {
|
|
|
591
606
|
}
|
|
592
607
|
const displayFiles = openFiles.slice(0, 5);
|
|
593
608
|
for (const { entry, content, fm } of displayFiles) {
|
|
594
|
-
// Extract first line of body after frontmatter
|
|
595
|
-
|
|
596
|
-
const
|
|
609
|
+
// Extract first line of body after frontmatter — the block `extractFrontmatter` read above
|
|
610
|
+
// (the one fence owner), so a `---` inside a value cannot end it early.
|
|
611
|
+
const block = frontmatterBlock(content);
|
|
612
|
+
const todoBody = block ? block.rest : content;
|
|
613
|
+
const firstLine = (0, text_lines_cjs_1.splitLines)(todoBody.trim())[0] || '';
|
|
597
614
|
const summary = (0, security_cjs_1.sanitizeForDisplay)(firstLine.slice(0, 100));
|
|
598
615
|
results.push({
|
|
599
616
|
filename: (0, security_cjs_1.sanitizeLabel)(entry.name),
|
|
@@ -816,9 +833,9 @@ function scanUatGaps(planDir, cwd) {
|
|
|
816
833
|
// document at this read boundary, same seam as `src/uat.cts`'s
|
|
817
834
|
// `readNormalizedDocument` — `platformReadSync` performs no line-ending
|
|
818
835
|
// normalization itself, and extractFrontmatter/status-derivation below
|
|
819
|
-
// degrade a lone-CR file's frontmatter to `unknown`, which
|
|
820
|
-
//
|
|
821
|
-
//
|
|
836
|
+
// degrade a lone-CR file's frontmatter to `unknown`, which this scan
|
|
837
|
+
// reports as an open gap unless `result: all_pass` (#4869 triage) — so
|
|
838
|
+
// an un-normalized terminal UAT would resurface as open.
|
|
822
839
|
const rawContent = (0, shell_command_projection_cjs_1.platformReadSync)(safeFilePath);
|
|
823
840
|
if (rawContent === null)
|
|
824
841
|
continue;
|
|
@@ -897,8 +914,10 @@ function scanVerificationGaps(planDir, cwd) {
|
|
|
897
914
|
continue;
|
|
898
915
|
const content = normalizeLineEndings(rawContent);
|
|
899
916
|
const fm = extractFrontmatter(content, safeFilePath);
|
|
900
|
-
|
|
901
|
-
|
|
917
|
+
// #5118: the owner's report reader judges `status` — exact match, no
|
|
918
|
+
// case folding; an out-of-set value throws VerificationStatusError.
|
|
919
|
+
const status = reportStatusOf(fm, safeFilePath);
|
|
920
|
+
if (status !== VERIFICATION_STATUS.GAPS_FOUND && status !== VERIFICATION_STATUS.HUMAN_NEEDED)
|
|
902
921
|
continue;
|
|
903
922
|
if (isAuditItemAcknowledged(fm, { snapshotKey: 'status', currentValue: status })) {
|
|
904
923
|
acknowledged++;
|
|
@@ -1074,8 +1093,10 @@ function scanDeferredItems(planDir, cwd) {
|
|
|
1074
1093
|
function auditOpenArtifacts(cwd) {
|
|
1075
1094
|
const planDir = planningDir(cwd);
|
|
1076
1095
|
const debugSessions = (() => {
|
|
1096
|
+
// #5042: debug sessions are shared project state; workflow writers use
|
|
1097
|
+
// the root .planning/debug directory even when a workstream is active.
|
|
1077
1098
|
try {
|
|
1078
|
-
return scanDebugSessions(
|
|
1099
|
+
return scanDebugSessions(debugDir(cwd));
|
|
1079
1100
|
}
|
|
1080
1101
|
catch {
|
|
1081
1102
|
return { items: [{ scan_error: true, slug: '', status: '', updated: '', hypothesis: '' }], acknowledged: 0 };
|
|
@@ -1098,7 +1119,7 @@ function auditOpenArtifacts(cwd) {
|
|
|
1098
1119
|
}
|
|
1099
1120
|
})();
|
|
1100
1121
|
const todos = (() => {
|
|
1101
|
-
// #4256:
|
|
1122
|
+
// #4256: todos are shared project state, like debug sessions (#5042),
|
|
1102
1123
|
// so the close gate reads todosDir(cwd) (the root), not the workstream-
|
|
1103
1124
|
// scoped planDir every other scan below receives. Reading planDir here
|
|
1104
1125
|
// made audit-open print "All artifact types clear. Safe to proceed."
|
|
@@ -1130,7 +1151,11 @@ function auditOpenArtifacts(cwd) {
|
|
|
1130
1151
|
try {
|
|
1131
1152
|
return scanVerificationGaps(planDir, cwd);
|
|
1132
1153
|
}
|
|
1133
|
-
catch {
|
|
1154
|
+
catch (err) {
|
|
1155
|
+
// #5118: an out-of-set report status is the owner's hard error, not a
|
|
1156
|
+
// scan failure to fold into a sentinel — let it reach the CLI seam.
|
|
1157
|
+
if (err instanceof VerificationStatusError)
|
|
1158
|
+
throw err;
|
|
1134
1159
|
return { items: [{ scan_error: true, phase: '', file: '', status: '' }], acknowledged: 0 };
|
|
1135
1160
|
}
|
|
1136
1161
|
})();
|
|
@@ -1399,6 +1424,26 @@ function resolvePhaseTargetDir(planDir, cwd, phase, archivedMilestone) {
|
|
|
1399
1424
|
* artifact identifier that resolves outside the project is refused before
|
|
1400
1425
|
* any read or write is attempted.
|
|
1401
1426
|
*/
|
|
1427
|
+
/**
|
|
1428
|
+
* #4802: splice the acknowledgement marker, surfacing `spliceFrontmatter`'s
|
|
1429
|
+
* write refusal (the one owner of that decision) as an `ioError` naming the
|
|
1430
|
+
* file — an unparseable frontmatter block is NOT an empty one, and splicing
|
|
1431
|
+
* over it would discard every field the author actually wrote.
|
|
1432
|
+
*/
|
|
1433
|
+
function spliceAcknowledgement(content, fm, fileLabel) {
|
|
1434
|
+
try {
|
|
1435
|
+
return spliceFrontmatter(content, fm);
|
|
1436
|
+
}
|
|
1437
|
+
catch (err) {
|
|
1438
|
+
if (!isFrontmatterWriteRefusal(err))
|
|
1439
|
+
throw err;
|
|
1440
|
+
if (err.code === 'FRONTMATTER_UNPARSEABLE') {
|
|
1441
|
+
ioError(`refusing to acknowledge — the frontmatter of "${fileLabel}" is not parseable YAML (splicing would discard every other frontmatter field); fix the YAML syntax error first, then re-run`);
|
|
1442
|
+
}
|
|
1443
|
+
ioError(`refusing to acknowledge "${fileLabel}" — ${err.message}`);
|
|
1444
|
+
throw err; // unreachable — ioError throws
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1402
1447
|
function cmdAuditAcknowledge(cwd, args, raw) {
|
|
1403
1448
|
// args already has the family + subcommand tokens stripped by the caller
|
|
1404
1449
|
// (audit-command-router.cts:147 passes `hubArgs.slice(2)`), so validation
|
|
@@ -1418,7 +1463,9 @@ function cmdAuditAcknowledge(cwd, args, raw) {
|
|
|
1418
1463
|
// All declared flags above are value flags, so each resolves to `string |
|
|
1419
1464
|
// null` at runtime; the cast narrows away the `boolean` arm of
|
|
1420
1465
|
// ParsedNamedArgs's value type that this call site never produces.
|
|
1421
|
-
|
|
1466
|
+
// #4905: without --at, the operator-facing local calendar day (#2136)
|
|
1467
|
+
// through the clock seam, which honors the GSD_NOW_MS pin (#474).
|
|
1468
|
+
const at = atFlag || clock_cjs_1.realClock.localToday();
|
|
1422
1469
|
const planDir = planningDir(cwd);
|
|
1423
1470
|
const markerBase = { milestone: milestone, at };
|
|
1424
1471
|
// #3078-CR MEDIUM 2: every `fs.readFileSync` in this function (below, and
|
|
@@ -1479,13 +1526,6 @@ function cmdAuditAcknowledge(cwd, args, raw) {
|
|
|
1479
1526
|
}
|
|
1480
1527
|
const content = node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
|
|
1481
1528
|
const fm = extractFrontmatter(content, safeFilePath);
|
|
1482
|
-
// #4802: an unparseable frontmatter block is NOT an empty one — splicing
|
|
1483
|
-
// the marker-marked object over the file would discard every field the
|
|
1484
|
-
// author actually wrote. Refuse and name the file (the write-path
|
|
1485
|
-
// counterpart of the read-side FRONTMATTER_UNPARSEABLE contract).
|
|
1486
|
-
if (fm[FRONTMATTER_UNPARSEABLE] === true) {
|
|
1487
|
-
ioError(`refusing to acknowledge — the frontmatter of "${file}" is not parseable YAML (splicing would discard every other frontmatter field); fix the YAML syntax error first, then re-run`);
|
|
1488
|
-
}
|
|
1489
1529
|
// Mixed-frame fix (security review, 4th instance on this branch): the
|
|
1490
1530
|
// splice above and below stays keyed to RAW `content` (raw byte offsets
|
|
1491
1531
|
// must not shift), but `scanUatGaps`/`scanContextQuestions` now derive
|
|
@@ -1516,7 +1556,7 @@ function cmdAuditAcknowledge(cwd, args, raw) {
|
|
|
1516
1556
|
currentValue = deriveOpenQuestionsDigest(deriveOpenQuestions(normalizedContent, fm));
|
|
1517
1557
|
}
|
|
1518
1558
|
fm.audit_acknowledged = { ...markerBase, [snapshotKey]: currentValue };
|
|
1519
|
-
const newContent =
|
|
1559
|
+
const newContent = spliceAcknowledgement(content, fm, file);
|
|
1520
1560
|
(0, shell_command_projection_cjs_2.platformWriteSync)(safeFilePath, newContent);
|
|
1521
1561
|
output({ acknowledged: true, category, phase, file, [snapshotKey]: currentValue }, raw, 'true');
|
|
1522
1562
|
return;
|
|
@@ -1645,18 +1685,12 @@ function cmdAuditAcknowledge(cwd, args, raw) {
|
|
|
1645
1685
|
return; // unreachable — ioError throws — satisfies TS control-flow analysis
|
|
1646
1686
|
}
|
|
1647
1687
|
const presenceOnly = category === 'todos';
|
|
1648
|
-
const
|
|
1649
|
-
|
|
1650
|
-
// createIfMissing never reaches this extract (it only fires when the file is
|
|
1651
|
-
// absent), so an existing file with broken YAML refuses instead of splicing
|
|
1652
|
-
// a near-empty object over every field the author wrote.
|
|
1653
|
-
if (!createIfMissing && fm[FRONTMATTER_UNPARSEABLE] === true) {
|
|
1654
|
-
ioError(`refusing to acknowledge — the frontmatter of "${safeFilePath}" is not parseable YAML (splicing would discard every other frontmatter field); fix the YAML syntax error first, then re-run`);
|
|
1655
|
-
}
|
|
1688
|
+
const existingContent = createIfMissing ? '' : node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
|
|
1689
|
+
const fm = createIfMissing ? fmForCreate : extractFrontmatter(existingContent, safeFilePath);
|
|
1656
1690
|
fm.audit_acknowledged = presenceOnly ? { ...markerBase } : { ...markerBase, [snapshotKey]: currentValue };
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1691
|
+
// #4802: createIfMissing splices into '' (no block to refuse on); an existing
|
|
1692
|
+
// file with broken YAML is refused by `spliceAcknowledgement`.
|
|
1693
|
+
const newContent = spliceAcknowledgement(existingContent, fm, safeFilePath);
|
|
1660
1694
|
(0, shell_command_projection_cjs_2.platformWriteSync)(safeFilePath, newContent);
|
|
1661
1695
|
output({ acknowledged: true, category, ...(presenceOnly ? {} : { [snapshotKey]: currentValue }) }, raw, 'true');
|
|
1662
1696
|
}
|