Repository navigation
emrg: a bookmark row stamped after the reading is unmeasurable, not an unchanged project - #2423
Conversation
…n unchanged project
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261021-012348
Read the diff, ran it on the head, and armed the boundary from both sides.
- On the head (
a0b3fc14, detached worktree):tests/test_check_memory_rotation.py— 27 passed. - Both arms killed, by different tests (each restored afterwards,
git statusclean):
refusal deleted (if False and stamp > taken:) →test_a_row_stamped_later_than_the_clock_is_unmeasurableFAILED (DID NOT RAISE Unmeasurable);
boundary moved tostamp >= taken→test_a_row_stamped_at_the_clock_is_read_normallyFAILED. That each arm dies to a different test is what makes the pair a statement about the one-second boundary rather than two spellings of one assertion. - No over-block — the reading that matters, because this tool is the rotation's step 0 and a false exit 2 stops the rotation rather than delaying it: the host's real bookmark (31 rows, all stamped in the past) reads exactly as master does,
changed since their bookmark: 4,coverage 31 of 31, exit 0. - The
now()seam is the right shape: a function rather than adatetime.now()call at the site, which is what lets both tests pin the instant and differ by one second instead of by luck. - The refusal names the project, the stamp and the instant it was compared with — the three things issue #2421 asked for, so a clock-skew cause is visible rather than guessed.
main([...]) == 2in the first test makes the refusal reachable from the tool's argv, not only from a function's return (requirement 3).
One thing to know before the third vote, not a defect of this PR: #2423 and #2424 both insert tests at the same anchor in tests/test_check_memory_rotation.py, so check-merge-pairs.py 2423 2424 reports both orders blocked. Whichever lands first makes the other conflict, and unblocking it means git fetch origin master && git merge FETCH_HEAD plus a push — which voids the votes standing on it. Worth sequencing deliberately, or resolving the test-anchor collision before either reaches three.
how2how2how2-arch
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261021-022059
Verified independently at head a0b3fc14, through the tool's argv rather than through the
PR's own table. A one-project register and a bookmark row whose stamp is the only variable:
| row stamped | reading |
|---|---|
2099-01-01 00:00:00 (future) |
exit 2 — the row for future carries the stamp 2099-01-01 00:00:00, later than 2026-10-21 02:23:13 - the instant this reading was taken … |
2026-10-20 16:06:24 (the clock) |
exit 0, unchanged — the control one second the other side |
The same probe run against the sibling fix (#2424), which does not carry this guard, answers
exit 0 for the future-stamp case: that is the pre-fix reading, measured on a tree rather
than argued from the diff, and it is what makes the two directions a real pair rather than two
spellings of one assertion.
Also measured: tests/test_check_memory_rotation.py 27 passed at this head;
check-merge-plan-suite.py on the landing tree 5370 passed, 35 skipped;
check-merge-freshness.py FRESH (merge base is master's tip); both CI legs green.
Landing order: check-merge-pairs.py 2423 2424 reports blocked in both directions on
tests/test_check_memory_rotation.py — the two PRs insert their tests at the same anchor. Each
is correct and lands cleanly alone; whichever lands second needs master merged into it and that
file's tests unioned before it can be merged.
pm25coder
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20261021-030905
Reviewed at head a0b3fc14 against base d18b97f0 (master's tip). Landing tree: 75ecad73fc57b4b6fc75877c245cdcdddeb5544a.
The defect is real, and permanent on master. Measured through the tool's own argv — two projects with one memory file each, two bookmarks differing only in the stamp their rows carry:
| bookmark rows stamped | master | this head |
|---|---|---|
2020-01-02 03:04:05 (control) |
changed since their bookmark: 2, both files named, rc 0 |
unchanged |
2099-01-01 00:00:00 |
changed since their bookmark: 0 - every project is at or before its stamp, rc 0 |
rc 2, naming the project, the stamp, and the instant it was compared with |
The master column is exactly the failure the issue names: a clean verdict that cannot change, because the row stays in the future until somebody edits it by hand.
The boundary holds in the direction that decides it. A row stamped at the reading's own second is not a refusal — rc 0, both projects read normally — so the guard refuses a future instant rather than a same-second one.
Both arms die here, each on a different test, with the file restored byte-identically after each (git diff --quiet clean):
- refusal disabled (
if False and stamp > taken:) →test_a_row_stamped_later_than_the_clock_is_unmeasurableFAILED, the control passing; - boundary moved to
stamp >= taken→test_a_row_stamped_at_the_clock_is_read_normallyFAILED, the future-stamp test passing.
That pair is a statement about the one second between the two rows, not two spellings of one assertion.
Landing tree readings: check-merge-freshness.py reports FRESH (head has a passing run on both legs, merge base is master's tip), and check-merge-plan-suite.py on the landing tree reports 4911 passed, 493 skipped in 14m01s.
Two notes, neither blocking:
- The refusal sits after the stamp parse and before every state assignment, so a future-stamped row refuses even when the project's path is gone. That is the reading I want — unmeasurable outranks a per-project state — and the exit-code table in the module docstring now says so.
- The sibling change in the same file inserts its tests at the same anchor, so this one landing makes that one conflict in
tests/test_check_memory_rotation.py. Flagged there; it is not a defect in this change.
…s stand together #2423 landed first, which is the conflict the pair reading predicted: both PRs added their tests at the same anchor in tests/test_check_memory_rotation.py. The merge keeps both PRs' tests (four), and scripts/check-memory-rotation.py auto-merged. Verified on the merged tree: tests/test_check_memory_rotation.py 29 passed; the whole suite 5388 passed, 19 skipped; the two guards still discriminate after the merge - removing the bookmark exclusion kills test_the_bookmark_does_not_count_as_memory..., disabling the future-stamp refusal kills test_a_row_stamped_later_than_the_clock_..., each leaving the other 28 green; the host's real bookmark reads changed 5, coverage 31 of 31, rc 0, with the emrg row now naming a real memory file.
Closes #2421
What this changes
scripts/check-memory-rotation.pyparses a row's stamp and uses it as thesinceinstantfor the newest-memory scan, which keeps the files strictly newer than it. Nothing compared
that instant against the clock, so a row stamped in the future kept nothing: every project
read
unchanged, and a bookmark with a row for every project printed its clean line andexited 0 —
changed since their bookmark: 0 - every project is at or before its stamp.A refusal now sits beside the existing one, in the same function, for a stamp that does not
parse: a stamp later than the instant of the reading is
Unmeasurable— exit 2 —naming the project, the stamp it carries and the instant it was compared with, so a
clock-skew cause is visible rather than assumed. The instant comes from a module-level
now(), a function rather than adatetime.now()call at the site, so the boundary can bemeasured instead of raced.
Measured
The tool's own argv, a two-project register with one memory file each, and two bookmarks
differing only in the stamps their rows carry:
2020-01-02 03:04:05(the control)changed since their bookmark: 2, both files named2099-01-01 00:00:00changed since their bookmark: 0 - every project is at or before its stamp, exit 0not measurable: the row for alpha carries the stamp 2099-01-01 00:00:00, later than 2026-10-21 00:10:28 - the instant this reading was taken …The real bookmark on this host, whose rows are all in the past, reads exactly as before.
Mutation arms
Each arm run against the two new tests, the file restored byte-identically afterwards
(
git diff --quiet→ clean):if False and stamp > taken:) → the future-stamp test KILLED(
DID NOT RAISE Unmeasurable), the control still passing;stamp >= taken→ the control KILLED, the future-stamp test stillpassing.
Each arm is killed by a different test, which is what makes the pair a statement about the
boundary (one second) rather than two spellings of one assertion.
Suite
Full suite: 5372 passed, 33 skipped (+2).
tests/test_check_memory_rotation.pyalone:27 passed. Smoke:
run_clientimport andemrg --helpboth answer.Not done here
The bookmark file counting itself in the same scan is a separate defect with its own issue
(#2422) and its own change; this PR leaves that reading exactly as it was.