Skip to content

test(manifest): cover the safety contract's read/write mode resolution - #610

Merged
pawellisowski merged 1 commit into
mainfrom
routine/test-hygiene-2026-10-02
Oct 11, 2026
Merged

pawellisowski merged 1 commit into
mainfrom
routine/test-hygiene-2026-10-02

Conversation

@pawellisowski

@pawellisowski pawellisowski commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Read/write classification determines whether an app node must declare a safety: block. Add regression coverage for dotted write names, manifest and node mode precedence, and the published enum words used by manifests, catalogs and lockfiles.

  • Ground seven name-convention entries in shipped agent manifests and cover the eight suffixes named by the app spec plus four additional implemented suffixes.
  • Check exact trailing-name matching, effective-mode overrides, and both refusal and acceptance through the safety validator.
  • Check Lifecycle, Mode and AgentStatus wire words and parser round-trips using exhaustive match helpers. Replace a legacy test of a test-local enum with an unknown-status assertion against the production Agent type.
  • Consolidate repeated manifest loading into a helper with path-specific errors.

Updated against current main, preserving its mode-basis tests and the 27-command Tekla manifest assertion. The PR history is consolidated under the maintainer identity so the commit-author gate can pass. Comments describe current coverage rather than historical suite counts.

Scope and limits

Tests and documentation only; no runtime behavior or dependencies change. The spec tokenizer follows the current *.<suffix> spelling and contiguous bullet-list formatting. The enum walks require exhaustive match arms, but do not enumerate variants automatically: a new variant wired only through its own arms can remain unreachable. These limits are documented in the tests.

The nested-node safety defect reported as #611 has already been addressed on main by #612. This PR adds classification coverage; it does not claim to fix that defect.

Validation

Verified on 7d1d5a2dc:

  • cargo fmt --all -- --check: passed.
  • cargo clippy --locked --all-targets -- -D warnings: passed.
  • cargo test --locked: full local Windows suite passed, exit 0.
  • Commit-author and PR-body trailer gate: passed.
  • sync_stats.py --check: passed.
  • Real CLI smoke checks in an isolated AWARE_HOME, using the shipped Navisworks manifest: clash.export without safety is rejected by validation and installation; with safety it validates, installs and compiles, and the lock records mode: write. Compilation also records write classification for the unsafe preview without executing it.
  • GitHub CI: all jobs passed on this head.
  • Codex CLI reviewed the final refreshed tree and reported no actionable regressions. No code changes followed that review.

Review rounds: 1 (self-feeding: 0) — rounds: Codex CLI review of the operator-requested refresh; outcome: no actionable findings; operator: explicitly requested the refresh and aware-agent-tag workflow on 2026-10-11. Historical reviews remain recorded in the PR conversation.

Copy link
Copy Markdown
Contributor Author

@codex review


Generated by Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T19:43:09.096437Z 7d1d5a2 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 386f16795f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cli/src/manifest/agent.rs Outdated
/// written out in full rather than derived.
#[test]
fn each_enum_writes_back_the_word_its_own_parser_reads() {
for v in [Lifecycle::Start, Lifecycle::Stop, Lifecycle::Single] {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make the enum case lists exhaustiveness-checked

When a new Lifecycle, Mode, or AgentStatus variant is added, these array literals remain valid without including it, so an incorrect as_str implementation for the new variant will still leave this test green. This defeats the stated goal of preventing variants from slipping past the parser/formatter round-trip check; add a compile-time exhaustiveness mechanism or otherwise assert that the enumerated cases cover every variant.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Review gate status — Codex is rate-limited, falling back to the local panel

@codex review was requested at the top of this PR. Codex started a review on 386f167 and then, seven seconds later, posted:

You have reached your Codex usage limits for code reviews.

So no cross-model review ran on this PR. CLAUDE.md §"PR review — non-negotiable" makes Codex primary; this is the explicit-refusal case, not silence, and there is no Codex CLI in this unattended cloud environment to try the other route with. Polling for a verdict naming 386f167 continues for the remainder of the window in case the limit clears.

The fallback named by that same rule is running instead — the vendored pr-review-toolkit panel at .claude/agents/, dispatched in parallel and each briefed to refute this change rather than bless it, with the enumerated list being whether each added test would actually fail if the code under it broke, or asserts on its own fixture and passes vacuously and whether the deleted test was in fact the only thing covering a real behaviour:

  • code-reviewer
  • silent-failure-hunter
  • pr-test-analyzer
  • comment-analyzer (included because this change is comment-heavy)

type-design-analyzer is not being run: no type was added or changed — the diff is the test module plus one test helper. code-simplifier runs last, and per the routine its output is recorded here rather than acted on unattended.

That panel is same-model. Whatever it concludes, this PR will say so plainly: a maintainer reading it should not have to work out which reviewer let something through.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Correction: Codex did review — my previous comment was wrong

The usage-limit message was not the end of it. Codex posted a COMMENTED review on 386f16795f — my head commit — at poll 4 of the window, with one P2 inline finding. So the cross-model gate CLAUDE.md §"PR review — non-negotiable" requires did run on this PR, and my comment above saying "no cross-model review ran" is withdrawn.

The finding is correct and I am fixing it, not rejecting it:

P2 — Make the enum case lists exhaustiveness-checked. When a new Lifecycle, Mode, or AgentStatus variant is added, these array literals remain valid without including it, so an incorrect as_str implementation for the new variant will still leave this test green.

That is exactly right, and it lands on the one gap the test’s own doc comment admitted rather than closed ("The arms are exhaustive, so a new variant cannot slip past as_str — but it can slip past the lists below"). Admitting a hole is not covering it: a test whose stated job is to stop a variant desynchronising from its parser, and which a new variant can walk straight past, fails at that job for the next variant. Fix inbound, then @codex review again on the new head — an approval of 386f167 would not cover it.

The local panel is still running and its results will be recorded here too, but it is no longer the gate.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

  • Added an assert_round_trip! test macro that derives both the exercised cases and an exhaustive match from the same variant list. A newly introduced enum variant now causes a compile-time failure until its round-trip case is included. cli/src/manifest/agent.rsL1033-L1064
  • Applied the exhaustive check to every Lifecycle, Mode, and AgentStatus variant, addressing the review finding without changing production behavior. cli/src/manifest/agent.rsL1066-L1076
  • Committed the changes as 036c7413 (test(manifest): exhaustively check enum round trips) and created the follow-up pull request metadata.

Testing

  • ✅ cargo fmt --all -- --check
  • ✅ git diff --check
  • ✅ git status --short --branch — working tree clean after commit.
  • ⚠️ cargo test --bin aware manifest::agent::tests::each_enum_writes_back_the_word_its_own_parser_reads — dependency compilation was blocked because the environment lacks the system dbus-1 development package.
  • ⚠️ apt-get update -qq && apt-get install -y -qq libdbus-1-dev libsecret-1-dev pkg-config — package indexes were unavailable because the environment proxy returned HTTP 403 responses.

View task →

Copy link
Copy Markdown
Contributor Author

Codex's P2 is fixed in 08fefc34 — plus four panel findings

@codex review

(Posting this as a PR comment rather than as a reply on the review thread: an empty PENDING review from pawellisowski is sitting on this PR — review id 5387982345, not created deliberately — and GitHub allows only one pending review per user, so thread replies 422. Deleting it needs a permission this unattended session does not have. Harmless, but worth dismissing by hand; it is invisible to everyone but its author.)

The P2: enum case lists were not exhaustiveness-checked

When a new Lifecycle, Mode, or AgentStatus variant is added, these array literals remain valid without including it, so an incorrect as_str implementation for the new variant will still leave this test green.

Correct, and it landed on a hole the test's own doc comment had written down and not acted on — "a new variant cannot slip past as_str — but it can slip past the lists below". Admitting a hole is not covering it.

Measured the gap before fixing it, to be sure it was the one named: added Lifecycle::Resume with an as_str arm writing "resumed" — a word its own parser rejects — and the test stayed green (1 passed; 0 failed). The array literal was exactly as inert as described.

Now the variants are walked through exhaustive successor matches (next_lifecycle / next_mode / next_agent_status), so the same addition no longer compiles:

error[E0004]: non-exhaustive patterns: `manifest::agent::Lifecycle::Resume` not covered
    --> src/manifest/agent.rs:1119:15

Two things beyond the literal ask:

  1. The walk is length-checked against a table of published words. The compile error alone can be resolved by giving the new variant its own => None arm beside the existing terminator — satisfying the compiler and orphaning the variant from the walk. The length check makes that red. The residual (doing exactly that and padding the table) is named in the doc comment rather than hidden.
  2. A neighbouring hole the finding did not name. Serde is an independent oracle for a one-sided typo, but a rename that moves the serde word and the as_str arm together round-trips perfectly and still breaks every consumer that matches the literal (commands::agent:2322, registry::catalog:111). Renaming AgentStatus::Available to "ready" on both sides left the module green. The published words are pinned as literals in the same table now; all three coordinated renames I tried go red.

Four panel findings, same commit

# Finding Fix
F1 every_suffix_the_app_spec_publishes_is_inferred_as_a_write read one line of app-spec.md and guarded with a count floor. Two reachable silent passes: suffixes published on a reflowed continuation line were skipped entirely; and swapping *.insert for *.export kept the count at 8 while .insert lost its only coverage in the repo — then dropping ".insert" from SUFFIXES left the whole suite green reads the whole bullet; asserts the set; extraction failure reported separately from contract drift
F2 .save / .publish pinned by nothing the_suffixes_the_code_adds_beyond_the_spec_are_pinned_too
F3 no added test went through validate_app_safety, which has four early continues before effective_mode — the resolver could be right while the gate stopped asking safety_check_demands_a_block_from_a_dotted_write_command_with_no_declared_mode, on an un-annotated revision.bump
F4 two doc comments asserted falsehoods about the suite corrected — see below

F4 is the one I am least comfortable about, because CLAUDE.md's "ground every load-bearing claim in a citable artifact" applies to a comment as much as to a reply, and I shipped two that did not survive a grep:

  • "overridden … which no test reached" — false. app_lock::tests::compile_honors_mode_read_on_mode_overridable_command reaches it end to end; the note it asserts is emitted only under if resolved.overridden.
  • "Nothing checked that the hand-written word is the word the derive accepts" — false for six of the eight rows. Both Mode words via app_lock's lock strings, all three AgentStatus words via registry::catalog's status_str, Lifecycle::Single via the catalog JSON golden. Only Lifecycle::Start / Stop were unpinned.
  • The AgentStatus consumer list named commands::app:1288, which matches an unrelated RunEvent::RunEnd status.

Both now state what was already covered and what is actually new.

Mutation ledger for this commit

Every fix proven under a mutation that was green before it and red after: the Lifecycle::Resume compile gate; three coordinated renames (Available→"ready", Mode::Read→"r", Lifecycle::Start→"begin") and one one-sided (Planned→"available"); the continuation-line scenario; the *.insert→*.export swap; a backtick-stripped bullet; .insert dropped from code while the spec names it; .save and .publish dropped; .bump dropped reaching the gate test end to end; validate_app_safety short-circuited to never ask; an emptied grounded-table row slice; and mode-overridable misspelled in a fixture.

Gates on 08fefc34, pinned toolchain 1.95.0: cargo fmt --all -- --check pass, cargo clippy --all-targets -- -D warnings pass, cargo test 65 binaries, 2090 passed, 0 failed, 1 ignored.

And the thing worth more than this PR

Reviewing the gate these tests feed turned up a live safety hole in shipped v0.146.0, filed as #611: validate_app_safety does not recurse into for-each do: bodies, so wrapping a write-mode node in for-each: '[1]' skips the safety: requirement entirely. Reproduced end to end — tekla.close refused at top level, accepted inside a loop, and compile then stamps it mode: write into the lock, so the compiler knows and the gate never asked. 10-core/app-spec.md:462 promises the opposite ("anywhere in the tree"), and the sibling validate_app_agents does recurse.

Filed rather than fixed here: this PR is test-only by remit, and that is a production change to a safety guard, which wants a human.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor Author

Leaving this open for a human — the gate is not satisfiable on the final commit

Final head is f1400aa7. All gates green there: cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, cargo test → 65 binaries, 2090 passed, 0 failed, 1 ignored, on the pinned 1.95.0 toolchain with CI's own apt deps.

No cross-model review covers this commit. Codex reviewed 386f167 and raised one P2, which is fixed — but the re-request on 08fefc34 came back You have reached your Codex usage limits for code reviews, and so did a later one. CLAUDE.md §Git workflow's self-merge carve-out requires Codex to have read the final commit with nothing outstanding; it has read neither 08fefc34 nor f1400aa7. The routine's narrower without-Codex path needs a same-model panel clean on the final commit, and that is not where this ended up either:

  • Round 1 (on 386f167): four reviewers, five substantive findings, all fixed.
  • Round 2 (on 08fefc34): re-dispatched — and before those reports landed I found another false claim in my own doc comment, fixed in f1400aa7. So the panel's one permitted re-run does not cover the head either.

The routine's limit is explicit: fix and re-run the panel once; if it is still not clean, stop and leave the PR open. That is the honest reading of where this is, so it stays open.

What I got wrong, since it is the most useful thing for you to know

One docstring took three attempts. It claimed first that nothing checked the as_str words against the derive, then that six of eight rows were already pinned. Both wrong, both in the same direction — understating existing coverage in a way that flattered the new test. The third version is measured, not reasoned:

status
one-sided (as_str lies, derive unchanged) already caught — Lifecycle::Start => "begin" alone reddens manifest::expose's two synthesis tests, which re-parse what synthesize_agent_manifest wrote and assert the variant back
coordinated (serde word + as_str arm renamed together — round-trips perfectly) already caught, widely — 36 tests outside the module for Lifecycle::Start, 29 for Mode::Read, because fixtures and shipped manifests spell these words literally and stop parsing
a newly added variant escaped everything — no fixture mentions a word that does not exist yet

So exactly one thing in that test is new coverage: the exhaustiveness gate. Which is precisely what Codex's P2 asked for, and nothing more. I would rather the comment say that than oversell.

That pattern — a justification written into the tree that does not survive a grep — is the thing worth your eye on the rest of this diff. The tests themselves held up under every mutation I and four reviewers could construct (see the two ledgers in the PR body); it was the prose that kept being wrong.

Also worth your time, independent of this PR

#611 — a live safety hole in shipped v0.146.0, found by reviewing the gate these tests feed. validate_app_safety does not recurse into for-each do: bodies, so wrapping a write-mode node in for-each: '[1]' skips the safety: requirement entirely. tekla.close is refused at top level and accepted inside a loop; compile then stamps it mode: write into the lock, so the compiler knows and the gate never asked. 10-core/app-spec.md:462 promises the opposite. Not fixed here — production change to a safety guard.

I think #611 matters more than this PR does.

Housekeeping

An empty PENDING review from pawellisowski (id 5387982345) sits on this PR and blocks review-thread replies, which is why my round-2 responses are PR comments. It needs dismissing by hand; deleting it required a permission this unattended session does not have.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Comment audit found five false claims — corrected in e4ee7832

The comment-analyzer pass on 08fefc34 came back with five factual errors in the prose, three of them introduced by the very commit meant to close the review findings. I re-verified each by mutation before fixing it. No test logic changed; gates still green (65 binaries, 2090 passed, 0 failed).

# Claim Verdict Correct fact
1 with ".bump" deleted, cargo test -- --skip manifest::agent::tests is 2070 passed, 0 failed FALSE on its own tree 1589 passed, 1 failed. The skip does not exclude validate.rs's safety_check_demands_a_block_from_a_dotted_write_command_with_no_declared_mode — the gate test this same PR adds, built on revision.bump. A recipe presented as reproducible that does not reproduce. Replaced with the correctly-scoped wording the validate.rs docstring already had; the bare total is gone, since it rots on every test added to the crate
2 Lifecycle::Start/Stop were the unpinned rows; Lifecycle::Single pinned via the catalog JSON golden; "six of eight" FALSE, inverted Seven of eight were already pinned. Start and Stop by manifest::expose, which emits the word through as_str (expose.rs:101) and asserts the variant back after re-parsing (:289, :507, :508). Lifecycle::Single was the one genuinely unpinned row — changing it to "start" leaves 1590 passed / 0 failed outside the module. The catalog golden credited with it is parse input into a String field and never reaches Lifecycle::as_str
3 "Three of the four rows were already covered end-to-end" FALSE Two. (fixed, Some(Read)) had no prior coverage through effective_mode either — the validator's conflict check compares against mode_of (validate.rs:1381). That row is newly pinned too, not the pure defence-in-depth I called it
4 validate.rs: "every other safety test declares an explicit mode:" FALSE (minor) Omits safety_check_skips_read_mode_nodes, the carve-out the agent.rs header states correctly. Added
5 variants blamed std::iter::successors; the extraction comment described an indentation test FALSE (minor) The walk is a hand-written while let (same non-termination property, different name); trim_start runs first, so continuation lines are captured however indented — over-capture, which fails loudly

Also corrected: the grounded table claimed a live safety consequence for all seven rows, but tekla save-attributes is status: planned — no valid app reaches it today, so the consequence does not yet bite for that row. Caveat stated rather than the row dropped; it is still the only shipped command grounding the save-attributes legacy arm.

Two pre-existing rots in the same file, fixed while it was open — the second is the more interesting one:

  • mode_of's own production doc comment listed ten of the twelve suffixes, omitting *.export-pdfs and *.export. A reader trusting it would have concluded navisworks clash.export is a read. That is precisely the misreading these tests exist to prevent, sitting in the doc comment of the function under test.
  • "All 3 commands declare category: curated" sat two lines above its own "(26 total)".

The honest shape of this PR

The code has held up: every added test is non-vacuous, ~35 mutations across three rounds all caught, two independent reviewers failed to break it. The prose has been wrong four times in the same place. That docstring's pin accounting took four attempts, and three of the four errors were in the direction of flattering the new test. The current version states only what was measured, with the measurement commands named.

That pattern is my recommendation for where a human reviewer spends their attention on this diff: not the assertions, the justifications.

Still: no cross-model review covers this head. Codex reviewed 386f167, raised the P2 that started all of this, and has been rate-limited since. e4ee7832 is three commits past what it read. The PR stays open for a human — unchanged from the decision above, and now with one more reason.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Audit tail closed in 6279012c — that comment pass is done

The same audit was re-delivered; its five substantive findings were already fixed in e4ee7832. What was left were precision items, now closed. Prose only, gates unchanged (65 binaries, 2090 passed, 0 failed).

Three citations pointed a few lines off what the prose around them claimed — each re-verified against the file rather than taken on report:

Cited Actually What the sentence described
app_lock:570 :573 the mode recorded in the .lock (:570 is the compile note beside it)
registry::catalog:216 :256 / :264 where the word enters the published catalog (:216 is status_str's signature)
registry::catalog:111 :115 the site that compares the literal (:111 is the "available" serde default)

Two claims were more certain than the code supports, and in both cases the honest version was already written elsewhere in the same file — which is its own small lesson:

  • "the walk cannot miss it" sat 55 lines above variants's own "one residual hole, named rather than hidden", which correctly notes a new variant can be given its own => None arm, satisfying the compiler while orphaning itself from the walk. Now states what the gate does and does not guarantee.
  • "the spec and the code have to name the same things" ignores that app-spec.md:806 introduces its list with "e.g.". The four entries the code carries beyond the spec are therefore not a divergence — they are the open contract question the_suffixes_the_code_adds_beyond_the_spec_are_pinned_too documents. The real divergence is the spec naming a suffix the code does not match, or dropping one it still does, which is what the assertions actually check.

Tally, for whoever reviews this

Four rounds of comment correction on a test-only diff. The assertions were never the problem — ~35 mutations across three rounds, four reviewers, nothing broke a test. Every single defect was in the prose, and the recurring failure was overstating how little existing coverage there was, which flattered the new tests. Worth knowing before reading the docstrings: they are now accurate, but they got there by being wrong four times, and the measurements they quote are the ones to spot-check if you want to verify anything.

Head is 6279012c. Still open for a human: Codex reviewed 386f167, raised the P2 that began this, and has been rate-limited since — so no cross-model review covers this head or the four commits before it.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Round-2 panel: one reachable silent pass, closed in be930e49

All four reviewers reported. Three found only prose issues (fixed in e4ee7832 / 6279012c); the silent-failure-hunter found a real silent pass in the one test whose stated job is preventing them, and it is reachable by an ordinary edit to app-spec.md. I reproduced it before fixing it.

The defect

every_suffix_the_app_spec_publishes_is_inferred_as_a_write read one bullet, with a take_while stopping at the next -, a blank line or a fence. The safety contract is written as a two-bullet "if either:" list, so the natural way to extend it is a third bullet — which was invisible. Adding

- the command destroys data (`*.purge`, `*.truncate`).

to the rule leaves all 2090 tests green while the published contract promises *.purge implies a write, is_write_by_convention resolves it Read, and aware app validate accepts a node calling model.purge with no safety: block. A nested sub-list, a blank-line continuation paragraph, and a fenced block in the bullet all did the same.

What makes it a defect rather than a limitation: the comment claimed the sub-list case was handled. That was true only for a whole move of the list, which yields nothing and trips the non-empty guard. An addition yields a non-empty subset and every check passed. A guarantee asserted in a comment that the code does not deliver — the exact failure mode this PR has been chasing, in the test built to catch it.

A second, milder one: the marker search took the first matching line anywhere in the file, so a stale recap of the rule higher up would win over the normative bullet, and the spec could withdraw a suffix with this test still reading the old wording.

The fix

Scope to ## Safety contract, scan the whole ### The rule region. All seven scenarios the reviewers built now go red — including the two that were green:

scenario before after
blank line then an indented continuation paragraph adding suffixes GREEN RED
fenced block in the rule, then more suffixes GREEN RED
a third bullet extending the rule GREEN RED
the eight moved into a nested sub-list RED RED
soft-wrap reflow (round-1 finding) RED RED
*.insert swapped for *.export, count stays 8 RED RED
stale recap earlier in the file, rule narrowed GREEN RED

The cost is over-capture: the region also holds the "Pure-read nodes" sentence, so a reword putting a *.-token on a read line makes this test red. That is the right way round — it fails loudly, naming the token.

Also corrected, each measured first

  • The gate test's rationale was wrong about why it earns its place. It cited the four early continues in validate_app_safety; each is already caught by two to five pre-existing safety tests. What nothing else catches is a normalising refactor at the gate — cmd_name.rsplit('.').next() — which kills all twelve dotted entries while leaving the two bare legacy names working. Measured: 1608 passed, 1 failed, this test alone in the suite. The docstring now says that.
  • The grounded table now leads with what it is uniquely load-bearing for. One reviewer reversed its own round-1 position here, with the measurement: as a SUFFIXES pin the table is fully redundant (zero of seven rows is a sole catcher), but it is the sole catcher across all 2090 tests of a shipped command drifting out of the convention — rename revision.bump to revision.increment and a live Revit write becomes a read. No fixture test can see that, because what changed is content, not code. I verified both halves.
  • "The five revit-2026 / navisworks / autocad-2026 rows" — six, contradicting the assertion two lines below it. Found by the fourth reviewer; introduced by the commit whose purpose was fixing false claims.
  • variants documented one residual hole; there are two. Collapsing the arms to _ => None removes the gate silently, and no match can forbid a wildcard in the match doing the forbidding.
  • The negative loop's six rows shared one message naming neither the arm nor the consequence; each now carries the loosening it guards against.

Where this leaves it

Head be930e49. Gates green: fmt, clippy -D warnings, 65 binaries / 2090 passed / 0 failed.

The panel's own summary of the diff, which I think is fair: it takes the dotted convention from 0 of 12 suffixes pinned anywhere to 12 of 12, the e2e gate test has verified teeth, and the deletion was provably vacuous. Against that, six rounds of correction — one real silent pass and the rest prose, with the recurring error being overstatement in the direction that flattered the new tests.

Still open for a human, and the case is now stronger. Codex reviewed 386f167; the head is five commits past that and Codex has been rate-limited throughout. One reviewer put it exactly right: an approval naming 08fefc34 would not cover commits that rewrote the very comments under review.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

@codex review

Re-requesting on the current head be930e49. Earlier attempts returned a usage-limit message; per this repo's review rule a single rate-limit is not treated as permanent, so this is a retry rather than a duplicate.

The head is four commits past 386f167, the only commit you have reviewed. Your P2 on that commit (make the enum case lists exhaustiveness-checked) is addressed. Since then the branch has taken one substantive fix and three rounds of comment correction:

  • be930e49 closes a reachable silent pass in every_suffix_the_app_spec_publishes_is_inferred_as_a_write: it read one markdown bullet, so extending the spec rule with a third bullet (- the command destroys data (*.purge, *.truncate).) left all 2090 tests green while the published contract promised *.purge implies a write and the code resolved it Read. Now scoped to ## Safety contract and scanning the whole ### The rule region.
  • 6279012c, e4ee7832, f1400aa7 correct false claims in the test documentation — five of them, including an inverted account of which enum variants were already pinned.

Worth your attention most: whether the widened extraction over-captures (the region also holds the "Pure-read nodes" sentence), and whether the measurements quoted in the docstrings hold — those claims have been wrong before in this very PR.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor Author

@codex review

Retrying on head be930e49 from a later sweep run. Every attempt in the previous run came back You have reached your Codex usage limits for code reviews (most recently 05:04Z); this repo's review rule treats a rate-limit as transient rather than permanent, so this is a fresh retry, not a duplicate request.

The only commit you have reviewed is 386f167. Your P2 there — make the enum case lists exhaustiveness-checked — is addressed via exhaustive successor matches plus a length-checked table of published words. Four commits have landed since: one substantive (be930e49, closing a reachable silent pass in the spec-parity extraction) and three correcting false claims in the test prose.

Most worth attacking:

  • every_suffix_the_app_spec_publishes_is_inferred_as_a_write now scans the whole ### The rule region instead of one bullet. Does the widened extraction over-capture? The region also holds the "Pure-read nodes" sentence, so a reword placing a *.-token on a read line would redden it.
  • The measurements quoted in the docstrings. Those claims have been wrong four times in this PR, each time in the direction that flattered the new tests.

Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor Author

@codex review

Head is now 1c0b245b. Codex's last read here was 386f167, and every re-request since has been refused for quota, so no cross-model review covers this head. The routine/test-hygiene-* carve-out permits a merge only on a clean local panel, so one ran on be930e49. This comment records what it found and what was done.

Fixed in 1c0b245b — each reproduced by mutation first, red after

Finding (reviewer) Fix
A sibling #### subsection naming *.purge was invisible: the region stopped at the first ####, suite stayed green (silent-failure-hunter, pr-test-analyzer) region now runs to the next ##
A withdrawal note ("*.insert was withdrawn") kept *.insert counted as published (code-reviewer) tokens split into the normative bullet list vs everything else; a token outside the list fails with its own message
A *.list read example failed with "the spec promises .list implies a write", the opposite of the spec (code-reviewer, pr-test-analyzer) same split, accurate message
A bare, un-backticked *.purge added to the bullet left the suite green (comment-analyzer, O1) tokens are now found with or without backticks
Grounded table asserted a count of 7, which a duplicate-entry swap preserves (silent-failure-hunter) asserts the set of convention entries; rename and mode-annotation messages say what to do
effective_mode truth table lacked the reachable row (non-overridable, node restates write); a spurious overridden: true stayed green (pr-test-analyzer) row added
Enum walk started from a hardcoded first, so a variant added ahead of it was never visited (code-reviewer, silent-failure-hunter) also walked backward from last; the two walks must agree. The "two residual holes" comment is corrected
validate.rs claimed each early continue is caught by "two to five" tests (code-reviewer, pr-test-analyzer, comment-analyzer) Measured instead: only frozen has a test; flipping each of the other three to push an error leaves the suite green. Comment now says so

Gates on 1c0b245b, pinned 1.95.0: cargo fmt --all -- --check 0, cargo clippy --all-targets -- -D warnings 0, cargo test 0 (65 binaries, 2090 passed, 0 failed).

Recorded, deliberately not acted on

comment-analyzer's remaining output is clarity and prose, which the routine says not to rewrite unattended. For a human reviewer, the factual items:

  • agent.rs:823 says "five" revit-2026/navisworks/autocad-2026 rows; the table has six.
  • agent.rs:1128 says nothing else in the file would notice a mode_of that always answers Read; four other tests do.
  • agent.rs:1330 and :1383 say "the one escape" where variants documents two.
  • app_lock:570 should be :568 (note body 566-569).
  • The ("delete.thing", …) row label names a loosening it does not guard against.
  • Bare totals (2090, 1608, 36, 29) and about 20 cross-file line citations will go stale on unrelated edits.

Not in this PR

validate_app_safety does not recurse into for-each do: bodies. That is #611, fixed by #612. Two reviewers independently re-found it.

Housekeeping

An empty PENDING review from pawellisowski (id 5387982345) sits on this PR; deleting it needs a permission this unattended session does not have. It is invisible to everyone but its author.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor Author

🔴 CI red on 1c0b245b — needs a force-push I am not authorized to make

no Claude co-author trailers fails on the current head. I reproduced it locally against the real PR range:

$ python3 scripts/no-claude-coauthor-trailers.py --range origin/main..origin/routine/test-hygiene-2026-10-02
error: CLAUDE.md §Git workflow forbids `Co-Authored-By: Claude ...` trailers, and these
would reach `main` in the squash commit:

  commit 1c0b245b (test(manifest): close the silent passes the local review panel measured):
    author: Claude <noreply@anthropic.com> — the message is clean, but GitHub generates
    the trailer from this identity when it squashes

The commit message is clean. The failure is the author identity: Claude <noreply@anthropic.com>. On squash, GitHub synthesises a Co-authored-by: trailer for every commit author other than the account clicking merge — so merging as-is lands the forbidden trailer on main, where §Git workflow's append-only rule makes it permanently unfixable. This is the exact route the script's own header documents as having broken the rule five times after the gate went in (#397, #396, #398, #399, #408).

A revert will not clear it. The gate checks the author identity of every commit in the PR range, so 1c0b245b stays flagged however many commits land on top.

The fix, from the gate's own output

git config user.name  "Pawel Lisowski"
git config user.email "pawellisowski@o2.pl"
git rebase be930e49 --exec 'git commit --amend --no-edit --reset-author'
git push --force-with-lease origin routine/test-hygiene-2026-10-02

Set the identity first — an amend re-reads the config, so restamping before fixing it rewrites Claude onto Claude.

Why I have not done it

Two reasons, both worth a human's call:

  1. CLAUDE.md §Git workflow: force-pushing requires explicit approval each time. Committing is pre-approved; this is not. I have no approval for a history rewrite.
  2. Another session is concurrently writing this branch. 1c0b245b was pushed by a later sweep run about 90 minutes after my be930e49. Rewriting under a live writer risks it pushing the flagged commit straight back, and --force-with-lease protects the ref, not the other session's working tree.

The session that authored 1c0b245b is best placed to restamp it, and may do so on its own next wake.

For the record: the commit itself is good work and should be kept, not dropped

It is not noise — it found real silent passes in my be930e49 fix:

  • the spec-parity region stopped at the first ####, so a sibling subsection naming *.purge was invisible — my widened scan was still too narrow;
  • tokens were not classified, so a read example produced the message "the spec promises .list implies a write", the opposite of what the spec says;
  • the grounded table asserted a count of seven, which a duplicate-entry swap preserves — it now asserts the set;
  • the enum walk started from a hardcoded first, so a variant added ahead of it was never visited.

And one correction I owe directly: it measured my validate.rs claim that each early continue is caught by "two to five" pre-existing tests and found that only frozen has one — flipping the other three leaves the suite green. I took that figure from a reviewer's report and verified the other claim in the same paragraph (the leaf-normalisation sole-catcher result) by mutation, but not this one. Writing an unverified measurement into the tree is the same failure this PR has been correcting six times over, and I repeated it.

So: restamp the author, keep the content.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

@codex review

Head is now 698d8aae. Round 2 of the local panel (run on 1c0b245b) found five more things; each was reproduced by mutation and is fixed here:

  • The validate.rs comment I wrote in round 1 about the four early continues was wrong for two of the three it called unexercised. I had measured with --bin aware, which skips cli/tests/. Re-measured across the full suite: the no-agent/command and agent-not-installed branches are caught by integration tests, and only the command-not-in-manifest branch is caught by nothing. Comment corrected.
  • A grounded-table row re-pointed at another command, keeping its old label, left the entry set unchanged; each row's name must now rest on its label.
  • The effective_mode override that raises an overridable read-manifest command to write was reached by no test; row added.
  • The spec-parity tokenizer cut a non-ASCII suffix to nothing and a multi-dot suffix at its second dot; both are now read whole, and the failure message states the list-termination rule.
  • The enum-walk doc comment overclaimed. It now states what the forward/backward walk does not catch: a head or tail variant wired through the compiler-forced arms only. That needs enumeration (a derive), a production dependency change outside a test-only PR.

Named as residual in the comments, not fixed: a write suffix spelled without the leading *.

Gates on 698d8aae, pinned 1.95.0: fmt 0, clippy -D warnings 0, cargo test 0 (65 binaries, 2090 passed, 0 failed).

Still no cross-model review of this head: Codex last read 386f167 and has refused every re-request since for quota. A final local panel is running on this head, and its outcome decides whether this PR stays open.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Copy link
Copy Markdown
Contributor Author

Addendum to the above: it is now two commits, and it will keep recurring

Not a repeat of the blocker already reported — the shape changed. 698d8aae landed with the same misconfigured identity, so the gate now flags two:

698d8aae  Claude <noreply@anthropic.com>  | test(manifest): second round of measured fixes…
1c0b245b  Claude <noreply@anthropic.com>  | test(manifest): close the silent passes…
be930e49  Pawel Lisowski <pawellisowski@o2.pl>  ✓

To whichever session is pushing these: your clone's git identity is Claude <noreply@anthropic.com>. Every commit you make under it fails no Claude co-author trailers and would carry an unremovable trailer onto append-only main. Fix it before your next commit, or offender #3 follows:

git config user.name  "Pawel Lisowski"
git config user.email "pawellisowski@o2.pl"

That is the preventive half and it needs no force-push. Restamping the two already committed does, and the remedy in my previous comment is unchanged and still correct for both — git rebase be930e49 --exec 'git commit --amend --no-edit --reset-author' picks up 1c0b245b and 698d8aae together. I have not run it: §Git workflow requires explicit approval for each force-push and I have none, and rewriting under a live concurrent writer is how the flagged commits come straight back.

Everything else on 698d8aae is green or still running; this check is the only failure. I am not re-running it — a re-run cannot change what the commits are.


Generated by Claude Code

Copy link
Copy Markdown
Contributor Author

Sweeper stopping here: left open, and I broke one check on this PR

The check I broke. no Claude co-author trailers is red on 698d8aae. The two commits I pushed (1c0b245b, 698d8aae) are authored by Claude <noreply@anthropic.com>, because the sandbox's git identity was that and I did not override it. The earlier commits on this branch are authored by Pawel Lisowski. That gate fails any PR with a Claude-authored commit, and a squash merge would also synthesise a Co-authored-by: Claude trailer on main, which CLAUDE.md forbids.

I cannot repair it: the fix is to rewrite those two commits, which needs a force-push, and the routine bars that. A new commit does not help, since the gate checks every commit in the range. For a human, either of:

  • git rebase be930e49 --exec 'git commit --amend --no-edit --reset-author' on routine/test-hygiene-2026-10-02 under the maintainer's identity, then git push --force-with-lease; or
  • cherry-pick the diff be930e49..698d8aae onto be930e49 as one commit under the maintainer's identity.

The tree at 698d8aae is what the gates were run on: fmt, clippy -D warnings and cargo test all exit 0 (65 binaries, 2090 passed, 0 failed), and every other CI check on that head is green.

Codex. It last read 386f167 and has refused every re-request since for quota (latest 07:37Z). No cross-model review covers this head.

Final local panel on 698d8aae. The code-reviewer found no defect. The mutation hunter found no new silent pass or false alarm: the label check, every effective_mode mutation and the tokenizer edge cases all went red where they should. The test analyzer reported, and I did not fix, because the two permitted fix rounds are spent:

  • the validate.rs comment lists app_install.rs and app_run.rs as the integration tests catching two early continues, but tests/app_validate.rs catches them too;
  • **bold**.word or a glob like snapshots/*.zip anywhere in the ## Safety contract section would raise a misleading "spec promises" failure;
  • stale prose near the delta: "both fixtures" (now three), the table-shape doc on the effective_mode test, and the earlier "five rows" (six);
  • 11 of 18 effective_mode cells are uncovered, among them overridable with no manifest mode: and no node mode:, which resolves by name convention to Read for exec, against the spec's "safe default is write".

Same-model panel only, so per the carve-out this PR does not self-merge.


Generated by Claude Code

@pawellisowski
pawellisowski force-pushed the routine/test-hygiene-2026-10-02 branch from 698d8aa to 7d1d5a2 Compare October 11, 2026 19:30

Copy link
Copy Markdown
Contributor Author

@codex review

Head is now 7d1d5a2d — a single commit, rebased onto current main, cleanly authored, all 15 checks green. Requesting again because nine days have passed since the last attempt: every try on 2026-10-02 came back with a usage-limit message, and this repo's review rule treats a rate-limit as temporary rather than as an absent reviewer.

No review covers this commit. The only review on record is yours from 2026-10-02 against 386f1679, four rewrites ago; the branch has since been squashed from eight commits to one.

Your P2 on 386f1679 — make the enum case lists exhaustiveness-checked — is addressed via a successor-chain walk whose match arms are exhaustive, so adding a variant stops the module compiling until it is wired in.

What changed since you last looked, and where I would spend your attention:

  • A reachable silent pass was found and fixed in every_suffix_the_app_spec_publishes_is_inferred_as_a_write. It read one markdown bullet, so extending 10-core/app-spec.md's write-mode rule with a third bullet left the suite green while the published contract promised *.purge implies a write and is_write_by_convention resolved it Read. The scan now covers the whole ### The rule region, scoped to ## Safety contract. Worth checking whether that widening over-captures: the region also holds the "Pure-read nodes" sentence.
  • Several rounds corrected false claims in the test documentation — among them an inverted account of which enum variants were already pinned, and a reproduction recipe that did not reproduce on its own tree. The docstrings quote specific measurements; those have been wrong before in this PR, so they are the part least worth taking on trust.
  • cli/src/validate.rs gains one end-to-end gate test asserting that validate_app_safety demands a safety: block from a dotted write command with no declared mode:.

Separately, and not fixed here: #611 is a live safety hole in shipped v0.146.0 — validate_app_safety does not recurse into for-each do: bodies, so a write-mode node wrapped in a loop skips the safety: requirement that app-spec.md promises applies anywhere in the tree.


Generated by Claude Code

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 7d1d5a2dca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link
Copy Markdown
Contributor Author

✅ Gate satisfied — ready to merge, but my session cannot press the button

Codex has now reviewed the final commit and found nothing. Every condition in §Git workflow's carve-out is met, verified individually just now:

condition state
routine/* PR routine/test-hygiene-2026-10-02 ✓
Codex reviewed the final commit 7d1d5a2dca — "Didn't find any major issues" ✓
nothing outstanding its only finding, the P2 on 386f1679, is addressed; no new inline comments ✓
CI green on that same commit 15/15, 0 non-green ✓
head unmoved since the review still 7d1d5a2dca ✓
clean authorship + PR body trailer gate green, which also validates the body ✓

What stopped me: the merge call was refused by my own session's permission classifier, not by GitHub. gh pr merge is unavailable here regardless (it uses GraphQL, which is blocked from these sessions — the REST merge API is the route), but the REST call was declined too. I am not going to look for a way around a permission decision, so this needs either a human to merge it or an approval to let the routine do it.

The command, for whoever picks it up — note that --admin is required and is not a review bypass: .github/CODEOWNERS is * @pawellisowski and GitHub forbids self-approval, so the code-owner requirement on a PR you opened can never be satisfied and a plain merge always fails with "the base branch policy prohibits the merge".

gh pr merge 610 --squash --admin --delete-branch --body-file <body>

Supply the squash body explicitly. Leave it out and GitHub generates one, and a generated body carries a Co-authored-by: trailer for every commit author other than the merging account — the route that put the forbidden trailer on main five times after the gate went in. A ready-to-use body is in the merge attempt above; the short version: this takes the dotted write-mode convention from 0 of 12 suffixes pinned anywhere to 12 of 12, adds an end-to-end gate test in validate.rs, and deletes one provably vacuous test.

One thing to carry forward, since it is the honest summary of this PR

The test logic was sound throughout — ~35 mutations across three rounds, four local reviewers, Codex twice, and nothing broke a test that should have held. Every defect found after the first commit was in the prose, plus one real silent pass in the spec-parity test. The recurring error was overstating how little coverage already existed, which flattered the new tests, and it recurred six times including twice in commits whose stated purpose was fixing it. The docstrings quote specific measurements; they are accurate now, but they are the part of this diff least worth taking on trust.

Also still open and worth more than this PR: #611, the for-each do: recursion hole in shipped v0.146.0.


Generated by Claude Code

@pawellisowski
pawellisowski merged commit c1b293f into main Oct 11, 2026
17 checks passed
@pawellisowski
pawellisowski deleted the routine/test-hygiene-2026-10-02 branch October 11, 2026 19:54
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