@opengsd/gsd-core 1.7.0-rc.6 → 1.8.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 +14 -0
- package/README.md +2 -0
- package/agents/gsd-debug-session-manager.md +42 -4
- package/agents/gsd-debugger.md +87 -29
- package/agents/gsd-executor.md +31 -3
- package/agents/gsd-planner.md +29 -36
- package/agents/gsd-security-auditor.md +13 -15
- package/agents/gsd-verifier.md +2 -2
- package/bin/install.js +1157 -84
- package/commands/gsd/ai-integration-phase.md +1 -1
- package/commands/gsd/mempalace-capture.md +31 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/plan-phase.md +5 -3
- package/commands/gsd/plan-review-convergence.md +3 -2
- package/commands/gsd/surface.md +6 -6
- package/gsd-core/bin/gsd-tools.cjs +1866 -2434
- package/gsd-core/bin/lib/adapter-imperative.cjs +8 -1
- package/gsd-core/bin/lib/agent-command-router.cjs +20 -5
- package/gsd-core/bin/lib/api-coverage.cjs +341 -49
- package/gsd-core/bin/lib/audit.cjs +7 -6
- package/gsd-core/bin/lib/broken-windows.cjs +716 -0
- package/gsd-core/bin/lib/capability-command-router.cjs +733 -0
- package/gsd-core/bin/lib/capability-registry.cjs +157 -88
- package/gsd-core/bin/lib/capability-writer.cjs +6 -1
- package/gsd-core/bin/lib/check-command-router.cjs +129 -26
- package/gsd-core/bin/lib/claude-orchestration-command-router.cjs +115 -27
- package/gsd-core/bin/lib/claude-orchestration.cjs +84 -9
- package/gsd-core/bin/lib/clock.cjs +19 -0
- package/gsd-core/bin/lib/command-aliases.cjs +14 -0
- package/gsd-core/bin/lib/commands.cjs +129 -13
- package/gsd-core/bin/lib/config-loader.cjs +20 -4
- package/gsd-core/bin/lib/config.cjs +81 -18
- package/gsd-core/bin/lib/core-utils.cjs +14 -3
- package/gsd-core/bin/lib/decisions.cjs +32 -8
- package/gsd-core/bin/lib/docs.cjs +6 -0
- package/gsd-core/bin/lib/drift.cjs +4 -4
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +14 -2
- package/gsd-core/bin/lib/frontmatter.cjs +22 -0
- package/gsd-core/bin/lib/gap-checker.cjs +17 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +2 -1
- package/gsd-core/bin/lib/init.cjs +138 -60
- package/gsd-core/bin/lib/install-engine.cjs +301 -25
- package/gsd-core/bin/lib/install-profiles.cjs +239 -1
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +2 -1
- package/gsd-core/bin/lib/installer-migrations/005-opencode-baseline-commands-dir.cjs +146 -0
- package/gsd-core/bin/lib/installer-migrations/006-pi-extension-cjs-to-js.cjs +91 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +45 -6
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +449 -0
- package/gsd-core/bin/lib/markdown-table.cjs +698 -0
- package/gsd-core/bin/lib/milestone.cjs +463 -43
- package/gsd-core/bin/lib/model-catalog.cjs +19 -4
- package/gsd-core/bin/lib/model-resolver.cjs +189 -7
- package/gsd-core/bin/lib/onboard-projection.cjs +11 -8
- package/gsd-core/bin/lib/phase-command-router.cjs +50 -2
- package/gsd-core/bin/lib/phase-id.cjs +26 -4
- package/gsd-core/bin/lib/phase-lifecycle.cjs +62 -36
- package/gsd-core/bin/lib/phase-locator.cjs +23 -2
- package/gsd-core/bin/lib/phase.cjs +636 -72
- package/gsd-core/bin/lib/plan-scan.cjs +73 -2
- package/gsd-core/bin/lib/roadmap-parser.cjs +225 -17
- package/gsd-core/bin/lib/roadmap.cjs +113 -52
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +14 -7
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +3 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +24 -9
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +41 -17
- package/gsd-core/bin/lib/schema-detect.cjs +2 -1
- package/gsd-core/bin/lib/security.cjs +1 -1
- package/gsd-core/bin/lib/shell-command-projection.cjs +61 -25
- package/gsd-core/bin/lib/smart-entry.cjs +73 -7
- package/gsd-core/bin/lib/state-document.cjs +7 -4
- package/gsd-core/bin/lib/state-transition.cjs +122 -46
- package/gsd-core/bin/lib/state.cjs +456 -137
- package/gsd-core/bin/lib/surface.cjs +53 -11
- package/gsd-core/bin/lib/template.cjs +2 -1
- package/gsd-core/bin/lib/uat.cjs +474 -13
- package/gsd-core/bin/lib/ui-safety-gate.cjs +23 -1
- package/gsd-core/bin/lib/validate.cjs +12 -8
- package/gsd-core/bin/lib/verification.cjs +112 -17
- package/gsd-core/bin/lib/verify.cjs +224 -25
- package/gsd-core/bin/lib/workstream.cjs +3 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +1 -1
- package/gsd-core/bin/lib/write-set.cjs +38 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -2
- package/gsd-core/references/api-coverage.md +37 -7
- package/gsd-core/references/checkpoints.md +13 -1
- package/gsd-core/references/common-bug-patterns.md +13 -0
- package/gsd-core/references/debugger-bug-taxonomy.md +111 -0
- package/gsd-core/references/debugger-fix-acceptance.md +157 -0
- package/gsd-core/references/debugger-philosophy.md +1 -0
- package/gsd-core/references/debugger-prevention.md +98 -0
- package/gsd-core/references/debugger-rca-branching.md +98 -0
- package/gsd-core/references/debugger-repro-hardening.md +130 -0
- package/gsd-core/references/debugger-sbfl.md +110 -0
- package/gsd-core/references/debugger-semantic-recall.md +81 -0
- package/gsd-core/references/execute-phase-quota-recovery.md +55 -0
- package/gsd-core/references/execute-phase-requirement-revert.md +8 -0
- package/gsd-core/references/execute-phase-response-language.md +7 -0
- package/gsd-core/references/planner-antipatterns.md +6 -0
- package/gsd-core/references/planner-mvp-mode.md +12 -13
- package/gsd-core/references/planner-preconditions.md +156 -0
- package/gsd-core/references/planner-reversibility.md +132 -0
- package/gsd-core/references/reviewer-instances.md +9 -7
- package/gsd-core/references/skeleton-template.md +1 -1
- package/gsd-core/references/thinking-models-planning.md +3 -1
- package/gsd-core/templates/DEBUG.md +5 -3
- package/gsd-core/workflows/add-phase.md +2 -0
- package/gsd-core/workflows/add-tests.md +4 -2
- package/gsd-core/workflows/add-todo.md +32 -1
- package/gsd-core/workflows/ai-integration-phase.md +4 -2
- package/gsd-core/workflows/audit-fix.md +2 -2
- package/gsd-core/workflows/check-todos.md +3 -1
- package/gsd-core/workflows/cleanup.md +7 -1
- package/gsd-core/workflows/code-review.md +17 -5
- package/gsd-core/workflows/complete-milestone.md +3 -0
- package/gsd-core/workflows/debug.md +27 -5
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- package/gsd-core/workflows/discovery-phase.md +7 -0
- package/gsd-core/workflows/discuss-phase/templates/context.md +16 -2
- package/gsd-core/workflows/discuss-phase-assumptions.md +3 -0
- package/gsd-core/workflows/do.md +7 -1
- package/gsd-core/workflows/docs-update.md +1 -0
- package/gsd-core/workflows/eval-review.md +3 -0
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +4 -4
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -2
- package/gsd-core/workflows/execute-phase.md +30 -37
- package/gsd-core/workflows/execute-plan.md +15 -4
- package/gsd-core/workflows/fast.md +8 -22
- package/gsd-core/workflows/graduation.md +3 -0
- package/gsd-core/workflows/health.md +7 -1
- package/gsd-core/workflows/help/modes/full.md +6 -2
- package/gsd-core/workflows/import.md +8 -2
- package/gsd-core/workflows/inbox.md +7 -0
- package/gsd-core/workflows/ingest-docs.md +15 -10
- package/gsd-core/workflows/manager.md +3 -1
- package/gsd-core/workflows/map-codebase.md +4 -4
- package/gsd-core/workflows/mvp-phase.md +3 -0
- package/gsd-core/workflows/new-milestone.md +69 -21
- package/gsd-core/workflows/new-project.md +17 -15
- package/gsd-core/workflows/new-workspace.md +3 -1
- package/gsd-core/workflows/onboard.md +3 -0
- package/gsd-core/workflows/plan-phase.md +14 -5
- package/gsd-core/workflows/plan-review-convergence.md +48 -3
- package/gsd-core/workflows/plant-seed.md +3 -0
- package/gsd-core/workflows/profile-user.md +7 -1
- package/gsd-core/workflows/progress.md +33 -5
- package/gsd-core/workflows/quick.md +21 -7
- package/gsd-core/workflows/remove-workspace.md +3 -0
- package/gsd-core/workflows/review.md +123 -68
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +4 -1
- package/gsd-core/workflows/settings-integrations.md +3 -0
- package/gsd-core/workflows/settings.md +3 -0
- package/gsd-core/workflows/ship.md +58 -5
- package/gsd-core/workflows/sketch.md +3 -0
- package/gsd-core/workflows/smart-entry.md +3 -0
- package/gsd-core/workflows/spec-phase.md +1 -1
- package/gsd-core/workflows/spike.md +7 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +3 -1
- package/gsd-core/workflows/ui-review.md +3 -0
- package/gsd-core/workflows/undo.md +7 -0
- package/gsd-core/workflows/update.md +2 -0
- package/gsd-core/workflows/validate-phase.md +3 -0
- package/gsd-core/workflows/verify-phase.md +2 -2
- package/gsd-core/workflows/verify-work.md +7 -3
- package/hooks/dist/gsd-context-monitor.js +27 -9
- package/hooks/dist/gsd-statusline.js +252 -17
- package/hooks/gsd-context-monitor.js +27 -9
- package/hooks/gsd-statusline.js +252 -17
- package/package.json +8 -4
- package/pi/gsd.cjs +8 -2
- package/scripts/changeset/lint.cjs +1 -0
- package/scripts/changeset/parse.cjs +26 -0
- package/scripts/check-glossary-refs.cjs +220 -0
- package/scripts/ci-rebase-check.cjs +48 -4
- package/scripts/ci-test-scope.cjs +39 -1
- package/scripts/gen-adr-index.cjs +526 -0
- package/scripts/gen-golden-install-parity-zcode.cjs +35 -45
- package/scripts/gen-install-tree-fixtures.cjs +75 -0
- package/scripts/gen-test-timings.cjs +201 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -1
- package/scripts/lint-portable-timeout.cjs +140 -0
- package/scripts/lint-table-schema-drift.cjs +157 -0
- package/scripts/lint-test-file-count.allowlist.json +1 -0
- package/scripts/release-tarball-smoke.cjs +18 -11
- package/scripts/run-tests.cjs +420 -58
- package/skills/gsd-ai-integration-phase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +31 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-plan-phase/SKILL.md +5 -3
- package/skills/gsd-plan-review-convergence/SKILL.md +3 -2
- package/skills/gsd-surface/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -153,11 +153,14 @@ function shouldPreserveExistingProgress(existingProgress, derivedProgress) {
|
|
|
153
153
|
return false;
|
|
154
154
|
const existing = existingProgress;
|
|
155
155
|
const derived = derivedProgress;
|
|
156
|
-
// total_phases
|
|
157
|
-
// take the freshly derived value so
|
|
158
|
-
//
|
|
156
|
+
// total_phases (#1446) and total_plans (#2440) are intentionally excluded
|
|
157
|
+
// from the ratchet: both must always take the freshly derived value so they
|
|
158
|
+
// can correct in BOTH directions. total_plans legitimately moves up (a new
|
|
159
|
+
// phase adds plans) and down (milestone reorganization removes phases).
|
|
160
|
+
// Ratcheting it freezes stale values. Only completed_phases and
|
|
161
|
+
// completed_plans keep ratchet behaviour — they are monotonic (once a
|
|
162
|
+
// phase/plan is complete, it stays complete).
|
|
159
163
|
return (existingProgressExceedsDerived(existing, derived, 'completed_phases') ||
|
|
160
|
-
existingProgressExceedsDerived(existing, derived, 'total_plans') ||
|
|
161
164
|
existingProgressExceedsDerived(existing, derived, 'completed_plans'));
|
|
162
165
|
}
|
|
163
166
|
function normalizeProgressNumbers(progress) {
|
|
@@ -28,7 +28,7 @@ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
|
28
28
|
const phase_lifecycle_cjs_1 = require("./phase-lifecycle.cjs");
|
|
29
29
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
30
30
|
const phaseIdMod = require("./phase-id.cjs");
|
|
31
|
-
const { extractFrontmatter, reconstructFrontmatter } = frontmatter;
|
|
31
|
+
const { extractFrontmatter, reconstructFrontmatter, stripFrontmatter } = frontmatter;
|
|
32
32
|
const { escapeRegex } = phaseIdMod;
|
|
33
33
|
// Stop predicate for section-body slicing: a level-2+ heading ends the section.
|
|
34
34
|
const STOP_H2_PLUS = (lv) => lv >= 2;
|
|
@@ -100,7 +100,28 @@ function applyStatePreservation(input) {
|
|
|
100
100
|
!resync &&
|
|
101
101
|
preFm &&
|
|
102
102
|
preFm['progress']) {
|
|
103
|
-
|
|
103
|
+
// #2440: when the caller opts in (deriveProgressKeys), total_plans and
|
|
104
|
+
// total_phases always take the derived (post-sync) value even under !resync.
|
|
105
|
+
// This is used by cmdStatePlannedPhase where total_plans must correct upward
|
|
106
|
+
// after plans are added. For body-only writes (state.update/patch without
|
|
107
|
+
// the flag), the wholesale restore preserves everything as before — the
|
|
108
|
+
// #3242 Bug A protection stays fully in force.
|
|
109
|
+
if (input.deriveProgressKeys && postFm['progress']) {
|
|
110
|
+
const curated = preFm['progress'];
|
|
111
|
+
const derived = (postFm['progress'] ?? {});
|
|
112
|
+
const merged = { ...derived };
|
|
113
|
+
if (curated) {
|
|
114
|
+
for (const [key, value] of Object.entries(curated)) {
|
|
115
|
+
if (key !== 'total_plans' && key !== 'total_phases') {
|
|
116
|
+
merged[key] = value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
postFm['progress'] = merged;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
postFm['progress'] = preFm['progress'];
|
|
124
|
+
}
|
|
104
125
|
mutated = true;
|
|
105
126
|
}
|
|
106
127
|
// status — #1230 body-delta heuristic. Table: preserve-when-unchanged.
|
|
@@ -226,7 +247,7 @@ function beginPhaseCore(content, intent, deps) {
|
|
|
226
247
|
const reassemble = (b) => hasFrontmatter
|
|
227
248
|
? `---\n${reconstructFrontmatter(existingFm)}\n---\n\n${b}`
|
|
228
249
|
: b;
|
|
229
|
-
const today = deps.clock.
|
|
250
|
+
const today = deps.clock.localToday();
|
|
230
251
|
// Consult the field-classification table for the frontmatter keys this
|
|
231
252
|
// transition touches (codex Phase 1 review: "table not consulted by
|
|
232
253
|
// transitionCore"). The table tracks FRONTMATTER keys (lowercase: `status`,
|
|
@@ -284,8 +305,8 @@ function beginPhaseCore(content, intent, deps) {
|
|
|
284
305
|
updated.push('Current focus');
|
|
285
306
|
}
|
|
286
307
|
// ## Current Position section mutation (#1104, #1365).
|
|
287
|
-
//
|
|
288
|
-
//
|
|
308
|
+
// `locateCurrentPosition` (fence-aware, tokenizeHeadings-based) locates
|
|
309
|
+
// the section; mirrors state.cts:2261-2324 byte-for-behaviour.
|
|
289
310
|
body = mutateCurrentPositionFirstTime(body, intent, today, updated);
|
|
290
311
|
}
|
|
291
312
|
else {
|
|
@@ -338,6 +359,20 @@ function sliceCurrentPositionSection(body) {
|
|
|
338
359
|
* First-time ## Current Position mutation: update Phase / Plan / Status /
|
|
339
360
|
* Last activity lines. Mirrors state.cts:2261-2324 byte-for-behaviour
|
|
340
361
|
* (inline regex first, pipe-table fallback via stateReplaceField — #1257).
|
|
362
|
+
*
|
|
363
|
+
* F2 (#2245 review, MAJOR): a prior revision of this function used
|
|
364
|
+
* `collectSection`/`replaceSection` here, whose default `levelBounded: true`
|
|
365
|
+
* only stops the section at the next heading of level <= the opener's own
|
|
366
|
+
* level (H1/H2 for a `##`-opened section) — an H3+ subsection nested under
|
|
367
|
+
* `## Current Position` was NOT a stop boundary and got folded into
|
|
368
|
+
* `sectionBody`, so the field regexes below (which run with the `m` flag,
|
|
369
|
+
* matching ANY line start in the body) could clobber a same-named line
|
|
370
|
+
* inside that subsection (the #2130/#2067/#2080 truncation/clobber class).
|
|
371
|
+
* Restored to the fence-aware `locateCurrentPosition` locator (which stops
|
|
372
|
+
* at ANY heading level >= 2, `STOP_H2_PLUS` — H2 through H6) + manual splice,
|
|
373
|
+
* exactly matching the `mutateCurrentPositionResume`/
|
|
374
|
+
* `mutateCurrentPositionForAdvance` siblings below, both of which use
|
|
375
|
+
* `locateCurrentPosition` directly.
|
|
341
376
|
*/
|
|
342
377
|
function mutateCurrentPositionFirstTime(body, intent, today, updated) {
|
|
343
378
|
const span = locateCurrentPosition(body);
|
|
@@ -414,25 +449,6 @@ function mutateCurrentPositionResume(body, intent, today, updated) {
|
|
|
414
449
|
}
|
|
415
450
|
return body.slice(0, span.start) + sectionBody + body.slice(span.end);
|
|
416
451
|
}
|
|
417
|
-
/**
|
|
418
|
-
* Strip ALL frontmatter blocks from the start of `content`.
|
|
419
|
-
*
|
|
420
|
-
* TODO (ADR-1769 follow-up): move to `frontmatter.cjs` or `state-document.cjs`
|
|
421
|
-
* so it's a shared primitive. Inlined here in Phase 1 to avoid touching
|
|
422
|
-
* `state.cjs` (which is the migration target itself) and to keep the Phase 1
|
|
423
|
-
* diff contained. Body is byte-identical to `state.cts:1653 stripFrontmatter`
|
|
424
|
-
* (same CRLF + stacked-block handling).
|
|
425
|
-
*/
|
|
426
|
-
function stripFrontmatter(content) {
|
|
427
|
-
let result = content;
|
|
428
|
-
while (true) {
|
|
429
|
-
const stripped = result.replace(/^\s*---\r?\n[\s\S]*?\r?\n---\s*/, '');
|
|
430
|
-
if (stripped === result)
|
|
431
|
-
break;
|
|
432
|
-
result = stripped;
|
|
433
|
-
}
|
|
434
|
-
return result;
|
|
435
|
-
}
|
|
436
452
|
/**
|
|
437
453
|
* Update fields within the ## Current Position section for advancePlan.
|
|
438
454
|
* Mirrors `updateCurrentPositionFields` (state.cts:496) byte-for-behaviour:
|
|
@@ -502,7 +518,7 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
|
|
|
502
518
|
* adapter to construct CLI output.
|
|
503
519
|
*/
|
|
504
520
|
function advancePlanCore(content, deps) {
|
|
505
|
-
const today = deps.clock.
|
|
521
|
+
const today = deps.clock.localToday();
|
|
506
522
|
// #1255: body-field replacements operate on body only (frontmatter stripped),
|
|
507
523
|
// not on the full content. The YAML `status:` key matches `^Status:\s*`
|
|
508
524
|
// before the body field if full content is passed (codex Phase 2 review:
|
|
@@ -591,8 +607,8 @@ function advancePlanCore(content, deps) {
|
|
|
591
607
|
* Migrates the inline STATE.md transform that lived inside `cmdPhaseComplete`
|
|
592
608
|
* (phase.cts) onto the substrate. Owns the field-classification-governed body
|
|
593
609
|
* mutations: Current Phase (preserving the `of total` shape and phase name),
|
|
594
|
-
* Current Phase Name, Status (`
|
|
595
|
-
* `Ready to plan`), Current Plan (`Not started`), Last Activity + Description,
|
|
610
|
+
* Current Phase Name, Status (`All phases complete` on the last phase, else
|
|
611
|
+
* `Ready to plan` per ADR-2207), Current Plan (`Not started`), Last Activity + Description,
|
|
596
612
|
* and the Completed/Total Phases + Progress percent block (re-derived from the
|
|
597
613
|
* roadmap via the injected `roadmapProvider`).
|
|
598
614
|
*
|
|
@@ -608,7 +624,7 @@ function advancePlanCore(content, deps) {
|
|
|
608
624
|
*/
|
|
609
625
|
function completePhaseCore(content, intent, deps) {
|
|
610
626
|
const updated = [];
|
|
611
|
-
const today = deps.clock.
|
|
627
|
+
const today = deps.clock.localToday();
|
|
612
628
|
// Consult the field-classification table for the frontmatter keys this
|
|
613
629
|
// transition touches (same guard beginPhaseCore applies). A missing row is a
|
|
614
630
|
// substrate defect — fail loudly rather than silently re-encoding policy.
|
|
@@ -672,8 +688,10 @@ function completePhaseCore(content, intent, deps) {
|
|
|
672
688
|
updated.push('Current Phase Name');
|
|
673
689
|
}
|
|
674
690
|
}
|
|
675
|
-
// Status — `
|
|
676
|
-
|
|
691
|
+
// Status — `All phases complete` on the final phase (ADR-2207), otherwise
|
|
692
|
+
// `Ready to plan`. Milestone termination (`<version> milestone complete`) is
|
|
693
|
+
// owned solely by the milestone-close verb (milestoneCompleteCore).
|
|
694
|
+
const statusValue = intent.isLastPhase ? 'All phases complete' : 'Ready to plan';
|
|
677
695
|
const statusAfter = (0, state_document_cjs_1.stateReplaceFieldWithFallback)(body, 'Status', null, statusValue);
|
|
678
696
|
if (statusAfter !== body) {
|
|
679
697
|
body = statusAfter;
|
|
@@ -762,7 +780,7 @@ function completePhaseCore(content, intent, deps) {
|
|
|
762
780
|
*/
|
|
763
781
|
function plannedPhaseCore(content, intent, deps) {
|
|
764
782
|
const updated = [];
|
|
765
|
-
const today = deps.clock.
|
|
783
|
+
const today = deps.clock.localToday();
|
|
766
784
|
for (const fmKey of ['status', 'last_activity', 'last_activity_desc']) {
|
|
767
785
|
const cls = getFieldClassification(fmKey);
|
|
768
786
|
if (cls === null) {
|
|
@@ -840,7 +858,7 @@ function plannedPhaseCore(content, intent, deps) {
|
|
|
840
858
|
* directly and must not run the steady-state `syncStateFrontmatter` post-sync.
|
|
841
859
|
*/
|
|
842
860
|
function milestoneSwitchCore(content, intent, deps) {
|
|
843
|
-
const today = deps.clock.
|
|
861
|
+
const today = deps.clock.localToday();
|
|
844
862
|
const updated = [
|
|
845
863
|
'milestone',
|
|
846
864
|
'milestone_name',
|
|
@@ -903,6 +921,70 @@ function milestoneSwitchCore(content, intent, deps) {
|
|
|
903
921
|
// ----------------------------------------------------------------------------
|
|
904
922
|
// milestoneComplete — intent implementation (Phase 5)
|
|
905
923
|
// ----------------------------------------------------------------------------
|
|
924
|
+
/**
|
|
925
|
+
* Replace a section's ENTIRE body with `newBody`, discarding whatever was
|
|
926
|
+
* there — the "wholesale reset" write pattern used by milestoneComplete's
|
|
927
|
+
* closure write (## Current Position / ## Operator Next Steps). Retires the
|
|
928
|
+
* fence-blind raw regex `(##\s*<heading>\s*\n)([\s\S]*?)(?=\n##|$)`, which a
|
|
929
|
+
* literal `##` inside a fenced code block in the section body could fool into
|
|
930
|
+
* stopping early (the #2130/#2067/#2080 truncation class) — heading location
|
|
931
|
+
* here goes through `tokenizeHeadings`, which is fence-aware.
|
|
932
|
+
*
|
|
933
|
+
* Byte-parity note: the retired regex's greedy `\s*` (before its mandatory
|
|
934
|
+
* `\n`) swallowed any blank line(s) immediately after the heading into the
|
|
935
|
+
* discarded match, and its non-greedy body match always left exactly ONE
|
|
936
|
+
* newline unconsumed before the next heading (or EOF), regardless of how many
|
|
937
|
+
* blank lines originally separated the section from what followed. Both
|
|
938
|
+
* edges are reproduced explicitly (rather than delegated to `collectSection`'s
|
|
939
|
+
* `trimEnd()`-based body, which trims a *different* amount and would drift
|
|
940
|
+
* the surrounding blank-line count) so `newBody`'s own leading/trailing
|
|
941
|
+
* formatting is exactly what appears in the output.
|
|
942
|
+
*
|
|
943
|
+
* Returns `null` when no heading matches `headingPredicate` (mirrors the
|
|
944
|
+
* retired regex's `pattern.test(body)` miss) — callers fall back to their own
|
|
945
|
+
* append-a-new-section path.
|
|
946
|
+
*/
|
|
947
|
+
function resetSectionVerbatim(content, headingPredicate, newBody) {
|
|
948
|
+
const headings = (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(content);
|
|
949
|
+
const idx = headings.findIndex(headingPredicate);
|
|
950
|
+
if (idx === -1)
|
|
951
|
+
return null;
|
|
952
|
+
const target = headings[idx];
|
|
953
|
+
const lines = content.split('\n');
|
|
954
|
+
const headingLineEnd = target.offset + lines[target.line - 1].length + 1;
|
|
955
|
+
// Swallow blank line(s) immediately after the heading (mirrors the retired
|
|
956
|
+
// regex's greedy `\s*` folding them into the discarded match).
|
|
957
|
+
//
|
|
958
|
+
// F7 (#2245 review, nit): recognise a CRLF blank line (`\r\n`), not only a
|
|
959
|
+
// bare LF — a lone `content[bodyStart] === '\n'` check never advances past
|
|
960
|
+
// a `\r` byte, so on a CRLF STATE.md the blank line right after the
|
|
961
|
+
// heading fell into the DISCARDED [bodyStart, bodyEnd) span instead of the
|
|
962
|
+
// KEPT prefix, silently dropping one blank line (contradicting this
|
|
963
|
+
// function's own byte-parity docstring).
|
|
964
|
+
let bodyStart = headingLineEnd;
|
|
965
|
+
while (bodyStart < content.length) {
|
|
966
|
+
if (content[bodyStart] === '\n') {
|
|
967
|
+
bodyStart += 1;
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
if (content[bodyStart] === '\r' && content[bodyStart + 1] === '\n') {
|
|
971
|
+
bodyStart += 2;
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
break;
|
|
975
|
+
}
|
|
976
|
+
// Stop at the next heading of level >= 2 (mirrors the retired regex's
|
|
977
|
+
// literal `##` lookahead, which matches any ATX heading two-or-more levels
|
|
978
|
+
// deep); leave exactly one newline unconsumed before it, or run to EOF.
|
|
979
|
+
let bodyEnd = content.length;
|
|
980
|
+
for (let j = idx + 1; j < headings.length; j++) {
|
|
981
|
+
if (STOP_H2_PLUS(headings[j].level)) {
|
|
982
|
+
bodyEnd = headings[j].offset - 1;
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
return content.slice(0, bodyStart) + newBody + content.slice(bodyEnd);
|
|
987
|
+
}
|
|
906
988
|
/**
|
|
907
989
|
* Apply a `milestoneComplete` transition to STATE.md content.
|
|
908
990
|
*
|
|
@@ -917,17 +999,11 @@ function milestoneSwitchCore(content, intent, deps) {
|
|
|
917
999
|
* runtime-specific next-milestone slash command, injecting it via
|
|
918
1000
|
* `intent.nextMilestoneCommand` so the core stays pure.
|
|
919
1001
|
*
|
|
920
|
-
* The two section resets use raw regex (with the pre-seam `allow-adhoc-markdown`
|
|
921
|
-
* waivers carried from milestone.cts) rather than tokenizeHeadings because the
|
|
922
|
-
* `## Operator Next Steps` section is non-canonical (not in STATE_MD_SECTIONS)
|
|
923
|
-
* and the existing behavior + its tests pin the exact regex semantics. A future
|
|
924
|
-
* collectSection migration (#1372) can swap both to section primitives.
|
|
925
|
-
*
|
|
926
1002
|
* Behavior is byte-for-byte with the pre-migration milestone.cts:314-353 block.
|
|
927
1003
|
*/
|
|
928
1004
|
function milestoneCompleteCore(content, intent, deps) {
|
|
929
1005
|
const updated = [];
|
|
930
|
-
const today = deps.clock.
|
|
1006
|
+
const today = deps.clock.localToday();
|
|
931
1007
|
const version = intent.version;
|
|
932
1008
|
for (const fmKey of ['status', 'last_activity', 'last_activity_desc']) {
|
|
933
1009
|
const cls = getFieldClassification(fmKey);
|
|
@@ -963,22 +1039,22 @@ function milestoneCompleteCore(content, intent, deps) {
|
|
|
963
1039
|
}
|
|
964
1040
|
// ## Current Position reset — stop resume/progress flows pointing at closed
|
|
965
1041
|
// execution instructions.
|
|
966
|
-
const positionPattern = /(##\s*Current Position\s*\n)([\s\S]*?)(?=\n##|$)/i; // allow-adhoc-markdown: pre-seam section write-modify carried from milestone.cts; pending collectSection migration #1372
|
|
967
1042
|
const closedPositionBody = `\nPhase: Milestone ${version} complete\n` +
|
|
968
1043
|
`Plan: —\n` +
|
|
969
1044
|
`Status: Awaiting next milestone\n` +
|
|
970
1045
|
`Last activity: ${today} — Milestone ${version} completed and archived\n\n`;
|
|
971
|
-
|
|
972
|
-
|
|
1046
|
+
const positionReset = resetSectionVerbatim(body, (h) => h.level === 2 && /^current\s+position$/i.test(h.text), closedPositionBody);
|
|
1047
|
+
if (positionReset !== null) {
|
|
1048
|
+
body = positionReset;
|
|
973
1049
|
}
|
|
974
1050
|
else {
|
|
975
1051
|
body = `${body.trimEnd()}\n\n## Current Position\n${closedPositionBody}`;
|
|
976
1052
|
}
|
|
977
1053
|
updated.push('Current Position');
|
|
978
1054
|
// ## Operator Next Steps — normalize stale tails that can persist after close.
|
|
979
|
-
const
|
|
980
|
-
if (
|
|
981
|
-
body =
|
|
1055
|
+
const operatorReset = resetSectionVerbatim(body, (h) => h.level === 2 && /^operator\s+next\s+steps$/i.test(h.text), `\n- Start the next milestone with ${intent.nextMilestoneCommand}\n\n`);
|
|
1056
|
+
if (operatorReset !== null) {
|
|
1057
|
+
body = operatorReset;
|
|
982
1058
|
}
|
|
983
1059
|
else {
|
|
984
1060
|
body = `${body.trimEnd()}\n\n## Operator Next Steps\n\n- Start the next milestone with ${intent.nextMilestoneCommand}\n`;
|
|
@@ -1175,7 +1251,7 @@ function pruneCore(content, intent) {
|
|
|
1175
1251
|
* (rather than silently writing fallback-derived wrong values).
|
|
1176
1252
|
*/
|
|
1177
1253
|
function syncCore(content, intent, deps) {
|
|
1178
|
-
const today = deps.clock.
|
|
1254
|
+
const today = deps.clock.localToday();
|
|
1179
1255
|
const changes = [];
|
|
1180
1256
|
let modified = content;
|
|
1181
1257
|
const updated = [];
|