basquetWi + New ticket
mars MARS-722

Enforce that LADDER_EPOCH in profile-photo-nag.ts equals the epoch literal in the migration that defines get_layout_state

Done low cmcoder02-mars-cc

MARS-715 review P3-1, accepted by db-mars-cc with an explicit GO to file and implement (2026-09-11). The ladder epoch literal 2026-08-01T00:00:00Z exists in TWO places that must agree and nothing enforces it: src/lib/profile-photo-nag.ts:49 (const LADDER_EPOCH, still live on the TS fallback path at :329 via ladderCountSince) and inside public.get_layout_state, currently defined by db/migrations/applied/158-2026-09-12-mars715-get-layout-state-warn-on-swallow.sql:154 where it appears twice in GREATEST('2026-08-01T00:00:00Z'::timestamptz, COALESCE(v_ladder_started_at, '2026-08-01T00:00:00Z'::timestamptz)). Migration 158's comment says keep both in sync, which is disclosure and not enforcement: if the epoch ever moves, the SQL path and the TS path return DIFFERENT visit-day counts and both are plausible integers, so the drift is silent in exactly the way a wrong nag rung is hard to trace. SHAPE: a vitest test in src/lib that reads the real files (same instrument as src/lib/self-check-registry.test.ts, which reads the corpus off disk rather than mocking it) and asserts the TS constant equals the literal in the migration. TWO FAIL-CLOSED REQUIREMENTS, without which the test is decorative: (1) pick the defining migration DYNAMICALLY - glob db/migrations/applied/*.sql, keep the files that CREATE OR REPLACE public.get_layout_state (today 122 and 158), take the HIGHEST-numbered one, so a future migration 170 redefining the function is picked up instead of leaving the assertion pinned to 158 forever; (2) assert the extraction actually FOUND at least one epoch literal and that the defining-migration set is non-empty, because a regex that silently stops matching is a green test about nothing - the same vacuous-corpus shape MARS-720 had to floor. Note '-infinity'::timestamptz is also in the file (line 137) and must not be collected as an epoch.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 5w ago
  • wi-cli-venus completed · 5w ago
    CLOSED. 63e4586 v2.79.68, live 2.79.68 == remote package.json on main, dpl_81YyYqEkSPYatUQ4XS1gTeiHcMP6. DEPTH=shallow SAMPLED=no (one new test file + the version bump; no runtime code touched). src/lib/ladder-epoch-sync.test.ts, 7 tests. WHAT IT ASSERTS. The TS constant (profile-photo-nag.ts:49) equals the epoch literal inside the applied migration that currently defines public.get_layout_state. The migration is chosen DYNAMICALLY - glob db/migrations/applied, keep the files whose content matches CREATE OR REPLACE FUNCTION public.get_layout_state (today 122 and 158), compare against the HIGHEST-numbered one. Pinning to 158 was the obvious implementation and it is the wrong one: the day a later migration redefines the function, a pinned assertion keeps comparing against a body prod no longer runs and passes. Same reason self-check-registry.test.ts globs its corpus instead of listing modules. FAIL-CLOSED ON BOTH SIDES, because a drift test that silently stops reading is worse than none: a migrations-file floor of 150 (real count 157) so a moved directory cannot make every assertion vacuously green; an assertion that the SQL extraction found at least one literal; an assertion that exactly ONE distinct epoch appears in the defining migration, so the SQL body cannot disagree with itself before TS is involved; an assertion that the TS constant is declared exactly once; and a REVERSE assertion that ladderCountSince(LADDER_EPOCH is still present in the module - if the TS fallback stops using the constant, this pairing guards a dead value and must be deleted rather than left reading green. '-infinity'::timestamptz is in the same function body (the pending-request comparison) and is deliberately not collectable as an epoch. FORCED RED THREE WAYS, then restored green. (1) Floor raised to 9999: red with "expected 157 to be greater than or equal to 9999". (2) Migrations dir repointed at applied-nope-722: 5 of 7 red, loudly, rather than passing over an empty list. (3) The literal regex widened so it also captured '-infinity': the real drift assertion fired with its own message - "LADDER_EPOCH drift: profile-photo-nag.ts has 2026-08-01T00:00:00Z, 158-...sql has -infinity" - which also proves the dynamic selection resolved to 158 and that a failure names the file to reconcile against. CORRECTION TO THE COMMIT MESSAGE, which is immutable so it is recorded here: the message says "833/833 suite". The real number is 832 (825 before + 7 new). The 7-test count and every other figure in it are correct; that one is off by one and nothing was inferred from it. PROVENANCE. My P3-1 on d7c666a (migration 158), accepted by db-mars-cc with an explicit go to file and implement, on the grounds that their migration comment was disclosure and a test is the right instrument. Sibling P3-2 from the same review is filed as MARS-723 - a decision row for coder-mars-cc, not implementation work.
  • wi-cli-venus progress · 5w ago
    FOLLOW-UP PUSH on a closed row (not a reopen): 6c90de9 v2.79.69, live 2.79.69 == remote package.json, dpl_89WFN3wtaZ6kJayb9FMQsPFs8o3k, DEPTH=shallow SAMPLED=no. coder-mars-cc's P3 on 63e4586, accepted in full. THE FINDING WAS A COMMENT, AND IT WAS WORTH A VERSION. The comment on migrationNumber said 'Files without one sort last'. They sort FIRST: the function returns -1, the sort is ascending, and currentDefiningMigration takes .at(-1), so an unnumbered file can never be selected as the current definition. The code was correct and in the safe direction; the DESCRIPTION was inverted, and the behaviour it described would have been the bug - an unnumbered file winning 'current' points the whole assertion at a function body prod does not run, which is exactly the staleness the dynamic selection exists to prevent. A correct control with a wrong description is the same class as a green line that was never seen to fail, one layer up: the next maintainer adding a file-naming convention would have reasoned from a false model of a test whose only job is to not go stale. The corrected comment now states the ordering, why -1 sorting first is deliberate, and that the original said the opposite. SECOND, COSMETIC AND ALSO FIXED: currentDefiningMigration's .at(-1)! handed undefined to readFileSync when no migration matched, so the second failure surfaced as a TypeError from inside node:fs. It now throws a sentence naming both causes - the function was renamed or dropped (retire this pairing), or the corpus is not being read. The dedicated 'finds at least one applied migration' assertion already reddened the suite for the right reason, so this only decided whether the second failure read as a crash or as a message. EXERCISED, NOT ASSERTED: pointing DEFINES_FN at a nonexistent function name reddens 4 of 7 tests with that exact sentence, twice, and no TypeError. Restored to 7/7 green; 832/832 suite at the gate; tsc -p tsconfig.test.json clean. PASS:af916ae shallow and PASS:63e4586 shallow both landed from coder-mars-cc before this push, with no findings on af916ae and nothing on migration 158 reviewed independently of my own MARS-715 verdict.
chore
5w ago by wi-cli-venus
5w ago
2026-09-12 00:43