feat: sentinel review — the archive owner's verdict, recorded on the file - #71
Merged
Conversation
khaneight
force-pushed
the
feat/learn-rung
branch
from
August 27, 2026 02:15
be015e7 to
40fe60a
Compare
…file Every other command is the tool's opinion. This one is the user's, and it is the only writer of `review:`. No skill invokes it: an agent that can approve its own work has a permission system in name only, so `review` joins `export` in the deliberately-not-agent-reachable list. Verdicts live in the document's own frontmatter, articles and persona traits alike. A verdict recorded in `meta/` comes apart from the thing it was about the first time a file is renamed by hand. They append, because the history is the point — "changes requested, then a note, then approved three weeks later" is the useful record, and a single mutable field keeps only the last word. The operative verdict is the latest that *decided* something; `comment` is deliberately not one, so a remark on approved work does not un-approve it. Three things it refuses to do. It will not attribute a verdict to a default: with no `--by`, `SENTINEL_REVIEWER` or `USER` it stops, because a record signed by nobody looks exactly like somebody agreeing. It will not resolve an ambiguous name — a verdict on the wrong document is worse than none. And it will not decide from a partial view, since the unreadable file could be the one the name really meant. The append is textual and confined to the frontmatter block, like `mv`'s citation rewriting and for the same reason: the block is a file the user also opens by hand, and a serde round trip would reorder its keys and strip its comments to add four lines. The entry itself is serialised, so quoting a note with colons, quotes and newlines is YAML's problem rather than this function's. A `review:` already written as a populated inline list is refused with instructions rather than mangled. A persona trait carries its standing twice — `status:` is what a reader sees at the top of the file, `review:` is the history behind it — so both are written together and `verdict-disagrees-with-status` reports them drifting apart. `/sentinel-improve` gains the repairs, and each one fixes a malformed record rather than supplying a missing one: a bad date can be recovered from context, a missing `by` never can. CLAUDE.md's context budget rises 12,000 → 14,000, after compressing rather than instead of it. The repository file absorbed a second document schema, a permission system and a ladder rung while growing 325 bytes: `mv`/`rm` reasoning and the case-sensitivity recipe moved to docs/design-notes.md, the command reference lost what `--help` already says, and "derived sets" and "one source of truth" turned out to be one invariant written twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
khaneight
force-pushed
the
feat/review-gate
branch
from
August 27, 2026 02:18
4f362ee to
861c3c0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #70. The permission system: approve, reject, request changes, comment.
No skill invokes it. It joins
exportinNOT_FOR_AGENTS: an agent thatcan approve its own work has a permission system in name only.
Where verdicts live, and why they append
In the document's own frontmatter. A verdict recorded in
meta/comes apartfrom the thing it was about the first time a file is renamed by hand.
They append rather than overwrite, because the history is the point — changes
requested → note → approved three weeks later is the useful record, and a
single mutable field keeps only the last word. The operative verdict is the
latest that decided something;
commentdeliberately isn't one, so a remarkleft on approved work does not un-approve it.
Three refusals
--by,SENTINEL_REVIEWERorUSERit stops rather than defaulting. A record attributed to a placeholder looks
exactly like somebody agreeing, which is the one outcome this whole mechanism
exists to prevent.
verdict on the wrong document is worse than recording none, so it names both
and stops.
meant — or the one that made it ambiguous.
The append is textual
Like
mv's citation rewriting, and for the same reason: the block is a file theuser also opens by hand, and a serde round trip would reorder its keys and strip
its comments to add four lines. Tested against a comment above
title:, aninline
tags: [a, b], a key after the list, and an indentedreview:nestedunder something else.
The entry itself is serialised, so a note containing colons, quotes and
newlines is YAML's problem rather than this function's. A
review:alreadywritten as a populated inline list is refused with instructions rather than
mangled.
Traits carry their standing twice
status:is what a reader sees at the top of the file;review:is the historybehind it. Deriving one silently from the other would mean a file that reads
proposedwhile the archive treats it as affirmed — so both are writtentogether and
verdict-disagrees-with-statusreports them drifting. Articles getno such treatment: their
statusis maturity, andstablemeans finished,not signed.
CLAUDE.md's budget: 12,000 → 14,000
Raised after compressing, not instead of it. The repository file absorbed a
second document schema, a permission system and a ladder rung while growing 325
bytes net:
mv/rmreasoning and the case-sensitivity recipe moved todocs/design-notes.md, the command reference lost what--helpalready says,and "derived sets" and "one source of truth" turned out to be one invariant
written twice. The reasoning is recorded on the constant, with an instruction to
compress first and say what came out before raising it again.
484 tests (+25), clippy and fmt clean.
🤖 Generated with Claude Code