* fix(#1658): make byPhaseTablePattern CRLF-tolerant on STATE.md tables byPhaseTablePattern required a bare \n after the header and separator rows, so a STATE.md with CRLF (\r\n) line endings (Windows, or hand-edited) had its By-Phase table treated as absent: phase complete never upserted the row (and the velocity-from- table derivation went stale). Make the header/separator terminators and the closing lookahead CRLF-tolerant ([ \t]*\r?\n, (?=\r?\n|$)). Backward-compatible with LF. Regression folded into tests/state.test.cjs: phase complete on a CRLF STATE.md upserts the row and removes the placeholder. CONTRIBUTING's QA matrix lists Mixed CRLF/LF as a required parser case. * chore(#1658): backfill changeset pr ref to 1662
This commit is contained in:
5
.changeset/sturdy-tigers-caper.md
Normal file
5
.changeset/sturdy-tigers-caper.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 1662
|
||||
---
|
||||
**`phase complete` now updates the By-Phase table on CRLF (Windows) STATE.md files** — the By-Phase table matcher required bare `\n` line endings, so a STATE.md written or hand-edited with CRLF (`\r\n`) was treated as having no table: the completed phase's row was never upserted (and, with the velocity-from-table derivation, the total went stale). The matcher is now CRLF-tolerant (`\r?\n`) on the header/separator/lookahead, so CRLF STATE.md files are handled identically to LF.
|
||||
@@ -263,7 +263,7 @@ function _stateLockBodyPid(lockPath: string): number | null {
|
||||
let _stateStealSeq = 0;
|
||||
|
||||
// Hoisted to module scope — compiled once, not per call (#320). Stateless (/i, used with .match).
|
||||
const byPhaseTablePattern = /(\|\s*Phase\s*\|\s*Plans\s*\|\s*Total\s*\|\s*Avg\/Plan\s*\|[ \t]*\n\|(?:[- :\t]+\|)+[ \t]*\n)((?:[ \t]*\|[^\n]*\n)*)(?=\n|$)/i;
|
||||
const byPhaseTablePattern = /(\|\s*Phase\s*\|\s*Plans\s*\|\s*Total\s*\|\s*Avg\/Plan\s*\|[ \t]*\r?\n\|(?:[- :\t]+\|)+[ \t]*\r?\n)((?:[ \t]*\|[^\n]*\n)*)(?=\r?\n|$)/i;
|
||||
|
||||
// ─── ADR-1372 T6: seam-based section splice helper ───────────────────────────
|
||||
|
||||
|
||||
@@ -2111,6 +2111,45 @@ describe('updatePerformanceMetricsSection', () => {
|
||||
// Total plans count updated correctly (1 pre-existing + 2 new summaries)
|
||||
assert.ok(stateAfter.match(/Total plans completed:\s*3/), 'Total plans completed should be 3 after upsert');
|
||||
});
|
||||
|
||||
test('#1658 — By-Phase table row upserts on a CRLF STATE.md (byPhaseTablePattern must be CRLF-tolerant)', () => {
|
||||
const content = [
|
||||
'# Project State', '',
|
||||
'**Current Phase:** 07', '**Status:** Executing Phase 7', '',
|
||||
'## Performance Metrics', '',
|
||||
'**Velocity:**',
|
||||
'- Total plans completed: [N]',
|
||||
'- Average duration: N/A',
|
||||
'- Total execution time: 0 hours', '',
|
||||
'**By Phase:**', '',
|
||||
'| Phase | Plans | Total | Avg/Plan |',
|
||||
'|-------|-------|-------|----------|',
|
||||
'| - | - | - | - |', '',
|
||||
'## Accumulated Context', '',
|
||||
].join('\n');
|
||||
const statePath = path.join(tmpDir, '.planning', 'STATE.md');
|
||||
// Force CRLF line endings across the whole STATE.md (Windows / hand-edited).
|
||||
fs.writeFileSync(statePath, content.replace(/\n/g, '\r\n'), 'utf8');
|
||||
|
||||
const phaseDir = path.join(tmpDir, '.planning', 'phases', '07-crlf');
|
||||
fs.mkdirSync(phaseDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(phaseDir, '07-01-PLAN.md'), '# Plan\n');
|
||||
fs.writeFileSync(path.join(phaseDir, '07-01-SUMMARY.md'), '# Summary\n');
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap\n\n## Phase 7: CRLF\n\n- [ ] Phase 7\n');
|
||||
|
||||
const result = runGsdTools('phase complete 7', tmpDir);
|
||||
assert.ok(result.success, `phase complete failed: ${result.error}`);
|
||||
|
||||
const after = fs.readFileSync(statePath, 'utf8');
|
||||
assert.ok(
|
||||
/\|\s*7\s*\|\s*1\s*\|/.test(after),
|
||||
'By-Phase row for phase 7 must be upserted even on a CRLF STATE.md (#1658)',
|
||||
);
|
||||
assert.ok(
|
||||
!/\|\s*-\s*\|\s*-\s*\|\s*-\s*\|\s*-\s*\|/.test(after),
|
||||
'placeholder row must be removed on CRLF STATE.md once a real row is upserted',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user