@opengsd/gsd-core 1.17.0 → 1.19.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 +2 -1
- package/.opencode/plugins/gsd-core.js +2 -3
- package/agents/gsd-advisor-researcher.compact.md +1 -1
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.compact.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +1 -1
- package/agents/gsd-code-fixer.compact.md +3 -3
- package/agents/gsd-code-fixer.md +5 -4
- package/agents/gsd-code-reviewer.compact.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-codebase-mapper.compact.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-verifier.compact.md +4 -4
- package/agents/gsd-doc-verifier.md +4 -3
- package/agents/gsd-doc-writer.compact.md +23 -5
- package/agents/gsd-doc-writer.md +19 -6
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +7 -3
- package/agents/gsd-integration-checker.compact.md +1 -1
- package/agents/gsd-integration-checker.md +1 -1
- package/agents/gsd-nyquist-auditor.compact.md +1 -1
- package/agents/gsd-nyquist-auditor.md +1 -1
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +2 -1
- package/agents/gsd-planner.md +5 -4
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +5 -5
- package/agents/gsd-research-synthesizer.md +5 -5
- package/agents/gsd-roadmapper.compact.md +1 -1
- package/agents/gsd-roadmapper.md +1 -1
- package/agents/gsd-security-auditor.compact.md +1 -1
- package/agents/gsd-security-auditor.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +1 -1
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.compact.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.compact.md +13 -1
- package/agents/gsd-ui-researcher.md +28 -1
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +460 -105
- package/commands/gsd/code-review.md +8 -0
- package/commands/gsd/pause-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +124 -18
- package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
- package/gsd-core/bin/lib/args-terminator.cjs +114 -0
- package/gsd-core/bin/lib/broken-windows.cjs +59 -1
- package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
- package/gsd-core/bin/lib/capability-lock.cjs +41 -8
- package/gsd-core/bin/lib/capability-registry.cjs +162 -74
- package/gsd-core/bin/lib/capability-source.cjs +3 -1
- package/gsd-core/bin/lib/capability-state.cjs +20 -11
- package/gsd-core/bin/lib/capability-validator.cjs +23 -4
- package/gsd-core/bin/lib/commands.cjs +5 -1
- package/gsd-core/bin/lib/config-loader.cjs +13 -3
- package/gsd-core/bin/lib/config.cjs +256 -58
- package/gsd-core/bin/lib/core-utils.cjs +34 -0
- package/gsd-core/bin/lib/decision-coverage-support.cjs +93 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +8 -3
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +21 -1
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +19 -1
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
- package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +114 -18
- package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
- package/gsd-core/bin/lib/git-info-exclude.cjs +57 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +18 -3
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
- package/gsd-core/bin/lib/host-integration.cjs +13 -6
- package/gsd-core/bin/lib/init.cjs +76 -5
- package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
- package/gsd-core/bin/lib/install-engine.cjs +246 -32
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -7
- package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
- package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +113 -0
- package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
- package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
- package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
- package/gsd-core/bin/lib/milestone.cjs +43 -1
- package/gsd-core/bin/lib/model-catalog.cjs +8 -0
- package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
- package/gsd-core/bin/lib/phase-id.cjs +21 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +37 -4
- package/gsd-core/bin/lib/phase.cjs +235 -37
- package/gsd-core/bin/lib/planning-snapshot.cjs +3 -1
- package/gsd-core/bin/lib/planning-workspace.cjs +18 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
- package/gsd-core/bin/lib/read-only-git-env.cjs +58 -0
- package/gsd-core/bin/lib/report-parser.cjs +57 -1
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +35 -2
- package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
- package/gsd-core/bin/lib/roadmap-parser.cjs +12 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +39 -2
- package/gsd-core/bin/lib/roadmap.cjs +89 -30
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +428 -16
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
- package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
- package/gsd-core/bin/lib/shell-command-projection.cjs +123 -16
- package/gsd-core/bin/lib/smart-entry.cjs +37 -16
- package/gsd-core/bin/lib/state-contract.cjs +29 -9
- package/gsd-core/bin/lib/state-document.cjs +47 -5
- package/gsd-core/bin/lib/state-transition.cjs +150 -34
- package/gsd-core/bin/lib/state.cjs +140 -88
- package/gsd-core/bin/lib/surface.cjs +9 -0
- package/gsd-core/bin/lib/task-command-router.cjs +7 -28
- package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
- package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
- package/gsd-core/bin/lib/verification.cjs +96 -9
- package/gsd-core/bin/lib/verify.cjs +38 -1
- package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/references/agent-contracts.md +1 -1
- package/gsd-core/references/agent-skills-bootstrap.md +26 -7
- package/gsd-core/references/git-integration.md +14 -3
- package/gsd-core/references/loop-hook-dispatch.md +13 -6
- package/gsd-core/references/planner-reversibility.md +18 -4
- package/gsd-core/references/planner-verify-command-grounding.md +18 -0
- package/gsd-core/references/planning-config.md +2 -0
- package/gsd-core/references/tdd.md +5 -4
- package/gsd-core/references/verifier-phase-gates.md +8 -1
- package/gsd-core/templates/DEBUG.md +16 -0
- package/gsd-core/templates/config.json +2 -1
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/add-backlog.md +10 -2
- package/gsd-core/workflows/add-phase.md +5 -1
- package/gsd-core/workflows/add-tests.md +5 -1
- package/gsd-core/workflows/add-todo.md +5 -1
- package/gsd-core/workflows/ai-integration-phase.md +5 -1
- package/gsd-core/workflows/audit-fix.md +2 -1
- package/gsd-core/workflows/audit-milestone.md +21 -9
- package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
- package/gsd-core/workflows/autonomous.md +52 -64
- package/gsd-core/workflows/check-todos.md +5 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
- package/gsd-core/workflows/code-review-fix.md +5 -1
- package/gsd-core/workflows/code-review.md +66 -8
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
- package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
- package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
- package/gsd-core/workflows/complete-milestone.md +64 -157
- package/gsd-core/workflows/debug.md +5 -1
- package/gsd-core/workflows/diagnose-issues.md +34 -6
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +44 -5
- package/gsd-core/workflows/discuss-phase-power.md +19 -2
- package/gsd-core/workflows/discuss-phase.md +11 -11
- package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
- package/gsd-core/workflows/docs-update.md +27 -5
- package/gsd-core/workflows/edit-phase.md +5 -1
- package/gsd-core/workflows/eval-review.md +11 -3
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +19 -8
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +21 -3
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +7 -3
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
- package/gsd-core/workflows/execute-phase.md +46 -11
- package/gsd-core/workflows/execute-plan.md +8 -4
- package/gsd-core/workflows/extract-learnings.md +6 -2
- package/gsd-core/workflows/forensics.md +5 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +6 -2
- package/gsd-core/workflows/insert-phase.md +5 -1
- package/gsd-core/workflows/manager.md +10 -2
- package/gsd-core/workflows/map-codebase.md +5 -1
- package/gsd-core/workflows/milestone-summary.md +10 -2
- package/gsd-core/workflows/new-milestone.md +13 -5
- package/gsd-core/workflows/new-project.md +9 -5
- package/gsd-core/workflows/next.md +8 -4
- package/gsd-core/workflows/pause-work.md +3 -3
- package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +7 -3
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
- package/gsd-core/workflows/plan-phase.md +54 -86
- package/gsd-core/workflows/plan-review-convergence.md +17 -5
- package/gsd-core/workflows/plant-seed.md +8 -4
- package/gsd-core/workflows/progress.md +6 -2
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
- package/gsd-core/workflows/quick-batch.md +5 -1
- package/gsd-core/workflows/quick.md +23 -8
- package/gsd-core/workflows/remove-phase.md +5 -1
- package/gsd-core/workflows/resume-project.md +5 -1
- package/gsd-core/workflows/review.md +22 -15
- package/gsd-core/workflows/scan.md +5 -1
- package/gsd-core/workflows/section-manifest.json +5 -0
- package/gsd-core/workflows/secure-phase.md +6 -2
- package/gsd-core/workflows/settings.md +4 -3
- package/gsd-core/workflows/ship.md +16 -14
- package/gsd-core/workflows/spec-phase.md +3 -6
- package/gsd-core/workflows/thread.md +10 -2
- package/gsd-core/workflows/transition.md +5 -1
- package/gsd-core/workflows/ui-phase.md +5 -1
- package/gsd-core/workflows/ui-review.md +6 -2
- package/gsd-core/workflows/ultraplan-phase.md +5 -1
- package/gsd-core/workflows/update.md +5 -1
- package/gsd-core/workflows/validate-phase.md +6 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
- package/gsd-core/workflows/verify-work.md +15 -6
- package/hooks/dist/gsd-agent-isolation-guard.js +14 -2
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -3
- package/hooks/dist/gsd-path-export.js +95 -0
- package/hooks/dist/gsd-secret-read-guard.js +244 -53
- package/hooks/dist/gsd-statusline.js +35 -2
- package/hooks/dist/gsd-validate-commit.sh +27 -5
- package/hooks/dist/gsd-windsurf-pre-write.js +28 -1
- package/hooks/dist/gsd-workflow-guard.js +1 -1
- package/hooks/dist/gsd-worktree-path-guard.js +28 -1
- package/hooks/dist/lib/isolation-sentinel.js +44 -0
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +14 -2
- package/hooks/gsd-cursor-subagent-start.js +8 -3
- package/hooks/gsd-path-export.js +95 -0
- package/hooks/gsd-secret-read-guard.js +244 -53
- package/hooks/gsd-statusline.js +35 -2
- package/hooks/gsd-validate-commit.sh +27 -5
- package/hooks/gsd-windsurf-pre-write.js +28 -1
- package/hooks/gsd-workflow-guard.js +1 -1
- package/hooks/gsd-worktree-path-guard.js +28 -1
- package/hooks/hooks.json +1 -0
- package/hooks/lib/isolation-sentinel.js +44 -0
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -6
- package/pi/gsd.cjs +1047 -10
- package/pi/package-agents/gsd-advisor-researcher.md +113 -0
- package/pi/package-agents/gsd-ai-researcher.md +111 -0
- package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
- package/pi/package-agents/gsd-code-fixer.md +769 -0
- package/pi/package-agents/gsd-code-reviewer.md +402 -0
- package/pi/package-agents/gsd-codebase-mapper.md +850 -0
- package/pi/package-agents/gsd-debug-session-manager.md +416 -0
- package/pi/package-agents/gsd-debugger.md +1274 -0
- package/pi/package-agents/gsd-doc-classifier.md +269 -0
- package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
- package/pi/package-agents/gsd-doc-verifier.md +212 -0
- package/pi/package-agents/gsd-doc-writer.md +626 -0
- package/pi/package-agents/gsd-dom-verifier.md +164 -0
- package/pi/package-agents/gsd-domain-researcher.md +144 -0
- package/pi/package-agents/gsd-eval-auditor.md +185 -0
- package/pi/package-agents/gsd-eval-planner.md +149 -0
- package/pi/package-agents/gsd-executor.md +894 -0
- package/pi/package-agents/gsd-framework-selector.md +159 -0
- package/pi/package-agents/gsd-integration-checker.md +476 -0
- package/pi/package-agents/gsd-intel-updater.md +338 -0
- package/pi/package-agents/gsd-mempalace-curator.md +49 -0
- package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
- package/pi/package-agents/gsd-pattern-mapper.md +340 -0
- package/pi/package-agents/gsd-phase-researcher.md +897 -0
- package/pi/package-agents/gsd-plan-checker.md +1077 -0
- package/pi/package-agents/gsd-planner.md +1003 -0
- package/pi/package-agents/gsd-project-researcher.md +611 -0
- package/pi/package-agents/gsd-research-synthesizer.md +259 -0
- package/pi/package-agents/gsd-roadmapper.md +779 -0
- package/pi/package-agents/gsd-security-auditor.md +171 -0
- package/pi/package-agents/gsd-ui-auditor.md +581 -0
- package/pi/package-agents/gsd-ui-checker.md +417 -0
- package/pi/package-agents/gsd-ui-researcher.md +469 -0
- package/pi/package-agents/gsd-user-profiler.md +174 -0
- package/pi/package-agents/gsd-verifier.md +963 -0
- package/pi/package-agents/manifest.json +39 -0
- package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
- package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
- package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
- package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
- package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
- package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
- package/pi/package-skills/gsd-capture/SKILL.md +68 -0
- package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
- package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
- package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
- package/pi/package-skills/gsd-config/SKILL.md +60 -0
- package/pi/package-skills/gsd-debug/SKILL.md +64 -0
- package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
- package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
- package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
- package/pi/package-skills/gsd-explore/SKILL.md +26 -0
- package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
- package/pi/package-skills/gsd-fast/SKILL.md +33 -0
- package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
- package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
- package/pi/package-skills/gsd-health/SKILL.md +36 -0
- package/pi/package-skills/gsd-help/SKILL.md +37 -0
- package/pi/package-skills/gsd-import/SKILL.md +46 -0
- package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
- package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
- package/pi/package-skills/gsd-manager/SKILL.md +46 -0
- package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
- package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
- package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
- package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
- package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
- package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
- package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
- package/pi/package-skills/gsd-next/SKILL.md +34 -0
- package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
- package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
- package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
- package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
- package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
- package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
- package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
- package/pi/package-skills/gsd-phase/SKILL.md +61 -0
- package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
- package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
- package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
- package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
- package/pi/package-skills/gsd-progress/SKILL.md +51 -0
- package/pi/package-skills/gsd-quick/SKILL.md +179 -0
- package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
- package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
- package/pi/package-skills/gsd-review/SKILL.md +50 -0
- package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
- package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
- package/pi/package-skills/gsd-settings/SKILL.md +30 -0
- package/pi/package-skills/gsd-ship/SKILL.md +27 -0
- package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
- package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
- package/pi/package-skills/gsd-spike/SKILL.md +53 -0
- package/pi/package-skills/gsd-stats/SKILL.md +22 -0
- package/pi/package-skills/gsd-surface/SKILL.md +177 -0
- package/pi/package-skills/gsd-thread/SKILL.md +29 -0
- package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
- package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
- package/pi/package-skills/gsd-undo/SKILL.md +38 -0
- package/pi/package-skills/gsd-update/SKILL.md +51 -0
- package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
- package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
- package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
- package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
- package/scripts/audit-workflow-script-paths.cjs +1 -1
- package/scripts/build-hooks.js +4 -0
- package/scripts/command-contract-helpers.cjs +1 -1
- package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
- package/scripts/docs-guard-registry.cjs +22 -7
- package/scripts/gen-health-docs.cjs +13 -1
- package/scripts/gen-pi-package-skills.cjs +217 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +19 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +41 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
- package/scripts/lint-phase-arg-assignment.cjs +2 -2
- package/scripts/lint-retired-runtime-name.cjs +5 -0
- package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
- package/scripts/npm-audit-baseline.cjs +41 -7
- package/scripts/release-tarball-smoke.cjs +1 -0
- package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
- package/scripts/select-docs-guards.cjs +1 -1
- package/scripts/self-test-report-policy.cjs +350 -0
- package/skills/gsd-code-review/SKILL.md +8 -0
- package/skills/gsd-pause-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.locateProgressTable = locateProgressTable;
|
|
25
|
+
exports.normalizeMilestoneToken = normalizeMilestoneToken;
|
|
26
|
+
exports.milestoneVersionOf = milestoneVersionOf;
|
|
25
27
|
exports.deriveProgressFromRoadmap = deriveProgressFromRoadmap;
|
|
26
28
|
exports.clampPercentFromFraction = clampPercentFromFraction;
|
|
27
29
|
exports.clampPercent = clampPercent;
|
|
@@ -68,6 +70,28 @@ function locateProgressTable(roadmapContent) {
|
|
|
68
70
|
}
|
|
69
71
|
return (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
|
|
70
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* #5165: a milestone token compared by identity, not spelling — `v1.1`, `V1.1`
|
|
75
|
+
* and ` 1.1 ` name one milestone. A hand-decorated Progress cell
|
|
76
|
+
* (`**v1.1**`, `` `v1.1` ``, `v1.1 (current)`) still names its first
|
|
77
|
+
* whitespace-separated token. Absent/blank tokens normalize to `''`.
|
|
78
|
+
*/
|
|
79
|
+
function normalizeMilestoneToken(value) {
|
|
80
|
+
// `[v1.1](ROADMAP.md#v11)` names its link text.
|
|
81
|
+
const unlinked = (milestoneVersionOf(value) ?? '').replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
|
|
82
|
+
const first = unlinked.split(/\s+/)[0] ?? '';
|
|
83
|
+
return first.replace(/^[*_`]+|[*_`:,;]+$/g, '').replace(/^v/i, '').toLowerCase();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* #5165: a STATE.md `milestone:` frontmatter value as a trimmed version string,
|
|
87
|
+
* or null when it is absent, blank or not scalar text.
|
|
88
|
+
*/
|
|
89
|
+
function milestoneVersionOf(value) {
|
|
90
|
+
if (typeof value !== 'string' && typeof value !== 'number')
|
|
91
|
+
return null;
|
|
92
|
+
const text = String(value).trim();
|
|
93
|
+
return text === '' ? null : text;
|
|
94
|
+
}
|
|
71
95
|
/**
|
|
72
96
|
* Derive completed_phases, total_phases, and total_plans from ROADMAP content.
|
|
73
97
|
* Root cause fix for issue #4 — see gen-phase-lifecycle.mjs for full documentation.
|
|
@@ -82,7 +106,7 @@ function locateProgressTable(roadmapContent) {
|
|
|
82
106
|
* 5-column milestone-grouped variant that inserts a `Milestone` column ahead
|
|
83
107
|
* of them).
|
|
84
108
|
*/
|
|
85
|
-
function deriveProgressFromRoadmap(roadmapContent) {
|
|
109
|
+
function deriveProgressFromRoadmap(roadmapContent, milestoneFilter) {
|
|
86
110
|
let completedPhases = null;
|
|
87
111
|
let totalPhases = null;
|
|
88
112
|
let totalPlans = null;
|
|
@@ -95,10 +119,19 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
95
119
|
// `RoadmapProgress` contract (nulls = absent) is unchanged.
|
|
96
120
|
const table = locateProgressTable(roadmapContent);
|
|
97
121
|
if (table) {
|
|
98
|
-
|
|
122
|
+
// #5165: the milestone-grouped Progress table (templates/roadmap.md) keeps
|
|
123
|
+
// every milestone's rows, shipped ones included. When the caller names the
|
|
124
|
+
// milestone and the table carries a `Milestone` column, count only that
|
|
125
|
+
// milestone's rows. A table without the column (or a caller without a
|
|
126
|
+
// milestone) keeps the whole-table behavior; a named milestone that matches
|
|
127
|
+
// no row yields nulls (nothing countable) rather than a foreign count.
|
|
128
|
+
const wantMilestone = normalizeMilestoneToken(milestoneFilter);
|
|
129
|
+
const scopedRows = wantMilestone !== '' && table.columns.includes('Milestone')
|
|
130
|
+
? table.rows.filter((r) => normalizeMilestoneToken(r['Milestone']) === wantMilestone)
|
|
131
|
+
: table.rows;
|
|
99
132
|
// Data rows only (exclude sentinel phases 0 and 999.x).
|
|
100
133
|
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
101
|
-
const dataRows =
|
|
134
|
+
const dataRows = scopedRows.filter((r) => {
|
|
102
135
|
const phase = (r['Phase'] ?? '').trim();
|
|
103
136
|
return /^\d/.test(phase) && !isSentinelPhaseId(phase);
|
|
104
137
|
});
|
|
@@ -110,7 +143,7 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
110
143
|
const completed = dataRows.filter((r) => (0, phase_status_cjs_1.parseRoadmapStatusCell)(r['Status']) === phase_status_cjs_1.WIRE_STATUS.COMPLETE).length;
|
|
111
144
|
completedPhases = completed > 0 ? completed : null;
|
|
112
145
|
let totalPlansSum = 0;
|
|
113
|
-
for (const r of
|
|
146
|
+
for (const r of scopedRows) {
|
|
114
147
|
const cell = (r['Plans Complete'] ?? '').trim();
|
|
115
148
|
const m = /(\d+)\s*\/\s*(\d+)/.exec(cell);
|
|
116
149
|
if (m)
|
|
@@ -54,13 +54,13 @@ const planningScopeMod = require("./planning-scope.cjs");
|
|
|
54
54
|
const { SCOPE } = planningScopeMod;
|
|
55
55
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- roadmap-parser.cjs is an export= CommonJS module
|
|
56
56
|
const roadmapParserMod = require("./roadmap-parser.cjs");
|
|
57
|
-
const { stripShippedMilestones, extractCurrentMilestone, currentMilestoneRawRanges, withPhaseSection, findMilestoneScopeHeadingLines } = roadmapParserMod;
|
|
57
|
+
const { stripShippedMilestones, extractCurrentMilestone, currentMilestoneRawRanges, withPhaseSection, editOutsideShippedBlocks, findMilestoneScopeHeadingLines } = roadmapParserMod;
|
|
58
58
|
// #4129: the single owner of "count the ROADMAP's milestone Complete rows"
|
|
59
59
|
// (pure computation, no I/O — no cycle on this path) for the intent-first
|
|
60
60
|
// progress counters the phase-complete transaction passes downstream.
|
|
61
61
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-lifecycle.cjs is an export= CommonJS module
|
|
62
62
|
const phaseLifecycleMod = require("./phase-lifecycle.cjs");
|
|
63
|
-
const { deriveProgressFromRoadmap: deriveProgressFromRoadmapForIntent, clampPercent: clampPercentForIntent } = phaseLifecycleMod;
|
|
63
|
+
const { deriveProgressFromRoadmap: deriveProgressFromRoadmapForIntent, clampPercent: clampPercentForIntent, milestoneVersionOf, locateProgressTable, normalizeMilestoneToken } = phaseLifecycleMod;
|
|
64
64
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
65
65
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
66
66
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
|
|
@@ -1067,6 +1067,62 @@ function describeGoalShapedTitle(description) {
|
|
|
1067
1067
|
return (`description looks goal-shaped, not title-shaped (${reasons}). It was written verbatim ` +
|
|
1068
1068
|
`as the phase title; consider a short title with the detail moved to **Goal:**.`);
|
|
1069
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Phase numbers the roadmap itself declares — a section header or a roadmap bullet — sentinels
|
|
1072
|
+
* excluded. This is deliberately NOT the allocation set: allocation also counts on-disk directories
|
|
1073
|
+
* and sibling-worktree phases so a new number cannot collide, but a `Depends on:` line has to point
|
|
1074
|
+
* at something a reader of THIS roadmap can find (#5113).
|
|
1075
|
+
*/
|
|
1076
|
+
function roadmapDeclaredPhaseNums(rawContent) {
|
|
1077
|
+
const { regex: headerPattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.LABEL_ONLY);
|
|
1078
|
+
const bulletPattern = /^[ \t]*-[ \t]*\[[^\]]{0,200}\][ \t]*\*{0,2}Phase[ \t]+(\d+)(?=[:.\s*]|$)/gim;
|
|
1079
|
+
const nums = new Set();
|
|
1080
|
+
let m;
|
|
1081
|
+
while ((m = headerPattern.exec(rawContent)) !== null) {
|
|
1082
|
+
const num = parseInt(m[phaseNumGroup], 10);
|
|
1083
|
+
if (!isSentinelPhaseId(num))
|
|
1084
|
+
nums.add(num);
|
|
1085
|
+
}
|
|
1086
|
+
while ((m = bulletPattern.exec(rawContent)) !== null) {
|
|
1087
|
+
const num = parseInt(m[1], 10);
|
|
1088
|
+
if (!isSentinelPhaseId(num))
|
|
1089
|
+
nums.add(num);
|
|
1090
|
+
}
|
|
1091
|
+
return [...nums];
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* The `**Depends on:**` line for a freshly added numeric phase: the nearest lower phase the roadmap
|
|
1095
|
+
* declares, or `Nothing` when it declares none. It was `newId - 1` regardless, which named a phase
|
|
1096
|
+
* that exists only on a sibling branch, as an orphan directory, or not at all when numbering has a
|
|
1097
|
+
* gap (#5113). Custom-named phases carry no dependency line.
|
|
1098
|
+
*/
|
|
1099
|
+
function dependsOnLine(newPhaseId, declared) {
|
|
1100
|
+
if (typeof newPhaseId !== 'number')
|
|
1101
|
+
return '';
|
|
1102
|
+
let nearest = null;
|
|
1103
|
+
for (const n of declared)
|
|
1104
|
+
if (n < newPhaseId && (nearest === null || n > nearest))
|
|
1105
|
+
nearest = n;
|
|
1106
|
+
return `\n**Depends on:** ${nearest === null ? 'Nothing' : `Phase ${nearest}`}`;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Splice a phase entry into `rawContent` at `at`, changing nothing else: the blank lines around the
|
|
1110
|
+
* block and its line endings are the block's own, so the surrounding document is published byte for
|
|
1111
|
+
* byte (#5113). `block` is LF text that starts and ends with a newline.
|
|
1112
|
+
*/
|
|
1113
|
+
function spliceRoadmapBlock(rawContent, atOffset, block) {
|
|
1114
|
+
const eol = rawContent.includes('\r\n') ? '\r\n' : '\n';
|
|
1115
|
+
// The insertion offset is found by searching for '\n---', which lands between the CR and the LF of
|
|
1116
|
+
// a CRLF pair; splice before the pair instead of through it.
|
|
1117
|
+
const at = rawContent[atOffset - 1] === '\r' && rawContent[atOffset] === '\n' ? atOffset - 1 : atOffset;
|
|
1118
|
+
const before = rawContent.slice(0, at);
|
|
1119
|
+
const after = rawContent.slice(at);
|
|
1120
|
+
const body = block.replace(/^\n+/, '').replace(/\n+$/, '\n').replace(/\n/g, eol);
|
|
1121
|
+
const blankBefore = before === '' || /(?:\r?\n){2}$/.test(before);
|
|
1122
|
+
const lead = blankBefore ? '' : /\n$/.test(before) ? eol : eol + eol;
|
|
1123
|
+
const tail = after === '' || /^\r?\n/.test(after) ? '' : eol;
|
|
1124
|
+
return before + lead + body + tail + after;
|
|
1125
|
+
}
|
|
1070
1126
|
/**
|
|
1071
1127
|
* #3163: compute the byte offset in `rawContent` where a new `### Phase N:`
|
|
1072
1128
|
* entry should be inserted — at the end of the active phase list, scoped to the
|
|
@@ -1169,7 +1225,7 @@ function collectSiblingWorktreePhaseNums(cwd, used) {
|
|
|
1169
1225
|
timeout: 10_000,
|
|
1170
1226
|
windowsHide: true,
|
|
1171
1227
|
maxBuffer: 4 * 1024 * 1024,
|
|
1172
|
-
});
|
|
1228
|
+
}); // allow-index-refresh: lists worktrees from .git metadata, never opens the index
|
|
1173
1229
|
}
|
|
1174
1230
|
catch {
|
|
1175
1231
|
return; // not a git repo / git unavailable — unchanged behavior
|
|
@@ -1310,13 +1366,11 @@ function cmdPhaseAdd(cwd, description, raw, customId) {
|
|
|
1310
1366
|
const dirPath = node_path_1.default.join(planningDir(cwd), 'phases', _dirName);
|
|
1311
1367
|
(0, shell_command_projection_cjs_1.platformEnsureDir)(dirPath);
|
|
1312
1368
|
(0, shell_command_projection_cjs_1.platformWriteSync)(node_path_1.default.join(dirPath, '.gitkeep'), '');
|
|
1313
|
-
const dependsOn = config.phase_naming === 'custom'
|
|
1314
|
-
? ''
|
|
1315
|
-
: `\n**Depends on:** Phase ${typeof _newPhaseId === 'number' ? _newPhaseId - 1 : 'TBD'}`;
|
|
1369
|
+
const dependsOn = config.phase_naming === 'custom' ? '' : dependsOnLine(_newPhaseId, roadmapDeclaredPhaseNums(rawContent));
|
|
1316
1370
|
const phaseEntry = `\n### Phase ${_newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run ${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', (0, runtime_slash_cjs_1.resolveRuntime)(cwd))} ${_newPhaseId} to break down)\n`;
|
|
1317
1371
|
const insertAt = phaseEntryInsertOffset(rawContent, cwd);
|
|
1318
|
-
const updatedContent = rawContent
|
|
1319
|
-
(0, shell_command_projection_cjs_1.platformWriteSync)(roadmapPath, updatedContent);
|
|
1372
|
+
const updatedContent = spliceRoadmapBlock(rawContent, insertAt, phaseEntry);
|
|
1373
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(roadmapPath, updatedContent, { verbatim: true });
|
|
1320
1374
|
return { newPhaseId: _newPhaseId, dirName: _dirName };
|
|
1321
1375
|
});
|
|
1322
1376
|
const titleWarning = describeGoalShapedTitle(description);
|
|
@@ -1432,12 +1486,10 @@ function cmdPhaseAddBatch(cwd, descriptions, raw) {
|
|
|
1432
1486
|
const dirPath = node_path_1.default.join(planningDir(cwd), 'phases', dirName);
|
|
1433
1487
|
(0, shell_command_projection_cjs_1.platformEnsureDir)(dirPath);
|
|
1434
1488
|
(0, shell_command_projection_cjs_1.platformWriteSync)(node_path_1.default.join(dirPath, '.gitkeep'), '');
|
|
1435
|
-
const dependsOn = config.phase_naming === 'custom'
|
|
1436
|
-
? ''
|
|
1437
|
-
: `\n**Depends on:** Phase ${typeof newPhaseId === 'number' ? newPhaseId - 1 : 'TBD'}`;
|
|
1489
|
+
const dependsOn = config.phase_naming === 'custom' ? '' : dependsOnLine(newPhaseId, roadmapDeclaredPhaseNums(rawContent));
|
|
1438
1490
|
const phaseEntry = `\n### Phase ${newPhaseId}: ${description}\n\n**Goal:** [To be planned]\n**Requirements**: TBD${dependsOn}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run ${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', (0, runtime_slash_cjs_1.resolveRuntime)(cwd))} ${newPhaseId} to break down)\n`;
|
|
1439
1491
|
const insertAt = phaseEntryInsertOffset(rawContent, cwd);
|
|
1440
|
-
rawContent = rawContent
|
|
1492
|
+
rawContent = spliceRoadmapBlock(rawContent, insertAt, phaseEntry);
|
|
1441
1493
|
added.push({
|
|
1442
1494
|
phase_number: typeof newPhaseId === 'number' ? newPhaseId : String(newPhaseId),
|
|
1443
1495
|
padded: typeof newPhaseId === 'number' ? String(newPhaseId).padStart(2, '0') : String(newPhaseId),
|
|
@@ -1447,7 +1499,7 @@ function cmdPhaseAddBatch(cwd, descriptions, raw) {
|
|
|
1447
1499
|
naming_mode: config.phase_naming,
|
|
1448
1500
|
});
|
|
1449
1501
|
}
|
|
1450
|
-
(0, shell_command_projection_cjs_1.platformWriteSync)(roadmapPath, rawContent);
|
|
1502
|
+
(0, shell_command_projection_cjs_1.platformWriteSync)(roadmapPath, rawContent, { verbatim: true });
|
|
1451
1503
|
return added;
|
|
1452
1504
|
});
|
|
1453
1505
|
output({ phases: results, count: results.length }, raw);
|
|
@@ -1525,6 +1577,21 @@ function scanExistingDecimalPhaseNumbers(phasesDir, rawContent, base) {
|
|
|
1525
1577
|
}
|
|
1526
1578
|
return decimalSet;
|
|
1527
1579
|
}
|
|
1580
|
+
/**
|
|
1581
|
+
* #5599: true when `token` (a heading/bullet phase number such as "13.1.2") is
|
|
1582
|
+
* a STRICT descendant of `ancestor` ("13"). Compared segment-wise so "130" is
|
|
1583
|
+
* not inside "13", and digit segments compare numerically so a zero-padded
|
|
1584
|
+
* "05.1" matches "5".
|
|
1585
|
+
*/
|
|
1586
|
+
function isPhaseTokenWithin(token, ancestor) {
|
|
1587
|
+
const t = token.split('.');
|
|
1588
|
+
const a = ancestor.split('.');
|
|
1589
|
+
if (t.length <= a.length)
|
|
1590
|
+
return false;
|
|
1591
|
+
// Zero padding is not significant ("05" = "5", "03A" = "3A"); case is not either.
|
|
1592
|
+
const canon = (segment) => segment.replace(/^0+(?=\d)/, '').toLowerCase();
|
|
1593
|
+
return a.every((segment, i) => canon(segment) === canon(t[i]));
|
|
1594
|
+
}
|
|
1528
1595
|
function cmdPhaseInsert(cwd, afterPhase, description, raw, allocation = 'nested') {
|
|
1529
1596
|
if (!afterPhase || !description) {
|
|
1530
1597
|
error('after-phase and description required for phase insert');
|
|
@@ -1586,6 +1653,11 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw, allocation = 'nested'
|
|
|
1586
1653
|
const siblingNextDecimal = siblingDecimalSet.size === 0 ? 1 : Math.max(...siblingDecimalSet) + 1;
|
|
1587
1654
|
_decimalPhase = `${parentBase}.${siblingNextDecimal}`;
|
|
1588
1655
|
}
|
|
1656
|
+
// #5599: the family whose subtree the new entry must land at the end of —
|
|
1657
|
+
// the parent for a sibling allocation, the target itself for a nested one.
|
|
1658
|
+
const subtreeRoot = allocation === 'sibling' && lastDotIndex !== -1
|
|
1659
|
+
? normalizedBase.slice(0, lastDotIndex)
|
|
1660
|
+
: normalizedBase;
|
|
1589
1661
|
const insertConfig = loadConfig(cwd);
|
|
1590
1662
|
const projectCode = insertConfig.project_code || '';
|
|
1591
1663
|
const pfx = projectCode ? `${projectCode}-` : '';
|
|
@@ -1623,13 +1695,17 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw, allocation = 'nested'
|
|
|
1623
1695
|
}
|
|
1624
1696
|
const bulletLineEnd = rawContent.indexOf(bulletMatchResult[0]) + bulletMatchResult[0].length;
|
|
1625
1697
|
const afterBullet = rawContent.slice(bulletLineEnd);
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1698
|
+
// #5599: land after the last bullet inside the allocation family's
|
|
1699
|
+
// subtree, not before the first following bullet of any number (which is
|
|
1700
|
+
// the target's own later decimal child when one exists).
|
|
1701
|
+
const bulletRe = /\r?\n-\s*\[[ x]\]\s*(?:\*\*)?Phase\s+(?<tok>\d[^\s:*]*)[^\r\n]*/gi;
|
|
1702
|
+
let insertIdx = bulletLineEnd;
|
|
1703
|
+
for (const m of afterBullet.matchAll(bulletRe)) {
|
|
1704
|
+
if (!isPhaseTokenWithin(m.groups.tok, subtreeRoot)) {
|
|
1705
|
+
insertIdx = bulletLineEnd + m.index;
|
|
1706
|
+
break;
|
|
1707
|
+
}
|
|
1708
|
+
insertIdx = bulletLineEnd + m.index + m[0].length;
|
|
1633
1709
|
}
|
|
1634
1710
|
updatedContent =
|
|
1635
1711
|
rawContent.slice(0, insertIdx) + bulletEntry + rawContent.slice(insertIdx);
|
|
@@ -1671,13 +1747,19 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw, allocation = 'nested'
|
|
|
1671
1747
|
// preserved exactly as the prior literal had it — a pre-existing,
|
|
1672
1748
|
// deliberately-unchanged asymmetry with the other sites in this file.
|
|
1673
1749
|
const nextPhaseHeadingPrefix = `#{2,4}\\s+${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, insertConvention)}`;
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1750
|
+
// #5599: the boundary is the first following heading OUTSIDE the
|
|
1751
|
+
// allocation family's subtree. Taking the first heading of any number
|
|
1752
|
+
// spliced the new section above the target's own later decimal children
|
|
1753
|
+
// (13, 13.2, 13.1, 14); the allocator already guarantees the new number
|
|
1754
|
+
// exceeds every existing descendant, so "after the last descendant" is
|
|
1755
|
+
// the numerically correct position.
|
|
1756
|
+
const nextPhaseRe = new RegExp(`\\r?\\n${nextPhaseHeadingPrefix}(?<tok>\\d[^\\s:]*)`, 'gi');
|
|
1757
|
+
let insertIdx = rawContent.length;
|
|
1758
|
+
for (const m of afterHeader.matchAll(nextPhaseRe)) {
|
|
1759
|
+
if (!isPhaseTokenWithin(m.groups.tok, subtreeRoot)) {
|
|
1760
|
+
insertIdx = headerIdx + headerMatch[0].length + m.index;
|
|
1761
|
+
break;
|
|
1762
|
+
}
|
|
1681
1763
|
}
|
|
1682
1764
|
updatedContent =
|
|
1683
1765
|
rawContent.slice(0, insertIdx) + phaseEntry + rawContent.slice(insertIdx);
|
|
@@ -3061,6 +3143,42 @@ function formatRequirementsLineWarning(phaseNum, rawLine, analysis) {
|
|
|
3061
3143
|
oversized,
|
|
3062
3144
|
};
|
|
3063
3145
|
}
|
|
3146
|
+
/**
|
|
3147
|
+
* #4972: advisory gaps for the phase artifacts that active `verify:post` steps
|
|
3148
|
+
* declared `onError: "halt"` and `produces` (SECURITY.md, VALIDATION.md).
|
|
3149
|
+
* Missing file, an unresolved `status: draft`, or (SECURITY.md) `threats_open`
|
|
3150
|
+
* above zero. Any other `status` spelling counts as closed — the audit skills
|
|
3151
|
+
* do not pin one value, so this does not invent a vocabulary.
|
|
3152
|
+
*/
|
|
3153
|
+
function auditArtifactWarnings(cwd, phaseFullDir) {
|
|
3154
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- lazy: loop-resolver pulls in the capability registry
|
|
3155
|
+
const loopResolver = require('./loop-resolver.cjs');
|
|
3156
|
+
const out = [];
|
|
3157
|
+
const files = node_fs_1.default.readdirSync(phaseFullDir);
|
|
3158
|
+
for (const hook of loopResolver.resolveActiveHooksForPoint(cwd, 'verify:post').activeHooks) {
|
|
3159
|
+
if (hook.kind !== 'step' || hook.onError !== 'halt')
|
|
3160
|
+
continue;
|
|
3161
|
+
for (const artifact of hook.produces ?? []) {
|
|
3162
|
+
if (!/^[A-Z][A-Z0-9-]*\.md$/.test(artifact))
|
|
3163
|
+
continue;
|
|
3164
|
+
const skill = hook.ref?.skill ?? hook.capId;
|
|
3165
|
+
const found = files.filter((f) => f === artifact || f.endsWith(`-${artifact}`)).sort()[0];
|
|
3166
|
+
if (!found) {
|
|
3167
|
+
out.push(`${artifact}: missing — the ${hook.capId} audit (/gsd:${skill}) never ran for this phase, and a verify:post step cannot block the close (#4972)`);
|
|
3168
|
+
continue;
|
|
3169
|
+
}
|
|
3170
|
+
const fm = frontmatterMod.extractFrontmatter(node_fs_1.default.readFileSync(node_path_1.default.join(phaseFullDir, found), 'utf-8'));
|
|
3171
|
+
if (typeof fm['status'] === 'string' && fm['status'].trim().toLowerCase() === 'draft') {
|
|
3172
|
+
out.push(`${found}: status is still draft — the ${hook.capId} audit (/gsd:${skill}) was not completed (#4972)`);
|
|
3173
|
+
}
|
|
3174
|
+
const open = Number(fm['threats_open']);
|
|
3175
|
+
if (artifact === 'SECURITY.md' && Number.isFinite(open) && open > 0) {
|
|
3176
|
+
out.push(`${found}: ${open} threat(s) still open (#4972)`);
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
}
|
|
3180
|
+
return out;
|
|
3181
|
+
}
|
|
3064
3182
|
function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
3065
3183
|
if (!phaseNum) {
|
|
3066
3184
|
error('phase number required for phase complete');
|
|
@@ -3274,6 +3392,23 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3274
3392
|
/* best-effort, same posture as the #2245 pre-scan above: an unreadable
|
|
3275
3393
|
* SUMMARY means one fewer advisory this run, never a blocked completion. */
|
|
3276
3394
|
}
|
|
3395
|
+
// #4972: a `verify:post` STEP declared `onError: "halt"` that produces a
|
|
3396
|
+
// phase artifact (security -> SECURITY.md, nyquist -> VALIDATION.md) reads as
|
|
3397
|
+
// a blocking precondition, but a step is advisory by construction (ADR-894
|
|
3398
|
+
// "steps are additive, gates block") and the workflow only prints a line the
|
|
3399
|
+
// operator can ignore — so a phase could close with the audit never run. This
|
|
3400
|
+
// verb is the one place every close passes through, so it names the gap on
|
|
3401
|
+
// the same ADVISORY `warnings[]` channel as the #2572 check above. Advisory,
|
|
3402
|
+
// not a refusal: both capabilities are on by default, so a hard block here
|
|
3403
|
+
// would stop every existing project's next phase close; the security
|
|
3404
|
+
// threats-open gate at `ship:pre` is the enforced one and is untouched.
|
|
3405
|
+
try {
|
|
3406
|
+
warnings.push(...auditArtifactWarnings(cwd, phaseFullDir));
|
|
3407
|
+
}
|
|
3408
|
+
catch {
|
|
3409
|
+
/* best-effort, same posture as the advisories above: an unresolvable hook
|
|
3410
|
+
* set or unreadable artifact means one fewer advisory, never a blocked close. */
|
|
3411
|
+
}
|
|
3277
3412
|
let nextPhaseNum = null;
|
|
3278
3413
|
let nextPhaseName = null;
|
|
3279
3414
|
let isLastPhase = true;
|
|
@@ -3346,6 +3481,10 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3346
3481
|
const runPhaseCompleteTransaction = () => {
|
|
3347
3482
|
const writes = [];
|
|
3348
3483
|
let roadmapContent = null;
|
|
3484
|
+
// #5165: the phase was ALREADY complete in the ROADMAP this transaction
|
|
3485
|
+
// started from (read before any mutation below): its checklist box was
|
|
3486
|
+
// checked, or its Progress-table row read Complete.
|
|
3487
|
+
let phaseWasAlreadyComplete = false;
|
|
3349
3488
|
if (node_fs_1.default.existsSync(roadmapPath)) {
|
|
3350
3489
|
const originalRoadmapContent = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
|
|
3351
3490
|
roadmapContent = originalRoadmapContent;
|
|
@@ -3383,6 +3522,12 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3383
3522
|
// its own `- [ ] Phase N: …` entries inside a fenced code block, but it
|
|
3384
3523
|
// is a real (and correct) behavioural difference on pathological input.
|
|
3385
3524
|
const checkboxPattern = new RegExp(`^[ \\t]*(-\\s*\\[)[ ](\\]\\s*(?:\\*\\*)?\\s*Phase\\s+${phaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s][^\\n]*)`, 'i');
|
|
3525
|
+
// #5165: pre-mutation read of the same checklist line, checked this time.
|
|
3526
|
+
const checkedBoxPattern = new RegExp(`^[ \\t]*(-\\s*\\[)[xX](\\]\\s*(?:\\*\\*)?\\s*Phase\\s+${phaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s][^\\n]*)`);
|
|
3527
|
+
const originalMilestoneScope = extractCurrentMilestone(originalRoadmapContent, cwd);
|
|
3528
|
+
phaseWasAlreadyComplete = originalMilestoneScope
|
|
3529
|
+
.split('\n')
|
|
3530
|
+
.some((line) => checkedBoxPattern.test(line));
|
|
3386
3531
|
// Progress table row: update Plans Complete/Status/Completed columns BY
|
|
3387
3532
|
// COLUMN NAME (handles 4- or 5-column RoadmapProgress tables) via the
|
|
3388
3533
|
// markdown-table seam (ADR-2143 §7) — supersedes the prior ordinal
|
|
@@ -3399,6 +3544,22 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3399
3544
|
const phaseCellRe = new RegExp(`^${phaseEscaped}\\.?(?:\\s|$)`, 'i');
|
|
3400
3545
|
const rowMatch = (row) => phaseCellRe.test((row['Phase'] ?? '').trim());
|
|
3401
3546
|
const dateShape = /^\d{4}-\d{2}-\d{2}$/;
|
|
3547
|
+
// #5165: a ROADMAP whose checklist box was never ticked (or has none) but
|
|
3548
|
+
// whose Progress-table row already reads Complete is just as complete. A
|
|
3549
|
+
// milestone-grouped table (a `Milestone` column) is read only for the
|
|
3550
|
+
// milestone ROADMAP binds, so a shipped milestone's same-numbered phase
|
|
3551
|
+
// cannot count.
|
|
3552
|
+
if (!phaseWasAlreadyComplete) {
|
|
3553
|
+
const progressTable = locateProgressTable(originalMilestoneScope);
|
|
3554
|
+
if (progressTable !== null) {
|
|
3555
|
+
const wantMilestone = progressTable.columns.includes('Milestone')
|
|
3556
|
+
? normalizeMilestoneToken(roadmapParserMod.getMilestoneInfo(cwd).value?.version)
|
|
3557
|
+
: null;
|
|
3558
|
+
phaseWasAlreadyComplete = progressTable.rows.some((row) => rowMatch(row)
|
|
3559
|
+
&& (0, phase_status_cjs_1.parseRoadmapStatusCell)(row['Status']) === phase_status_cjs_1.WIRE_STATUS.COMPLETE
|
|
3560
|
+
&& (wantMilestone === null || (wantMilestone !== '' && normalizeMilestoneToken(row['Milestone']) === wantMilestone)));
|
|
3561
|
+
}
|
|
3562
|
+
}
|
|
3402
3563
|
/**
|
|
3403
3564
|
* Within `text` (already scoped to one milestone window by the
|
|
3404
3565
|
* caller), scope further to the `## Progress` heading section (up to
|
|
@@ -3536,7 +3697,11 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3536
3697
|
const plansResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Plans Complete', ` ${summaryCount}/${planCount} `);
|
|
3537
3698
|
if (plansResult.ok)
|
|
3538
3699
|
text = plansResult.value;
|
|
3539
|
-
|
|
3700
|
+
// #5165: a Status cell that already reads Complete is left untouched —
|
|
3701
|
+
// re-padding it rewrote a cell the re-run had no business editing.
|
|
3702
|
+
const statusResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Status', (current) => (0, phase_status_cjs_1.parseRoadmapStatusCell)(current) === phase_status_cjs_1.WIRE_STATUS.COMPLETE
|
|
3703
|
+
? current
|
|
3704
|
+
: ` ${(0, phase_status_cjs_1.toRoadmapStatusCell)(phase_status_cjs_1.PHASE_STATUS.COMPLETE).padEnd(11)} `);
|
|
3540
3705
|
if (statusResult.ok)
|
|
3541
3706
|
text = statusResult.value;
|
|
3542
3707
|
// Preserve only a valid ISO date (#1161: idempotent; self-heal
|
|
@@ -3562,12 +3727,15 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3562
3727
|
const planId = summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '');
|
|
3563
3728
|
if (!planId)
|
|
3564
3729
|
continue;
|
|
3565
|
-
|
|
3730
|
+
// #5542: only a summary paired with a live (non-superseded) plan
|
|
3731
|
+
// flips a row — the same pairing rule the counts use.
|
|
3732
|
+
if (!coreUtilsMod.summaryPairsWithAnyPlan(phaseInfo['plans'] ?? [], summaryFile))
|
|
3733
|
+
continue;
|
|
3566
3734
|
// ADR-5057 §6 (Phase 13, #5217): the per-plan flip is a bullet
|
|
3567
3735
|
// mutation, so it goes through `updateBullet` like the phase
|
|
3568
3736
|
// checkbox flip above — first matching bullet only, never a
|
|
3569
3737
|
// fenced line, never a mid-line occurrence.
|
|
3570
|
-
const planCheckboxPattern =
|
|
3738
|
+
const planCheckboxPattern = coreUtilsMod.planRowPattern(planId, { anchored: true });
|
|
3571
3739
|
b = (0, markdown_sectionizer_cjs_1.updateBullet)(b, (_bulletText, rawLine) => planCheckboxPattern.test(rawLine), (rawLine) => rawLine.replace(planCheckboxPattern, '$1x$2'));
|
|
3572
3740
|
}
|
|
3573
3741
|
return b;
|
|
@@ -3589,7 +3757,13 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3589
3757
|
}
|
|
3590
3758
|
}
|
|
3591
3759
|
else {
|
|
3592
|
-
|
|
3760
|
+
// #5543: the active milestone could not be resolved (no STATE
|
|
3761
|
+
// `milestone:`, no marker). Writing the whole document would let
|
|
3762
|
+
// `withPhaseSection` pick the first `### Phase N:` in document order,
|
|
3763
|
+
// which can sit inside a shipped `<details>` block. Hide the shipped
|
|
3764
|
+
// blocks from the mutation and restore them untouched; a ROADMAP with
|
|
3765
|
+
// no shipped block is mutated whole, as before.
|
|
3766
|
+
roadmapContent = editOutsideShippedBlocks(roadmapContent, mutateMilestonePhase);
|
|
3593
3767
|
}
|
|
3594
3768
|
writes.push({
|
|
3595
3769
|
filePath: roadmapPath,
|
|
@@ -4183,6 +4357,19 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
4183
4357
|
}
|
|
4184
4358
|
if (node_fs_1.default.existsSync(statePath)) {
|
|
4185
4359
|
const originalStateContent = (0, shell_command_projection_cjs_1.platformReadSync)(statePath) || '';
|
|
4360
|
+
// #5165: the milestone STATE asserts (ROADMAP-bound identity first,
|
|
4361
|
+
// else STATE's own frontmatter) scopes the post-completion progress
|
|
4362
|
+
// derivation to that milestone's Progress-table rows.
|
|
4363
|
+
const stateFm = frontmatterMod.extractFrontmatter(originalStateContent, statePath);
|
|
4364
|
+
const storedMilestone = roadmapParserMod.getMilestoneInfo(cwd).value?.version
|
|
4365
|
+
?? milestoneVersionOf(stateFm['milestone']);
|
|
4366
|
+
// #5165: re-completing a phase that is already complete in ROADMAP.md
|
|
4367
|
+
// while STATE has moved past it is a no-op for STATE's position —
|
|
4368
|
+
// the transition, the metrics row and the authoritative current-phase
|
|
4369
|
+
// pin would all rewind it. A first-time completion (box unchecked, or
|
|
4370
|
+
// STATE not past N, #2028 out-of-order) still transitions.
|
|
4371
|
+
const stateAlreadyPast = phaseWasAlreadyComplete
|
|
4372
|
+
&& (0, state_transition_cjs_1.isStatePastPhase)(originalStateContent, phaseNum, statePath);
|
|
4186
4373
|
let stateContent = originalStateContent;
|
|
4187
4374
|
// ADR-1769 Phase 3: the STATE.md field-update policy (Current Phase
|
|
4188
4375
|
// shape/name, Status, Current Plan, Last Activity + Description, and
|
|
@@ -4197,8 +4384,10 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
4197
4384
|
// NOT go through readModifyWriteStateMd because STATE.md is
|
|
4198
4385
|
// committed atomically with ROADMAP/REQUIREMENTS, ADR-3408 §8.3 /
|
|
4199
4386
|
// #3374 / #3469).
|
|
4200
|
-
const nextPhaseDisplayName =
|
|
4201
|
-
|
|
4387
|
+
const nextPhaseDisplayName = stateAlreadyPast
|
|
4388
|
+
? null
|
|
4389
|
+
: phaseDisplayNameFromRoadmap(roadmapContent, nextPhaseNum) ??
|
|
4390
|
+
phaseDisplayNameFromSlug(nextPhaseName);
|
|
4202
4391
|
// #4824: classify the SELECTED next phase from disk (the same ladder
|
|
4203
4392
|
// roadmap analyze uses) so the Status / Current Plan / Stopped-at
|
|
4204
4393
|
// handoff does not say "ready to plan" for a phase already planned or
|
|
@@ -4219,7 +4408,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
4219
4408
|
/* best-effort: an unreadable next phase keeps `Ready to plan` */
|
|
4220
4409
|
}
|
|
4221
4410
|
}
|
|
4222
|
-
const completeResult = (0, state_transition_cjs_1.transitionCore)(stateContent, {
|
|
4411
|
+
const completeResult = stateAlreadyPast ? { content: stateContent } : (0, state_transition_cjs_1.transitionCore)(stateContent, {
|
|
4223
4412
|
kind: 'completePhase',
|
|
4224
4413
|
phaseNum,
|
|
4225
4414
|
nextPhaseNum,
|
|
@@ -4228,13 +4417,22 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
4228
4417
|
planCount,
|
|
4229
4418
|
summaryCount,
|
|
4230
4419
|
nextPhaseDisk,
|
|
4420
|
+
// #5165: the body counter and the frontmatter progress below count the
|
|
4421
|
+
// SAME milestone's Progress rows.
|
|
4422
|
+
milestone: storedMilestone,
|
|
4231
4423
|
}, {
|
|
4232
4424
|
clock: clock_cjs_1.realClock,
|
|
4233
|
-
|
|
4425
|
+
// #4901: the body counter derives from the same current-milestone scope
|
|
4426
|
+
// as its frontmatter twin below (postCompletionRoadmapScope), not the
|
|
4427
|
+
// whole file, so the two writers cannot disagree on which
|
|
4428
|
+
// `## Progress` table they count.
|
|
4429
|
+
roadmapProvider: () => (roadmapContent !== null ? extractCurrentMilestone(roadmapContent, cwd) : null),
|
|
4234
4430
|
sourcePath: statePath,
|
|
4235
4431
|
});
|
|
4236
4432
|
stateContent = completeResult.content;
|
|
4237
|
-
|
|
4433
|
+
if (!stateAlreadyPast) {
|
|
4434
|
+
stateContent = updatePerformanceMetricsSection(stateContent, cwd, phaseNum, planCount, summaryCount);
|
|
4435
|
+
}
|
|
4238
4436
|
// #2736: the transition holds the next phase's exact display name in
|
|
4239
4437
|
// the intent; pass it as authoritative so the sync's prose
|
|
4240
4438
|
// re-derivation cannot rewrite current_phase_name to the name's own
|
|
@@ -4267,7 +4465,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
4267
4465
|
? extractCurrentMilestone(roadmapContent, cwd)
|
|
4268
4466
|
: null;
|
|
4269
4467
|
const postCompletionRoadmapProgress = postCompletionRoadmapScope !== null
|
|
4270
|
-
? deriveProgressFromRoadmapForIntent(postCompletionRoadmapScope)
|
|
4468
|
+
? deriveProgressFromRoadmapForIntent(postCompletionRoadmapScope, storedMilestone)
|
|
4271
4469
|
: null;
|
|
4272
4470
|
const authoritativeProgress = postCompletionRoadmapProgress && postCompletionRoadmapProgress.completedPhases !== null
|
|
4273
4471
|
? postCompletionRoadmapProgress.totalPhases !== null && postCompletionRoadmapProgress.totalPhases > 0
|
|
@@ -129,7 +129,9 @@ function buildPhaseSnapshot(phasesDir, dir, convention) {
|
|
|
129
129
|
dir,
|
|
130
130
|
complete: completionResult.value.complete,
|
|
131
131
|
verificationStatus: completionResult.value.verification.status,
|
|
132
|
-
verificationStatusError: statusError
|
|
132
|
+
verificationStatusError: statusError
|
|
133
|
+
? { file: statusError.file, message: statusError.message, rawStatus: statusError.rawStatus }
|
|
134
|
+
: null,
|
|
133
135
|
planCount: scanResult.planCount,
|
|
134
136
|
summaryCount: scanResult.summaryCount,
|
|
135
137
|
scope: worstScope(completionResult.scope, scanResult.scope),
|
|
@@ -630,6 +630,22 @@ function findContextMdIn(absDirOrFiles) {
|
|
|
630
630
|
return { file: null, files: [], scope: SCOPE.UNREADABLE };
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Is `name` the phase's requirements SPEC (#5629)? A plain `NN-SPEC.md`: the `-AI-SPEC.md` (AI design
|
|
635
|
+
* contract) and `-UI-SPEC.md` (UI design contract) variants have other purposes and never lock requirements.
|
|
636
|
+
* The one definition behind the init payloads' `has_spec` / `spec_path`, the discuss and plan-phase workflows
|
|
637
|
+
* (which read those fields) and the context-drift gate, so they cannot disagree about which file is "the SPEC".
|
|
638
|
+
*/
|
|
639
|
+
function isPhaseSpecFile(name) {
|
|
640
|
+
return name.endsWith('-SPEC.md') && !name.endsWith('-AI-SPEC.md') && !name.endsWith('-UI-SPEC.md');
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* The phase SPEC in an already-read directory listing, or `null`. The lexically first match, so the answer
|
|
644
|
+
* does not depend on the filesystem's readdir order. The caller's array is not reordered.
|
|
645
|
+
*/
|
|
646
|
+
function findPhaseSpecIn(files) {
|
|
647
|
+
return files.filter(isPhaseSpecFile).sort()[0] ?? null;
|
|
648
|
+
}
|
|
633
649
|
module.exports = {
|
|
634
650
|
worktreesOptedOut,
|
|
635
651
|
readScopedConfigValue,
|
|
@@ -655,6 +671,8 @@ module.exports = {
|
|
|
655
671
|
diagnoseUnresolvedActiveWorkstream,
|
|
656
672
|
describeUnresolvedWorkstreamReason,
|
|
657
673
|
findContextMdIn,
|
|
674
|
+
isPhaseSpecFile,
|
|
675
|
+
findPhaseSpecIn,
|
|
658
676
|
// Test seam (audit M1): inject a deterministic isPidAlive so the liveness-gated
|
|
659
677
|
// steal decision is exercised without real pids. Mirrors capability-lock.cts.
|
|
660
678
|
_setLockProbes(probes) {
|
|
@@ -34,6 +34,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
34
34
|
const node_path_1 = __importDefault(require("node:path"));
|
|
35
35
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
36
36
|
const node_child_process_1 = require("node:child_process");
|
|
37
|
+
const read_only_git_env_cjs_1 = require("./read-only-git-env.cjs");
|
|
37
38
|
/**
|
|
38
39
|
* SHA-256 hex digest of a file's raw bytes. Byte-for-byte the same digest
|
|
39
40
|
* install.js fileHash() records into manifests and backup-meta.json.
|
|
@@ -144,13 +145,20 @@ function isCleanRelativePosixPath(relPath) {
|
|
|
144
145
|
}
|
|
145
146
|
function gitExec(gitDir, args) {
|
|
146
147
|
return (0, node_child_process_1.execFileSync)('git', args, {
|
|
148
|
+
// windowsHide (#685): a console-window flash per git call would spam the
|
|
149
|
+
// user on Windows for what is a background, read-only history walk. Kept
|
|
150
|
+
// high in the options so the completeness guard's 400-char scan finds it.
|
|
151
|
+
windowsHide: true,
|
|
147
152
|
cwd: gitDir,
|
|
148
153
|
encoding: 'utf8',
|
|
154
|
+
// #5048: every call here is a history/objects walk that never opens the
|
|
155
|
+
// index, so the variable is inert today — set anyway so a future `status` /
|
|
156
|
+
// `diff` caller inherits it. The block is owned by
|
|
157
|
+
// src/read-only-git-env.cts and shared with every other read-only seam.
|
|
158
|
+
// Parity: tests/git-optional-locks-parity.test.cjs.
|
|
159
|
+
env: (0, read_only_git_env_cjs_1.readOnlyGitEnv)(),
|
|
149
160
|
timeout: GIT_SUBPROCESS_TIMEOUT_MS,
|
|
150
161
|
maxBuffer: GIT_MAX_BUFFER_BYTES,
|
|
151
|
-
// windowsHide (#685): a console-window flash per git call would spam the
|
|
152
|
-
// user on Windows for what is a background, read-only history walk.
|
|
153
|
-
windowsHide: true,
|
|
154
162
|
// stderr is discarded: "file absent in commit" is an expected walk outcome,
|
|
155
163
|
// not operator-visible diagnostics.
|
|
156
164
|
stdio: ['ignore', 'pipe', 'ignore'],
|