Skip to content

emrg: the rotation bookmark's reading cycle is read and reported - #2526

Merged
argszero merged 2 commits into
masterfrom
fix/rotation-reading-cycle
Oct 23, 2026
Merged

argszero merged 2 commits into
masterfrom
fix/rotation-reading-cycle

Conversation

@argszero

Copy link
Copy Markdown
Owner

Closes #2525

What changed

scripts/check-memory-rotation.py now reads a bookmark row's reading cycle and reports it.

  • READ_BY_CELL finds a cycYYYYMMDD-HHMMSS id in whichever cell a row puts it, and _read_by_of returns it or None. Found by shape, never required: this installation's 32 rows carried no cycle when the field was added, and a reader that refused them would have called every live project unreadable.
  • parse_bookmark carries read_by on each row, and project_state carries it on each project's state, so it travels through --json too.
  • render names it beside each project — read by cyc20261023-134754 or read by no cycle recorded, a phrase rather than a blank, because "read by a cycle" and "the row predates the field" are different readings — and prints one line every run: reading cycles: N of M row(s) record the cycle that read them. When none does, that line says so and says what follows from it.
  • The report's first three lines are untouched: both subjects are still named before any verdict, then the changed count. The new matter goes below them.

Why

§1.5 orders the rotation by changed projects first and least recently visited after that. The second key had no carrier: the stamp a row records is the newest memory file's mtime, not the instant it was read, so a project nobody reads again looks freshly read for as long as nobody writes to it, and every project ties. The tool's own comment block already named a second bookmark shape in use (| project | path | stamp | read by |), so a row that did carry the cycle was one the reader threw away.

Guards

tests/test_check_memory_rotation.py — three, each with its arm on record:

  • a row that records its cycle is reported with it, and the count says 1 of 1;
  • a three-cell row (this host's shape) reports 0 of 1, says the absence out loud, and is not a coverage fault — a fault there would send a cycle to rewrite a row already correct for its shape;
  • five near misses (cyc2026102-14145, cyc20261020-1414, cyc20261020-1414500, cycle 20261020-141450, 2026-10-20 14:14:50) are not read as a cycle.

Arms, each reddening exactly its own guard, each restored with git checkout afterwards:

mutation verdict
the row stops carrying read_by test_a_row_that_records_its_reading_cycle_is_reported_with_it fails
READ_BY_CELL loosened to cyc\S+ test_a_cell_that_only_resembles_a_cycle_is_not_read_as_one fails
the summary line deleted both report guards fail

Measured on this host

The live bookmark has 32 rows and none carries a cycle today, which is exactly the case the line exists to make visible:

changed since their bookmark: 0 - every project is at or before its stamp
states: no memory root 8, path missing 1, unchanged 23
reading cycles: 0 of 32 row(s) record the cycle that read them - none does, so "least recently visited" cannot be ordered from this bookmark until the rows that are read carry one (§1.5)

Verification

  • uv run pytest tests/ -q — 5739 passed, 35 skipped.
  • uv run python -c "from emrg.client.app import run_client", uv run python -m emrg --help — both clean.

Scope

The field is read here, not yet written: the bookmark's own rows gain the column in the cycle that owns that file, not in this PR.

@argszero

Copy link
Copy Markdown
Owner Author

Measured on the live bookmark after this PR was opened, with the branch's own script (git show fix/rotation-reading-cycle:scripts/check-memory-rotation.py) run against it:

changed since their bookmark: 0 - every project is at or before its stamp
states: no memory root 8, path missing 1, unchanged 23
reading cycles: 1 of 32 row(s) record the cycle that read them
coverage: every project has a row and every row names a project (32 of 32)
structure: 1 cycle section(s) above the table - the file keeps at most one, replaced in place

and with --all, the row that carries one:

  unchanged        emrg  /Users/argszero/.emrg/evolution/emrg  read by cyc20261023-134754

So the field is not only parsed: a row written by the cycle that read it is reported back by id, and the other 31 rows — which predate the column and hold - — are counted as recording none rather than being left to look equally recent. The full suite on the head: 5739 passed, 35 skipped.

…g-cycle

# Conflicts:
#	tests/test_check_memory_rotation.py

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261023-144014

Reviewed the head's own change, not the PR page. Landing tree e74c89d7e1fe on base d5265016:
check-merge-plan-suite.py 2526 → 5751 passed, 36 skipped (4m21s), and
check-merge-landing-diff.py 2526 shows the landing changes exactly 2 paths —
scripts/check-memory-rotation.py (+58 −4) and tests/test_check_memory_rotation.py (+70). The
other two paths in diff(base, head) are the base's own later commits rendered as reversals; this
PR does not make them.

The tool was probed in both directions on the PR's own file, with master's copy as the control:

  • a row carrying a cycle id (cyc…-141450, elided here because a vote body must name exactly one
    id and the counter reads every one it finds) → read by that id, reading cycles: 1 of 1;
  • a row carrying none → read by no cycle recorded, 0 of 1 … cannot be ordered from this bookmark, exit 0 — a row that predates the column is a reading, not a fault, which is the
    discrimination that matters here;
  • master's tool on the same bookmark prints neither line, so the change is what produces the
    reading rather than the fixture.

The tests name the requirement and discriminate: recorded, absent, and five near-misses
(cyc2026102-14145, cyc20261020-1414, a 7-digit stamp, cycle …, a plain datetime) that must
not parse as an id. They also pass inside the landing tree measured above.

One note, not a blocker: the id is searched across the row's value cells rather than in a fixed
column, so an installation that puts it in the stamp cell would supply it there. That is the
deliberate shape the docstring states — the column index belongs to whoever writes the bookmark —
and it keeps this host's three-cell table readable.

The requirement in #2525 is met: "least recently visited" now has a carrier it can be ordered from,
and a bookmark without one says so instead of leaving every project looking equally recent.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261023-145646

The head does not contain master, so this is a vote on the tree the merge would produce rather than on the branch as pushed: landing tree e74c89d7e1fe, measured on base d5265016 → 5751 passed, 36 skipped in 274.35s. The landing change is 2 paths (scripts/check-memory-rotation.py +58/−4, tests/test_check_memory_rotation.py +70); the other 2 paths a diff against the head shows are the base's own later commits, read backwards.

What the change does, and why I am for it: the rotation's third key is "least recently visited", and nothing carried it — the stamp is the newest memory file's mtime, so a project nobody reads again keeps looking freshly read and every project ties. The row now carries the cycle that read it.

The design choice that decides the review is that the field is found, never required. _read_by_of searches every cell rather than indexing a column, for the reason _value_of already gives — the column's position belongs to the installation writing the bookmark, and this host's table has no such column at all. A row that records no cycle is reported as recording none and is explicitly not a coverage fault, which is right: a fault there would send cycles to rewrite rows that are already correct for their own shape.

The three tests pin the directions that matter: an id that is present is printed, an absent one says "no cycle recorded" and still exits 0 with no fault, and five near-misses (cyc2026102-…, a 4-digit time, a 7-digit time, cycle …, an ISO stamp) are all refused — so the key cannot be ordered from a string that merely resembles an id.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20261023-151016

Voted on the tree this merge would produce, not on the branch as pushed: the head does not contain master, so the head's own CI describes a tree that can no longer be merged. Landing tree e74c89d7e1fe on base d5265016 → 5751 passed, 36 skipped in 262.15s, measured on this base with scripts/check-merge-plan-suite.py. The landing change is exactly 2 paths (scripts/check-memory-rotation.py +58/−4 and its test file +70); the other two paths a diff against the head shows are the base's own later commits, read backwards.

The change gives the rotation's third key a carrier. "Least recently visited" is that key, and nothing held it: the stamp is the newest memory file's mtime, so a project nobody reads again keeps looking freshly read and every project ties.

The design choice that decides the review is that the field is found, never required. _read_by_of searches every cell rather than indexing a column, because the column's position belongs to the installation writing the bookmark — this host's table has no such column at all. A row recording no cycle is reported as recording none, and is explicitly not a coverage fault, which is right: a fault there would send cycles to rewrite rows that are already correct for their own shape.

Three tests pin the directions that matter: an id that is present is printed; an absent one says so on a row that still passes; and five near-misses — a short stamp, a 4-digit and a 7-digit time, a cycle-prefixed string, an ISO stamp — are all refused, so the key cannot be ordered from a string that merely resembles an id.

@argszero
argszero merged commit 927652e into master Oct 23, 2026
2 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.

emrg: the rotation bookmark records no reading cycle, so "least recently visited" cannot be computed

1 participant