Skip to content

feat(trim): SESSION_NOTES.md joins the ledger table; warn of an uncommitted front-matter edit (refs #93) - #98

Open
rmsharp wants to merge 3 commits into
KJ5HST:mainfrom
rmsharp:feat/trim-session-notes-ledger-issue93
Open

rmsharp wants to merge 3 commits into
KJ5HST:mainfrom
rmsharp:feat/trim-session-notes-ledger-issue93

Conversation

@rmsharp

@rmsharp rmsharp commented Oct 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

This is the second pull request you asked for in your last comment on #94 ("Open the second PR now, rebased on main"). It sits on main at d50ce29, after #94 merged, and has three commits. All three concern how the trimmer, starter-kit/methodology_trim.py, treats SESSION_NOTES.md and the front matter (the text above the first record) of any ledger. The trimmer moves a ledger's oldest records into a frozen archive file (a "shard") and writes a .verify.sh proof beside it that re-derives from git that nothing was lost.

The three commits

  1. SESSION_NOTES.md joins the trimmer's ledger table (1.9.0), 39825279. This is the entry you asked me to split out of fix(trim): name a bundled stub finalize and show its diff, add --reverify, and make the proof's aborts and reads exact (refs #93) #94 (point 3 of your first review): the commit you re-ran as 8de50d2, replayed onto main with no conflict. On main the table holds CHANGELOG.md and HANDOFFS.md only. The runner's Phase 1B writes a placeholder record (a "stub") into SESSION_NOTES.md when a session starts, and Phase 3D overwrites it in place at close-out, as it does the status: pending receipt in HANDOFFS.md. The proof has to tell that overwrite (a stub being finalized) from a real loss, so the table entry (in methodology_trim.py) says how to recognize a stub. A record starts at ### What Session N Did or ### Session N-1 Handoff Evaluation (by Session N) and at nothing else, so the seed's ### What You Must Do stays front matter. The entry declares no footer. The stub marker needs both Phase 1B lines (the **Status:** Session claimed line at the start of a line, and the (IN PROGRESS) tag), because a finished record in 3 of 12 projects' notes files still carries the status line.
  2. Your three follow-ups (1.9.1), 3df5b0cd. These are the three small follow-ups at the end of your last comment on fix(trim): name a bundled stub finalize and show its diff, add --reverify, and make the proof's aborts and reads exact (refs #93) #94. Wording only: no exit code, finding code or verdict changes.
    • Follow-up 1. For a shard of a file that is not in the ledger table at all, --reverify said "the ledger table declares none for the live basename". It now says "the ledger table has no entry for <file>", and for CHANGELOG.md, which is in the table and declares no stub marker on purpose, "the ledger table's entry for <file> declares no stub marker". You offered two options: say so, or refuse with exit 3 as for PREFIX. I chose to say so and keep re-deriving, because refusing would also stop --reverify on every CHANGELOG.md shard written before the stub label existed, since that ledger declares none on purpose. Refusing is a small change if you prefer it.
    • Follow-up 2. --reverify refuses, at exit 3, a proof whose PREFIX value differs from today's template (point 6 of your first review; your example was a project whose ledgers had moved under methodology/). The refusal's reason read "rebased for a different depth". It now reads "the proof was written for a different layout".
    • Follow-up 3. Exit 3 is now described as every case where no verdict was reached, in the three places you named: the unfinished comment in the trimmer, the --reverify help text and FRAMEWORK_APPARATUS.md. The cases are a ref or object git cannot read, a working-tree file that cannot be opened, a history walk that fails, and a repository root the proof cannot reach. The document also says that a proof written before 1.8.1 is frozen and exits 1 where git cannot read the commit or object. I measured that by running a proof written by each trimmer in a depth-1 clone (the trim commit's parent absent): 1.8.0 exits 1, 1.8.1 and 1.10.0 exit 3.
  3. FRONTIER_FRONT_MATTER_UNCOMMITTED (1.10.0), 65990cda. In point 4 of your first review you found that an ACTIVE TASK edit in the same commit as a trim fails the proof's L2 check even though the trimmer had printed L2_OK. I asked whether a warning for that belonged in fix(trim): name a bundled stub finalize and show its diff, add --reverify, and make the proof's aborts and reads exact (refs #93) #94 or in this pull request, and you answered "(b) Put it in the second PR". The cause: the proof's L2 check requires every non-blank line of the parent commit's front matter to survive in the trim commit, while the trimmer's own L2 check compares the working tree before and after its run. A front-matter edit that is uncommitted when the trimmer runs (for example a SESSION_NOTES.md ACTIVE TASK, which sessions rewrite at every claim) is invisible to the trimmer, and if it then lands in the trim commit the proof fails: [L2_OK] at write time, L2 FRONT MATTER lost afterwards. The new check runs the proof's own L2 test at write time, against the copy of the ledger in HEAD. When lines would be lost it prints the finding, with the count, the first lost line and the way out (commit the front-matter edit first, then trim in a commit of its own). It exits 0 and refuses nothing: it is a third advisory warning, beside the two that already warn about a stub (FRONTIER_PENDING_STUB and FRONTIER_FINALIZE_UNCOMMITTED). It is silent wherever the proof would pass, for example when a line is only added or the lines are only reordered. It does not see an edit already committed together with an earlier trim, or a trim made by hand.

Measured

  • Tests. The trimmer's suite goes from 227 to 270 (2 skipped); the trimmer-unit-tests floor in .quality-gates.json from 227 to 270. quality_ratchet.py --run in a clean --no-local clone of this branch with the hooks set: all 12 gates pass (results 1b096f787d67, manifest 9f6dc2ebeb91).
  • The consequence the warning names is a test, not an assumption. With an uncommitted front-matter edit the write prints [L2_OK]; the committed trim's proof then exits 1 with L2 FRONT MATTER lost 1 line(s). Committing the edit first and trimming in its own commit gives exit 0.
  • rmsharp/nprcgenekeepr, the 54 proofs from Generated .verify.sh reports FAIL on lossless trims: 10 of 54 proofs in one adopter repo, three causes #93 at 82433662, --reverify of each shard: 46 exit 0 and 8 exit 4, row for row what 8de50d2 gave and what you reproduced. (Exit 0 means the proof holds; exit 1 that it fails; exit 4 that it fails and the failure is named as a stub being finalized, so it is labelled and still red; exit 3 that no verdict was reached.) Without the table entry (main alone) the same 54 give 46 exit 0, 1 exit 4 and 7 exit 1.
  • 11 projects with ledgers (a twelfth repository is empty), 33 ledger files, --no-local clones at HEAD, dry run. The new warning fired on none of the 33 clean trees and nothing crashed. After a one-line edit to each file's first line it fired on all 15 files whose run reaches the check; the other 18 stop earlier, at checks that come first (undocumented commits since the ledger's last entry, the archive-ratio refusal, a grammar mismatch, a ledger with no records). For the 22 CHANGELOG.md and HANDOFFS.md files, the exit code is the same as main's trimmer gives. On the projects' own working trees (read-only; git status identical before and after) it fired on none. Of the 5 notes files whose dry run gets as far as planning a trim, 4 plan it and 1 is refused (question 3 below).
  • Mutation. 12 one-expression mutants of the new check: 11 killed. The survivor ignores one part of a field pattern that is literal text in every declared pattern, so it cannot change any result. 4 mutants of the follow-up code: 4 killed, one of them only after a stronger assertion.

Three questions this pull request leaves open

All three are about SESSION_NOTES.md. The first two come from your first review of #94, which said: "A 123,325 B notes file fails context_budget while --check says no trim is due" and "If only some headings match (e.g. lowercase did), a trim can't get the file under the limit and says nothing about why." The third turned up when I ran the first commit over real notes files. Questions 1 and 2 are reproduced on throwaway repositories built to the shape described, not on a real project's file; question 3 is a real project's file.

1. When should --check say a notes file is due for a trim?

Today the trimmer's --check says a root SESSION_NOTES.md is due only at 196,608 B, the size it uses for root CHANGELOG.md and HANDOFFS.md (is_root_class_a, the function you pointed at in your review). But the runner has every session read SESSION_NOTES.md at Phase 0, and the seed .context-budget.json that bin/sync installs classes it read-mandated and caps it at 400 lines, 120,000 B and 25,000 tokens. So a notes file between 120,000 B and 196,608 B is over its budget and, to the trimmer, not due.

Reproduced with a throwaway repository holding a notes file of 123,325 B (your figure): context_budget.py --status exits 2, over its byte, line and token ceilings, while methodology_trim.py --check exits 0 and prints trigger does not fire.

  • (a) Take this file out of that class. --check would then use the 56,750 B "one-read cap" (the size past which a whole-file read comes back truncated) that nested and unclassified ledgers get, so it would fire before the budget's 120,000 B and never be silent while context_budget.py is red. The cost: it would call a notes file due at 56,750 B, well inside its own budget.
  • (b) Keep 196,608 B, and say in FRAMEWORK_APPARATUS.md and in the table entry's comment that for this file the budget trips first and --check is not the test of whether the file is over budget.

Which do you prefer?

2. Should the trimmer say so when a heading does not match?

The entry recognizes a record by exactly the two heading forms in commit 1. Everything above the first matching heading counts as front matter, however long it is. When no heading matches at all the trimmer does not stay silent: with dated headings it refuses (GRAMMAR_MISMATCH, exit 3), with undated ones it reports zero records. The gap is the partial match.

Reproduced with a throwaway notes file of 160 records and 249,840 B whose newest 135 records say ### What Session N did (lowercase) and whose older 25 say Did. The trimmer reads 25 records and treats the 135 newer ones as a very large front matter. The dry run (no --cut, so the trimmer chooses) reports would archive 24 of 25 record(s) and live 249,840 B → 213,031 B: it archives all but the newest of the 25 it recognized, leaves the file far above its limits, and says nothing about the 135 sections it did not read.

  • (a) A finding whenever the front matter contains a heading-shaped line (### ... Session <number> ...), so the dry run and the write say what they did not read. This covers any variant of the heading.
  • (b) Match Did case-insensitively. This fixes the case above and no other, and it combines with (a).

Which do you prefer?

3. A --- line inside the oldest record the trim keeps

The entry declares no footer, so nothing after the last record is a separate zone. A standalone --- line that has more text after it inside the oldest record the trim keeps (the last record in the file) is therefore text the entry's grammar has no name for.

One real project's notes file hit it. It has four records. --cut 3 (keep the newest three) would keep ### What Session 205 Did as the last record, and that record has a --- at line 31 followed by its close-out block. The trimmer plans the trim, finds that the text it would write does not parse under its own declared grammar, and the dry run ends ARTIFACT_UNPARSEABLE ("the text this run would write does not parse under its own declared grammar"). That message names neither the line nor the cause. Removing that one line lets the same dry run plan the trim, and so does --cut 2, which keeps a last record whose rule is followed by nothing. It is the same at 1.9.0 (commit 1), so it predates commits 2 and 3. It is a refusal, not a loss.

  • (a) A footer mode for notes files that counts a trailing rule as part of the last record.
  • (b) Keep the refusal, and make the finding name the line and say what to do.

Which do you prefer?

Underneath: is SESSION_NOTES.md transient, or does it accumulate?

The framework says both. FRAMEWORK_APPARATUS.md (line 562 on this branch) and the seed's opening lines call the file transient: "This file is transient — it is overwritten every session". But the runner has every session add records to it (Phase 1B writes a ### What Session N Did stub, Phase 3A writes a ### Session N-1 Handoff Evaluation (by Session N) record), nothing in the runner removes an old one, and the seed's last line says "Session history accumulates below this line. Newest session at the top." So in practice it accumulates until someone trims it. The project in #93 had reached 6,370,574 B (620 records) when it was first trimmed, using a table entry in a local copy of the trimmer that every bin/sync overwrites. Of the 13 notes files in my own projects, 3 are over the seed's 120,000 B ceiling today (the largest is 577,539 B, 97 records), and the canonical trimmer cannot trim them.

  • If it accumulates, it is a ledger, commit 1 is right, and questions 1 to 3 above are the work that remains. The documents that say "transient" need correcting.
  • If it is transient, it stays short, the cumulative record lives in CHANGELOG.md, and the trimmer should not have an entry for it. Commit 1 would not merge, and questions 1 to 3 would not arise.

This pull request assumes the first, because the runner's own steps make the file accumulate. If you would rather settle it first, the first commit can wait. The second does not apply to main without the first (it conflicts in methodology_trim.py and .quality-gates.json), so I would then rebase it and the third and re-run the suite.

Not in this pull request: #97 (a lossy trim followed by deleting and restoring the shard reads OK, because the proof's history walk takes the newest add).

Adopter impact

bin/sync copies methodology_trim.py (1.8.2 to 1.10.0) and the changed paragraphs of FRAMEWORK_APPARATUS.md, and README.md's file tree gains the third ledger. A project can then run methodology_trim.py --file SESSION_NOTES.md --check and trim that file with the canonical tool. A project that carries its own table entry for this file (nprcgenekeepr does) will have bin/sync stop on its locally edited methodology_trim.py (exit 2, naming the file, --force to overwrite), as for any local edit; after a forced sync the canonical entry read that project's notes file as its own did (measured earlier, when this entry was the fourth commit of #94). A notes file in which no heading matches the runner's two forms is refused with GRAMMAR_MISMATCH (exit 3) when its headings carry a date, and read as zero records ("nothing to archive") when they do not; question 2 is the partial case, where some headings match. The only new output of a trim is the warning line, when a front-matter line HEAD holds is missing from the working tree; --reverify and the PREFIX refusal word two messages differently, as in the second commit.

🤖 Generated with Claude Code

rmsharp and others added 3 commits October 26, 2026 15:51
… 1.9.0)

Seven of the ten failing proofs in the repository issue KJ5HST#93 reports on are SESSION_NOTES.md
shards; the table had no entry for that file, so no stub marker reached them. The entry: the
runner's two record shapes, no footer, a best-effort date, and a stub marker that needs both
Phase 1B lines (the status line alone also appears in finished records in 3 of 12 adopters).
Split out of the proof fixes at the maintainer's request. The date comment now says it decides
`--cut DATE`; README's file tree lists the third ledger. 245 tests (from 227), gate 227 -> 245.
On the adopter's 54 proofs: 46 exit 0, 8 exit 4 (without this: 46 / 1 exit 4 / 7 exit 1).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…(issue KJ5HST#93; 1.9.1)

The maintainer's follow-ups after merging KJ5HST#94. `--reverify` says "has no entry for
<file>" for a file the ledger table lacks and "the entry declares no stub marker"
for CHANGELOG.md, which the old sentence ("declares none for the live basename")
ran together. The PREFIX refusal says the proof was written for a different layout,
not a different depth. The `unfinished` comment, the --reverify legend and
FRAMEWORK_APPARATUS.md name every case that exits 3, and say a proof written before
1.8.1 exits 1 where git cannot read the commit or object (measured in a depth-1
clone: 1.8.0 exits 1, 1.8.1 and 1.9.1 exit 3). Wording only. 250 tests (from 245);
the trimmer-unit-tests gate 245 -> 250.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… front-matter edit (issue KJ5HST#93; 1.10.0)

The proof's L2 needs every non-blank line of the parent's front matter in the trim
commit; the writer's own L2 compares the working tree before and after its run. An
edit above the first record that is uncommitted when the trimmer runs (a
SESSION_NOTES.md ACTIVE TASK) is invisible to the writer and ends the proof red
("L2 FRONT MATTER lost") after it printed L2_OK. The new advisory finding runs the
proof's test against HEAD's copy at write time and names the way out: commit the
edit first, then trim in a commit of its own. Silent where the proof is (added
lines, blank lines, a reorder, a regenerated field's value, converted line endings).
The maintainer asked for it in this pull request. 270 tests (from 250); gate 250 -> 270.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 27, 2026
…e trimmer's SESSION_NOTES.md entry, his follow-ups and the front-matter warning); read back equal

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 27, 2026
…5HST#98 (read back equal); the HANDOFFS trim and fold ran first; the receipt complete; nothing else sent upstream

Deliverable: PR KJ5HST#98 on KJ5HST/methodology (the trimmer's SESSION_NOTES.md entry, the maintainer's three follow-ups, the front-matter warning). Receipt complete (self 8, predecessor 7); gates 24/24 in a clean clone; tests.sh 692 passed. Fork main was not pushed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 28, 2026
…98ca7fc9 (the trimmer) added to pr2b/candidate as 1.9.0 (62680cd, 5 files); three judgments, two put to him; 13/13 gates and 298/0/0 in a clean clone; it meets KJ5HST#98 in 5 regions; fork only, nothing sent upstream

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 28, 2026
…e's commit 98ca7fc9 added to pr2b/candidate as trimmer 1.9.0 (62680cd); fork main pushed at his go-ahead; the receipt complete; it meets KJ5HST#98 in 5 regions and the version is provisional; nothing sent upstream

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 28, 2026
…e9c5cb86 (context_budget.py 1.4.0) added to pr2b/candidate (f767ced, 4 files); applied cleanly and equal to the original; 13/13 gates and 298/0/0 in a clean clone; adds 0 regions against KJ5HST#98 and KJ5HST#96; fork only, nothing sent upstream

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 28, 2026
…dd581fed (methodology_dashboard.py 2.22.0) added to pr2b/candidate (e521060, 5 files); applied cleanly and equal to the original; 13/13 gates and 298/0/0 in a clean clone; adds 0 regions against KJ5HST#98 and KJ5HST#96; fork only, nothing sent upstream

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 28, 2026
…4c0bdded (Test 15 in bin/tests.sh counts the status table's rows) added to pr2b/candidate (07ec025, 1 file); applied cleanly and equal to the original; 13/13 gates and 298/0/0 in a clean clone; adds 0 regions against KJ5HST#98 and KJ5HST#96; fork only, nothing sent upstream

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 30, 2026
…ction 6 re-run on pr2b/candidate against upstream d50ce29; expectations saved before the run; suite 302/0/0 (upstream 261/0), gates 18/18 (upstream 12/12), 33 commits, 0 refusals; KJ5HST#98 reads 5 regions in 3 files, KJ5HST#96 3 in 2; nothing sent to KJ5HST/methodology

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 30, 2026
…rived facts the text states (per-commit sizes, earlier-work share 37.5%, the trimmer's 17 versions, KJ5HST#98's versions, merge-tree with and without union); the merge-file and distribution re-runs; nothing sent to KJ5HST/methodology

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 30, 2026
…ext, draft 11 checked claim by claim against the rebuilt branch (33 commits on d50ce29); decision 3 now asks about KJ5HST#98; frozen for a cold read; nothing sent to KJ5HST/methodology

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
rmsharp added a commit to rmsharp/methodology that referenced this pull request Oct 30, 2026
…t, re-checked on the rebuilt branch pr2b/candidate (33 commits on upstream d50ce29): draft 12 approved by him as committed; the facts re-run (suite 302/0/0, gates 18/18, KJ5HST#98 5 regions in 3 files, KJ5HST#96 3 in 2); the fork's gates pass 24 of 24; nothing sent to KJ5HST/methodology

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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