diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c1125ee1..efbdc9a13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,8 @@ Run `~/.claude/skills/gstack/bin/gstack-brain-sync --status`. Each held file is - The outside-review classifier reported reviews with severity words and no `[Pn]` tags as clean. - /context-restore picked an older checkpoint from a nested repo or a task worktree. (#3065) - Browse cookbook: the script mistakes that look like page bugs (non-serializable `evaluate` results, a top-level `return`, `pg.press`, empty DOM reads). (#3063, #3064) +- `ship-measure report` no longer crashes on a measurement saved by v1.91.32.0; it lists that round as an older format to re-measure. +- The paid shared-code cases recognize Claude Code 2.1.292's own internal git probe, which now disables every git hook by name, instead of reading it as an unguarded git call from the skill. - Credits in the v1.64.0.0 entry: the extension token fix (#1822) goes to @Mike-E-Log, and the polyfill `exited` promise (#1743) to @habassa5. (#3054) #### For contributors diff --git a/scripts/ship-measure.ts b/scripts/ship-measure.ts index 187d232f9..c8e5c1ff1 100644 --- a/scripts/ship-measure.ts +++ b/scripts/ship-measure.ts @@ -487,6 +487,10 @@ export function formatReport(outDir: string): string { for (const round of ledger?.rounds ?? []) { const m = readJson(path.join(dir, round, 'measurement.json')); if (!m) continue; + if (m.schema !== 'gstack-ship-measure/2') { + rows.push(`| ${m.case} | ${m.kind ?? '—'} | ${round} | — | — | older measurement format (${String(m.schema).replace(/\|/g, '/')}); not read, re-measure on this release | — | — |`); + continue; + } const bar = m.counted > m.plan.trials ? m.plan.pooledBar : m.plan.bar; const what = m.fix ? `${m.round}: after fix at ${m.fix.replace(/\|/g, '/')}` : m.round; const batches = m.sets.length > 1 ? ` (${m.sets.map(s => `${s.set} ${s.passes}/${s.trials}${s.void ? ' void' : ''}`).join(', ')})` : ''; diff --git a/test/ship-measure.test.ts b/test/ship-measure.test.ts index b4d2a12a6..9612f9d81 100644 --- a/test/ship-measure.test.ts +++ b/test/ship-measure.test.ts @@ -210,6 +210,18 @@ describe('spend, approval, rounds and artifacts', () => { expect(report).toContain('| browse-basic | — | — | — | — | unmeasured (provider outage before the first turn); not a pass | — | — |'); expect(report).toContain('never change a recorded verdict'); }); + + test('a measurement in the older v1 format is listed as not read instead of crashing the report', () => { + const outDir = tmp('ship-measure-report-v1-'); + const dir = path.join(outDir, 'old-case'); + fs.mkdirSync(path.join(dir, 'round-1'), { recursive: true }); + fs.writeFileSync(path.join(dir, 'ledger.json'), JSON.stringify({ rounds: ['round-1'] })); + fs.writeFileSync(path.join(dir, 'round-1', 'measurement.json'), JSON.stringify({ + schema: 'gstack-ship-measure/1', label: 'diagnostic', verdict: null, case: 'old-case', kind: 'rule', round: 'round-1', + status: 'measured', plan: { kind: 'rule', units: 10, trialsPerUnit: 1, trials: 10, unitTarget: 9, trialTarget: 9 }, trials: [], + })); + expect(formatReport(outDir)).toContain('| old-case | rule | round-1 | — | — | older measurement format (gstack-ship-measure/1); not read, re-measure on this release | — | — |'); + }); }); describe('ship-measure CLI with a documented single-case command', () => {