@opengsd/gsd-core 1.18.0 → 1.20.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-code-fixer.compact.md +7 -7
- package/agents/gsd-code-fixer.md +13 -7
- package/agents/gsd-code-reviewer.compact.md +49 -8
- package/agents/gsd-code-reviewer.md +53 -14
- package/agents/gsd-executor.md +8 -4
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +422 -103
- package/commands/gsd/update.md +5 -2
- package/gsd-core/bin/gsd-tools.cjs +179 -30
- 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-lifecycle.cjs +1 -12
- package/gsd-core/bin/lib/capability-lock.cjs +13 -2
- package/gsd-core/bin/lib/capability-registry.cjs +376 -103
- package/gsd-core/bin/lib/capability-state.cjs +14 -8
- package/gsd-core/bin/lib/capability-validator.cjs +32 -0
- package/gsd-core/bin/lib/commands.cjs +27 -17
- 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 +45 -0
- package/gsd-core/bin/lib/decision-coverage-support.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +21 -3
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +2 -2
- package/gsd-core/bin/lib/gate-context-drift.cjs +9 -4
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +6 -1
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +6 -1
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
- package/gsd-core/bin/lib/gate-phase-context.cjs +7 -8
- package/gsd-core/bin/lib/gate-schema-drift.cjs +1 -1
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +63 -22
- package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
- package/gsd-core/bin/lib/git-info-exclude.cjs +52 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +31 -12
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +9 -7
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +3 -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 +245 -68
- package/gsd-core/bin/lib/install-engine.cjs +246 -32
- package/gsd-core/bin/lib/install-profiles.cjs +9 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
- package/gsd-core/bin/lib/io.cjs +6 -0
- package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +147 -1
- package/gsd-core/bin/lib/markdown-table.cjs +23 -0
- package/gsd-core/bin/lib/mcp-server.cjs +8 -5
- 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 +61 -51
- 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-entry-grammar.cjs +119 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +2 -0
- package/gsd-core/bin/lib/phase-id.cjs +629 -38
- package/gsd-core/bin/lib/phase-lifecycle.cjs +12 -44
- package/gsd-core/bin/lib/phase-locator.cjs +328 -19
- package/gsd-core/bin/lib/phase.cjs +3066 -653
- package/gsd-core/bin/lib/planning-document.cjs +26 -5
- package/gsd-core/bin/lib/planning-inspect.cjs +27 -35
- package/gsd-core/bin/lib/planning-snapshot.cjs +15 -8
- package/gsd-core/bin/lib/planning-workspace.cjs +21 -3
- package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
- package/gsd-core/bin/lib/read-only-git-env.cjs +79 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +2 -2
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +57 -2
- package/gsd-core/bin/lib/review-lane-invocation.cjs +125 -18
- package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +11 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +735 -137
- package/gsd-core/bin/lib/roadmap-progress.cjs +136 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +47 -7
- package/gsd-core/bin/lib/roadmap.cjs +383 -123
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +381 -14
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
- package/gsd-core/bin/lib/shell-command-projection.cjs +151 -19
- package/gsd-core/bin/lib/smart-entry.cjs +35 -11
- package/gsd-core/bin/lib/state-contract.cjs +39 -15
- package/gsd-core/bin/lib/state-document.cjs +344 -27
- package/gsd-core/bin/lib/state-transition.cjs +156 -32
- package/gsd-core/bin/lib/state.cjs +527 -186
- package/gsd-core/bin/lib/surface.cjs +9 -0
- package/gsd-core/bin/lib/task-command-router.cjs +5 -25
- package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
- package/gsd-core/bin/lib/template.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +3 -2
- package/gsd-core/bin/lib/vendor/re2js.cjs +863 -577
- package/gsd-core/bin/lib/vendor/re2js.d.cts +284 -219
- package/gsd-core/bin/lib/verification.cjs +96 -9
- package/gsd-core/bin/lib/verify.cjs +4 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +26 -20
- 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/bin/verify-reapply-patches.cjs +411 -10
- package/gsd-core/references/cicd-review-patterns.md +45 -0
- package/gsd-core/references/git-integration.md +11 -0
- package/gsd-core/references/loop-hook-dispatch.md +13 -6
- package/gsd-core/references/necessity-review.md +48 -0
- package/gsd-core/references/planning-config.md +2 -0
- package/gsd-core/references/tdd.md +2 -2
- 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/audit-fix.md +2 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
- package/gsd-core/workflows/code-review-fix.md +46 -5
- package/gsd-core/workflows/code-review.md +64 -7
- 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 +37 -150
- package/gsd-core/workflows/diagnose-issues.md +29 -5
- package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +33 -2
- package/gsd-core/workflows/discuss-phase-power.md +19 -2
- package/gsd-core/workflows/discuss-phase.md +6 -10
- 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 +7 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
- package/gsd-core/workflows/execute-phase.md +10 -6
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +2 -2
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +13 -9
- package/gsd-core/workflows/plan-phase.md +14 -14
- package/gsd-core/workflows/pr-branch.md +2 -0
- 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.md +1 -1
- package/gsd-core/workflows/reapply-patches.md +104 -0
- package/gsd-core/workflows/review.md +70 -154
- package/gsd-core/workflows/section-manifest.json +5 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +57 -9
- package/gsd-core/workflows/settings.md +1 -0
- package/gsd-core/workflows/ship.md +15 -4
- package/gsd-core/workflows/spec-phase.md +3 -6
- package/gsd-core/workflows/verify-work.md +4 -3
- package/hooks/dist/gsd-agent-isolation-guard.js +11 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
- package/hooks/dist/gsd-graphify-update.sh +6 -0
- package/hooks/dist/gsd-path-export.js +95 -0
- package/hooks/dist/gsd-secret-read-guard.js +172 -39
- package/hooks/dist/gsd-statusline.js +37 -3
- package/hooks/dist/gsd-windsurf-pre-write.js +29 -2
- package/hooks/dist/gsd-workflow-guard.js +3 -2
- package/hooks/dist/gsd-worktree-path-guard.js +29 -2
- package/hooks/dist/lib/git-probe.js +16 -0
- 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 +11 -1
- package/hooks/gsd-cursor-subagent-start.js +6 -2
- package/hooks/gsd-graphify-update.sh +6 -0
- package/hooks/gsd-path-export.js +95 -0
- package/hooks/gsd-secret-read-guard.js +172 -39
- package/hooks/gsd-statusline.js +37 -3
- package/hooks/gsd-windsurf-pre-write.js +29 -2
- package/hooks/gsd-workflow-guard.js +3 -2
- package/hooks/gsd-worktree-path-guard.js +29 -2
- package/hooks/hooks.json +1 -0
- package/hooks/lib/git-probe.js +16 -0
- package/hooks/lib/isolation-sentinel.js +44 -0
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +15 -8
- 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 +774 -0
- package/pi/package-agents/gsd-code-reviewer.md +441 -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 +898 -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 +54 -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/build-hooks.js +4 -0
- package/scripts/ci-next-health.cjs +246 -23
- package/scripts/command-contract-helpers.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +22 -7
- package/scripts/gen-features.cjs +14 -2
- package/scripts/gen-health-docs.cjs +13 -1
- package/scripts/gen-pi-package-skills.cjs +217 -0
- package/scripts/lib/drift-scan.cjs +27 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +17 -1
- package/scripts/lib/platform-conformance-tier.generated.cjs +37 -1
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
- package/scripts/lint-phase-enumeration-drift.cjs +8 -4
- package/scripts/lint-retired-runtime-name.cjs +5 -0
- package/scripts/lint-roadmap-progress-drift.cjs +421 -0
- package/scripts/mutation-matrix.cjs +23 -1
- package/scripts/npm-audit-baseline.cjs +41 -7
- 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/scripts/sync-next-version.cjs +38 -6
- package/skills/gsd-update/SKILL.md +5 -2
- package/vscode/package.json +1 -1
- package/scripts/diagnose-validate-commit-in-tier.cjs +0 -270
- package/scripts/diagnose-validate-commit-phases.cjs +0 -348
- package/scripts/xtrace-phase-timing.cjs +0 -205
|
@@ -19,6 +19,7 @@ exports.STATE_MD_SECTIONS = exports.FRONTMATTER_BODY_SOURCE = exports.FIELD_CLAS
|
|
|
19
19
|
exports.formatProgressMachineSegment = formatProgressMachineSegment;
|
|
20
20
|
exports.stateReplaceProgressPercent = stateReplaceProgressPercent;
|
|
21
21
|
exports.beginFrontmatterReassembly = beginFrontmatterReassembly;
|
|
22
|
+
exports.isSessionScopedBodyField = isSessionScopedBodyField;
|
|
22
23
|
exports.getFrontmatterBodySource = getFrontmatterBodySource;
|
|
23
24
|
exports.frontmatterKeyForBodyField = frontmatterKeyForBodyField;
|
|
24
25
|
exports.getFieldClassification = getFieldClassification;
|
|
@@ -30,13 +31,18 @@ exports.applyPreserveWhenUnchanged = applyPreserveWhenUnchanged;
|
|
|
30
31
|
exports.applyStatePreservation = applyStatePreservation;
|
|
31
32
|
exports.transitionCore = transitionCore;
|
|
32
33
|
exports.sliceCurrentPositionSection = sliceCurrentPositionSection;
|
|
34
|
+
exports.isStatePastPhase = isStatePastPhase;
|
|
33
35
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
36
|
const frontmatter = require("./frontmatter.cjs");
|
|
35
37
|
const state_document_cjs_1 = require("./state-document.cjs");
|
|
36
38
|
const state_document_cjs_2 = require("./state-document.cjs");
|
|
37
39
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
38
40
|
const phase_lifecycle_cjs_1 = require("./phase-lifecycle.cjs");
|
|
41
|
+
const roadmap_progress_cjs_1 = require("./roadmap-progress.cjs");
|
|
39
42
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
|
|
44
|
+
const phaseIdMod = require("./phase-id.cjs");
|
|
45
|
+
const { comparePhaseNum } = phaseIdMod;
|
|
40
46
|
// #4129: the completion-ratio kernel for the resync-arm ratchet's percent
|
|
41
47
|
// (planning-scope's SCOPE — state-document's own dependency, no cycle here:
|
|
42
48
|
// state-document never imports this module).
|
|
@@ -246,6 +252,13 @@ exports.FRONTMATTER_BODY_SOURCE = Object.freeze(FRONTMATTER_BODY_SOURCE_KEY_ORDE
|
|
|
246
252
|
* docstring). `updateCore` was still doing the whole-body replace.
|
|
247
253
|
*/
|
|
248
254
|
const SESSION_SCOPED_KEYS = new Set(['stopped_at', 'paused_at']);
|
|
255
|
+
/**
|
|
256
|
+
* #4998 review: the `## Session` fields with no frontmatter key. `state-snapshot`
|
|
257
|
+
* reads them from the session section only, with fenced blocks blanked, so
|
|
258
|
+
* `state update` / `state patch` write them there too — never a fenced example
|
|
259
|
+
* above the live line, and never an archive section's copy.
|
|
260
|
+
*/
|
|
261
|
+
const SESSION_BODY_FIELDS = new Set(['resume file', 'last session', 'last date']);
|
|
249
262
|
/**
|
|
250
263
|
* The `(primary, fallback)` label pair for a session-scoped frontmatter KEY.
|
|
251
264
|
*/
|
|
@@ -257,7 +270,8 @@ function sessionLabelsForKey(key) {
|
|
|
257
270
|
}
|
|
258
271
|
/**
|
|
259
272
|
* The same pair, resolved from a BODY LABEL the caller named (`Stopped At`,
|
|
260
|
-
* `Stopped at`, `Paused At
|
|
273
|
+
* `Stopped at`, `Paused At`, or a SESSION_BODY_FIELDS label as named). `null`
|
|
274
|
+
* for anything else.
|
|
261
275
|
*
|
|
262
276
|
* Deliberately does NOT accept a frontmatter key. An earlier cut resolved both
|
|
263
277
|
* spellings through one function and used it for the write, which made
|
|
@@ -268,7 +282,13 @@ function sessionLabelsForKey(key) {
|
|
|
268
282
|
*/
|
|
269
283
|
function sessionLabelsForBodyField(field) {
|
|
270
284
|
const key = frontmatterKeyForBodyField(field);
|
|
271
|
-
|
|
285
|
+
if (key !== null)
|
|
286
|
+
return sessionLabelsForKey(key);
|
|
287
|
+
return SESSION_BODY_FIELDS.has(field.trim().toLowerCase()) ? { primary: field, fallback: null } : null;
|
|
288
|
+
}
|
|
289
|
+
/** #4998 review: whether `state update` / `state patch` write the body field `field` inside the session section. */
|
|
290
|
+
function isSessionScopedBodyField(field) {
|
|
291
|
+
return sessionLabelsForBodyField(field) !== null;
|
|
272
292
|
}
|
|
273
293
|
/**
|
|
274
294
|
* Would a session-scoped write actually land? Asks by attempting the real write
|
|
@@ -643,18 +663,24 @@ function mergeResyncProgressRatchet(curatedRecord, derivedRecord) {
|
|
|
643
663
|
merged[key] = value;
|
|
644
664
|
}
|
|
645
665
|
}
|
|
646
|
-
|
|
647
|
-
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);
|
|
648
|
-
// Same verbatim rule for percent: assign only when the recomputed value
|
|
649
|
-
// numerically differs, so a string-spelled derived percent ("67") is not
|
|
650
|
-
// churned into a number-spelled 67 (phantom-divergence noise, not a
|
|
651
|
-
// change).
|
|
652
|
-
if (recomputed !== null && recomputed !== (0, state_document_cjs_2.toFiniteNumber)(merged.percent)) {
|
|
653
|
-
merged.percent = recomputed;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
666
|
+
recomputeMergedProgressPercent(merged, derivedRecord);
|
|
656
667
|
return merged;
|
|
657
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* Make `merged.percent` the progress computation over the counters `merged` actually carries (#5619,
|
|
671
|
+
* #4129). Only when the derived side measured a percent: a derivation that withheld one (an incomplete
|
|
672
|
+
* scope, #1761/#3217) stays withheld, never invented. Same verbatim rule as the counters: assign only
|
|
673
|
+
* when the recomputed value numerically differs, so a string-spelled derived percent ("67") is not
|
|
674
|
+
* churned into a number-spelled 67 (phantom-divergence noise, not a change).
|
|
675
|
+
*/
|
|
676
|
+
function recomputeMergedProgressPercent(merged, derivedRecord) {
|
|
677
|
+
if ((0, state_document_cjs_2.toFiniteNumber)(derivedRecord.percent) === null)
|
|
678
|
+
return;
|
|
679
|
+
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);
|
|
680
|
+
if (recomputed !== null && recomputed !== (0, state_document_cjs_2.toFiniteNumber)(merged.percent)) {
|
|
681
|
+
merged.percent = recomputed;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
658
684
|
/**
|
|
659
685
|
* Executor for `preservation: 'preserve-always'` (ADR-3408 §8.1). Only
|
|
660
686
|
* `progress` carries this policy today. Preserves #3242/#1446/#2440/#2969
|
|
@@ -719,18 +745,23 @@ function applyPreserveAlways(field, cls, ctx) {
|
|
|
719
745
|
// completed after the plan count grew) — ratcheting UP only, never
|
|
720
746
|
// deriving downward (preserves the #3242 curated-progress protection
|
|
721
747
|
// for cases unrelated to plan-count growth, e.g. a deleted SUMMARY).
|
|
722
|
-
// percent
|
|
723
|
-
// disk
|
|
724
|
-
//
|
|
748
|
+
// percent is recomputed over the counters actually written (#5619): the
|
|
749
|
+
// disk-derived percent is only coherent with the DERIVED counters, and a
|
|
750
|
+
// stale curated percent would be incoherent against the ratcheted-up
|
|
751
|
+
// completed counts (e.g. 54/54 at 93%).
|
|
725
752
|
const ratchetUpKeys = new Set(['completed_plans', 'completed_phases']);
|
|
726
753
|
for (const [key, value] of Object.entries(curatedRecord)) {
|
|
727
754
|
if (key === 'total_plans' || key === 'total_phases' || key === 'percent')
|
|
728
755
|
continue;
|
|
729
756
|
if (ratchetUpKeys.has(key)) {
|
|
730
|
-
|
|
731
|
-
|
|
757
|
+
// Frontmatter scalars arrive as STRINGS ("2", not 2): coerce through
|
|
758
|
+
// `toFiniteNumber`, never a `typeof === 'number'` test (#5619 — that
|
|
759
|
+
// test made both sides -Infinity and the ratchet unreachable).
|
|
760
|
+
const derivedNum = (0, state_document_cjs_2.toFiniteNumber)(derivedRecord[key]) ?? -Infinity;
|
|
761
|
+
const curatedNum = (0, state_document_cjs_2.toFiniteNumber)(value) ?? -Infinity;
|
|
732
762
|
// Take the derived value only when it ratchets up (strictly
|
|
733
|
-
// greater — #2969's `>` not `>=`); else keep curated
|
|
763
|
+
// greater — #2969's `>` not `>=`); else keep curated, with its
|
|
764
|
+
// spelling, on a tie too.
|
|
734
765
|
if (derivedNum > curatedNum)
|
|
735
766
|
continue;
|
|
736
767
|
merged[key] = value;
|
|
@@ -740,6 +771,7 @@ function applyPreserveAlways(field, cls, ctx) {
|
|
|
740
771
|
}
|
|
741
772
|
}
|
|
742
773
|
}
|
|
774
|
+
recomputeMergedProgressPercent(merged, derivedRecord);
|
|
743
775
|
next = merged;
|
|
744
776
|
}
|
|
745
777
|
else {
|
|
@@ -876,7 +908,36 @@ exports.STATE_MD_SECTIONS = {
|
|
|
876
908
|
* Phases 2–7 add cases for the remaining 9 intent kinds. A missing case is
|
|
877
909
|
* a compile-time error (the function would not return on that path).
|
|
878
910
|
*/
|
|
911
|
+
/**
|
|
912
|
+
* The transitions that rewrite single-line body fields (`Status:`, `Current focus:`, `Plan:` …) with
|
|
913
|
+
* plain line-anchored patterns. Those patterns match a fenced EXAMPLE of a field as readily as the
|
|
914
|
+
* live line (#5460), so these run over the document body with fenced blocks hidden.
|
|
915
|
+
* `milestone*`, `prune` and `rebuild` restructure sections or re-derive the document and are not
|
|
916
|
+
* field-by-field writers.
|
|
917
|
+
*/
|
|
918
|
+
const FENCE_MASKED_TRANSITIONS = new Set([
|
|
919
|
+
'beginPhase', 'advancePlan', 'completePhase', 'plannedPhase', 'patch', 'update', 'sync',
|
|
920
|
+
]);
|
|
879
921
|
function transitionCore(content, intent, deps) {
|
|
922
|
+
if (!FENCE_MASKED_TRANSITIONS.has(intent.kind))
|
|
923
|
+
return transitionCoreUnmasked(content, intent, deps);
|
|
924
|
+
// Only the body is masked: a fence opener inside a frontmatter block scalar must not swallow the
|
|
925
|
+
// closing `---` (the frontmatter is not where the live fields are written).
|
|
926
|
+
const body = stripFrontmatter(content);
|
|
927
|
+
if (!content.endsWith(body))
|
|
928
|
+
return transitionCoreUnmasked(content, intent, deps);
|
|
929
|
+
const prefix = content.slice(0, content.length - body.length);
|
|
930
|
+
const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(body);
|
|
931
|
+
const result = transitionCoreUnmasked(prefix + fenceMask.masked, intent, deps);
|
|
932
|
+
const restored = fenceMask.restore(result.content);
|
|
933
|
+
// Cannot happen short of a writer bug (a transition dropped or duplicated a placeholder line); a
|
|
934
|
+
// refusal is the one outcome that cannot report a rewrite that did not land.
|
|
935
|
+
if (restored === null) {
|
|
936
|
+
throw new Error(`${intent.kind}: fenced code blocks could not be restored after the edit; refusing to write STATE.md`);
|
|
937
|
+
}
|
|
938
|
+
return { ...result, content: restored };
|
|
939
|
+
}
|
|
940
|
+
function transitionCoreUnmasked(content, intent, deps) {
|
|
880
941
|
switch (intent.kind) {
|
|
881
942
|
case 'beginPhase':
|
|
882
943
|
return beginPhaseCore(content, intent, deps);
|
|
@@ -980,7 +1041,7 @@ function beginPhaseCore(content, intent, deps) {
|
|
|
980
1041
|
tryField('Current Phase Name', intent.phaseName);
|
|
981
1042
|
}
|
|
982
1043
|
tryField('Current Plan', String(startPlan));
|
|
983
|
-
if (intent.planCount) {
|
|
1044
|
+
if (intent.planCount != null) {
|
|
984
1045
|
tryField('Total Plans in Phase', String(intent.planCount));
|
|
985
1046
|
}
|
|
986
1047
|
// **Current focus:** body text line (#1104).
|
|
@@ -1104,7 +1165,9 @@ function mutateCurrentPositionFirstTime(body, intent, today, updated) {
|
|
|
1104
1165
|
const span = locateCurrentPosition(body);
|
|
1105
1166
|
if (span === null)
|
|
1106
1167
|
return body;
|
|
1107
|
-
|
|
1168
|
+
// #5460: the section's fenced examples are hidden while the live lines are rewritten.
|
|
1169
|
+
const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(body.slice(span.start, span.end));
|
|
1170
|
+
let sectionBody = fenceMask.masked;
|
|
1108
1171
|
// Phase line — inline first, then pipe-table fallback (#1257).
|
|
1109
1172
|
const phaseLabel = `${intent.phaseNumber}${intent.phaseName ? ` (${intent.phaseName})` : ''} — EXECUTING`;
|
|
1110
1173
|
if (/^Phase:/m.test(sectionBody)) {
|
|
@@ -1116,7 +1179,7 @@ function mutateCurrentPositionFirstTime(body, intent, today, updated) {
|
|
|
1116
1179
|
sectionBody = replaced;
|
|
1117
1180
|
}
|
|
1118
1181
|
// Plan line.
|
|
1119
|
-
const planValue = `${intent.startPlan} of ${intent.planCount
|
|
1182
|
+
const planValue = `${intent.startPlan} of ${intent.planCount ?? '?'}`;
|
|
1120
1183
|
if (/^Plan:/m.test(sectionBody)) {
|
|
1121
1184
|
sectionBody = sectionBody.replace(/^Plan:.*$/m, `Plan: ${planValue}`);
|
|
1122
1185
|
}
|
|
@@ -1146,8 +1209,11 @@ function mutateCurrentPositionFirstTime(body, intent, today, updated) {
|
|
|
1146
1209
|
if (replaced !== null)
|
|
1147
1210
|
sectionBody = replaced;
|
|
1148
1211
|
}
|
|
1212
|
+
const restored = fenceMask.restore(sectionBody);
|
|
1213
|
+
if (restored === null)
|
|
1214
|
+
return body;
|
|
1149
1215
|
updated.push('Current Position');
|
|
1150
|
-
return body.slice(0, span.start) +
|
|
1216
|
+
return body.slice(0, span.start) + restored + body.slice(span.end);
|
|
1151
1217
|
}
|
|
1152
1218
|
/**
|
|
1153
1219
|
* Resume ## Current Position mutation: only update Last activity line
|
|
@@ -1158,7 +1224,8 @@ function mutateCurrentPositionResume(body, intent, today, updated) {
|
|
|
1158
1224
|
const span = locateCurrentPosition(body);
|
|
1159
1225
|
if (span === null)
|
|
1160
1226
|
return body;
|
|
1161
|
-
|
|
1227
|
+
const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(body.slice(span.start, span.end));
|
|
1228
|
+
let sectionBody = fenceMask.masked;
|
|
1162
1229
|
const resumeActivity = `Last activity: ${today} — Phase ${intent.phaseNumber} execution resumed (wave continue)`;
|
|
1163
1230
|
if (/^Last activity:/im.test(sectionBody)) {
|
|
1164
1231
|
sectionBody = sectionBody.replace(/^Last activity:.*$/im, resumeActivity);
|
|
@@ -1173,7 +1240,10 @@ function mutateCurrentPositionResume(body, intent, today, updated) {
|
|
|
1173
1240
|
updated.push('Last activity (resume)');
|
|
1174
1241
|
}
|
|
1175
1242
|
}
|
|
1176
|
-
|
|
1243
|
+
const restored = fenceMask.restore(sectionBody);
|
|
1244
|
+
if (restored === null)
|
|
1245
|
+
return body;
|
|
1246
|
+
return body.slice(0, span.start) + restored + body.slice(span.end);
|
|
1177
1247
|
}
|
|
1178
1248
|
const PLAN_SHAPE_N = /^(\d+)(?:\s.*)?$/;
|
|
1179
1249
|
const PLAN_SHAPE_N_OF_M = /^(\d+)\s+of\s+(\d+)(?:\s.*)?$/;
|
|
@@ -1230,7 +1300,8 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
|
|
|
1230
1300
|
const span = locateCurrentPosition(content);
|
|
1231
1301
|
if (span === null)
|
|
1232
1302
|
return content;
|
|
1233
|
-
|
|
1303
|
+
const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(content.slice(span.start, span.end));
|
|
1304
|
+
let sectionBody = fenceMask.masked;
|
|
1234
1305
|
let mutated = false;
|
|
1235
1306
|
// #3395: Phase is always replaced when a caller passes it — system-derived,
|
|
1236
1307
|
// not executor-authored (same rule as Plan below). plannedPhaseCore uses
|
|
@@ -1343,7 +1414,10 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
|
|
|
1343
1414
|
}
|
|
1344
1415
|
if (!mutated)
|
|
1345
1416
|
return content;
|
|
1346
|
-
|
|
1417
|
+
const restored = fenceMask.restore(sectionBody);
|
|
1418
|
+
if (restored === null)
|
|
1419
|
+
return content;
|
|
1420
|
+
return content.slice(0, span.start) + restored + content.slice(span.end);
|
|
1347
1421
|
}
|
|
1348
1422
|
// ----------------------------------------------------------------------------
|
|
1349
1423
|
// advancePlan — intent implementation (Phase 2)
|
|
@@ -1642,6 +1716,29 @@ function nextPhaseWording(disk) {
|
|
|
1642
1716
|
return { status: 'Ready to plan', currentPlan: 'Not started', stoppedAt: 'ready to plan' };
|
|
1643
1717
|
}
|
|
1644
1718
|
}
|
|
1719
|
+
/**
|
|
1720
|
+
* #5165: is STATE.md already positioned PAST phase `phaseNum`? True when its
|
|
1721
|
+
* current phase (frontmatter `current_phase`, else the body `Current Phase` /
|
|
1722
|
+
* `Phase` field's leading phase token) compares greater than `phaseNum`.
|
|
1723
|
+
* Re-completing a phase STATE has already moved past must not rewind the
|
|
1724
|
+
* position; the caller combines this with "the phase is already complete in
|
|
1725
|
+
* ROADMAP.md" so a first-time out-of-order completion (#2028) still
|
|
1726
|
+
* transitions. An unparseable or absent current phase is "not past".
|
|
1727
|
+
*/
|
|
1728
|
+
function isStatePastPhase(content, phaseNum, sourcePath) {
|
|
1729
|
+
const fm = extractFrontmatter(content, sourcePath);
|
|
1730
|
+
const fromFm = fm['current_phase'];
|
|
1731
|
+
let raw = typeof fromFm === 'string' || typeof fromFm === 'number' ? String(fromFm) : null;
|
|
1732
|
+
if (raw === null || raw.trim() === '') {
|
|
1733
|
+
const body = stripFrontmatter(content);
|
|
1734
|
+
raw = (0, state_document_cjs_1.stateExtractField)(body, 'Current Phase') || (0, state_document_cjs_1.stateExtractField)(body, 'Phase') || null;
|
|
1735
|
+
}
|
|
1736
|
+
// `Phase 4` / `phase 4 of 9` name phase 4 as surely as `4 of 9` does.
|
|
1737
|
+
const m = raw === null ? null : /^\s*(?:phase\s+)?(\d+(?:\.\d+)*)/i.exec(raw);
|
|
1738
|
+
if (!m)
|
|
1739
|
+
return false;
|
|
1740
|
+
return comparePhaseNum(m[1], String(phaseNum)) > 0;
|
|
1741
|
+
}
|
|
1645
1742
|
/**
|
|
1646
1743
|
* Apply a `completePhase` transition to STATE.md content.
|
|
1647
1744
|
*
|
|
@@ -1669,6 +1766,12 @@ function completePhaseCore(content, intent, deps) {
|
|
|
1669
1766
|
const updated = [];
|
|
1670
1767
|
const today = deps.clock.localToday();
|
|
1671
1768
|
const nextWording = nextPhaseWording(intent.isLastPhase ? null : intent.nextPhaseDisk ?? null);
|
|
1769
|
+
// #5165: the milestone the caller resolved (ROADMAP-bound identity first) wins;
|
|
1770
|
+
// else the milestone STATE asserts. It scopes the roadmap-derived counters
|
|
1771
|
+
// below to that milestone's Progress-table rows, the same milestone
|
|
1772
|
+
// `phase complete` scopes its frontmatter progress to.
|
|
1773
|
+
const currentMilestone = (0, roadmap_progress_cjs_1.milestoneVersionOf)(intent.milestone)
|
|
1774
|
+
?? (0, roadmap_progress_cjs_1.milestoneVersionOf)(extractFrontmatter(content, deps.sourcePath)['milestone']);
|
|
1672
1775
|
// Consult the field-classification table for the frontmatter keys this
|
|
1673
1776
|
// transition touches (same guard beginPhaseCore applies). A missing row is a
|
|
1674
1777
|
// substrate defect — fail loudly rather than silently re-encoding policy.
|
|
@@ -1812,7 +1915,7 @@ function completePhaseCore(content, intent, deps) {
|
|
|
1812
1915
|
let derivedTotalPhases = null;
|
|
1813
1916
|
const roadmapContent = deps.roadmapProvider ? deps.roadmapProvider() : null;
|
|
1814
1917
|
if (roadmapContent) {
|
|
1815
|
-
const derived = (0, phase_lifecycle_cjs_1.deriveProgressFromRoadmap)(roadmapContent);
|
|
1918
|
+
const derived = (0, phase_lifecycle_cjs_1.deriveProgressFromRoadmap)(roadmapContent, currentMilestone);
|
|
1816
1919
|
// #4901: completed counters only ratchet up, like the frontmatter twin
|
|
1817
1920
|
// (applyAuthoritativeProgressSubkeys, completedOnlyRaise). A derivation
|
|
1818
1921
|
// that lands BELOW the recorded counter read a narrower scope than the
|
|
@@ -1933,8 +2036,16 @@ function plannedPhaseCore(content, intent, deps) {
|
|
|
1933
2036
|
// Phase is system-derived, always replaced (Knuth invariant does not apply);
|
|
1934
2037
|
// Status / Last activity stay template-aware.
|
|
1935
2038
|
const beforePos = body;
|
|
2039
|
+
// #5369: with no --name the label reuses the curated frontmatter name (when it is a real one), so the
|
|
2040
|
+
// body line and the frontmatter agree on it (the #3834 reassert keeps that name in frontmatter). A
|
|
2041
|
+
// status fragment a pre-fix write left there is never copied back into the name slot.
|
|
2042
|
+
const curatedName = existingFm['current_phase_name'];
|
|
2043
|
+
const labelName = intent.phaseName
|
|
2044
|
+
?? (typeof curatedName === 'string' && curatedName.trim() !== '' && !phaseIdMod.isPlannedPhaseStatusPhrase(curatedName)
|
|
2045
|
+
? curatedName.trim()
|
|
2046
|
+
: null);
|
|
1936
2047
|
body = mutateCurrentPositionForAdvance(body, {
|
|
1937
|
-
phase: `${intent.phaseNumber}${
|
|
2048
|
+
phase: `${intent.phaseNumber}${labelName ? ` (${labelName})` : ''} — READY TO EXECUTE`,
|
|
1938
2049
|
status: 'Ready to execute',
|
|
1939
2050
|
lastActivity: `${today} — Phase ${intent.phaseNumber} planning complete`,
|
|
1940
2051
|
}, statusDefaults, lastActivityDefaults);
|
|
@@ -2255,7 +2366,20 @@ function patchCore(content, intent) {
|
|
|
2255
2366
|
// `^field:` pattern — e.g. `status` matching both the frontmatter key
|
|
2256
2367
|
// and a `Status:` body line). Frontmatter is only consulted when the
|
|
2257
2368
|
// body has no match for this key.
|
|
2258
|
-
|
|
2369
|
+
// #4998: a session field is written the way `state update` writes it —
|
|
2370
|
+
// inside the session section, never a fenced example there — so the
|
|
2371
|
+
// patch agrees with the session readers the frontmatter sync uses.
|
|
2372
|
+
const sessionWriteLabels = sessionLabelsForBodyField(field);
|
|
2373
|
+
let replaced;
|
|
2374
|
+
if (sessionWriteLabels) {
|
|
2375
|
+
const inSession = (0, state_document_cjs_1.stateReplaceFieldInSession)(body, sessionWriteLabels.primary, sessionWriteLabels.fallback, value);
|
|
2376
|
+
// Unchanged is a miss only when the field is absent: one already holding
|
|
2377
|
+
// the value matched (#4488), as stateReplaceField reports it.
|
|
2378
|
+
replaced = inSession !== body || sessionSourceExists(body, sessionWriteLabels) ? inSession : null;
|
|
2379
|
+
}
|
|
2380
|
+
else {
|
|
2381
|
+
replaced = (0, state_document_cjs_1.stateReplaceField)(body, field, value);
|
|
2382
|
+
}
|
|
2259
2383
|
if (replaced !== null) {
|
|
2260
2384
|
body = replaced;
|
|
2261
2385
|
updated.push(field);
|
|
@@ -2321,10 +2445,10 @@ function updateCore(content, intent) {
|
|
|
2321
2445
|
const sessionWriteLabels = sessionLabelsForBodyField(intent.field);
|
|
2322
2446
|
let result;
|
|
2323
2447
|
if (sessionWriteLabels) {
|
|
2324
|
-
// Replace-only by contract:
|
|
2325
|
-
// session section, which is a miss, not a write.
|
|
2448
|
+
// Replace-only by contract: the session writer never inserts a missing field.
|
|
2326
2449
|
const replaced = (0, state_document_cjs_1.stateReplaceFieldInSession)(body, sessionWriteLabels.primary, sessionWriteLabels.fallback, intent.value);
|
|
2327
|
-
|
|
2450
|
+
// A field already holding the value matched; unchanged with no field is the miss (#4488).
|
|
2451
|
+
result = replaced !== body || sessionSourceExists(body, sessionWriteLabels) ? replaced : null;
|
|
2328
2452
|
}
|
|
2329
2453
|
else {
|
|
2330
2454
|
result = (0, state_document_cjs_1.stateReplaceField)(body, intent.field, intent.value);
|