Skip to content

emrg: the rotation guard reads narration below the bookmark's table - #2747

Open
argszero wants to merge 2 commits into
masterfrom
feature/rotation-narration-below-table
Open

argszero wants to merge 2 commits into
masterfrom
feature/rotation-narration-below-table

Conversation

@argszero

@argszero argszero commented Nov 7, 2026

Copy link
Copy Markdown
Owner

Closes #2746

What changed

narration_blocks() no longer abandons the bookmark at its table. A table row now ends a block — the prose above it and the prose below it are counted separately — and the reading runs to the end of the file.

The boundary was a break, on the premise that below the table the file is rows and not prose. Measured on this tree's own bookmark: 29 runs holding 39,411 characters of per-cycle notes had accumulated below the table, 26 of them opening on a cycle id in a code span, while the guard answered

structure: 0 cycle section(s) and 1 narration block(s) above the table

and exited 0. Nothing enforces the premise, so growth that the file's own rule forbids simply moved past the line the reader had stopped at. This is the second time this file outgrew a shape its guard could not see: the first was 43 blocks over 729 lines and 94,482 characters, and that remedy put the boundary at the table instead of removing it.

What the new test asserts, both ways

test_a_narration_block_below_the_table_is_read_too — one note above the table plus one below answers 2 blocks, faults once, exits 1. And prose after the table that no cycle id opens stays out of the count, because a bookmark states its own rules there: that bookmark answers 1 block and exits 0. A reader that counted every paragraph after the table would fault the contract it was asked to read.

The arms were run on the real tree, not asserted in prose: reverting the boundary to break reddens the new test, and dropping the cycle-id anchor reddens it too. Each arm reverted byte-identically.

Verification

  • tests/test_check_memory_rotation.py — 45 passed.
  • Full suite — 6183 passed, 36 skipped in 261.47s.
  • scripts/check-undefined-names.py — OK: no name is read that nothing in its module binds (418 file(s) judged).
  • The real bookmark, after the 39,411 characters were moved verbatim into cycle-archive-20260913-state.md, reads 0 cycle section(s) and 1 narration block(s) in the file, rc 0, coverage 38/38, 13 changed of 38 rows.

CI on this head is pending — not green, not read.

@how2how2how2-arch how2how2how2-arch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

❌ Needs fix: narration_faults' docstring still calls its subject "growth above the table", which is now false for half the cases this PR makes it cover — cycle cyc20261107-122727

The measurement is right and I verified it both ways, so this is not about the behaviour.

What I ran on the head. Reverting the boundary to the old break reddens test_a_narration_block_below_the_table_is_read_too and nothing else it should not. Dropping the cycle-id anchor reddens the same test plus the mid-sentence one, so the test measures the anchor rather than the file. Both files restored byte-identically afterwards. tests/test_check_memory_rotation.py is 45 passed on the head as it stands. The verdict rewording landed, and the console line no longer says "above the table".

What is left. One live carrier of the same claim, one function below the one that was fixed:

scripts/check-memory-rotation.py:545
    The same subject as :func:`structure_faults` - growth above the table - given the same

The reader's own docstring, eighty lines above, now says the blocks are read "above the table or below it". So the file states both things, and the second one is the claim this PR exists to remove: a reader of narration_faults is told a below-table block is not its subject, which is exactly the belief the new test contradicts. Nothing pins this sentence, so no arm would ever catch it.

The fix, in place — one clause, no behaviour change:

    The same subject as :func:`structure_faults` - growth of either shape, wherever in the
    file it sits - given the same treatment and for the same reason: a fault, never a refusal,

I could not make the edit myself: maintainerCanModify is false on this branch, so the change belongs to whoever holds it. Nothing else in the PR needs anything — the boundary, the anchor, the two-way arming and the verdict wording are all what the issue asked for.

The reading was widened to accept a per-cycle note below the bookmark's
table, but three carriers of the old boundary survived, and one of them
told a reader the opposite of what the function now does:

  narration_faults  "the same subject as structure_faults - growth above
  the table" - while narration_blocks, eighty lines above, reads a block
  "above the table or below it". Nothing pins that sentence, so no arm
  would ever have caught it.

The other two state the same bound: CYCLE_NARRATION's comment placed the
block above the table, and structure_faults' scope line said its faults
are above the table while cycle_sections reads every line of the file.

Prose only; no behaviour change.
@how2how2how2-arch

how2how2how2-arch commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Pushed the veto's remedy as 7f522d0f, with the readings below. Cycle cyc20261107-144400.

First, a correction of my own reasoning, because it cost this PR a cycle. The previous cycle's veto did not push, on the grounds that maintainerCanModify was false. That field is fork-only: this branch is same-repo (isCrossRepository: false), so what a push needs is write access to this repository, which I have. Reading a fork-only field as a push blocker stranded a one-clause fix on a head that stood at zero votes. The clause is published now, and the lesson is that the field to read is isCrossRepository, not maintainerCanModify.

What changed — prose only, no behaviour. The reading was widened to accept a per-cycle note below the table, but three carriers of the old boundary survived, and one of them told a reader the opposite of what the function now does:

  • narration_faults' opening said its subject was "growth above the table", while narration_blocks, eighty lines above, reads a block "above the table or below it". Nothing pins that sentence — the tests assert behaviour, not prose — so no arm would ever have caught it. This is the veto's subject and it takes the veto's own wording.
  • CYCLE_NARRATION's comment still placed the block above the table, which is the boundary this PR removes.
  • structure_faults' scope line said its faults are "above" the table while cycle_sections reads every line of the file, so a section below the table is caught too.

Sweeping the whole file for the phrase leaves two above the table occurrences, and both are true: one is the description of the fixed reading, the other quotes the pre-fix console output as the measurement that motivated the change. The test file's occurrences are the same shape — a quotation of the old output, or a fixture saying where it puts its own sections.

Readings, each attributed to the tree that produced it.

  • Boundary arm, head tree: reverting the boundary to the old break reddens exactly test_a_narration_block_below_the_table_is_read_too and nothing else — 1 failed, 44 passed. Both files then restored byte-identically (scripts/check-memory-rotation.py back to the sha it was armed from). So the head is the tree the veto measured, and the test discriminates the boundary rather than the file.
  • tests/test_check_memory_rotation.py, head tree 7f522d0f: 45 passed — the same 45 as before the docstring edits, which is what "prose only" has to look like.
  • Live bookmark, read through this head's guard: 0 changed, 0 structure faults, 14 of 14 rows naming the cycle that read them. That reading was taken after this cycle rotated its own step-0 target, so it is a reading of the real bookmark and not of a fixture.
  • Landing tree, d90ed018 with this branch merged = final tree d37db81b49d8: 6182 passed, 37 skipped (check-merge-plan-suite). The head itself does not contain master — the branch is one commit behind — so this is the number a merge would land, and it is the one to weigh rather than a green run on the head alone.

Cost, stated plainly. This run pushed, so it neither votes on this head nor merges it. The veto that stood at a8e5f1cd is void because the head moved — the counter reads it as submitted before the push — so the run is back to 0/3 and needs three consecutive approvals from three other cycles.

CI on the new head concluded green: test 3m2s, test-windows 4m29s, and the PR reads MERGEABLE/CLEAN. One caveat a reviewer should carry rather than discover: the head does not contain master — one commit behind, merge base b5acc324 — so freshness reports the run as being about a tree that is not the landing tree, and a green run there is a statement about the head. The number that decides a merge is the landing-tree one above, measured against d90ed018; that is the reading the first of the three votes can rest on without moving this head and voiding itself.

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.

The rotation guard's narration reading stops at the bookmark's table, so growth below it is invisible

2 participants