* fix(#4469): anchor beginPhaseCore's focusPattern regex to line start focusPattern (src/state-transition.cts, beginPhaseCore's first-time- execution branch) rewrites the **Current focus:** body line via a regex with no line anchor and \s* (crosses newlines) instead of a same-line whitespace class -- the same defect class already fixed for a different function, stateReplaceField, in #4243/PR #4453. A bold **Current focus:** quoted mid-sentence anywhere in the body (e.g. an Accumulated Context bullet documenting the field) matched first and had the rest of its line silently overwritten with the new focus label -- the #4010 data-loss class, applied to a different field. Anchored using the exact same idiom PR #4453 established: ^([ \t]*\*\*Current focus:\*\*[ \t]*)(.*)$ with the /m flag. [ \t]* (not \s*) avoids consuming the newlines before the label into the match; $ documents the match ends at end-of-line. Added a regression test to tests/state-transition.test.cjs: a body with NO real **Current focus:** field yet present, but a mid-sentence prose mention of the same bold text in an Accumulated Context bullet -- proving the unanchored pattern would corrupt the prose (empirically verified via direct node -e execution before wiring the test) while the anchored pattern's .test() correctly returns false and leaves it untouched. stateReplaceProgressPercent's bold branch (~line 51-71) is explicitly NOT touched -- the issue itself flags it as entangled with the recorded #2177 form-priority decision, needing a maintainer call. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(#4469): backfill changeset PR number Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -374,6 +374,51 @@ describe('ADR-1769 Phase 1: beginPhase first-time body field updates', () => {
|
||||
assert.ok(result.updated.includes('Current focus'),
|
||||
`updated should include 'Current focus'; got ${JSON.stringify(result.updated)}`);
|
||||
});
|
||||
|
||||
// #4469: same defect class as #4243/PR #4453's stateReplaceField bold-branch
|
||||
// fix, applied here to beginPhaseCore's own standalone `focusPattern` regex,
|
||||
// which carried no `^` anchor / `/m` flag and used `\s*` (crosses newlines)
|
||||
// rather than same-line whitespace. A body with NO real `**Current focus:**`
|
||||
// field yet present at the top, but a mid-sentence prose mention of the same
|
||||
// bold text further down (an Accumulated Context bullet documenting the
|
||||
// format), demonstrates the corruption: the unanchored pattern's first (and
|
||||
// only, since it's not global) match is the prose occurrence, and its rest-
|
||||
// of-line gets silently overwritten with the new focus label.
|
||||
test('anchored **Current focus:** rewrite leaves a mid-sentence prose lookalike untouched (#4469)', () => {
|
||||
const body = [
|
||||
'# Project State',
|
||||
'',
|
||||
'**Status:** Planning',
|
||||
'**Current Phase:** 02',
|
||||
'**Current Phase Name:** Previous Phase',
|
||||
'**Current Plan:** 02',
|
||||
'**Total Plans in Phase:** 3',
|
||||
'**Last Activity:** 2026-06-20',
|
||||
'**Last Activity Description:** previous work',
|
||||
'',
|
||||
'## Accumulated Context',
|
||||
'',
|
||||
'### Decisions',
|
||||
'',
|
||||
'- Archived phases historically rendered `**Current focus:**` inline in prose. Must not change.',
|
||||
'',
|
||||
'## Current Position',
|
||||
'',
|
||||
'Phase: 2 (Previous Phase)',
|
||||
'Plan: 2 of 3',
|
||||
'Status: Planning',
|
||||
'Last activity: 2026-06-20 — context gathered',
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
const result = transitionCore(body, intent, deps);
|
||||
const proseLine = result.content.split('\n').find((l) => l.includes('Archived phases historically'));
|
||||
assert.strictEqual(
|
||||
proseLine,
|
||||
'- Archived phases historically rendered `**Current focus:**` inline in prose. Must not change.',
|
||||
`a mid-sentence prose mention of **Current focus:** must survive byte-identically, got: ${JSON.stringify(proseLine)}`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Fixture for resume: a STATE.md body where Status already contains
|
||||
|
||||
Reference in New Issue
Block a user