fix(diagnose): LOAD-02 counted the record written to close its own row - #22
Merged
ranjiao merged 15 commits intoAug 20, 2026
Merged
Conversation
…gnature C1 (TASK-127) is dispatched. C3 (TASK-128) is a human gate and is in review with the exact edit written out. C4 (TASK-129) is blocked on it. TASK-126 is the reason test_diagnose is still red after the merge. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ao 2026-08-20) DESIGN-007 step 2. The third ownership change to the hand-off contract and the second to carry its own signature. - SKILL.md § The hand-off contract: one row, work's cell only. goals and decide are byte-identical across the edit. 20410 → 20457 of a 20480 cap. - schema/state-schema.json: role-card owner user → work, and the note that argued for `user` rewritten to record what the change cost rather than being deleted. - tests/test_ownership.py: the mapping the check demanded once the contract listed the path. It refused first, which is what it is for. - reference/hand-off-contract.md: the account, including the 23 bytes of router headroom left and the stale "Two changes" paragraph. Unblocks TASK-129 (Agent becomes a store). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TASK-133. One track added to .perry/config.md § Tracks: intake, mode queue. One file changed, zero state files rewritten (BOARD.md, tasks.jsonl and OKR.md byte-identical by md5). The result: a queue-track row carried a kr: edge to a project-mode KR with no friction at all. attribution.linked 4 → 5, no error, no warning. There is no mechanical gate anywhere — 'No objectives cascade' is prose in modes/queue.md and nothing else. Three defects found on the way, none of them the one under test: TASK-135 no existing row can be moved to a track, TASK-136 a track SLA is never measured against anything, TASK-137 a new queue row is born in the second stage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…r ran TASK-138, and it is why CI has been red on every PR. GNU coreutils `stat -f` is `--file-system`: it exits 0 and prints four lines about the filesystem. So `stat -f %m || stat -c %Y || echo 0` never reaches either fallback on Linux, mtime() returned a paragraph, and `$((now - last))` died with "syntax error in expression". Green on every mac, red on every CI run. Three scripts carried it. perry-update-check runs on EVERY Perry invocation, so on Linux the 7-day throttle has been reading a filesystem block as a timestamp all along, silently. The fix tries the GNU form first — BSD stat rejects `-c` outright, which `-f` does not — and accepts an answer only if it is all digits, because an exit code is not evidence that the output means what was asked for. Reproduced on this mac with a fake GNU stat on PATH, so the defect is catchable without a Linux box; reverting the fix reddens the new tests. A grep guard fails if the chain reappears anywhere in bin/. Also fixes two checks that read this repository's live state as their expected value, both reddened by declaring the first track (TASK-133): test_md_store asserted every config record is a `setting`, and test_track_attribution asserted Perry itself has no track register. Both now derive the expectation from the file, and the second proves its no-op property on a project that has no register rather than on this one. Adds DESIGN-008 (draft): Mode is three axes wearing one name. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
#1 went against the document's own draft. It had bolded three axes; what beat it is that the third one needs no axis to exist — Default rung is already an overridable column, and this repository overrode it on the intake track today without any axis machinery. Two axes, and the rung stays the plain field it is. #2 was the one genuinely contested slot and resolved to 'do not declare it at all': each spine value implies exactly one unit, so a declarable field could only ever hold a wrong value, and it would surface only when two presets are mixed — which is the case this design creates. #3, #4 and #6 are one decision in three places, all forced by goal 2: the preset name survives, a blank leg inherits from the preset, nothing migrates. #5 follows #3. Title, section 5.1's example, section 5.2's slot table, one risk row and section 8's second open question all moved as a consequence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ASK-139) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pre-flight clean: 10/10 required sections non-empty, 0 open decisions, 5 implementation steps. The advisory input-quality pass added section 5.4 (blast radius, nine surfaces) and a detection signal on every risk — the same two gaps DESIGN-003 closed at its own lock. One point was overridden rather than fixed and recorded as overridden: decision #5 is arguably agent-decidable. DESIGN-003 section 9 now points here, with the measurement that made this a revision rather than an opinion. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Step 1 only. Steps 2-5 are functions of its output and stay closed until it lands, so their acceptance criteria can be falsifiable rather than 'per step 1's table'. Dispatch pre-flight refuses on `design/` and this one is a genuine hit, not a false positive: the row's deliverable is an edit to a locked design document. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Local is ahead 10 / behind 13 with all four PRs merged on the remote — that is step 1 of the handoff, because the same divergence cost half of today. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The user asked whether TASK-037's blocked status was right. It was not, and it was not alone: both rows had every dependency closed, TASK-037 on TASK-092 and TASK-045 on the TASK-044 -> TASK-047 chain. bin/perry-task:4728 reads the stored status before blocked_by, so a stale 'blocked' can never be contradicted by the computed state — both rows carried blocked_by [] and startable False at once. Opened TASK-141. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The signal was already firing. next_action_cites_closed named exactly TASK-037 and TASK-045, and earlier the same day the PMO read that check as prose hygiene and rewrote the cells to silence it. A check that reports a pattern without its meaning gets suppressed by whoever reads it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
PR #14 (TASK-100) put both store files into claims[] at e3f8621. PR #15 (TASK-110) shipped test_state_cost asserting the pre-claim world: `perry/tasks.jsonl (unclaimed)` and `.perry/events.jsonl` rolling up under the `.perry/` row. Each was green on its own base; the merge was red on two tests neither PR could have seen. The behaviour never changed — only which row states it. The history assertion now reads the file's own row and its number is unchanged. The unclaimed-file test writes a file nothing will ever claim rather than borrowing one that happened to be unclaimed that week; it needed a commit too, because the tool reads git. Both proved non-vacuous by mutation: pointing the row back at `.perry/` reddens one, dropping the commit reddens the other. Closes TASK-100, TASK-111, TASK-127, TASK-133 at V3 — all four verified locally now that the merged code is here. TASK-094 stays open: its PR merged but verification item 1 is unmet. Opens TASK-143. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TASK-113 fixed the dangling-id check and measured `dangling: []`. Then it wrote the dispatch record that closes a row — and a record has to narrate what the check said, which put `REL-00` and `DESIGN-900` straight back in. Every live mention of both is in that one file. So the row could not be closed without writing a record, and writing the record reopened it. Adds a fourth structural mark, argued in the same block as the other three: in a document that REPORTS ON A CHECK, a mention of an id the project has ALREADY reported on is narration, not a lookup. The first three marks ask whether a LINE is a report; this one asks whether the ID has one, so it lives in `split_dangling` (two passes) rather than `report_lines`. Both halves are required and each keeps the other honest. Drop the document half and one report anywhere silences an id everywhere — burying a live reference would silence a true finding. Drop the id half and every document discussing a check exempts every id in it, which for this repository is `perry/evidence/**` under another name. Three new tests hold both halves and the anti-vacuity direction; no existing assertion was changed. Verified by re-run, not assertion: a temp project whose only content is `Blocked on ZZZ-404 until Friday.` still reports `dangling: ['ZZZ-404']`; an `evidence/`-shaped dispatch record that names LOAD-02 and a `test_` name but is genuinely blocked on two ids still reports `['QQQ-77', 'ZZZ-404']`. Neutralising the mark reddens both named tests plus two of the three new ones. `git diff -- perry/` is empty — no record was edited. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 20, 2026
TASK-121 — a check that reads live project state as its expected value is now found mechanically
#28
Open
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.
The defect
bin/perry-diagnose's dangling-id check (LOAD-02) reported['DESIGN-900', 'REL-00']on this repository, and every live mention of both ids was insideperry/evidence/2026-08/TASK-113-dispatch-2026-08-20-1813.md— the record written to close the row that fixed this exact check (lines 11, 16, 18, 35). Every other mention in the repository already classified correctly as a report.So the row could not be closed without writing a record, and writing the record reopened it. The defect is in the rule, not in the two ids, and no wording of a true account gets them out.
The fix — spec option 1, a fourth structural mark
Argued in the same block as the existing three, in the same voice:
The first three marks ask whether this line is a report; the fourth asks whether this id has one. That cannot be decided from one file, so it lives in
split_dangling(now two passes) rather thanreport_lines. The document half reusesnames_a_checkread over the whole file — no second vocabulary.Both halves are load-bearing and each keeps the other honest:
test_one_live_reference_is_enough_to_keep_an_id_dangling);perry/evidence/**under another name.Option 2 (a distinct fourth outcome) was rejected: it needs the identical classification work and would additionally require rewriting the assertion in
test_perrys_own_repository_reports_the_exemption_it_used. A wholesaleperry/evidence/**path exemption was considered and rejected — it fails the spec's second anti-vacuity direction outright.Verification (V3)
perry-diagnose --only=user_load --root .→dangling: [],dangling_in_reports: ['DESIGN-900', 'REL-00', 'ZZZ-404']. Before:dangling: ['DESIGN-900', 'REL-00'],dangling_in_reports: ['ZZZ-404']— exactly two ids moved, nothing else changed.Blocked on ZZZ-404 until Friday.→dangling: ['ZZZ-404']evidence/-shaped dispatch record that does nameLOAD-02and atest_name, and is genuinely blocked on two ids →dangling: ['QQQ-77', 'ZZZ-404']. A wholesale evidence exemption would have printed[]here.document_reports_on_a_checkreddens both named tests plus two of the three new ones (4 failures). Restored → green.python3 tests/parallel -j 4→ 65 modules · 1933 tests · all green (own baseline at13cfe2f: 65 modules · 1930 tests ·test_diagnosered, 2 failures).bash tests/rungreen.python3 bin/perry-lintexit 0 with the same 3 pre-existing NS-01 warnings as baseline.git diff --checkclean.git diff -- perry/empty.🤖 Generated with Claude Code