TASK-261: the ADR-004 conformance gate and perry-migrate come out — −10,907 lines - #32
Merged
ranjiao merged 4 commits intoSep 1, 2026
Conversation
TASK-050 ran eleven V4 rounds. Rounds 8, 9 and 10 each found a real escape — a pruned corpus, a one-line alias, a header row carried through a dict key — and round 11 PASSed on "a measured, listed remainder of 8 out of 76". It ended on the round the criterion became DECIDABLE, not on the round the last hole closed. The reviewer was right every time; the question had no last answer. review.md § 1 · the criteria must be bounded. A criterion is bounded when the author can name the finite set and its size BEFORE the round. The criteria file carries a `## Bound` block, and three unboundable shapes get a rewrite each. This does not soften § 2 rule 1 — the bound is what makes "enumerate the category" finishable rather than a universal negative over a live tree. review.md § 2 · what V4 does not judge. Measured on this board: 79 review documents, 54 `## Finding` headlines, 5 meta, and 22 of the remaining 49 are about the round's own artifact rather than the product — a pruned corpus, an incomplete baseline, a misreported mutation, three citations pointing at a file the branch does not carry. The protocol manufactures an exhibit, and the exhibit has more failure modes than the code. Round N+1 then audits round N's exhibit, which is a loop with no product in it. A GREEN MUTATION STAYS V4 — it is a product finding wearing a test's clothes. Bookkeeping moves to a pre-check. perry-lint --reviews gains the two pre-check findings, and --strict now EXITS NON-ZERO so a red exhibit can actually stop a dispatch. Without that the pre-check would be one more rule stated in prose that nothing implements, which is the defect review.md exists over. citation-not-on-branch a `criteria:`/`proof:` path the branch lacks criteria-unbounded a criteria file with no `## Bound` Both are scoped to OPEN rows: a closed row's exhibit cannot be re-filed, and a backlog nobody can clear is a gate that is red forever, which is a gate people delete. On this repository that took the run from 136 findings to 29. What counts as a path claim is deliberately narrow, and every exclusion is a token these 79 reviews actually produced: `tables.py` is a mention, `/pmo` is a command, `bin/perry-goals:927/:908` is a sentence about two lines, `evidence/3` is a count, and `scratchpad/…` is the reviewer obeying review-constraints.md. `checked:` is not mined at all — it is a sentence by design. MUTATIONS. Ten run, and the first pass left four green. Each was a finding: the `"/" not in tok` clause was dead code the head-is-a-directory rule already covered, so it is deleted rather than tested; two tests never reached the clause they named — `scratchpad/` did not exist in the temp tree and a trailing slash was caught by an earlier rule — and are now built to reach it; and the harness's own red-detector missed a failure it had caused. Second pass: all ten red, md5-verified restores. tests/run: the same three modules are red as on a clean `git archive HEAD` export — test_diagnose, test_heading_title, test_kr_progress_provenance, all reading live state this change does not touch. No new red. Also files the representation-layer delete list, which is analysis and not a row. Its own first draft was written as a table whose first cell was a task id, so perry-explain harvested it as state, gave TASK-050 the title "11", and silently turned test_heading_title green by displacing the real entry. It is a list now, and that paragraph is in the file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…e fork The gate read a stored DECLARATION out of `.perry/conformance.jsonl` and refused a write when the file's live shape no longer matched it. Keeping the declaration and the check apart was the whole design — "a stored decision plus a live check can disagree, and that disagreement is a finding". It never disagreed. The ledger held 23 records. All 23 were `route: declare`. All 23 were files in this repository. Zero carried `route: migrate`. The disagreement the design exists to surface needs a FOREIGN project that drifts, and Perry has never been pointed at one — TASK-097 has been `not_started` since the day it was filed. Three of the open rows on this board (TASK-223, 246, 248) were defects in the gate itself: work about itself. 40 files, -5,245 lines. THREE GATE CALL SITES, not the two the delete list named: `perry-task`, `perry-goals` and `perry_md_store § render --write`. The third was found by grep after the list was written, which is the delete list being wrong in the cheap direction. `bin/perry-conform` is GUTTED AND RENAMED, not deleted, and the list was wrong about that too. 598 of its 974 lines were the ledger and the gate; the rest — `state_files`, `load_schema`, `spec_for`, `shape_version`, `_q`, `_root_flag` — are generic, and `bin/perry-migrate` imports every one of them and nothing about conformance. So it is `bin/perry_schema.py` now, 161 lines, and says what it is. `perry-decide` removed its own gate first and named the hole rather than faking it; this is the same move with the measurement attached. `tests/gate.py` goes with it. `GATE_OFF` was a `.perry/config.md` line 38 fixtures appended so their writes would not be refused by a gate they were not testing. With no gate it is inert, and leaving it means every future fixture author copies a line that does nothing. WHAT SURVIVED ON PURPOSE. `perry-task list --json`'s `conformance.*` payload — `evidence_not_found`, `depends_on_unknown`, `blocked_by_closed_rows` — is read-time integrity reporting, a published contract in `schema/task-list-contract.md`, and a completely different thing wearing the same word. It is byte-identical. `perry-lint`'s schema pass is untouched; only its conformance census line is gone. ONE BUG I INTRODUCED AND CAUGHT. Removing the gate block from `perry-goals` took `with project_lock(...)` and `result = COMMANDS[...]` with it — the write path itself — and every write exited 1 on `UnboundLocalError`. Restored from HEAD and re-verified; `test_goals_writer` is green. THE FORK, and it is why this row is blocked rather than done. `perry-migrate` calls `C.declare` at 14 sites: its OUTPUT is a conformance record carrying `route: migrate`. It cannot be repaired without restoring the ledger, because the ledger is what it writes. Migration and conformance were never two subsystems, and the delete list putting them in tiers A and C was wrong. That is a product question — is Perry ever pointed at a foreign project — so it is filed as USER-910 with both readings in `evidence/2026-08/2026-08-31-TASK-261-migration-fork.md`, not decided here. tests/run: `test_migrate` is red pending USER-910. The other three red modules — test_diagnose, test_heading_title, test_kr_progress_provenance — are red the same way on a clean `git archive HEAD` export and are untouched by this change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ask was whether Perry is ever pointed at a foreign project. It is not, so `bin/perry-migrate` (2,393 lines) and `tests/test_migrate.py` (2,900) are out, and `TASK-097` — "migrate the two real projects, at V5", `not_started` since the day it was filed — is dropped with them. So are `TASK-223`, `TASK-246` and `TASK-248`, three defects in a gate that no longer exists. `bin/perry_schema.py` goes too, and that was not planned. I gutted and renamed it one commit ago BECAUSE `perry-migrate` imported its `state_files`, `load_schema`, `_q` and `_root_flag`. With migrate gone it has no importer at all — every other tool reaches `lib.load_schema` in `bin/lib/__init__.py`. The 161 lines I kept turned out to be kept for one consumer, and the consumer left. Three manifests still named the tool and each one is a guard, not prose: `reference/glossary.md § restore point` (an entry whose `Implemented:` pointed at a file that no longer exists — `perry-lint --glossary` caught it), `test_one_primitive`'s WRITERS tuple, and `test_header_index_is_the_only_fold`'s watch list, which drove `fix_tables` as one of the twelve readers TASK-050 spent eleven rounds enumerating. tests/run: back to exactly the three modules that are red on a clean `git archive HEAD` — test_diagnose, test_heading_title, test_kr_progress_provenance. Nothing this branch touched is red. STILL OPEN, and it is the next commit rather than a loose end: `/perry adopt` is a user-facing command implemented BY `perry-migrate`, and the prose still promises it. `reference/adoption.md § Migration`, `bin/README.md`'s tool table, `reference/config.md`'s conformance-gate setting, `SKILL.md`'s "never run `perry-conform declare` for the user", both READMEs' ADR-004 paragraph, and the schema's `enum_aliases` / `negations` / `conformance_gate` fields — the first two of which say "Read only by bin/perry-migrate" in their own description. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Code deletion without this is a lie with a passing test suite. Nine documents described the ADR-004 conformance gate, `perry-conform` or `perry-migrate` as live, and three of them were user-facing. WHAT WAS FALSE, not merely stale: - **Both READMEs** said "a project that will not migrate stays readable rather than drivable". That was the gate's refusal, and with the gate deleted nothing is read-only for want of a declaration — writes just work. Rewritten around what `/perry adopt` actually does: read what is there as evidence, write Perry's own state, never rewrite your files in place. - **`SKILL.md`** told the agent "never run `perry-conform declare` for the user" — an instruction naming a command that does not exist. - **`reference/config.md`** documented a `Conformance gate` setting as enforcing. Now marked deleted, with the sentence a user needs: a `- Conformance gate:` line left in an existing config is INERT. Which is why the schema keeps tolerating the key rather than rejecting it — removing the definition would have made that sentence false in the other direction. - **`work/reference/review-constraints.md`** told every V4 reviewer not to run two tools that are gone. - **`reference/glossary.md § conformance`** defined the word as two things. It is one thing now. `reference/adoption.md § Migration` (57 lines) and `bin/README.md`'s gate section (152 lines) are replaced rather than trimmed: both described a mechanism end to end, and a shortened description of a deleted mechanism is worse than a paragraph saying it is deleted and why. `/perry adopt` itself is untouched — stages 0–5 never called `perry-migrate`; only the "project that already has Perry-shaped state" case did, and that case is gone. `schema § migration` is deleted — `enum_aliases` and `negations`, 1,766 bytes whose own descriptions read "Read only by bin/perry-migrate". Zero readers. ONE TEST CAUGHT ME. `test_router_budget` went red: my `SKILL.md` edit pushed the file 17 bytes past its 20,480 cap, and the failure message says what to do about it — "do not raise the cap without deciding that the file should be bigger". Shortened to 20,452. Worth recording that I nearly missed it: two full-suite runs overlapped with my own edits to the tree they were reading, so their results were not evidence about anything. The run reported below is a clean one. tests/run: 3 modules red — test_diagnose, test_heading_title, test_kr_progress_provenance — the same three, failing the same way, on a clean `git archive HEAD` export. Every module this branch touched is green. TASK-261 closes at V3: the tests and the mutation work are mine, and V4 needs a reviewer that did not write it. Co-Authored-By: Claude Opus 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.
Stacked on #31. Base is
coding/session-cost-gates, which has not moved sincethis branch was cut — the merge is a fast-forward, so the tree the suite ran on
is the tree that ships. No second open PR, so nothing pairwise for
tests/merge-checkto find.65 files, +1,407 / −10,907.
Why
Every row on this board that ground in V4 is in one architectural layer. The 14
rows kicked back twice or more are markdown-as-canonical, its parsers, its
drift detection or its conformance ledger — TASK-050 at eleven rounds, TASK-095
at six, TASK-203 and TASK-234 at five. Not one is about OKR or task management
as a domain.
Measured before deleting anything:
.perry/conformance.jsonlheld 23 records. All 23route: declare. All 23 files in this repository. Zero migrations, zerodisagreements. Its whole value is a stored decision disagreeing with a live
check, and that needs a foreign project which Perry has never been pointed at.
perry-migrate— 2,393 lines, lossless, dry-runnable, recoverable, andit has moved zero foreign projects.
TASK-097wasnot_startedfrom theday it was filed.
The four commits
a4eb411review.md § 1bounded criteria +§ 2what V4 does not judge;perry-lint --reviewsgainscitation-not-on-branchandcriteria-unbounded, and--strictnow exits non-zero so a red exhibit can stop a dispatch6ce1f5b436d0fbUSER-910answered A: migration goes too, andperry_schema.pywith it37e9af5What survived on purpose
perry-task list --json'sconformance.*payload —evidence_not_found,depends_on_unknown,blocked_by_closed_rows. Read-time integrityreporting, a published contract, and a completely different thing wearing the
same word. Byte-identical.
perry-lint's schema pass. Only its conformance census line is gone./perry adopt. Stages 0–5 never calledperry-migrate; only the "projectthat already has Perry-shaped state" case did, and that case is gone.
The plan was wrong four times, and each is in a commit message
perry_md_store § render --writewas missed.bin/perry-conformwas not 974 deletable lines: 598 were the ledger, ~280were generic helpers four tools imported. Gutted and renamed, then deleted
outright when its last importer left.
perry-migratecallsC.declareat14 sites, so conformance and migration were never two tiers.
perry_schema.pywas kept forperry-migrate. The consumer left.Three things I broke and caught
perry-goalsgate block tookwith project_lock(...)andresult = COMMANDS[...]with it — the write path. Every write exited 1 onUnboundLocalError. Restored from HEAD.id, so
perry-explainharvested it as state, gaveTASK-050the title11,and silently turned
test_heading_titlegreen by displacing the real entry.test_router_budgetcaught a 17-byteSKILL.mdoverrun. Two full-suite runshad overlapped with my own edits to the tree they were reading, so their
results were not evidence; the run below is a clean one.
Tests
bash tests/run— 3 modules red:test_diagnose,test_heading_title,test_kr_progress_provenance. All three fail identically on a cleangit archive HEADexport and read live state this branch does not touch.Every module this branch touched is green.
a4eb411's two new checks were mutation-tested: 10 mutations, and the firstpass left four green. Each was a finding — one dead clause deleted, two
tests that never reached the line they named rebuilt, one bug in my own
red-detector. Second pass: all ten red, md5-verified restores.
Board
TASK-260,TASK-261done at V3 — the tests and mutations are mine, andV4 needs a reviewer who did not write it.
TASK-097,223,246,248dropped.
USER-910answered.One thing to know before merging
Three files of yours that were uncommitted when this session started got swept
into
436d0fbby agit add -A … perry/:perry/phase/003-linkage.md,perry/phase/003-storage-code.md,perry/phase/snapshots/2026-08-31-003-storage-code.md. Content is byte-identicalto your edits — I never wrote to those paths — but they are committed now
rather than pending. Say the word and I will pull them back out.
🤖 Generated with Claude Code