Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 16 additions & 13 deletions .perry/conformance.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,19 @@

| File | Shape version | Declared | Route |
|---|---|---|---|
| .perry/config.md | 2 | 2026-08-17 | declare |
| .perry/hook.md | 2 | 2026-08-17 | declare |
| BOARD.md | 2 | 2026-08-17 | declare |
| DECISIONS.md | 2 | 2026-08-17 | declare |
| OKR.md | 2 | 2026-08-17 | declare |
| design/DESIGN-001-resumable-pipelines.md | 2 | 2026-08-17 | declare |
| design/DESIGN-002-namespace-collision.md | 2 | 2026-08-17 | declare |
| design/DESIGN-003-work-modes.md | 2 | 2026-08-17 | declare |
| design/DESIGN-004-deterministic-writes.md | 2 | 2026-08-17 | declare |
| design/DESIGN-005-state-and-contracts.md | 2 | 2026-08-17 | declare |
| design/DESIGN-006-roles-and-knowledge.md | 2 | 2026-08-17 | declare |
| phase/001-linkage.md | 2 | 2026-08-17 | declare |
| phase/001-work-modes-live.md | 2 | 2026-08-17 | declare |
| .perry/config.md | 2 | 2026-08-20 | declare |
| .perry/hook.md | 2 | 2026-08-20 | declare |
| BOARD.md | 2 | 2026-08-20 | declare |
| DECISIONS.md | 2 | 2026-08-20 | declare |
| OKR.md | 2 | 2026-08-20 | declare |
| design/DESIGN-001-resumable-pipelines.md | 2 | 2026-08-20 | declare |
| design/DESIGN-002-namespace-collision.md | 2 | 2026-08-20 | declare |
| design/DESIGN-003-work-modes.md | 2 | 2026-08-20 | declare |
| design/DESIGN-004-deterministic-writes.md | 2 | 2026-08-20 | declare |
| design/DESIGN-005-state-and-contracts.md | 2 | 2026-08-20 | declare |
| design/DESIGN-006-roles-and-knowledge.md | 2 | 2026-08-20 | declare |
| design/DESIGN-007-the-entity-model.md | 2 | 2026-08-20 | declare |
| knowledge/toolchain/pycache-staleness.md | 2 | 2026-08-20 | declare |
| phase/001-linkage.md | 2 | 2026-08-20 | declare |
| phase/001-work-modes-live.md | 2 | 2026-08-20 | declare |
| phase/002-fields-are-typed.md | 2 | 2026-08-20 | declare |
6 changes: 6 additions & 0 deletions .perry/events.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,9 @@
{"ts": "2026-08-20T15:08:17", "event": "evidence", "id": "TASK-107", "title": "the dispatch safety gate matches its fragments as bare substrings, so ordinary English trips it", "track": "main", "actor": "agent", "from": "—", "to": "evidence/2026-08/TASK-107-spec.md"}
{"ts": "2026-08-20T15:13:45", "event": "add", "id": "TASK-108", "title": "LOAD-03 counts prose about a decision, so documenting an open question makes the count go up", "track": "main", "mode": "project", "priority": "P1", "actor": "agent", "summary": "", "depends_on": [], "from": null, "to": "not_started"}
{"ts": "2026-08-20T15:16:40", "event": "start", "id": "TASK-108", "title": "LOAD-03 counts prose about a decision, so documenting an open question makes the count go up", "track": "main", "actor": "agent", "from": "not_started", "to": "in_progress"}
{"ts": "2026-08-20T15:25:23", "event": "next", "id": "TASK-047", "title": "Flip the conformance gate to enforce", "track": "main", "actor": "agent", "from": "user decides the subjective line: enforce by default while both 2026-08-18 blockers are still live, or revert to advisory (one line). Then merge PR #8", "to": "PR #8 merged and the gate enforces on this branch; awaiting the user's V5 sign-off text (name, date, what was checked)"}
{"ts": "2026-08-20T15:26:29", "event": "start", "id": "TASK-085", "title": "Decision status has no word for a proposal, and lives in three places", "track": "main", "actor": "agent", "from": "not_started", "to": "in_progress"}
{"ts": "2026-08-20T15:41:45", "event": "done", "id": "TASK-047", "title": "Flip the conformance gate to enforce", "track": "main", "owner": "Coding Agent", "role": "", "actor": "agent", "from": "review", "to": "done", "evidence": "evidence/2026-08/TASK-047-dispatch-2026-08-20-1416.md", "rung": "V5"}
{"ts": "2026-08-20T15:47:04", "event": "evidence", "id": "TASK-108", "title": "LOAD-03 counts prose about a decision, so documenting an open question makes the count go up", "track": "main", "actor": "agent", "from": "—", "to": "evidence/2026-08/TASK-108-dispatch-2026-08-20-1547.md"}
{"ts": "2026-08-20T15:47:05", "event": "status", "id": "TASK-108", "title": "LOAD-03 counts prose about a decision, so documenting an open question makes the count go up", "track": "main", "actor": "agent", "depends_on": [], "from": "in_progress", "to": "review", "reason": ""}
{"ts": "2026-08-20T15:49:41", "event": "add", "id": "TASK-109", "title": "a V5 sign-off is composed by selection from measured facts, not authored from memory", "track": "main", "mode": "project", "priority": "P1", "actor": "agent", "summary": "", "depends_on": [], "from": null, "to": "not_started"}
37 changes: 36 additions & 1 deletion bin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,11 +236,46 @@ discovered in the field.
| | What it costs | What removes the cost |
|---|---|---|
| **1 · migration does not always reach zero on a real board** | On a `~/proj/gimegime-pmo` copy, `perry-migrate` takes `BOARD.md` from 3 errors to **1**, and the residue is a row reading `Status: 半解`. That file stays refused until a human edits it and runs `perry-conform declare BOARD.md`. The refusal names both commands, so it is a door that needs a hand — not a wall. | A path for the residue that is not a hand edit. The three classes seen were: a `Status` cell in the user's own words, a tier-1 file over its size cap, and a KR table whose columns are the project's. **Not** widening the enums — `半解` is a real distinction the user drew, and coercing it to `in_progress` is the confidently-wrong-value class. |
| **2 · a brand-new project asks for one declaration before its first write** | A project with **zero** lint errors is still `undeclared`, and undeclared is refused. `SKILL.md § Conformance gate` forbids an agent from running `perry-conform declare` on the user's behalf (`perry/OKR.md` — *adoption proposes; the user declares*), so the first `perry-task add` on a project Perry itself just wrote asks the user for one command. | Setup or adopt ending in the user's own declaration — one prompt, at the point where the files are created. That is a better first run than a refusal, but it is a convenience, not a road: the road already exists and the refusal names it. |
| **2 · every new file is born undeclared, in a new project and an old one alike** | A file with **zero** lint errors is still `undeclared`, and undeclared is refused. `SKILL.md § Conformance gate` forbids an agent from running `perry-conform declare` on the user's behalf (`perry/OKR.md` — *adoption proposes; the user declares*), so the first `perry-task add` on a project Perry itself just wrote asks the user for one command. **This is not confined to first runs** — see the measurement below. | Setup or adopt ending in the user's own declaration — one prompt, at the point where the files are created. That is a better first run than a refusal, but it is a convenience, not a road: the road already exists and the refusal names it. |

Both are checked by `tests/test_conformance.py § TestTheGateEnforces`, so the day
either becomes false a test says so rather than the paragraph going stale.

**Cost 2 was first written at the wrong scope, and the correction is the part
worth keeping.** It read *a brand-new project asks for one declaration*, which
is true and too narrow: the same thing happens to **every file Perry creates
after the last declaration, in a project that has been declared for weeks**.
Measured 2026-08-20 on a declared scratch project with the gate enforcing:

```
perry-decide bootstrap → wrote ['decisions/', 'DECISIONS.md']
perry-conform status → · DECISIONS.md undeclared
perry-decide new <slug> … → refused — DECISIONS.md already matches Perry's
shape at version 2, but no one has declared it
```

Two facts hold that together, and only both make it survivable:

- **Creation is not gated.** The file is written. A gate that refused creation
would leave a project unable to open a phase, a decision or a knowledge card
at all, which is not a door needing a hand — it is the wall this checklist
exists to avoid.
- **The next write to it is.** The refusal names `perry-conform declare` with
the exact path, so the road is one command, exactly as in row 1.

Concretely, in Perry's own repository on the day of the flip: `phase/002`,
`DESIGN-007` and one knowledge card were undeclared, because the last
declaration ran 2026-08-17 and all three were created on the 18th and 19th. None
of them was malformed. They were simply younger than the last time a human said
*yes, this is Perry's shape*.

This is a **consequence of the design, not a gap in it.** A writer that declared
its own output would be certifying its own work, which is the thing ADR-004's
*adoption proposes; the user declares* exists to prevent. Naming the real scope
does not argue for changing it — it argues that "one declaration at setup" is
the wrong mental model, and "a declaration each time the shape of your state
grows" is the right one.

**Going back is per project, not per release.** A project that wants the old
behaviour sets `- Conformance gate: advisory` in `.perry/config.md`; a single
command gets `PERRY_CONFORMANCE=advisory`. Both branches stay live and both stay
Expand Down
Loading
Loading