From f65866f17da6c80a2c77420be536c192218ea5bd Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Mon, 29 Jun 2026 20:08:17 -0400 Subject: [PATCH] fix(#1831): resolve ESLint errors and unused-var warnings from #1829/#1830 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two hard errors in src/state-transition.cts: - reconcileCurrentPosition: `!== null` guards on `Record` values don't narrow the type to a primitive, causing @typescript-eslint/no-base-to-string to fire on String(fm.current_phase) and String(fm.current_phase_name). Extract to typed locals + use typeof narrowing so the rule sees string | number — which is the actual invariant. Four unused-var warnings (warnings are still defects per RULESET): - stripTemplatePlaceholders: `placeholder` was assigned value.trim() but never read — the value came from the loop variable itself, so removed. - deduplicateSessionArchive: `sectionContent` was sliced but the filter below uses the raw hs offsets directly — variable was dead code; removed. - state-rebuild.test.cjs: first transitionCore call in the orphan-row test is covered by the dedicated Leaky-Abstractions guard test below it; remove the no-op call rather than silently ignoring its result. - state-rebuild.test.cjs: `before = state` in the sync regression guard test was never read — remove the dead assignment. Co-Authored-By: Claude Sonnet 4.6 --- src/state-transition.cts | 13 ++++++------- tests/state-rebuild.test.cjs | 4 +--- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/state-transition.cts b/src/state-transition.cts index ac6ef6299..f7051d6ab 100644 --- a/src/state-transition.cts +++ b/src/state-transition.cts @@ -1666,8 +1666,9 @@ function reconcileCurrentPosition( // by other transitions (beginPhase / completePhase) and reconstructed from // total-phase counts; rebuild reconciles only the `**Current Phase:**` body // field that frontmatter is the canonical source for. - if (fm.current_phase !== undefined && fm.current_phase !== null) { - const canonicalPhase = String(fm.current_phase); + const fmPhase = fm.current_phase; + if (typeof fmPhase === 'string' || typeof fmPhase === 'number') { + const canonicalPhase = String(fmPhase); const existing = stateExtractField(modified, 'Current Phase'); if (existing !== null && existing !== canonicalPhase) { const replaced = stateReplaceField(modified, 'Current Phase', canonicalPhase); @@ -1686,8 +1687,9 @@ function reconcileCurrentPosition( } // Phase name prose. - if (fm.current_phase_name !== undefined && fm.current_phase_name !== null) { - const canonicalName = String(fm.current_phase_name); + const fmPhaseName = fm.current_phase_name; + if (typeof fmPhaseName === 'string' || typeof fmPhaseName === 'number') { + const canonicalName = String(fmPhaseName); const existing = stateExtractField(modified, 'Current Phase Name'); if (existing !== null && existing !== canonicalName) { const replaced = stateReplaceField(modified, 'Current Phase Name', canonicalName); @@ -1821,7 +1823,6 @@ function stripTemplatePlaceholders( const fieldName = m[1]; const value = m[2]; if (TEMPLATE_PLACEHOLDER_VALUE.test(value)) { - const placeholder = value.trim(); const cleared = `**${fieldName}:** (pending)`; replacements.push({ lineIdx: i, before: line, after: cleared, fieldName }); } @@ -1874,8 +1875,6 @@ function deduplicateSessionArchive( for (let i = sectionIdx + 1; i < hs.length; i++) { if (hs[i].level === 2) { sectionEnd = hs[i].offset; break; } } - const sectionContent = content.slice(sectionStart, sectionEnd); - // Count `### Session — …` H3 sub-headings inside the section. const archiveHeadings = hs.filter( (h) => h.level === 3 && h.offset >= sectionStart && h.offset < sectionEnd && SESSION_ARCHIVE_H3.test(h.text), diff --git a/tests/state-rebuild.test.cjs b/tests/state-rebuild.test.cjs index b2f7e361b..ba4492e85 100644 --- a/tests/state-rebuild.test.cjs +++ b/tests/state-rebuild.test.cjs @@ -367,8 +367,7 @@ describe('ADR-1817 §2: rebuild reconciles **By Phase:** table via phaseInventor { number: '3', name: 'Test Phase', planCount: 5, summaryCount: 4 }, ], }; - const result = transitionCore(drifted, { kind: 'rebuild' }, baseDeps); - // Note: passing baseDeps here (no provider) → reconcile is a no-op. + // First call with no phaseInventoryProvider → no-op (covered by its own test below). // Re-run with the provider-wired deps: const result2 = transitionCore(drifted, { kind: 'rebuild' }, deps); // Strip the audit log so we only inspect LIVE table rows (the log's @@ -421,7 +420,6 @@ describe('ADR-1817 §2: rebuild reconciles **By Phase:** table via phaseInventor describe('ADR-1817 §5/§6: rebuild does not affect sync or prune (regression guard, criterion #7)', () => { test('sync still patches its three frontmatter fields when rebuild is also available', () => { const state = cleanState(); - const before = state; const result = transitionCore( state, { kind: 'sync', totalPlansInPhase: 7, percent: 50 },