Skip to content

fix(test): defuse a date-bound fixture that turned the commit gate red; CI runs the assertion half with the clock +400 days - #238

Merged
cfdude merged 4 commits into
mainfrom
dev
Sep 28, 2026
Merged

cfdude merged 4 commits into
mainfrom
dev

Conversation

@cfdude

@cfdude cfdude commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Since 2026-09-27 every commit failed the pre-commit gate: archived-progress.test.mjs 1.3 archived under a fixed 2026-09-25 directory, which 0.50.0's date rule (an archive more than a day older than a live epic's createdAt cannot end it) set aside once the calendar moved. Fixed with archiveDay(). A full-suite run with the clock shifted +1/+7/+400 days found no other time bomb in the assertion half.

Guard: a CI step runs the assertion half with scripts/test/fixtures/future-clock.mjs (+400 days, verified it actually shifted); ci-workflow requires the step and rejects four broken forms. Also carries the GitHub-issue sync (15 epics) and conductor bookkeeping. Test/CI/bookkeeping only — no plugin version bump, no user-facing change.

https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM

…6-09-25

The documented-sequence fixture registers its epic "now" and then archives the change
under a hardcoded `2026-09-25-seq-change`. 0.50.0's date rule refuses an archive dated
more than a day before the epic's createdAt, so the heal stopped flipping it from
2026-09-27 onward and the per-commit gate went red for everyone (2 tests, "the heal
flipped it": actual `active`). The directory now uses archiveDay(), the helper that
exists for exactly this. No assertion changed.

RED: red-archived-progress.txt (assertion half at offset 0 on 2026-09-28: 1521/1523).

Item 1 sweep: every fixture that names an archive directory was exercised by running
both halves under a clock shifted +1, +7 and +400 days; this was the only date-dependent
one. The other literals in this file (lines 30-78) feed the resolver directly with no
createdAt and are date-independent.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
A fixture that hardcodes a date the engine compares against "now" is green the day it
is written and red on some later day, on every commit, for everyone. The per-commit gate
cannot see that class by construction. This adds:

- scripts/test/fixtures/future-clock.mjs: a preload that shifts Date.now(), new Date()
  and Date() by PM_TEST_CLOCK_OFFSET_DAYS (unset/0 changes nothing).
- CI step "Assertion half, clock +400 days" on every matrix leg. It loads the preload via
  NODE_OPTIONS with an absolute path, so every per-file process and every spawned node
  child inherits it. It MEASURES the shift before the suite and refuses on a mismatch, so a
  preload that loads nothing cannot make the leg a green duplicate. It has the same status
  gate, floor, and zero-count refusal as the other bucket steps.
- ci-workflow.test.mjs: clockStepRefusals() requires that step (offset >= 365, absolute
  NODE_OPTIONS preload, self-check before the runner, bucket/reporter shape). A second
  test shows it refuses no offset, a zero offset, a relative preload, and a missing
  self-check.
- CONTRIBUTING: derive dates; how to reproduce the leg locally.

Why a CI leg rather than a per-commit literal scan: a scan cannot tell a deliberately old
fixture from a time bomb, and "expects a heal" cannot be matched mechanically. The shifted
run tests behaviour, catches any date dependence (not just archive dirs), and adds nothing
to the hook's time.

Assertion half only: under the shift the functional half's lock-age tests
(Math.abs(now - mtimeMs), store.mjs:450/489, commit-watch.mjs:159) compare against real
file mtimes, and 8 of them fail at +1, +7 and +400 alike. That comes from the shift and
is not a time bomb. Those tests hold no date literals that the engine compares with now.

RED: red-ci-clock.txt (ci-workflow 8/10 before the step existed).
Mutation proof: archived-progress set back to "2026-09-28-seq-change" passes at offset
0 (7/7). The step, run locally exactly as written, fails with the 2 x 1.3 tests. With the
preload unloaded, the self-check refuses: "shifted node by 0 days, not 400".

Item 1: the step's readers are ci-workflow.test.mjs (G-C1 and 4.2 find the FIRST step
that names assert/*.test.mjs, which is still "Assertion half"; the new step is placed after
it and checked by clockStepRefusals). Inverse: none needed. The offset leg is additive.

Claude-Session: https://claude.ai/code/session_01BoqzgrFwRC6QTUKuBkw8kM
@cfdude
cfdude merged commit 8910b93 into main Sep 28, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant