Releases: robworks-code/notation
Release list
v0.12.0
The audit path already had mechanical detectors backed by mutation-checked tests. The capture path - the half that writes to your files - was prose and prompting only. This release gives it a deterministic core, so capture and audit compute the same quantities from the same code instead of describing them twice.
The core
Seven subcommands behind scripts/notation-core.py: open, register, measure, route, price, price-record, gate, close. Stdlib-only Python, no dependencies, 3.9 floor.
stdout carries JSON only, stderr carries the explanation, and the exit code carries the verdict - 0 clean, 1 gate refused, 2 usage or internal error, never conflated. A crash returning 1 would read as a refusal; a crash returning 0 would read as clean.
What it changes in practice
The budget is now enforced across a whole run, not per proposal. Three additions that are each individually legal can still take the global file over budget, and nothing that looks at one proposal at a time can see that. A run-scoped ledger is what makes the verdict possible.
notate-all is the main beneficiary. It applies every proposal with no interactive picker, so previously nothing stood between a bad extraction and ~/.claude/CLAUDE.md. The gate is neither optional nor skippable there.
Capture now checks its own work. After the writes land, the run re-measures and compares predicted against actual with zero tolerance. An append that succeeded while its removal did not is now a reported finding naming the file and the gap, rather than a silent success. Interference from another session is reported separately from your own edit not landing - they are different problems with different fixes.
A zero says what it searched. Ranked note candidates come back with the count of notes searched and the tokens that matched, so "searched 87, none fit" cannot be mistaken for "searched 0 because the path was wrong" - the second would otherwise license minting a new note every time.
Scope stays asymmetric. The global file gets a hard gate; a project CLAUDE.md gets the same measurements as advice and is never refused. If a verdict cannot be produced at all, a global write is blocked and a project write proceeds with a warning.
Thresholds
Every size threshold now lives in one file. Roughly forty restatements were removed from the audit references, both command docs, the README, and the plugin description, and a drift test fails the build if one comes back. Changing a threshold is one edit, and prose can no longer disagree with the gate.
Verification
./scripts/verify.sh remains the one command, now around 4.4 seconds across fourteen suites. It also runs on Linux and on Python 3.9, 3.11 and 3.13 in CI, which caught a portability problem on its first run.
Follow-ups tracked as #27 (ledger retention), #28 (a path-collision case), #29 (a PreToolUse hook so the gate cannot be skipped), and #30 (measuring the skill listing against its own budget).
v0.11.0
Backup handling, rule precedence, and a stronger gate.
Backups are sorted by kind (#11, #14, #20)
The audit previously treated every .bak as interchangeable clutter and advised pruning to "the most recent one or two" - a number the skill invented. Against a real setup with 106 CLAUDE.md snapshots and a rotation script whose policy is to compress rather than delete, that read as a proposal to destroy 104 files, in the one check that proposes destroying anything.
Backups are now two classes with opposite treatment:
- Your own snapshots - reported, never proposed for deletion. Compression is the safe offer; deletion needs an age threshold you name. An existing rotation policy of your own always wins.
- Notation's run backups - the rollback window for one run, offered for cleanup only once that run's verification passed.
Notation now writes every backup under ~/.claude/notation-backups/{global,<project>}/<stamp>/, one directory per run and never beside the original. Previously it wrote ~/.claude/CLAUDE.md.bak.<stamp> - the same pattern people use by hand - so the two were indistinguishable and a rotation script would gzip notation's droppings while pushing real snapshots out of its keep-uncompressed window.
A run directory is marked .verified only after its checks pass. Unmarked means the run failed, was interrupted, or was restored, and its backup is the only remaining copy of whatever it moved - never offered for deletion, and never sorted by age, since the oldest directory is exactly as likely to be that one.
Other projects' backups are reported as an aggregate and never deleted: an audit resolves one project and cannot verify another's run passed.
Conflicting rules now state which one wins (#9, #10)
Three issues have come from the same defect - two rules pulling opposite ways with neither mentioning the other. One principle settles them: does an index line buy findability, or only tidiness? Note bytes cost nothing until a note is opened; an index line costs context in every session, forever.
- Routing quality beats index-line cost. Preferring an existing note stops where that note is a stretch. If a future session searching for the fact would not open that file, mint the new note and offset the line. A fact filed where nobody looks passes every check here, including the preservation probe, because the string is on disk.
- The budget beats splitting an oversized note. A split buys no findability - those facts are already reachable - so it is deferred while the global file is at or over target, and said out loud rather than silently skipped. Merging thin siblings is the mirror case and always welcome.
Each conflict has one ruling that every participating rule points back at, so neither side can be read in isolation.
Guard against encoding drift (#17)
The project-directory encoding is inlined at nine call sites because shell state does not survive between tool calls. Only two were drift-guarded, and the repo-wide ban covered a single historically broken form - so a site rewritten into a differently-wrong one shipped clean while failing exactly like the original bug. Every call site is now checked.
Gate
./scripts/verify.sh grew two suites - rule precedence and backup lifecycle - and now runs seven. Each new guard was mutation-tested: 27 rows across the three changes, every one confirmed to fail the gate before being trusted.
v0.10.0 - project scope and global scope, handled independently
Scope was only ever settled for the two CLAUDE.md files. Everywhere else it was unstated, and in two places wrong.
Fixed
- The project-dir path encoding omitted the
.->-replacement. Claude Code encodes both/and., so any path containing a dot resolved to a directory that does not exist - losing the session transcript and the project's memory dir, then offering to create a duplicate the harness would never load. The rule now has one canonical home and a test that executes it. - The audit located the memory dir with a
projects/*/memoryglob, which matches every project on the machine and so can never identify the current one. Capture and audit both resolve by exact path, and a test bans the glob from shipped files. - Routing asked "is this tool-specific?" before "is this repo-specific?" A learning that is both - one repo's deployment detail on a shared platform - landed in a global note and loaded in every other session. Scope is now a gate ahead of the routing tree.
- Nothing detected leakage in either direction. New audit check 9 finds a global note whose subject is really one repo, and a project memory file holding a fact that is true everywhere. It partitions cleanly against the cross-tier duplication check: one copy in the wrong scope is a move, two copies straddling scopes is a delete.
- A strict run's backup of
./CLAUDE.mdlanded in the repo working tree. Project-scope backups now go to~/.claude/notation-backups/<encoded-cwd>/, so notation never leaves an artifact in a user's repo. - The audit scorecard never named the project it resolved, so a run in the wrong working directory was invisible in the output.
Note for anyone reading the source
The encoding is inlined self-contained at each call site rather than defined once as a shell helper. The Bash tool does not persist shell state between invocations, so a function defined in one block is unset in the next and the path collapses to empty - reproducing the exact bug this release fixes.
Closes #15.
v0.9.0 - relocations are verified, deltas are measured
Two fixes to the audit, both found by dogfooding it against a real 59,065-char CLAUDE.md. Closes #7 and #8.
Relocations are now proved, not assumed. A move is two writes - remove from the source, append to the destination - so a failed destination write makes the file smaller, and every size check reads that as success. Shrinking is the failure signature of data loss, which made wc -c structurally incapable of catching the thing it was guarding.
The audit now backs up every file a run removes content from before the first write, then after applying checks that each destination grew by at least the bytes that left (a point probe cannot bound a truncated tail), that distinctive strings picked beforehand now hit their destination, and that they are gone from the source - the inverse failure, where the append lands but the removal does not, otherwise passes silently as an under-delivering estimate. A failure restores that one relocation and reports the loss instead of the size win.
Rewrites that legitimately shorten text in place are classified as compressions and never probed, so a correct edit is not destroyed by a false failure.
Deltas are measured rather than felt. The report format demanded a number 'computed from the diff text' at a stage where no diff exists yet, so every row except index compression was guessed - about 19% optimistic on the real run, which turned a promised one-pass landing into four. Each of the seven tactics now has a stated method built on one principle: the content being removed is always measurable, and the line replacing it is always short enough to draft at report time. Rows whose replacement length was assumed carry a ~, projections are labelled estimates, and converging over two or three passes is documented as expected rather than treated as a failure.
New: scripts/verify.sh is the single local gate - manifest, ASCII, cross-references, and mutation tests that build each data-loss scenario and assert the procedure catches it.
v0.8.0 - project CLAUDE.md headroom
A project's own ./CLAUDE.md is no longer held to the global file's strict budget.
The global ~/.claude/CLAUDE.md loads into every prompt of every session, so it keeps its hard budget: net delta at or below zero on every audit run, and active reduction until it lands under the ~40,000-char warning threshold.
A repo's own ./CLAUDE.md only costs context inside that repo, so it now gets headroom instead:
- Under 20,000 chars - silent. Not reported, not a finding. Most project files live here.
- 20,000 to 40,000 - one advisory ledger line, zero findings. Growing into this band is expected for a large or long-lived codebase.
- Over 40,000 - a real finding, but still advisory. Decline it and the audit accepts that.
There is no no-growth rule for a project file. When one does need to shed weight, its detail relocates to project-local homes - ./.claude/docs/, the repo's own docs, or project memory - never into ~/.claude/notes/, which is global and would leak one repo's specifics into every other session.
Strict enforcement is available on demand: ask for it ("audit this repo strictly") for a single run, or record the preference in that project's memory and it persists across sessions. A strict run always prints the project ledger line, at any size, and names which trigger produced it.
v0.7.1 - CLAUDE.md size budget
The global ~/.claude/CLAUDE.md is now a budgeted resource across both halves of the plugin. It loads into every prompt of every session, so an audit that leaves it bigger than it found it has failed - and capture is the only thing that grows it.
notation-audit
- New
references/size-budget.md: target <= 40,000 chars (Claude Code's large-memory-file warning floor; the real ceiling ismax(40000, 5% of the context window)), green band <= 32,000. - No-growth rule - the net character delta of an applied audit is <= 0, always, even when already under budget.
- Reduction rule - when over target, propose enough relocation to land under it, or state the best achievable number and what is blocking the rest.
- Reduction tactics ranked best-first: subsection -> note, fat inline entry -> note plus a one-line trigger, append to an existing note rather than minting a new one, cross-tier duplicate removal, index-line compression, note consolidation.
- Measures with
wc -cup front, scores every finding with a signed character delta, reports a size ledger, and re-measures after applying rather than trusting the projection. - Additive findings (a missing index line) are counted as positive deltas and must be offset in the same run.
/notation:notate and /notation:notate-all
- Step 1 measures the file; Step 2's proposal schema gains a
deltafield. - Budget gate: global-tier proposals are priced in characters, inline entries stay one line with detail pushed to
notes/, and at or over 40,000 chars the global tier closes by default - a promotion has to re-pass the routing question and name the size. - Summaries re-measure and report before -> after, offering an audit run when still over target.
notate-allrestates both, since no picker reviews the global tier in auto-apply mode.
Preservation is unchanged. The budget is met by relocating detail into linked note files, never by deleting a still-true line. If the target could only be met by destroying a fact, the audit misses the target and says why.
Closes #4.
v0.6.1
Improves /notation:feedback (mirrors sharding v0.0.7).
- Offers optional email + one-line context up front, and states plainly what it attaches automatically (version, OS, timestamp - nothing else).
- Anonymous tickets get a simple thank-you with no unreachable tracking link; with an email, the success message links to the support-portal ticket.
Pairs with a server-side fix so the returned link is the branded support portal, not the admin dashboard.
v0.4.1
v0.4.0 - Adaptive flow and structured presentation
What's new
Adaptive session read (Step 0)
Both /notation:notate and /notation:notate-all now read the conversation before running the capture flow. Step 0 classifies the session type, gauges how many learnings there are, identifies which tiers were actually exercised, and early-exits when there is nothing worth saving. It announces the chosen flow in one line before proceeding.
Pass full to force the complete default flow regardless of how thin the session looks.
Structured presentation
A new shared reference (skills/notation-audit/references/output-format.md) defines a consistent three-zone format used across all modes:
- Scorecard header - one-line tally of the read and what was found
- Per-tier tables - numbered rows, high-confidence first, with title / kind / conf / destination columns
- Diffs below - full diffs keyed by row number, always above the picker
The notation-audit skill uses the same format for its findings (move / fix / tidy severity groups).
Upgrade
/plugin marketplace update