Independent review against 74dc994. Two findings, both blocking, and both
the same defect -- a requirement stated without the decision it requires,
leaving execution to make a design choice silently.
The convergence loop was not actually bounded. It demanded a bound and
named no limit, so execution would have chosen when non-convergence becomes
failure, changing what the experiment means. Now pinned at one normalising
step: with B0 = B_raw and B(n+1) = serialize_document(document_from_bundle(Bn),
uuid), compute at most B1 and B2, permitted maximum n = 1, with a three-row
outcome table. B1 == B0 means B_raw was already fixed. B1 != B0 with
B2 == B1 is the expected case. B2 != B1 is a hard failure that must report
all three image lengths and the first differing offset.
The bound is one step because it is a property, not a tolerance.
document_from_bundle canonicalises, so serialize_document after
document_from_bundle must reach its canonical form in a single application.
If it does not, there is no canonical form, no principled reference
artifact, and M7 is invalid as a whole -- a finding about the projection
rather than a signal to iterate further. A loop that runs until it happens
to settle tests nothing; it reports how long it took. Raising the bound
needs its own amendment and review round.
M7's location was unchosen. "In a crate that can reach the real constant"
is true of two crates and decisive for neither, and render_text_document is
pub(crate) to epiphany-textproj, so epiphany-testkit could host M7 only via
an unpinned visibility change to another crate's public API. The harness is
now pinned to epiphany-textproj, which alone has both the renderer and, via
its epiphany-ops dependency, the real constant. It lands under existing
touch row 9; no new row.
render_text_document stays pub(crate). Handoff section 1.3 records it as the
one intentional hole in the text refusal, existing solely so a negative
vector can carry the spelling it asserts is refused. Widening it to host a
mutation that gets reverted would leave a permanently widened public surface
behind, which is how a temporary harness becomes an API change nobody
ratified. That improvisation is what execution would have reached for on
hitting the wall, which is why the decision belongs here.
Still NOT RATIFIED, NOT DISPATCHABLE. Findings 9, 6, 6, 5, 4, 3, 3, 2, 2, 1,
3, 2. Blocking 4, 4, 4, 4, 2, 3, 3, 2, 2, 1, 2, 2. Twelve rounds, none
clean. The last two rounds found the same kind of defect -- a requirement
that reads as a decision but is not one -- so the next scan should hunt
remaining instructions that name a constraint without naming its value.
Everything M7 now specifies is pinned to a number, a crate or a named
artifact, which is a checkable property a round can test directly.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ps1szk2mSfgp4Cz21eVH9x