@opengsd/gsd-core 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +12 -0
- package/agents/gsd-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +458 -0
- package/agents/gsd-code-fixer.md +5 -5
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +345 -0
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +63 -35
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-plan-checker.md +76 -57
- package/agents/gsd-planner.md +14 -0
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-checker.md +19 -3
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +29 -0
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +23 -1
- package/bin/install.js +444 -134
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +1 -0
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/ns-workflow.md +2 -1
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +2 -1
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/quick-batch.md +105 -0
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/surface.md +18 -8
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +532 -174
- package/gsd-core/bin/lib/adr-parser.cjs +1 -1
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +39 -22
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-activation.cjs +27 -0
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +528 -116
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-state.cjs +7 -1
- package/gsd-core/bin/lib/capability-validator.cjs +134 -5
- package/gsd-core/bin/lib/capability-writer.cjs +14 -4
- package/gsd-core/bin/lib/check-command-router.cjs +198 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
- package/gsd-core/bin/lib/clusters.cjs +1 -0
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/command-aliases.cjs +16 -0
- package/gsd-core/bin/lib/commands.cjs +981 -79
- package/gsd-core/bin/lib/config-loader.cjs +4 -0
- package/gsd-core/bin/lib/config.cjs +153 -38
- package/gsd-core/bin/lib/core-utils.cjs +34 -7
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +343 -28
- package/gsd-core/bin/lib/edge-probe.cjs +14 -1
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +137 -23
- package/gsd-core/bin/lib/gap-checker.cjs +22 -13
- package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-integration.cjs +57 -5
- package/gsd-core/bin/lib/init-command-router.cjs +14 -0
- package/gsd-core/bin/lib/init.cjs +539 -60
- package/gsd-core/bin/lib/install-engine.cjs +199 -14
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
- package/gsd-core/bin/lib/install-profiles.cjs +36 -14
- package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
- package/gsd-core/bin/lib/io.cjs +35 -0
- package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
- package/gsd-core/bin/lib/markdown-table.cjs +123 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +41 -10
- package/gsd-core/bin/lib/model-resolver.cjs +101 -10
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
- package/gsd-core/bin/lib/phase-id.cjs +412 -31
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase.cjs +941 -98
- package/gsd-core/bin/lib/plan-document.cjs +10 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
- package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
- package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
- package/gsd-core/bin/lib/quick-batch.cjs +840 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
- package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
- package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
- package/gsd-core/bin/lib/roadmap.cjs +292 -69
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
- package/gsd-core/bin/lib/smart-entry.cjs +7 -9
- package/gsd-core/bin/lib/state-document.cjs +159 -32
- package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
- package/gsd-core/bin/lib/state-transition.cjs +465 -62
- package/gsd-core/bin/lib/state.cjs +906 -151
- package/gsd-core/bin/lib/surface.cjs +83 -10
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
- package/gsd-core/bin/lib/uat.cjs +1420 -516
- package/gsd-core/bin/lib/update-context.cjs +36 -26
- package/gsd-core/bin/lib/validate.cjs +230 -12
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +316 -23
- package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
- package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
- package/gsd-core/bin/lib/verify.cjs +531 -36
- package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
- package/gsd-core/references/agent-contracts.md +3 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- package/gsd-core/references/edge-probe.md +17 -13
- package/gsd-core/references/execute-mvp-tdd.md +18 -16
- package/gsd-core/references/execute-phase-response-language.md +6 -0
- package/gsd-core/references/executor-examples.md +42 -0
- package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
- package/gsd-core/references/loop-hook-dispatch.md +18 -0
- package/gsd-core/references/model-profiles.md +12 -3
- package/gsd-core/references/mvp-concepts.md +2 -2
- package/gsd-core/references/plan-checker-examples.md +41 -0
- package/gsd-core/references/planner-antipatterns.md +25 -0
- package/gsd-core/references/planner-chunked.md +5 -1
- package/gsd-core/references/planner-coupling.md +42 -0
- package/gsd-core/references/planner-quick-batch.md +71 -0
- package/gsd-core/references/planner-reviews.md +47 -0
- package/gsd-core/references/planner-revision.md +75 -2
- package/gsd-core/references/planning-config.md +5 -1
- package/gsd-core/references/response-language-directive.md +9 -0
- package/gsd-core/references/revision-loop.md +118 -11
- package/gsd-core/references/tdd.md +17 -9
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verifier-evidence-gate.md +160 -0
- package/gsd-core/references/worktree-path-safety.md +112 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/phase-prompt.md +4 -0
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- package/gsd-core/templates/verification-report.md +5 -0
- package/gsd-core/workflows/add-backlog.md +2 -0
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +1 -1
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +1 -1
- package/gsd-core/workflows/analyze-dependencies.md +2 -0
- package/gsd-core/workflows/audit-fix.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +2 -0
- package/gsd-core/workflows/audit-uat.md +2 -0
- package/gsd-core/workflows/autonomous.md +15 -10
- package/gsd-core/workflows/check-todos.md +5 -3
- package/gsd-core/workflows/cleanup.md +4 -2
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
- package/gsd-core/workflows/code-review-fix.md +5 -3
- package/gsd-core/workflows/code-review.md +211 -43
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +40 -254
- package/gsd-core/workflows/debug.md +1 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
- package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
- package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
- package/gsd-core/workflows/discuss-phase-power.md +2 -0
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +43 -13
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +15 -156
- package/gsd-core/workflows/edit-phase.md +2 -0
- package/gsd-core/workflows/eval-review.md +1 -1
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
- package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
- package/gsd-core/workflows/execute-phase.md +78 -159
- package/gsd-core/workflows/execute-plan.md +28 -15
- package/gsd-core/workflows/explore.md +2 -0
- package/gsd-core/workflows/extract-learnings.md +2 -0
- package/gsd-core/workflows/fast.md +6 -0
- package/gsd-core/workflows/forensics.md +2 -0
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +1 -1
- package/gsd-core/workflows/help/modes/brief.md +2 -0
- package/gsd-core/workflows/help/modes/default.md +2 -0
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +12 -0
- package/gsd-core/workflows/help/modes/topic.md +2 -0
- package/gsd-core/workflows/help.md +3 -1
- package/gsd-core/workflows/import.md +3 -3
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- package/gsd-core/workflows/insert-phase.md +2 -0
- package/gsd-core/workflows/list-phase-assumptions.md +2 -0
- package/gsd-core/workflows/list-seeds.md +2 -0
- package/gsd-core/workflows/list-workspaces.md +2 -0
- package/gsd-core/workflows/manager.md +3 -3
- package/gsd-core/workflows/map-codebase.md +52 -3
- package/gsd-core/workflows/milestone-summary.md +2 -0
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +55 -13
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- package/gsd-core/workflows/new-project.md +37 -205
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +2 -0
- package/gsd-core/workflows/node-repair.md +2 -0
- package/gsd-core/workflows/note.md +2 -0
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +19 -4
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
- package/gsd-core/workflows/plan-phase.md +144 -185
- package/gsd-core/workflows/plan-review-convergence.md +102 -10
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +30 -10
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
- package/gsd-core/workflows/progress.md +25 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
- package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
- package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
- package/gsd-core/workflows/quick-batch.md +203 -0
- package/gsd-core/workflows/quick.md +21 -4
- package/gsd-core/workflows/reapply-patches.md +79 -3
- package/gsd-core/workflows/remove-phase.md +2 -0
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +6 -2
- package/gsd-core/workflows/review.md +215 -10
- package/gsd-core/workflows/scan.md +2 -0
- package/gsd-core/workflows/section-manifest.json +12 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -0
- package/gsd-core/workflows/settings-advanced.md +2 -0
- package/gsd-core/workflows/settings-integrations.md +9 -8
- package/gsd-core/workflows/settings.md +19 -6
- package/gsd-core/workflows/ship.md +10 -10
- package/gsd-core/workflows/sketch-wrap-up.md +2 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +1 -1
- package/gsd-core/workflows/spec-phase.md +24 -19
- package/gsd-core/workflows/spike-wrap-up.md +2 -0
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +2 -0
- package/gsd-core/workflows/sync-skills.md +12 -4
- package/gsd-core/workflows/thread.md +2 -0
- package/gsd-core/workflows/transition.md +2 -0
- package/gsd-core/workflows/ui-phase.md +26 -5
- package/gsd-core/workflows/ui-review.md +1 -1
- package/gsd-core/workflows/ultraplan-phase.md +2 -0
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/update.md +48 -43
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work.md +68 -182
- package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
- package/hooks/dist/gsd-check-update-worker.js +19 -2
- package/hooks/dist/gsd-context-monitor.js +371 -27
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-node-runner.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +30 -5
- package/hooks/dist/gsd-read-guard.js +2 -0
- package/hooks/dist/gsd-read-injection-scanner.js +5 -5
- package/hooks/dist/gsd-secret-read-guard.js +1105 -0
- package/hooks/dist/gsd-statusline.js +18 -10
- package/hooks/dist/gsd-validate-commit.sh +474 -7
- package/hooks/dist/gsd-workflow-guard.js +2 -1
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/git-cmd.js +210 -1
- package/hooks/dist/lib/injection-patterns.js +36 -6
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +42 -16
- package/hooks/gsd-check-update-worker.js +19 -2
- package/hooks/gsd-context-monitor.js +371 -27
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-node-runner.sh +1 -0
- package/hooks/gsd-prompt-guard.js +30 -5
- package/hooks/gsd-read-guard.js +2 -0
- package/hooks/gsd-read-injection-scanner.js +5 -5
- package/hooks/gsd-secret-read-guard.js +1105 -0
- package/hooks/gsd-statusline.js +18 -10
- package/hooks/gsd-validate-commit.sh +474 -7
- package/hooks/gsd-workflow-guard.js +2 -1
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/hooks.json +6 -0
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/git-cmd.js +210 -1
- package/hooks/lib/injection-patterns.js +36 -6
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +13 -9
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +11 -4
- package/scripts/check-contract-drift.cjs +4 -1
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +133 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +17 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +67 -15
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
- package/scripts/lib/shellcheck-fetch.cjs +247 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
- package/scripts/lint-phase-enumeration-drift.cjs +24 -6
- package/scripts/lint-phase-id-drift.cjs +465 -15
- package/scripts/lint-portable-grep.cjs +176 -0
- package/scripts/lint-response-language-coverage.cjs +530 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-test-file-count.allowlist.json +4 -1
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
- package/scripts/lint-workflow-shellcheck.cjs +614 -0
- package/scripts/npm-audit-baseline.cjs +376 -0
- package/scripts/prompt-injection-scan.sh +22 -0
- package/scripts/require-issue-link-policy.cjs +16 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +1 -0
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-ns-workflow/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +2 -1
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-quick-batch/SKILL.md +105 -0
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-surface/SKILL.md +18 -8
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -16,6 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.STATE_MD_SECTIONS = exports.FRONTMATTER_BODY_SOURCE = exports.FIELD_CLASSIFICATION = void 0;
|
|
19
|
+
exports.formatProgressMachineSegment = formatProgressMachineSegment;
|
|
20
|
+
exports.stateReplaceProgressPercent = stateReplaceProgressPercent;
|
|
19
21
|
exports.beginFrontmatterReassembly = beginFrontmatterReassembly;
|
|
20
22
|
exports.getFrontmatterBodySource = getFrontmatterBodySource;
|
|
21
23
|
exports.frontmatterKeyForBodyField = frontmatterKeyForBodyField;
|
|
@@ -34,10 +36,72 @@ const state_document_cjs_2 = require("./state-document.cjs");
|
|
|
34
36
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
35
37
|
const phase_lifecycle_cjs_1 = require("./phase-lifecycle.cjs");
|
|
36
38
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
39
|
+
// #4129: the completion-ratio kernel for the resync-arm ratchet's percent
|
|
40
|
+
// (planning-scope's SCOPE — state-document's own dependency, no cycle here:
|
|
41
|
+
// state-document never imports this module).
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
43
|
+
const planningScopeMod = require("./planning-scope.cjs");
|
|
37
44
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
45
|
const stateMdSchemaMod = require("./state-md-schema.cjs");
|
|
39
46
|
const { STATE_FIELD_SCHEMA } = stateMdSchemaMod;
|
|
40
47
|
const { extractFrontmatter, reconstructFrontmatter, stripFrontmatter, FRONTMATTER_UNPARSEABLE } = frontmatter;
|
|
48
|
+
function formatProgressMachineSegment(percent) {
|
|
49
|
+
// ADR-3180 Decision 7: rounding and the 100 ceiling belong to the
|
|
50
|
+
// completion-ratio kernel. The floor is added here because this helper is
|
|
51
|
+
// also fed persisted frontmatter values (hand-editable, unlike the
|
|
52
|
+
// count-shaped entries into that kernel). Bar and printed percent use the
|
|
53
|
+
// clamped value so the two halves of the segment can never disagree.
|
|
54
|
+
// #4294: the CELL count is the render kernel's — `renderProgressBar` holds a
|
|
55
|
+
// sub-100 percent one cell short of full, so `[██████████]` beside `95%`
|
|
56
|
+
// cannot recur here as a seventh inline copy of the rounding.
|
|
57
|
+
const clamped = Math.max(0, (0, phase_lifecycle_cjs_1.clampPercentFromFraction)(percent / 100));
|
|
58
|
+
return `[${(0, phase_lifecycle_cjs_1.renderProgressBar)(clamped, 10)}] ${clamped}%`;
|
|
59
|
+
}
|
|
60
|
+
// Consumers (a future STATE.md writer that bypasses all three reintroduces the
|
|
61
|
+
// #4213 divergence class): `cmdStateUpdateProgress` and `syncCore`'s progress
|
|
62
|
+
// intent (both in this module) plus the post-sync body reconciliation in
|
|
63
|
+
// `applyPostSyncPreservation` (src/state.cts). `cmdStateSync` never reaches
|
|
64
|
+
// that reconciliation — ADR-3408 §8.3: `state sync` lets the body win, so
|
|
65
|
+
// preservation must NOT run — which is why its correctness comes from
|
|
66
|
+
// `syncCore`'s call here.
|
|
67
|
+
function stateReplaceProgressPercent(content, percent) {
|
|
68
|
+
const body = stripFrontmatter(content);
|
|
69
|
+
// #2177: bold `**Progress:**` takes priority over the plain `^Progress:`
|
|
70
|
+
// form, so an earlier free-text line starting with `Progress:` cannot
|
|
71
|
+
// capture the rewrite ahead of the real status line.
|
|
72
|
+
//
|
|
73
|
+
// #4243 (follow-up to #4453, maintainer ruling 2026-09-07): the bold form
|
|
74
|
+
// is also ANCHORED to line start, with same-line leading whitespace only —
|
|
75
|
+
// the exact idiom #4453 applied to stateReplaceField's bold branch. The
|
|
76
|
+
// pre-fix pattern carried no `^` and no `m` flag, so a bold percent-ish
|
|
77
|
+
// label quoted MID-SENTENCE inside prose (an Accumulated Context bullet
|
|
78
|
+
// mentioning `**Progress:**`) captured the machine-segment rewrite and
|
|
79
|
+
// destroyed the rest of its line, silently, while the real Progress line
|
|
80
|
+
// stayed stale — every caller (cmdStateUpdateProgress, syncCore's percent
|
|
81
|
+
// arm, applyPostSyncPreservation) feeds the whole document. #2177's own
|
|
82
|
+
// recorded requirements are unaffected: the frontmatter is stripped before
|
|
83
|
+
// matching (its defect was the YAML `progress:` key shadowing the body
|
|
84
|
+
// line), the suffix-preserving machine-segment swap is untouched, and the
|
|
85
|
+
// bold-beats-plain priority now governs LINE-START forms. The leading class
|
|
86
|
+
// is `[ \t]*`, deliberately NOT `\s*` — `^\s*\*\*` can consume the newlines
|
|
87
|
+
// before the label into the match and drop them on rebuild (#4010's
|
|
88
|
+
// same-line confinement hazard). `$` is explicit-and-inert (`[^\r\n]*`
|
|
89
|
+
// never crosses line terminators) and documents that the match ends at
|
|
90
|
+
// end-of-line.
|
|
91
|
+
const boldProgressPattern = /^([ \t]*\*\*Progress:\*\*[ \t]*)([^\r\n]*)$/im;
|
|
92
|
+
const plainProgressPattern = /^(Progress:[ \t]*)([^\r\n]*)/im;
|
|
93
|
+
const pattern = boldProgressPattern.test(body)
|
|
94
|
+
? boldProgressPattern
|
|
95
|
+
: plainProgressPattern.test(body)
|
|
96
|
+
? plainProgressPattern
|
|
97
|
+
: null;
|
|
98
|
+
if (!pattern)
|
|
99
|
+
return null;
|
|
100
|
+
const machineSegment = /(?:\[[^\]\r\n]*\][ \t]*)?\d{1,3}%/;
|
|
101
|
+
const progress = formatProgressMachineSegment(percent);
|
|
102
|
+
const updatedBody = body.replace(pattern, (_match, prefix, value) => (`${prefix}${machineSegment.test(value) ? value.replace(machineSegment, progress) : progress}`));
|
|
103
|
+
return content.slice(0, content.length - body.length) + updatedBody;
|
|
104
|
+
}
|
|
41
105
|
/**
|
|
42
106
|
* ADR-3473 §8.1 (#3881, consequence 2 wiring): does `existingFm` carry the
|
|
43
107
|
* `FRONTMATTER_UNPARSEABLE` marker `extractFrontmatter` sets when a
|
|
@@ -468,6 +532,72 @@ function preservedValuesEqual(a, b) {
|
|
|
468
532
|
}
|
|
469
533
|
return a === b;
|
|
470
534
|
}
|
|
535
|
+
/**
|
|
536
|
+
* #4129: the resync-arm progress merge. A resyncing write whose scan MEASURED
|
|
537
|
+
* something no longer wholesale-replaces the curated block — the declared
|
|
538
|
+
* `progress-ratchet` mergeStrategy ("completed_plans/completed_phases only ever
|
|
539
|
+
* ratchet UP toward the derived value (#2969)", state-md-schema.cts) now holds
|
|
540
|
+
* on the write path too, matching what the read path (`shouldPreserveExistingProgress`)
|
|
541
|
+
* has always enforced. Rules, mirroring the `deriveProgressKeys` branch above:
|
|
542
|
+
*
|
|
543
|
+
* - total_plans / total_phases always take the derived value (#2440 — totals
|
|
544
|
+
* correct in BOTH directions).
|
|
545
|
+
* - completed_plans / completed_phases take the derived value only when it is
|
|
546
|
+
* strictly GREATER (#2969's `>` not `>=`); else the curated value survives
|
|
547
|
+
* (a hand-corrected or previously-correct counter can never be re-derived
|
|
548
|
+
* downward — the #4129 clobber).
|
|
549
|
+
* - any other key keeps the curated value (the existing branch's convention).
|
|
550
|
+
* - percent is RECOMPUTED from the merged counters through the single kernel
|
|
551
|
+
* (`computeProgressPercent`), because either side's stored percent was
|
|
552
|
+
* computed against that side's counters and the merged block may mix them
|
|
553
|
+
* (curated completed, derived totals). Recomputation runs ONLY when the
|
|
554
|
+
* derived block itself carried a percent — an upstream withhold
|
|
555
|
+
* (#1761 milestone-unbounded, #3217 scope) nulled percent deliberately and
|
|
556
|
+
* this merge must not resurrect it.
|
|
557
|
+
*
|
|
558
|
+
* Frontmatter scalars arrive as STRINGS ("2", not 2), so every comparison
|
|
559
|
+
* coerces through `toFiniteNumber` — never a `typeof === 'number'` test
|
|
560
|
+
* (scanMeasuredSomething's own convention).
|
|
561
|
+
*/
|
|
562
|
+
function mergeResyncProgressRatchet(curatedRecord, derivedRecord) {
|
|
563
|
+
const merged = { ...derivedRecord };
|
|
564
|
+
for (const [key, value] of Object.entries(curatedRecord)) {
|
|
565
|
+
if (key === 'total_plans' || key === 'total_phases' || key === 'percent')
|
|
566
|
+
continue;
|
|
567
|
+
if (key === 'completed_plans' || key === 'completed_phases') {
|
|
568
|
+
const derivedNum = (0, state_document_cjs_2.toFiniteNumber)(derivedRecord[key]) ?? -Infinity;
|
|
569
|
+
const curatedNum = (0, state_document_cjs_2.toFiniteNumber)(value) ?? -Infinity;
|
|
570
|
+
// Ratchet up only (strictly greater, #2969); else keep curated.
|
|
571
|
+
if (derivedNum > curatedNum)
|
|
572
|
+
continue;
|
|
573
|
+
// Numerically EQUAL keeps the derived value VERBATIM. The two sides
|
|
574
|
+
// arrive in different scalar shapes (the re-parsed derived block
|
|
575
|
+
// carries string totals "2" while the curated snapshot carries numbers
|
|
576
|
+
// 2), and substituting the curated spelling over an equal derived one
|
|
577
|
+
// is a no-op in substance but a shape churn the §8.7 reporting loop
|
|
578
|
+
// would surface as a phantom `preserved-over-disagreeing-derived`
|
|
579
|
+
// warning (it diffs structurally). Only a curated counter that is
|
|
580
|
+
// STRICTLY greater replaces the derived value.
|
|
581
|
+
if (derivedNum === curatedNum)
|
|
582
|
+
continue;
|
|
583
|
+
merged[key] = value;
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
merged[key] = value;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
if ((0, state_document_cjs_2.toFiniteNumber)(derivedRecord.percent) !== null) {
|
|
590
|
+
const recomputed = (0, state_document_cjs_2.computeProgressPercent)((0, state_document_cjs_2.toFiniteNumber)(merged.completed_plans), (0, state_document_cjs_2.toFiniteNumber)(merged.total_plans), (0, state_document_cjs_2.toFiniteNumber)(merged.completed_phases), (0, state_document_cjs_2.toFiniteNumber)(merged.total_phases), planningScopeMod.SCOPE.COMPLETE);
|
|
591
|
+
// Same verbatim rule for percent: assign only when the recomputed value
|
|
592
|
+
// numerically differs, so a string-spelled derived percent ("67") is not
|
|
593
|
+
// churned into a number-spelled 67 (phantom-divergence noise, not a
|
|
594
|
+
// change).
|
|
595
|
+
if (recomputed !== null && recomputed !== (0, state_document_cjs_2.toFiniteNumber)(merged.percent)) {
|
|
596
|
+
merged.percent = recomputed;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
return merged;
|
|
600
|
+
}
|
|
471
601
|
/**
|
|
472
602
|
* Executor for `preservation: 'preserve-always'` (ADR-3408 §8.1). Only
|
|
473
603
|
* `progress` carries this policy today. Preserves #3242/#1446/#2440/#2969
|
|
@@ -483,25 +613,39 @@ function applyPreserveAlways(field, cls, ctx) {
|
|
|
483
613
|
const derived = ctx.postFm[field];
|
|
484
614
|
const derivedMeasured = scanMeasuredSomething(cls, derived);
|
|
485
615
|
const curatedMeasured = scanMeasuredSomething(cls, curated);
|
|
486
|
-
// On a resyncing write the fresh derivation is authoritative
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
//
|
|
493
|
-
//
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
|
|
616
|
+
// On a resyncing write the fresh derivation is authoritative in two cases
|
|
617
|
+
// (#3756 / ADR-3473 §8.6, unchanged): when the caller EXPLICITLY named a
|
|
618
|
+
// progress-affecting field (`preserve-always`'s contract is "never
|
|
619
|
+
// overwrite unless the caller explicitly names this field" — `state update
|
|
620
|
+
// Progress` is exactly that naming, pre-existing #3242 behavior), and when
|
|
621
|
+
// the derivation measured something the curated block did not (an
|
|
622
|
+
// unmeasured CURATED block is not worth protecting). The unmeasured-DERIVED
|
|
623
|
+
// guard also stands: an incidental resync (e.g. `state add-decision`, whose
|
|
624
|
+
// `resync` defaults true for reasons that have nothing to do with
|
|
625
|
+
// `progress`) that measured nothing must not drop a real curated block
|
|
626
|
+
// (#3756's archived-milestone case) — that falls through to the wholesale
|
|
627
|
+
// restore below.
|
|
628
|
+
//
|
|
629
|
+
// #4129 narrows the remaining arm. A resyncing write whose scan MEASURED
|
|
630
|
+
// something while the curated block is also real previously wholesale-
|
|
631
|
+
// replaced the curated block with the derived one — no monotonic guard, so
|
|
632
|
+
// any under-counting derivation (a stale-dated verification, #2348) silently
|
|
633
|
+
// reverted every hand-correction and every correct value an earlier write
|
|
634
|
+
// had persisted, while the read path (`shouldPreserveExistingProgress`)
|
|
635
|
+
// kept reporting the higher stored counters. That arm now falls through to
|
|
636
|
+
// `mergeResyncProgressRatchet` — the declared `progress-ratchet`
|
|
637
|
+
// mergeStrategy, finally enforced on the write path: totals derived both
|
|
638
|
+
// directions (#2440), completed counters up-only (#2969), percent
|
|
639
|
+
// recomputed from the merged counters.
|
|
640
|
+
if (ctx.resync && (ctx.explicitProgressField || (derivedMeasured && !curatedMeasured)))
|
|
502
641
|
return;
|
|
503
642
|
let next;
|
|
504
|
-
if (cls.mergeStrategy === 'progress-ratchet' && ctx.
|
|
643
|
+
if (cls.mergeStrategy === 'progress-ratchet' && ctx.resync && derived && derivedMeasured && curatedMeasured) {
|
|
644
|
+
// #4129 resync arm — see mergeResyncProgressRatchet's doc. Reached only
|
|
645
|
+
// after the early-out above, so curatedMeasured is guaranteed true here.
|
|
646
|
+
next = mergeResyncProgressRatchet(curated, (derived ?? {}));
|
|
647
|
+
}
|
|
648
|
+
else if (cls.mergeStrategy === 'progress-ratchet' && ctx.deriveProgressKeys && derived && derivedMeasured) {
|
|
505
649
|
// #2440: total_plans and total_phases always take the derived (post-sync)
|
|
506
650
|
// value even under !resync. This is used by cmdStatePlannedPhase where
|
|
507
651
|
// total_plans must correct upward after plans are added. For body-only
|
|
@@ -784,7 +928,17 @@ function beginPhaseCore(content, intent, deps) {
|
|
|
784
928
|
const focusLabel = intent.phaseName
|
|
785
929
|
? `Phase ${intent.phaseNumber} — ${intent.phaseName}`
|
|
786
930
|
: `Phase ${intent.phaseNumber}`;
|
|
787
|
-
|
|
931
|
+
// #4469: anchored to line start with same-line whitespace only, mirroring
|
|
932
|
+
// #4243/PR #4453's fix to stateReplaceField's bold branch. The pre-fix
|
|
933
|
+
// pattern carried no `^`/`m` and used `\s*` (crosses newlines), so a bold
|
|
934
|
+
// `**Current focus:**` quoted mid-sentence elsewhere in the body (e.g. an
|
|
935
|
+
// Accumulated Context bullet) matched first and had the rest of its line
|
|
936
|
+
// silently overwritten with the new focus label -- the same #4010
|
|
937
|
+
// data-loss class. `[ \t]*` (not `\s*`) avoids consuming the newlines
|
|
938
|
+
// before the label into the match; `$` documents the match ends at
|
|
939
|
+
// end-of-line (inert here since `.` never crosses line terminators
|
|
940
|
+
// without `/s`, which is not set).
|
|
941
|
+
const focusPattern = /^([ \t]*\*\*Current focus:\*\*[ \t]*)(.*)$/im;
|
|
788
942
|
if (focusPattern.test(body)) {
|
|
789
943
|
body = body.replace(focusPattern, (_match, prefix) => `${prefix}${focusLabel}`);
|
|
790
944
|
updated.push('Current focus');
|
|
@@ -955,6 +1109,46 @@ function mutateCurrentPositionResume(body, intent, today, updated) {
|
|
|
955
1109
|
}
|
|
956
1110
|
return body.slice(0, span.start) + sectionBody + body.slice(span.end);
|
|
957
1111
|
}
|
|
1112
|
+
const PLAN_SHAPE_N = /^(\d+)(?:\s.*)?$/;
|
|
1113
|
+
const PLAN_SHAPE_N_OF_M = /^(\d+)\s+of\s+(\d+)(?:\s.*)?$/;
|
|
1114
|
+
/**
|
|
1115
|
+
* Parse a decimal group into a plan number, or `null` if it is not a value we
|
|
1116
|
+
* are willing to do arithmetic on.
|
|
1117
|
+
*
|
|
1118
|
+
* `parseInt` is deliberately not used on the raw field: it truncates (`"2 of 5"`
|
|
1119
|
+
* -> 2), accepts a sign (`"+2"`), and silently loses precision past
|
|
1120
|
+
* `Number.MAX_SAFE_INTEGER`, where the number we report and the string we write
|
|
1121
|
+
* back stop agreeing. The grammars above already exclude signs and trailing
|
|
1122
|
+
* text, so the only remaining hazard is magnitude.
|
|
1123
|
+
*/
|
|
1124
|
+
function planNumberFrom(digits) {
|
|
1125
|
+
const n = Number(digits);
|
|
1126
|
+
return Number.isSafeInteger(n) ? n : null;
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Advance the leading integer of a written plan value, preserving everything
|
|
1130
|
+
* the author wrote around it: the zero-padding width ("04" -> "05") and any
|
|
1131
|
+
* trailing remainder ("2 of 99" -> "3 of 99", and the `\r` of a CRLF file).
|
|
1132
|
+
*
|
|
1133
|
+
* The three parse branches disagree about the field NAME and about whether a
|
|
1134
|
+
* total is carried inline, but they agree completely about this: only the
|
|
1135
|
+
* leading digits are the plan number, and nothing else on the line belongs to
|
|
1136
|
+
* this transition. Writing `String(newPlan)` instead — as the legacy branch
|
|
1137
|
+
* did — discards the author's text on a branch nobody was reading.
|
|
1138
|
+
*
|
|
1139
|
+
* padStart never truncates, so 09 -> 10 widens rather than clipping.
|
|
1140
|
+
*/
|
|
1141
|
+
function bumpLeadingNumber(raw, next) {
|
|
1142
|
+
const digits = /^\d+/.exec(raw);
|
|
1143
|
+
// Total rather than pass-through. `raw.replace(/^\d+/, …)` returns the input
|
|
1144
|
+
// unchanged when there are no leading digits, so `+2` advanced in `data` and
|
|
1145
|
+
// wrote the file untouched — the command reported progress it had not made
|
|
1146
|
+
// and could be re-run forever. The grammars make that unreachable today;
|
|
1147
|
+
// returning null keeps it unreachable if a fourth branch is ever added.
|
|
1148
|
+
if (!digits)
|
|
1149
|
+
return null;
|
|
1150
|
+
return raw.replace(/^\d+/, () => String(next).padStart(digits[0].length, '0'));
|
|
1151
|
+
}
|
|
958
1152
|
/**
|
|
959
1153
|
* Update fields within the ## Current Position section for advancePlan.
|
|
960
1154
|
* Mirrors `updateCurrentPositionFields` (state.cts:496) byte-for-behaviour:
|
|
@@ -1006,19 +1200,80 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
|
|
|
1006
1200
|
mutated = true;
|
|
1007
1201
|
}
|
|
1008
1202
|
}
|
|
1009
|
-
if (fields.plan) {
|
|
1203
|
+
if (fields.plan || fields.currentPlan) {
|
|
1010
1204
|
// Plan is always replaced — system-derived, not executor-authored.
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1205
|
+
//
|
|
1206
|
+
// Which NAME to write is decided by what the SECTION carries, not by which
|
|
1207
|
+
// header field the value was read from. Mirroring the header was wrong in
|
|
1208
|
+
// both directions: a legacy header with a `Current Plan:` section line left
|
|
1209
|
+
// the section a plan behind, and a hybrid header with a `Plan:` section line
|
|
1210
|
+
// mutated nothing at all. The invariant is per-name — every site spelled
|
|
1211
|
+
// `Current Plan` gets the `Current Plan` value, every `Plan` site gets the
|
|
1212
|
+
// `Plan` value — so both are passed in and each is written where its own
|
|
1213
|
+
// name appears.
|
|
1214
|
+
//
|
|
1215
|
+
// Title-Case LITERALS reach both the regex and stateReplaceField
|
|
1216
|
+
// (ADR-3408 §8.3(b)): a literal cannot collide with a lowercase/snake_case
|
|
1217
|
+
// frontmatter key, whatever the caller passed.
|
|
1218
|
+
//
|
|
1219
|
+
// The replacements go through a replacer FUNCTION, never a replacement
|
|
1220
|
+
// string. `fields.plan` is derived from file content, and `String.replace`
|
|
1221
|
+
// expands `$&`, `` $` `` and `$'` in a replacement string — a STATE.md
|
|
1222
|
+
// carrying `Current Plan: 04 of 06 $&` would splice part of itself into the
|
|
1223
|
+
// document. `stateReplaceField` already uses a function for this reason;
|
|
1224
|
+
// these arms now agree with it.
|
|
1225
|
+
// Each name is written INDEPENDENTLY, and each falls back on its own.
|
|
1226
|
+
//
|
|
1227
|
+
// Two defects lived in the previous shape, both of which produced the
|
|
1228
|
+
// split-brain document this arm exists to prevent:
|
|
1229
|
+
//
|
|
1230
|
+
// - The fallback was guarded by `!mutated`, and `mutated` is FUNCTION-wide
|
|
1231
|
+
// — already set by the `phase`/`status`/`lastActivity` arms above, which
|
|
1232
|
+
// `advancePlanCore` always populates. A section spelled `**Current
|
|
1233
|
+
// Plan:**` (bold) or as a pipe-table row therefore skipped its fallback
|
|
1234
|
+
// because an UNRELATED field had been refreshed, and the section stayed a
|
|
1235
|
+
// plan behind the header.
|
|
1236
|
+
// - The fallback then picked ONE name by ternary. In the legacy shape both
|
|
1237
|
+
// values are populated, so it always chose `Current Plan` and a
|
|
1238
|
+
// `**Plan:**` section line — which base did write — got nothing.
|
|
1239
|
+
//
|
|
1240
|
+
// `planWritten` is local, so nothing outside this arm can satisfy its guard.
|
|
1241
|
+
let planWritten = false;
|
|
1242
|
+
const writePlanField = (name, value) => {
|
|
1243
|
+
if (!value)
|
|
1244
|
+
return;
|
|
1245
|
+
// Plain `Name:` line first. Title-Case LITERALS reach both the regex and
|
|
1246
|
+
// stateReplaceField (ADR-3408 §8.3(b)), and the replacement goes through a
|
|
1247
|
+
// replacer FUNCTION so a `$&` / `` $` `` / `$'` in the author's text is not
|
|
1248
|
+
// expanded into the document.
|
|
1249
|
+
if (name === 'Current Plan') {
|
|
1250
|
+
if (/^Current Plan:/m.test(sectionBody)) {
|
|
1251
|
+
sectionBody = sectionBody.replace(/^Current Plan:.*$/m, () => `Current Plan: ${value}`);
|
|
1252
|
+
planWritten = true;
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
const replaced = (0, state_document_cjs_1.stateReplaceField)(sectionBody, 'Current Plan', value);
|
|
1256
|
+
if (replaced !== null) {
|
|
1257
|
+
sectionBody = replaced;
|
|
1258
|
+
planWritten = true;
|
|
1259
|
+
}
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
if (/^Plan:/m.test(sectionBody)) {
|
|
1263
|
+
sectionBody = sectionBody.replace(/^Plan:.*$/m, () => `Plan: ${value}`);
|
|
1264
|
+
planWritten = true;
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const replaced = (0, state_document_cjs_1.stateReplaceField)(sectionBody, 'Plan', value);
|
|
1017
1268
|
if (replaced !== null) {
|
|
1018
1269
|
sectionBody = replaced;
|
|
1019
|
-
|
|
1270
|
+
planWritten = true;
|
|
1020
1271
|
}
|
|
1021
|
-
}
|
|
1272
|
+
};
|
|
1273
|
+
writePlanField('Current Plan', fields.currentPlan);
|
|
1274
|
+
writePlanField('Plan', fields.plan);
|
|
1275
|
+
if (planWritten)
|
|
1276
|
+
mutated = true;
|
|
1022
1277
|
}
|
|
1023
1278
|
if (!mutated)
|
|
1024
1279
|
return content;
|
|
@@ -1030,8 +1285,10 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
|
|
|
1030
1285
|
/**
|
|
1031
1286
|
* Apply an `advancePlan` transition to STATE.md content.
|
|
1032
1287
|
*
|
|
1033
|
-
* Parses Current Plan / Total Plans
|
|
1034
|
-
* "Plan: X of Y"
|
|
1288
|
+
* Parses Current Plan / Total Plans in any of three shapes — the legacy
|
|
1289
|
+
* separate fields, the compound "Plan: X of Y", or the hybrid
|
|
1290
|
+
* "Current Plan: X of Y" (legacy name, compound value, no Total Plans
|
|
1291
|
+
* sibling) — increments the plan number, updates body fields
|
|
1035
1292
|
* and the ## Current Position section. When currentPlan >= totalPlans,
|
|
1036
1293
|
* takes the phase-complete branch (sets Status to "Phase complete — ready
|
|
1037
1294
|
* for verification") instead of advancing.
|
|
@@ -1077,30 +1334,105 @@ function advancePlanCore(content, deps) {
|
|
|
1077
1334
|
};
|
|
1078
1335
|
}
|
|
1079
1336
|
}
|
|
1080
|
-
// Parse plan number — legacy first, then compound.
|
|
1337
|
+
// Parse plan number — legacy pair first, then the hybrid, then compound.
|
|
1338
|
+
//
|
|
1339
|
+
// These branches decide ONE thing: which numbers the advance is computed
|
|
1340
|
+
// from. They deliberately do not record which FIELD supplied them, because
|
|
1341
|
+
// the write path no longer asks — every spelling is written back from its own
|
|
1342
|
+
// raw text (#3791 review round 6, B1/M1). An earlier revision tracked a
|
|
1343
|
+
// `planSourceField`/`planRawValue` pair here and then wrote the OTHER
|
|
1344
|
+
// spelling from this one's numbers, which is precisely how a field ended up
|
|
1345
|
+
// holding a value nothing had derived for it.
|
|
1081
1346
|
const legacyPlan = (0, state_document_cjs_1.stateExtractField)(content, 'Current Plan');
|
|
1082
1347
|
const legacyTotal = (0, state_document_cjs_1.stateExtractField)(content, 'Total Plans in Phase');
|
|
1083
1348
|
const planField = (0, state_document_cjs_1.stateExtractField)(content, 'Plan');
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1101
|
-
if (
|
|
1349
|
+
// Every branch below reads its numbers out of an ANCHORED match's capture
|
|
1350
|
+
// groups. Nothing here calls parseInt on a raw field value, so a value the
|
|
1351
|
+
// grammar does not fully describe cannot half-parse into a plausible number.
|
|
1352
|
+
const legacyNMatch = legacyPlan ? PLAN_SHAPE_N.exec(legacyPlan) : null;
|
|
1353
|
+
const legacyNofMMatch = legacyPlan ? PLAN_SHAPE_N_OF_M.exec(legacyPlan) : null;
|
|
1354
|
+
const totalNMatch = legacyTotal ? PLAN_SHAPE_N.exec(legacyTotal) : null;
|
|
1355
|
+
const planNofMMatch = planField ? PLAN_SHAPE_N_OF_M.exec(planField) : null;
|
|
1356
|
+
const planNMatch = planField ? PLAN_SHAPE_N.exec(planField) : null;
|
|
1357
|
+
let parsedCurrent = null;
|
|
1358
|
+
let parsedTotal = null;
|
|
1359
|
+
if (legacyPlan && legacyTotal && (legacyNMatch || legacyNofMMatch) && totalNMatch) {
|
|
1360
|
+
// Legacy pair wins whenever both fields are present and both are readable,
|
|
1361
|
+
// even if the Current Plan value also carries an "of M" — the explicit
|
|
1362
|
+
// sibling field is the stated intent, so it supplies the total.
|
|
1363
|
+
parsedCurrent = planNumberFrom((legacyNMatch ?? legacyNofMMatch)[1]);
|
|
1364
|
+
parsedTotal = planNumberFrom(totalNMatch[1]);
|
|
1365
|
+
}
|
|
1366
|
+
else if (legacyNofMMatch) {
|
|
1367
|
+
// Hybrid: legacy field name, compound value, no readable Total Plans
|
|
1368
|
+
// sibling. Written by hand (and by agents) often enough to be worth
|
|
1369
|
+
// reading — #3784.
|
|
1370
|
+
parsedCurrent = planNumberFrom(legacyNofMMatch[1]);
|
|
1371
|
+
parsedTotal = planNumberFrom(legacyNofMMatch[2]);
|
|
1372
|
+
}
|
|
1373
|
+
else if (planNofMMatch) {
|
|
1374
|
+
parsedCurrent = planNumberFrom(planNofMMatch[1]);
|
|
1375
|
+
parsedTotal = planNumberFrom(planNofMMatch[2]);
|
|
1376
|
+
}
|
|
1377
|
+
// No branch for a bare `Plan: N` paired with a `Total Plans in Phase: M`
|
|
1378
|
+
// sibling and no `Current Plan` at all (#3791 review round 6, M2). A revision
|
|
1379
|
+
// of this PR accepted it; base did not (its `else if (planField)` arm had no
|
|
1380
|
+
// `of M` match and errored via NaN), and it is out of #3784's scope, which is
|
|
1381
|
+
// the hybrid `Current Plan: N of M`. It cannot be given the schema-row +
|
|
1382
|
+
// forcing-test coupling the other shapes have, either: `Plan` is body-only,
|
|
1383
|
+
// `buildStateFrontmatter` never reads it into frontmatter, so there is no
|
|
1384
|
+
// `current_*` key to hang a row on. An accepted shape with no schema row and
|
|
1385
|
+
// no forcing test is exactly the drift this diff is otherwise built to
|
|
1386
|
+
// prevent, so the shape is refused and named in the error instead.
|
|
1387
|
+
if (parsedCurrent === null || parsedTotal === null) {
|
|
1102
1388
|
return { content: reassemble(body), updated: [], data: { error: true } };
|
|
1103
1389
|
}
|
|
1390
|
+
const currentPlan = parsedCurrent;
|
|
1391
|
+
const totalPlans = parsedTotal;
|
|
1392
|
+
// Each SPELLING's own plan number, read from its own value (#3791 review
|
|
1393
|
+
// round 6, B1/M1). The parse above picks ONE field to advance FROM; these are
|
|
1394
|
+
// what each field independently claims, and they are the only honest basis
|
|
1395
|
+
// for writing that field back.
|
|
1396
|
+
const legacyOwnCurrent = legacyNMatch || legacyNofMMatch
|
|
1397
|
+
? planNumberFrom((legacyNMatch ?? legacyNofMMatch)[1])
|
|
1398
|
+
: null;
|
|
1399
|
+
const planOwnCurrent = planNofMMatch || planNMatch
|
|
1400
|
+
? planNumberFrom((planNofMMatch ?? planNMatch)[1])
|
|
1401
|
+
: null;
|
|
1402
|
+
// A document carrying BOTH spellings with DIFFERENT plan numbers disagrees
|
|
1403
|
+
// with itself, and no rule here can say which half is right. Refuse.
|
|
1404
|
+
//
|
|
1405
|
+
// This is the #3807 posture one field over: name the conflict, let the caller
|
|
1406
|
+
// resolve it, never pick. The alternative shipped in an earlier revision of
|
|
1407
|
+
// this PR and was the round-6 Blocker — with `Plan` as the parse source, the
|
|
1408
|
+
// write path re-stamped `Current Plan`'s value with the number it had just
|
|
1409
|
+
// derived from `Plan`, so `Current Plan: 7` beside `Plan: 2 of 5` silently
|
|
1410
|
+
// became `Current Plan: 3`. A number with no relationship to the field it was
|
|
1411
|
+
// written into, no error, no diagnostic.
|
|
1412
|
+
//
|
|
1413
|
+
// Placed BEFORE the phase-complete branch deliberately. Guarding only the
|
|
1414
|
+
// normal advance leaves `Current Plan: 7` beside `Plan: 5 of 5` writing a
|
|
1415
|
+
// terminal "Phase complete — ready for verification" into a document whose
|
|
1416
|
+
// two spellings never agreed on where execution was.
|
|
1417
|
+
//
|
|
1418
|
+
// Differing TOTALS are NOT a disagreement about position and are preserved,
|
|
1419
|
+
// not resolved: `Current Plan: 2` / `Total Plans in Phase: 5` beside
|
|
1420
|
+
// `Plan: 2 of 9` advances to `3` and `3 of 9`. Reconciling the two totals
|
|
1421
|
+
// would be this transition inventing an answer to a question nobody asked it.
|
|
1422
|
+
if (legacyOwnCurrent !== null && planOwnCurrent !== null && legacyOwnCurrent !== planOwnCurrent) {
|
|
1423
|
+
return {
|
|
1424
|
+
content: reassemble(body),
|
|
1425
|
+
updated: [],
|
|
1426
|
+
data: {
|
|
1427
|
+
error: true,
|
|
1428
|
+
reason: 'ambiguous_plan_position',
|
|
1429
|
+
plan_candidates: [
|
|
1430
|
+
`Current Plan: ${legacyPlan}`,
|
|
1431
|
+
`Plan: ${planField}`,
|
|
1432
|
+
],
|
|
1433
|
+
},
|
|
1434
|
+
};
|
|
1435
|
+
}
|
|
1104
1436
|
const updated = [];
|
|
1105
1437
|
const statusDefaults = state_document_cjs_2.KNOWN_TEMPLATE_DEFAULTS['Status'];
|
|
1106
1438
|
const lastActivityDefaults = state_document_cjs_2.KNOWN_TEMPLATE_DEFAULTS['Last Activity'];
|
|
@@ -1122,24 +1454,98 @@ function advancePlanCore(content, deps) {
|
|
|
1122
1454
|
}
|
|
1123
1455
|
// Normal advance branch.
|
|
1124
1456
|
const newPlan = currentPlan + 1;
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1457
|
+
// The value each SPELLING should carry after the advance. A document may hold
|
|
1458
|
+
// both names (a `Current Plan:` header and a `Plan:` line in the section, or
|
|
1459
|
+
// the reverse), and each has always rendered differently — the legacy field
|
|
1460
|
+
// holds a bare/padded number while the section's `Plan:` line holds the
|
|
1461
|
+
// compound `N of M`.
|
|
1462
|
+
//
|
|
1463
|
+
// Each is advanced from ITS OWN raw text, never from the other's numbers
|
|
1464
|
+
// (#3791 review round 6, B1/M1). `bumpLeadingNumber` replaces only the leading
|
|
1465
|
+
// digits, so the field's zero-padding width, its own ` of M` and any trailing
|
|
1466
|
+
// annotation all survive — which is what the changeset claims, and what the
|
|
1467
|
+
// previous revision did only for whichever field happened to be the parse
|
|
1468
|
+
// source. The other field it re-stamped from numbers that were never its own.
|
|
1469
|
+
const advanceOwn = (raw, own) => {
|
|
1470
|
+
if (raw === null)
|
|
1471
|
+
return undefined;
|
|
1472
|
+
// Present but unreadable (`Plan: TBD`). Leave it exactly as authored: this
|
|
1473
|
+
// transition cannot advance what it cannot read, and writing a derived
|
|
1474
|
+
// number over it is the fabrication B1 was filed for. Stale-and-untouched is
|
|
1475
|
+
// honest; refusing the whole document because an unrelated line is
|
|
1476
|
+
// unreadable would be a narrowing #3784 does not license.
|
|
1477
|
+
if (own === null)
|
|
1478
|
+
return undefined;
|
|
1479
|
+
return bumpLeadingNumber(raw, newPlan) ?? undefined;
|
|
1480
|
+
};
|
|
1481
|
+
// Title-Case LITERALS to stateReplaceField (ADR-3408 §8.3(b)): a literal
|
|
1482
|
+
// cannot collide with a lowercase/snake_case frontmatter key, so it is safe
|
|
1483
|
+
// regardless of how the content argument was derived. `body` here is in fact
|
|
1484
|
+
// `stripFrontmatter(content)`, but the write-path drift guard does not do
|
|
1485
|
+
// dataflow tracking (by design), and satisfying its invariant by construction
|
|
1486
|
+
// is better than asking a reader to re-derive that it holds.
|
|
1487
|
+
// One value per SPELLING, then write both names everywhere they appear.
|
|
1488
|
+
//
|
|
1489
|
+
// `Current Plan` — whatever the author wrote, advanced in place: padding
|
|
1490
|
+
// and any ` of M` preserved.
|
|
1491
|
+
// `Plan` — likewise, so its OWN total survives. `Plan: 2 of 9`
|
|
1492
|
+
// beside a `Total Plans in Phase: 5` advances to
|
|
1493
|
+
// `3 of 9`, not `3 of 5`: the two totals disagreeing is
|
|
1494
|
+
// the document's business, not this transition's to
|
|
1495
|
+
// reconcile.
|
|
1496
|
+
//
|
|
1497
|
+
// The two are deliberately different strings for the legacy shape, which is
|
|
1498
|
+
// why this is a per-name value rather than one shared display value. Writing
|
|
1499
|
+
// only the name the value was PARSED from is what left the other name stale:
|
|
1500
|
+
// a `**Plan:** 2 of 6` header beside a `Current Plan:` line advanced one and
|
|
1501
|
+
// not the other, in whichever direction the precedence happened to fall.
|
|
1502
|
+
//
|
|
1503
|
+
// Each write is a no-op when that name is absent (`stateReplaceField` returns
|
|
1504
|
+
// null), so a document carrying only one spelling is unaffected — and
|
|
1505
|
+
// `undefined` means "present but not advanceable", which is left untouched
|
|
1506
|
+
// rather than overwritten.
|
|
1507
|
+
const currentPlanDisplayValue = advanceOwn(legacyPlan, legacyOwnCurrent);
|
|
1508
|
+
const planDisplayValue = planField === null
|
|
1509
|
+
// No top-level `Plan:` field to advance, but the `## Current Position`
|
|
1510
|
+
// section may still carry a `Plan:` line in a shape `stateExtractField`
|
|
1511
|
+
// does not read. There is no raw text here to preserve, so it gets the
|
|
1512
|
+
// compound rendering that line has always carried.
|
|
1513
|
+
? `${newPlan} of ${totalPlans}`
|
|
1514
|
+
: advanceOwn(planField, planOwnCurrent);
|
|
1515
|
+
if (currentPlanDisplayValue !== undefined) {
|
|
1516
|
+
body = (0, state_document_cjs_1.stateReplaceField)(body, 'Current Plan', currentPlanDisplayValue) || body;
|
|
1517
|
+
}
|
|
1518
|
+
// Only touch `Plan` when the document actually declares one. Writing it
|
|
1519
|
+
// unconditionally meant a `stateReplaceField` whose first match could be any
|
|
1520
|
+
// `Plan:` line anywhere in the body — including prose outside
|
|
1521
|
+
// `## Current Position` that was never a field. `planField` is the read of
|
|
1522
|
+
// that same field from the top of this function, so the write is scoped to a
|
|
1523
|
+
// document that has one.
|
|
1524
|
+
if (planField !== null && planDisplayValue !== undefined) {
|
|
1128
1525
|
body = (0, state_document_cjs_1.stateReplaceField)(body, 'Plan', planDisplayValue) || body;
|
|
1129
1526
|
}
|
|
1130
|
-
else {
|
|
1131
|
-
planDisplayValue = `${newPlan} of ${totalPlans}`;
|
|
1132
|
-
body = (0, state_document_cjs_1.stateReplaceField)(body, 'Current Plan', String(newPlan)) || body;
|
|
1133
|
-
}
|
|
1134
1527
|
body = (0, state_document_cjs_1.stateReplaceFieldIfTemplate)(body, 'Status', statusDefaults, 'Ready to execute') || body;
|
|
1135
1528
|
body = (0, state_document_cjs_1.stateReplaceFieldIfTemplate)(body, 'Last Activity', lastActivityDefaults, today) || body;
|
|
1136
1529
|
body = (0, state_document_cjs_1.stateReplaceFieldIfTemplate)(body, 'Last activity', lastActivityDefaults, today) || body;
|
|
1137
1530
|
body = mutateCurrentPositionForAdvance(body, {
|
|
1138
1531
|
status: 'Ready to execute',
|
|
1139
1532
|
lastActivity: today,
|
|
1533
|
+
// Both spellings, each with its own value. The section writes whichever
|
|
1534
|
+
// name it actually carries; passing only the header's name is what left
|
|
1535
|
+
// two sites disagreeing about where execution is.
|
|
1140
1536
|
plan: planDisplayValue,
|
|
1537
|
+
currentPlan: currentPlanDisplayValue,
|
|
1141
1538
|
}, statusDefaults, lastActivityDefaults);
|
|
1142
|
-
|
|
1539
|
+
// Report `Current Plan` only when it actually moved. The write above is
|
|
1540
|
+
// conditional now — a `Current Plan:` that is present but unreadable is left
|
|
1541
|
+
// as authored — so an unconditional push here would report progress this
|
|
1542
|
+
// transition had not made, which is the same sin `bumpLeadingNumber` returns
|
|
1543
|
+
// null to avoid. `reconcileReportedFields` at the `state.cts` caller would
|
|
1544
|
+
// catch it against the persisted bytes, but `transitionCore`'s own `updated`
|
|
1545
|
+
// is consumed directly too and has to be true on its own.
|
|
1546
|
+
if (currentPlanDisplayValue !== undefined)
|
|
1547
|
+
updated.push('Current Plan');
|
|
1548
|
+
updated.push('Status', 'Last Activity', 'Current Position');
|
|
1143
1549
|
return {
|
|
1144
1550
|
content: reassemble(body),
|
|
1145
1551
|
updated,
|
|
@@ -2004,13 +2410,10 @@ function syncCore(content, intent, deps) {
|
|
|
2004
2410
|
if (currentProgress) {
|
|
2005
2411
|
const currentPercent = parseInt(currentProgress.replace(/[^\d]/g, ''), 10);
|
|
2006
2412
|
if (currentPercent !== intent.percent) {
|
|
2007
|
-
const
|
|
2008
|
-
const filled = Math.round((intent.percent / 100) * barWidth);
|
|
2009
|
-
const bar = '█'.repeat(filled) + '░'.repeat(barWidth - filled);
|
|
2010
|
-
const progressStr = `[${bar}] ${intent.percent}%`;
|
|
2011
|
-
changes.push(`Progress: ${currentProgress} -> ${progressStr}`);
|
|
2012
|
-
const result = (0, state_document_cjs_1.stateReplaceField)(modified, 'Progress', progressStr);
|
|
2413
|
+
const result = stateReplaceProgressPercent(modified, intent.percent);
|
|
2013
2414
|
if (result) {
|
|
2415
|
+
const progressStr = formatProgressMachineSegment(intent.percent);
|
|
2416
|
+
changes.push(`Progress: ${currentProgress} -> ${progressStr}`);
|
|
2014
2417
|
modified = result;
|
|
2015
2418
|
updated.push('Progress');
|
|
2016
2419
|
}
|