fix(#2736): write current_phase_name from the transition intent, not the lossy prose round-trip (#2821)

* fix(#2736): intent-first current_phase_name on transitions; dash-first prose precedence

Primary: completePhase (adapter) and beginPhase (via readModifyWriteStateMd
options) pass the intent-held display name to syncStateFrontmatter as an
authoritative override, applied after every derive/preserve/carry-forward
step — so the lossy prose round-trip can never destroy a name the transition
just resolved. Names containing a parenthetical
(`Closer-ruling measurement (D1a)`) now land in frontmatter verbatim instead
of collapsing to the parenthetical (`D1a`).

Secondary (#1695 AC #3 residual): parsePhaseFromProse prefers the em-dash
name when it is a genuine name (not a status keyword, not a `Milestone:`
tail), else falls back to the parenthetical — satisfying both first-party
writer shapes (`N — Name (aside)` and `N (Name) — EXECUTING`). Still lossy
for paren-containing names, which is why the intent-first override is the
primary fix.

plannedPhase carries no name in its intent, so it is naturally out of scope.

Fixes #2736

* docs(changeset): backfill PR number for #2736 fragment

* fix(#2736): drop an unnecessary type assertion on result.data

StateTransitionResult.data is already `Record<string, unknown> | undefined`,
so the cast was a no-op and tripped @typescript-eslint/no-unnecessary-type-
assertion (CI lint-tests red on the first push; every test lane was green).

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
0xdhx
2026-07-30 12:53:39 -05:00
committed by GitHub
parent 3f6b063fbb
commit 82ca13f5a5
8 changed files with 439 additions and 13 deletions

View File

@@ -534,7 +534,11 @@ function beginPhaseCore(
body = mutateCurrentPositionResume(body, intent, today, updated);
}
return { content: reassemble(body), updated };
// #2736: surface the #3127 resume decision so the adapter can drop its
// intent-first current_phase_name override on a resume — the core just
// preserved the mid-flight name, and an override would drift frontmatter
// away from the preserved body value.
return { content: reassemble(body), updated, data: { resumed: isAlreadyExecuting } };
}
// Local frontmatter type aliases matching frontmatter.cts so we can call