* fix(#2440): per-counter progress ratchet — total_plans always takes derived value Two sites fixed (targeted — existing body-only write tests preserved): Site A — read path: shouldPreserveExistingProgress (state-document.cts:167) removed total_plans from the all-or-nothing ratchet check. It now joins total_phases as an always-derived counter. Only completed_phases and completed_plans keep ratchet behaviour (they are monotonic). This fixes gsd-tools query state.json reporting stale total_plans when a curated completed_plans triggers the ratchet. Site B — write path: applyStatePreservation (state-transition.cts:162) gained a deriveProgressKeys opt-in flag. When true (passed by cmdStatePlannedPhase only), total_plans and total_phases take the derived (post-sync) value instead of the wholesale curated restore. When false (the default — state.update, state.patch), the existing #3242 wholesale protection stays fully in force. This fixes the state planned-phase verb writing a stale total_plans. The opt-in approach preserves all 8 existing #3242/#1264/#500 body-only write tests that assert wholesale progress preservation during non- progress updates. Tests: - tests/state.test.cjs: 4 unit tests for shouldPreserveExistingProgress (total_plans upward/downward/equality + completed_plans ratchet active). - tests/state-transition.test.cjs: 2 #2440 regression tests for deriveProgressKeys=true (total_plans takes derived; boundary at equality). The existing !resync wholesale-restore test stays unchanged (default behavior preserved). References: #2440; #1446 (total_phases read-path fix — same principle); #3242 Bug A (body-only preservation — protection preserved via the opt-in gate); ADR-1769 (applyStatePreservation table-driven preservation). * chore(#2440): backfill pr:2468 in .changeset/mellow-eagles-chatter.md
This commit is contained in:
@@ -10659,5 +10659,43 @@ describe('bug #1514 — extractRetiredPhaseNumbers property: returns exactly the
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── #2440: total_plans excluded from progress ratchet (per-counter) ──────────
|
||||
//
|
||||
// Pre-fix: shouldPreserveExistingProgress included total_plans in the ratchet.
|
||||
// Fix: total_plans joins total_phases as always-derived (both move in both
|
||||
// directions). Only completed_phases and completed_plans keep ratchet behaviour.
|
||||
|
||||
describe('bug #2440 — shouldPreserveExistingProgress does not ratchet total_plans', () => {
|
||||
const { shouldPreserveExistingProgress } = require('../gsd-core/bin/lib/state-document.cjs');
|
||||
|
||||
test('existing total_plans:50 > derived:64 → returns false (upward correction)', () => {
|
||||
const existing = { total_phases: 2, completed_phases: 1, total_plans: 50, completed_plans: 49 };
|
||||
const derived = { total_phases: 2, completed_phases: 1, total_plans: 64, completed_plans: 49 };
|
||||
assert.equal(shouldPreserveExistingProgress(existing, derived), false,
|
||||
'total_plans upward correction must NOT trigger ratchet');
|
||||
});
|
||||
|
||||
test('existing total_plans:50 > derived:30 → returns false (downward correction)', () => {
|
||||
const existing = { total_phases: 2, completed_phases: 1, total_plans: 50, completed_plans: 30 };
|
||||
const derived = { total_phases: 2, completed_phases: 1, total_plans: 30, completed_plans: 30 };
|
||||
assert.equal(shouldPreserveExistingProgress(existing, derived), false,
|
||||
'total_plans downward correction must NOT trigger ratchet');
|
||||
});
|
||||
|
||||
test('boundary: existing total_plans == derived → false', () => {
|
||||
const existing = { total_phases: 2, completed_phases: 1, total_plans: 50, completed_plans: 50 };
|
||||
const derived = { total_phases: 2, completed_phases: 1, total_plans: 50, completed_plans: 50 };
|
||||
assert.equal(shouldPreserveExistingProgress(existing, derived), false,
|
||||
'total_plans equality must NOT trigger ratchet');
|
||||
});
|
||||
|
||||
test('completed_plans:5 > derived:2 → true (ratchet still active for completed_plans)', () => {
|
||||
const existing = { total_phases: 2, completed_phases: 1, total_plans: 6, completed_plans: 5 };
|
||||
const derived = { total_phases: 2, completed_phases: 1, total_plans: 6, completed_plans: 2 };
|
||||
assert.equal(shouldPreserveExistingProgress(existing, derived), true,
|
||||
'completed_plans ratchet must still work');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user