Make Batch Review Requirements an executable per-batch protocol - #118
Merged
Conversation
A Profile could declare a Judgment Item, but nothing said whether it was
a standing per-batch, per-page obligation; the batch-review wrapper bound
only the Delta page receipt IDs; Extension Gates are property-state
transitions and the wrong shape for a content judgment that changes no
property; and check_batch_close runs after merge-ready, too late to be
the first gate. The first real adopter demonstrated the consequence: a
standing four-class content-form obligation, declared in prose since its
3.6.3, was skipped entirely on a closed batch with twelve gates green
(its register: the sorting duty knew it had exactly one chance, and
"not writing it down equals not checking" had no machine face).
This ships the chain that closes the gap:
Profile Judgment Item
-> declared as a Batch Review Requirement (new registry section)
-> expanded deterministically at queued -> open (Frozen Review Plan,
activation protocol card-first-readback-v2, delivered with the
Card Activation Bundle, hash frozen in the admission receipt)
-> answered per target through record_batch_judgment.py
(manual-attestation class; binds the batch's current activation,
the target's semantic content fingerprint, and the authorized
Profile contract fingerprint)
-> bound exactly by the batch-review wrapper
(review_requirement_set_sha256, judgment_receipt_ids,
judgment_record_set_sha256)
-> enforced at open -> merge-ready: expected = actual, exact set.
One missing, extra, duplicated, forged, mis-roled, drifted, or reused
record refuses the transition. A requirement differs from an Extension
Gate on purpose — it proves a judgment happened for every applicable
target and writes nothing back; the two are documented against each
other in the template. First-version enums are closed (each-manifest-page
| batch, before-merge-ready, manual-attestation, page-batch-judgment-v1):
no natural-language applicability, so a declared rule can never again be
one the machine does not know when to apply.
Era rules: requirement-free Profiles keep their exact wrapper shape and
their contract fingerprints are byte-identical (the fingerprint includes
the requirement block only when non-empty); a v1 activation predates the
plan, imposes no obligations, must not carry the fields, and upgrades by
reactivation; a reopened batch gets a new activation receipt, so old
judgments fail their opening binding and the new attempt re-judges;
page edits invalidate per-target judgments through the semantic
fingerprint; Profile revisions invalidate the frozen expansion through
the set hash. Sealed history replays under its own producer era.
Versions: check_queue 1.22.0 -> 1.23.0 (v2 admission receipts, judgment
validator), update_queue 1.7.0 -> 1.8.0 (release identity only; the
transition receipt shape and its 1.5.0 producer era are unchanged).
K00/12 producer cells, K12/14, K12/17, K13/19 (new Frozen Review Plan
section), the K13 MOC row, the receipt-schema template, Tools/README,
and the example profile (judgment item, content-reviewer role,
configured requirement) move together; K00/16 registers the two new
leaf-size exceptions and re-measures K12/17; seven Cards restamped with
the R07 During loop naming the per-record judgment duty; compiled
CLI/MCP/host/metadata artifacts regenerated (45 tools).
Validation: make check green end to end; 11 new acceptance tests cover
the exact-set happy path, missing record, absent bindings, duplicate,
recording-time role refusal, forged role at merge, unregistered target,
page drift, reused activation binding, requirement-free shape stability,
and legacy-era replay; the full update_queue/check_queue/card_activation/
profile_contract suites pass.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI caught three gaps the local run missed. The seventeen batch-review finding codes join check_profile.FINDING_CATEGORIES, classified exactly as their extension-gates siblings: the two blank-content findings are semantic-unresolved, the fifteen structural/reference/enum findings are mechanical. record_batch_judgment joins the README distribution sentence. And the judgment validator treated a batch with no resolvable activation receipt as an error, which re-judged sealed pre-delivery-era fixtures; a batch activated before delivery receipts existed predates the review era exactly as a v1 activation does — it owes nothing and must carry nothing, and the runtime validator, not this gate, is what keeps a current-era batch from shedding its activation receipt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The finding-classification and pre-era commits changed check_profile and check_queue, whose bytes the compiled CLI contract and metadata execution contract bind. make check verified rc=0 end to end after regeneration. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
BatchReviewRequirementTests subclasses UpdateQueueTests for its fixture helpers, which reran all 85 inherited tests under the subclass — no new coverage, roughly doubled runtime, and the s-z CI shard already sits near its 15-minute budget; the 3.14 run went over and was canceled. The inherited tests are now explicitly skipped on the subclass and keep running exactly once, on the base class. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Closes the gap behind #117: a Profile obligation could be declared but nothing made it a counted, per-batch, per-target duty. This PR ships the full chain — declaration, activation-time freezing, delivery, per-target evidence, exact-set aggregation, and enforcement at the correct boundary.
What changed
Batch Review Requirementsregistry section (template + typed IR inprofile_contract.py). A configured row references a registered Judgment Item and closes every enum: target selectoreach-manifest-page|batch, triggerbefore-merge-ready, producermanual-attestation, receipt schemapage-batch-judgment-v1, plus a resolvable pass-authority role. An absent section is the unregistered state; a requirement-free Profile's contract fingerprint stays byte-identical (the fingerprint includes the block only when non-empty).card_activation.py): protocolcard-first-readback-v2expands the requirements against the frozen manifest into a deterministic expected set, freezesreview_requirement_set_sha256in the admission receipt, and delivers the readable plan inside the Bundle (batch_review_plan), so the executing Agent starts with its obligations in context.queued -> openrecompiles to exact equality. v1 receipts replay under their own era.record_batch_judgment.py, new): one invocation answers one frozen record; validates target against the frozen expansion and reviewer against the registered role; binds the batch's current activation receipt, the target's semantic content fingerprint, and the authorized Profile contract fingerprint; rebuilds every binding under the runtime write lock immediately before publication.review_requirement_set_sha256,judgment_receipt_ids,judgment_record_set_sha256;update_queuerefusesopen -> merge-readyunless expected = actual exactly — one missing, extra, duplicated, forged, mis-roled, drifted, or reused record refuses the transition (check_queue.batch_review_judgment_errors).agent-atlas-content-form-classification+content-reviewer), compiled CLI/MCP/host/metadata artifacts regenerated (45 tools).Validation
make check: green end to endBatchReviewRequirementTests): exact-set happy path; missing record; wrapper without bindings; duplicate; recording-time role refusal; forged role at merge; unregistered target; page drift after judgment; reused activation binding; requirement-free shape stability; legacy-era replaytest_update_queue,test_check_queue,test_card_activation,test_profile_contractsuites passprofile-load2.0.0 with the configured requirementScope boundary
check_batch_closeis deliberately untouched: the enforcement boundary isopen -> merge-ready, and close-time replay keeps producer-era semantics. Natural-language applicability predicates are deliberately excluded from v1 of the registry. Extension Gates are unchanged — the two mechanisms are documented against each other in the template.Fixes #117