Skip to content

TASK-261: the ADR-004 conformance gate and perry-migrate come out — −10,907 lines - #32

Merged
ranjiao merged 4 commits into
coding/session-cost-gatesfrom
coding/task-261-drop-conformance-gate
Sep 1, 2026
Merged

TASK-261: the ADR-004 conformance gate and perry-migrate come out — −10,907 lines#32
ranjiao merged 4 commits into
coding/session-cost-gatesfrom
coding/task-261-drop-conformance-gate

Conversation

@ranjiao

@ranjiao ranjiao commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Stacked on #31. Base is coding/session-cost-gates, which has not moved since
this 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-check to 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:

  • the ADR-004 gate.perry/conformance.jsonl held 23 records. All 23
    route: declare. All 23 files in this repository. Zero migrations, zero
    disagreements.
    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, and
    it has moved zero foreign projects. TASK-097 was not_started from the
    day it was filed.

The four commits

a4eb411 review.md § 1 bounded criteria + § 2 what V4 does not judge; perry-lint --reviews gains citation-not-on-branch and criteria-unbounded, and --strict now exits non-zero so a red exhibit can stop a dispatch
6ce1f5b the gate out — three call sites, not the two the plan named
436d0fb USER-910 answered A: migration goes too, and perry_schema.py with it
37e9af5 nine documents stop promising a gate and a migrator that do not exist

What survived on purpose

  • perry-task list --json's conformance.* payload — evidence_not_found,
    depends_on_unknown, blocked_by_closed_rows. Read-time integrity
    reporting, 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 called perry-migrate; only the "project
    that 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

  1. The gate had three call sites — perry_md_store § render --write was missed.
  2. bin/perry-conform was not 974 deletable lines: 598 were the ledger, ~280
    were generic helpers four tools imported. Gutted and renamed, then deleted
    outright when its last importer left.
  3. "Tier A precondition: none" was false — perry-migrate calls C.declare at
    14 sites, so conformance and migration were never two tiers.
  4. perry_schema.py was kept for perry-migrate. The consumer left.

Three things I broke and caught

  • Removing the perry-goals gate block took with project_lock(...) and
    result = COMMANDS[...] with it — the write path. Every write exited 1 on
    UnboundLocalError. Restored from HEAD.
  • The delete-list document's first draft was 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.
  • test_router_budget caught a 17-byte SKILL.md overrun. Two full-suite runs
    had 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/run3 modules red: test_diagnose, test_heading_title,
test_kr_progress_provenance. All three fail identically on a clean
git archive HEAD export 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 first
pass 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-261 done at V3 — the tests and mutations are mine, and
V4 needs a reviewer who did not write it. TASK-097, 223, 246, 248
dropped. USER-910 answered.

One thing to know before merging

Three files of yours that were uncommitted when this session started got swept
into 436d0fb by a git 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-identical
to 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

ranjiao and others added 4 commits August 31, 2026 20:27
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>
@ranjiao
ranjiao merged commit b0ebffb into coding/session-cost-gates Sep 1, 2026
1 check failed
@ranjiao
ranjiao deleted the coding/task-261-drop-conformance-gate branch September 1, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant