Skip to content

TASK-127 — diff each contract document against the payload it describes - #21

Merged
ranjiao merged 6 commits into
feat/work-modesfrom
coding/task-127-contract-key-parity
Aug 20, 2026
Merged

TASK-127 — diff each contract document against the payload it describes#21
ranjiao merged 6 commits into
feat/work-modesfrom
coding/task-127-contract-key-parity

Conversation

@ranjiao

@ranjiao ranjiao commented Aug 20, 2026

Copy link
Copy Markdown
Owner

What this is

KR-O2.4's metric is a count: contract-payload keys documented but not emitted, or emitted but not documented. Nothing in the repository computed it, so the KR could only be scored by hand.

tests/contract_key_parity.py computes it. For each page found by schema/*-contract.md it reads the jsonc payload sketch and the key tables, runs the invocation the page states in its own # heading, and diffs the two sets of field paths in both directions.

The denominator

The KR text says "all three contracts". The glob finds five.

Contract documented / emitted documented_not_emitted emitted_not_documented
perry-decide/list/1.0 27 / 24 0 0
perry-events/list/1.0 23 / 23 0 0
perry-goals/list/2.0 54 / 59 0 5
perry-roles/list/1.0 4 / 4 0 0
perry-task/list/1.11 87 / 99 0 12
total 0 17

Neither count is asserted to be zero. The baseline in tests/fixtures/contract-key-parity.json is what makes a change to either one visible. Closing the gap is whatever rows the measurement produces — this row measures it.

schema/README.md § The three read contracts carries the same three-vs-five error and a stale perry-goals/list/1.0 row (it is 2.0). Both are reported, not edited: the goals lane owns that text.

What could not be compared, by name

  • perry-task/list — the 6 fields of an intake row and the 8 fields of an ask. intake.rows and asks.items are empty in Perry's own state, so those collections offer no entry to compare against.
  • perry-roles/list — the 6 frozen card fields. Perry's own repo has no .perry/roles/, so cards is [].
  • perry-decide/listexpired_sunsets[].id, .title, .sunset. Same reason.

23 keys in total. They are printed by name with the reason, never dropped: a silently narrowed denominator is worse than a smaller one that is stated.

Verification — V3, by mutation

Mutation Result
removed startable from all four of its emit sites in bin/perry-task perry-task/list/1.11: keys the document declares and the tool does not emit changed / was: [] / now: ['tasks[].startable'] / appeared: ['tasks[].startable']
planted task["undeclared_key"] on the live list path ... keys the tool emits and the document does not declare changed ... appeared: ['tasks[].undeclared_key']
both reverted totals return to 0 / 17, module green
moved schema/roles-list-contract.md out 5 != 4 : the number of contract files changed — decide-list-contract.md, events-list-contract.md, goals-list-contract.md, task-list-contract.md
added a sixth page declaring one unemitted key, no edit to the check contract files discovered: 6 and perry-sixth/list/1.0 … documented_not_emitted: 1 / invented_key

Tests

  • python3 tests/parallel -j 4 — 64 modules, 1880 tests, 152s. test_diagnose red, and it was red on the same worktree before this branch (63 modules / 1867 tests / 144s at the cut): dangling: ['DESIGN-900', 'REL-00'], the TASK-126 cause. Not this row's.
  • bash tests/run — steps 1, 3 and 4 clean; step 2 carries the same test_diagnose red.
  • python3 bin/perry-lint — 0 errors, 3 pre-existing NS-01 warnings.
  • git diff --check — clean.

Out of scope, and untouched

No contract document's content edited, no contract version bumped, KR-O2.4's text not edited, tests/test_contract_invariance.py's baseline not touched. The one schema/README.md change is a new subsection saying where the baseline lives, as the spec's file scope permits.

🤖 Generated with Claude Code

ranjiao and others added 6 commits August 20, 2026 18:55
TASK-092's reviewer answered the question the row was dispatched on: byte
identity is real, not echoed, so the argument that overrode a locked
not-dispatchable verdict holds. It also found the repair path the tools
advertise leaves trailing whitespace, and that the tests cannot catch it —
TASK-122. And that the goals writer takes the file as truth rather than the
store, which means phase 002's P-O1.2 may not be met by that row alone —
TASK-123, where the direction is the user's to choose.

TASK-070 is dropped on the evidence it asked for. USER-004 is answered.
The queue is empty for the first time today.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The concurrency lesson was the wrong fix stated the right way: the cap counts
agents, tests/parallel counts 8 workers, and 3 x 8 on 14 cores is what stalled
two agents. Narrowing the workers rather than the agents keeps the third slot.

TASK-094 is the chain head — two P0s say in their own cells to stay open until
it lands, because four review rounds went into hardening a category ADR-007
decision 4 removes rather than fixes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The sweep in TASK-111 found one sibling with zero CI coverage
(tests/test_conformance.py:440) and left one shape uncovered on
purpose; both are now rows rather than notes in an evidence file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #20 lands the store-backed read for the fifteen task columns and the
KR tables. Verification item 1 asked for 0 call sites across all three
stores; OKR.md and .perry/config.md reach 0, BOARD.md does not — four
registers have no store. The row does not close on this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The remote merged TASK-113 (checks read live state) and TASK-115
(readonly guard coverage) at 10:51Z; the local branch never took them,
so three of their own target checks were red here and green on CI.
…ibes

KR-O2.4's metric is a count of contract-payload keys documented but not
emitted, or emitted but not documented, and nothing in the repository
computed it. `test_contract_invariance` records the payload's SHAPE against
a baseline, so an emitted-and-undocumented key passes it cleanly and a
documented-and-never-emitted key was never in its baseline to begin with;
the seven other modules that read a contract file each assert that one
named field appears in the prose. Seven spot checks are not a count.

`tests/contract_key_parity.py` reads each page's `jsonc` payload sketch and
its key tables, runs the invocation the page states in its own `# ` heading,
and diffs the two sets of field paths in both directions. Discovery is
`schema/*-contract.md` and the run prints how many files it matched — the KR
text says "all three contracts" and the glob finds FIVE. The per-contract
result is recorded in `tests/fixtures/contract-key-parity.json` so the number
is comparable by someone who was not here.

Measured today: 0 documented-not-emitted, 17 emitted-not-documented. Neither
is asserted to be zero. This row measures the gap; closing it is whatever
rows the measurement produces.

What could not be compared is named rather than dropped: 20 documented keys
across `perry-task/list` and `perry-roles/list` sit in collections Perry's
own state leaves empty (`intake.rows`, `asks.items`, `roles.cards`), and 3
more in `expired_sunsets`. A silently narrowed denominator is worse than a
smaller one that is stated.

Proved by mutation, both directions: removing `startable` from its emit sites
names `tasks[].startable` in `documented_not_emitted`; planting a key names
`tasks[].undeclared_key` in `emitted_not_documented`; moving one page out of
`schema/` takes the discovered count from 5 to 4; a sixth page declaring an
unemitted key is reported with no edit to the check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ranjiao
ranjiao merged commit efeafca into feat/work-modes Aug 20, 2026
1 check failed
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