Professional claim wording, and every agent-posted comment discloses that it is agent-posted - #2131
Conversation
…s authorship Closes #2130 Two changes to what agent-posted forge comments say. They edit the same lines -- every claim/release body takes both -- so splitting them would guarantee a conflict on each one. 1. Claim phrasing. "paws off until I'm done" becomes "please hold off ...", and the release marker "paws off released" becomes "claim released", across 19 files. Every detector now matches BOTH wordings. Claims posted before today sit on open PRs and a claim stays live on activity rather than age, so an old-wording claim can be live right now; a matcher narrowed to the new phrase returns nothing on such a thread, which is indistinguishable from an unclaimed one -- licensing the exact parallel session the convention prevents. shared/workflow/claim-pr.md states when the alternation may be dropped. 2. Agent disclosure. A comment posted through `gh`/`glab` under the account holder's credentials carries their login, avatar and MEMBER association and reads as `type: User`, so nothing distinguishes it from one they typed -- memories/github.md already recorded auditors making that mistake. Every agent-posted comment now ends with: _Posted by Claude Code (AI agent) --- not written by a human._ The marker deliberately avoids the robot emoji. check-pr-fully-clean.py matches that emoji as a REVIEW_BODY_MARKERS entry, so a disclosed claim comment would be admitted into the fully-clean verdict scan as a review -- and a claim carries no findings, so it would scan as a CLEAN one. Verified against the checker with a negative control: the new marker returns has_review_body_marker() False on claim, unclaim, release and bare-marker bodies, while the emoji form returns True. New: shared/workflow/disclose-agent-authorship.md, CLAUDE.md and AGENTS.md sections, and hooks/require-agent-disclosure.py -- a warn-only PreToolUse guard (28 tests). It warns rather than blocks because a missing marker is cheap to repair while a blocked `gh pr comment` interrupts the action that makes a claim visible, and because --body-file/-F body=@/$BODY put the text where a literal scan cannot see it, so it reports those as unreadable rather than as missing. skills/defer-issue's "don't add attribution" line is narrowed rather than reversed: it governs issue BODIES, which stay unattributed. Exempt: a comment another machine parses as a command (`@dependabot rebase`). Out of scope: hooks/test-remind-brief-premises.py's `grep -n paws` strings, which are arbitrary example commands in fixtures, not the claim phrasing. Checks: 41/41 hook suites, markdownlint 0 errors, validate-skills, check-links (2274 links), hook-catalog, hook-output-shape, context-closure pin.
Fifteen findings from the adversarial self-review on c3bc43a. All addressed except one deferred with a tracking issue. Detectors - commands/release-pr.md matched the ISSUE-form claim sentence while reading PR comments, and the PR-form claim is not a superstring of it -- so /release-pr on a PR claimed today would have found no claim and refused to post. Both it and shared/workflow/claim-pr.md now match the two-word invariant `hold off` (plus `paws off`) rather than any whole sentence, and claim-pr.md says why a whole-sentence matcher is the trap the rename introduces. Hook (hooks/require-agent-disclosure.py), 28 -> 52 tests - Segmentation was newline-splitting, which cut the trailing marker off the very command it discloses -- every correctly-disclosed multi-line comment warned. Split is now quote-aware. - Judged per call, so one disclosed body vouched for an undisclosed sibling in a batched round. Now judged per command-position segment. - Heredoc handling skipped the whole command on any heredoc, silencing `--body-file -` and `$(cat <<EOF)` -- the forms memories/git.md prescribes for bodies with backticks. Now strips heredoc bodies for ANCHORING and keeps them for the MARKER test, reusing warn-pr-create-without-dupe-check.py's approach. - Missed `gh api .../comments|/replies` (the only review-reply route) and the `glab mr|issue comment` aliases, both verified against `glab ... --help`. - Anchor missed `then`/`else`/`do`/`!`/backtick positions. - The bot exemption matched a body merely STARTING with a bot handle; now anchored whole-body. Adds the review re-request, assembled non-contiguously per memories/mention-triggers.md since a diff view renders this file. - `-F` is gh pr comment's own `--body-file` shorthand, not gh api's `-F body=@`; the real form was misdiagnosed as a missing marker rather than an unreadable body. `--editor`, `--web` and the no-body-flag interactive form likewise. - The emoji branch fired on a body merely MENTIONING the emoji, displacing the applicable advice with an inapplicable one. - No MCP coverage at all, which is the only path in a remote/web session. - The docstring misattributed require-gh-repo-flag.py's incident (it matched too much, not too little). Prose and sites - `\n\n` inside a bash double-quoted string is two characters, not a newline, so three sites would have posted a run-on body. Converted to fenced blocks with real newlines; the one remaining instance is labelled as notation. - skills/ard/SKILL.md's per-round summary and CLAUDE.md's feedback comment -- the two highest-frequency comment sites -- had no marker. Both now do; ard's arrives via --body-file, which the guard cannot read, so it states the requirement itself. - The fragment claimed chores' two Dependabot commands were the only exemption instances. False: ardi's review re-request is a third. Corrected, with the deriving query beside it rather than a recollected count. - Marker indentation: dedenting to column 0 ends the enclosing list item, which closes the fence (markdownlint MD049). Source keeps its indentation and the fragment now says to match the marker as a substring, not a whole line. - Semantic line breaks applied to all new prose. Deferred: three older skill files are hard-wrapped in paragraphs pre-dating this change. Reflowing them here would bury the real diff in unrelated churn, so it is #2148. Note for anyone re-measuring: `--all` reports a would-change on essentially every file including untouched ones, so it is not a conformance measure; the default changed-line scope against origin/main is. Checks: 52/52 hook tests, markdownlint 0 errors, links (2277), validate-skills, hook-catalog, hook-output-shape, context-closure pin.
Twelve findings on c41f0d3. All addressed except one rebutted. Guard was silent or wrong where it mattered most - Registered under matcher `Bash` only, so `MCP_POST_TOOLS` and `verdict_mcp()` were unreachable at runtime -- dead code in exactly the population they were written for, since a remote/web session has no `gh` at all. hooks.json matches by tool name, so the fix is a second entry under `mcp__github__.*`. Three statements asserting MCP coverage were false until it landed. - A heredoc body anywhere in a call vouched for every segment, so writing a doc that QUOTES the marker silenced a bare comment posted in the same call -- the exact per-segment property the notes claimed. Heredoc bodies are now replaced by position-keyed placeholders, so each body reaches only the segment that opened it. `strip_heredocs` also erased `<<`, which had been the only signal a segment used one. - `gh api .../comments` matched with no method or field discriminator, so the review-READ that CLAUDE.md prescribes and every ARDI round runs warned. So did `gh pr review --approve`, which posts no prose. Both now require a body field. A guard that cries wolf on the common read is one nobody reads on the rare write. - The bot exemption anchored to end-of-segment, so BOTH corpus sites failed: chores' commands carry a trailing `# COMMENT_PR`. Acting on that warning would mean appending prose to a body Dependabot parses. - Missed `glab api .../notes` and `gh api graphql addDiscussionComment` entirely. Sites and claims - Review-thread replies (both forges), discussion comments, and migrate-discussion's step 3 carried no marker though the fragment names them in scope; two were invisible to the guard as well. - release-pr enumerated 3 of the 5 release markers the corpus posts, so an ARDI release (`Done --- PR is free.`) read as unreleased and would draw a stray release comment. It now lists all five and gives the query to re-derive them. - push's claim query could no longer see a release at all: the old wording made it free, since `paws off released` contains `paws off`, while `claim released` contains no claim term. A released PR read as live-claimed and the skill would refuse a legitimate push. - The orchestrator posted `--` where the canonical marker has `---`, so a literal grep found every site but that one. - The exemption's "derive it with this grep" could not find the third site: the review handle is deliberately never spelled contiguously in a source file. The fragment now says so and gives both queries. - Two test labels claimed coverage their assertions did not provide. Measured rather than asserted - The MD049 claim was wrong. Re-measured repo-wide (the CI condition, not a single-file lint, which reports differently -- verify-the-right-artifact): the dedent raises MD049 in post-merge, MD046 in release-pr, and nothing in st. Worse, the marker is underscore-emphasised, so one that escapes its fence sets MD049's `consistent` expectation and fails untouched lines elsewhere -- which is what my own migrate-discussion edit did, breaking two lines 70 below it. Marker indentation now tracks the enclosing fence. - Reviewer reproducers for findings 2, 3, 5 and 8 are now permanent test cases. 61 hook tests, up from 52. Rebutted: em dashes on 26 added lines (finding 12). Every one is in a file that already carries 8-43 of them, and the three new files this PR adds carry none. Converting only mine would make those files internally inconsistent. The corpus-wide condition is already tracked as #685, #731 and #735; a fourth issue would be a duplicate. Checks: 41/41 hook suites (61 in this one), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin. Merge-tree against the 7 commits main gained mid-session is clean, verified against a negative control that returns rc=1 on a real conflict.
Twelve findings on d8216c3. All addressed. The guard matched nothing the corpus actually writes Two detectors added last round passed their own tests and fired on no real site, which is fixtures-are-not-evidence exactly: both fixtures were single-line inventions, and the WITH-marker one was vacuous -- removing the marker did not change its result, because the marker check never ran. - `[^\n;|&]*` cannot cross a backslash line-continuation, and every review-thread reply in skills/ard uses one. Both raw-API detectors were silent on the only sites they exist for. - Every GraphQL comment site puts `addDiscussionComment` several lines below `gh api graphql`, inside a quoted query that spans newlines. - The first repair -- skipping quoted spans atomically -- was worse: the path segment these detectors match (`/notes`, `/comments`) is INSIDE the quoted URL, so the quote-skipping alternative swallowed the thing being matched. Now two gap classes: `_GAP` admits a line continuation, `_GAP_ML` is bounded and non-greedy for an argument that genuinely spans lines. The fixtures are replaced with verbatim corpus command lines, and the WITH-marker case now flips when the marker is removed. Misdiagnoses, both directions - `-F body=` and `--raw-field body=` were accepted as posting routes but omitted from HAS_INLINE_BODY_RE, so a body in plain sight drew "cannot read" -- the weaker note, which invites no correction. - The variable-expansion clause covered `--body`/`--message` but not `-b`/`-m`, so a compliant `-b "$BODY"` was told its marker was missing. - `-F body="@file"` reported MISSING; the `@` sigil is routinely quoted. - EMOJI_DISCLOSURE_RE lacked word boundaries, so "CI regenerated" and "badge rewritten" matched and got told to swap markers rather than add one. - split_segments applied plain single-quote rules to ANSI-C `$'...'`, reading the quote as closed and splitting the body on its newlines. Detectors and sites - push's alternation missed post-merge's release wording, so a released PR read as live-claimed and push would refuse a legitimate push -- the exact outcome the prose beside it says the alternation prevents. - post-merge matched a whole claim sentence, which the rule added four files away forbids, and its quoted prefix used `---` while its own emitter four steps below posts an em-dash. - tool-mappings.md -- the registry CLAUDE.md designates the single source of truth, and the substitution point for the very remote sessions the MCP registration exists to cover -- had no marker in any template. False claims corrected - The markdownlint cause claim was wrong. markdownlint-cli2 UNIONS a command-line path with the config globs (verified: 573 files either way), so there is no single-file scope to get wrong. The real cause was my own measurement: it grepped rule codes from the whole run without filtering by filename, so a different broken file's MD049 was attributed to each file in turn. metacognitive-monitoring's cause check, gone unasked. - ard's "nothing will warn you" was false; the guard warns, weakly. - The marker's searchable invariant is `(ai agent)`, not the full line -- the literal full line cannot find a Codex- or Gemini-posted comment, returning nothing, indistinguishable from an undisclosed thread. Named in the fragment with the query. - A comment posted under a genuine BOT token needs no marker, since the forge discloses it there. The fragment's scope sentence did not carve that out. The test is the token, not the workflow, with the query to settle it. - release-pr said the corpus "posts five" release markers; one is retired. Checks: 41/41 hook suites (70 in this one, up from 61), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin, codex wrappers re-synced.
Eleven findings on fffc9aa. All addressed. The fix from round 3 introduced a bug the code it replaced did not have post-merge's claim check now matches the two-word invariant `paws off`, and the retired release marker is `... done --- paws off released.`, which contains it. The sentence matcher that preceded it did not collide. So a released PR read as claimed, the conflict sweep skipped it, and the conflict went unresolved with nothing reporting why. It now checks the same comment for a release term first. The raw-API detectors were still wrong, for a third distinct reason Rounds 2 and 3 each tried one regex spanning the gap between `gh api` and its comment path. `[^\n;|&]*` could not cross a line continuation. `[^;&]{0,400}?` put the COMMENT BODY inside the gap, so an ordinary semicolon in the prose, an ampersand, or a body over 400 characters made the detector silent -- on exactly the longer, human-looking replies the rule targets. And the round-3 fixture added to stop testing "single-line inventions" used a 12-character placeholder with no punctuation, so it passed on the one input that concealed it. Replaced with a two-part test over the whole segment, which the quote-aware splitter has already bounded to one command: an anchored `gh|glab api`, a comment-bearing target, and a body field or a GraphQL comment mutation. Order independence falls out, which also fixes `gh api -f body=... <url>`. As a side effect the polynomial blowup the reviewer measured (7.47s on 8000 `/comments` substrings) is gone with the gap regexes -- 0.008s on the same input. Other misdiagnoses - `(?<!-)-F\s+(?!body=)\S` was meant for gh pr comment's `-F <file>` shorthand and fired on any `-F`, so `gh api ... -F in_reply_to=5 -F body="..."` -- the corpus's own review-reply shape -- reported "cannot read" over a visible body. Narrowed to a bare token with no `=`. - verdict_mcp synthesized `--body "<body>"` to reuse the shell-shaped exemption pattern, so a `"` inside the body closed the synthetic argument early and `@dependabot rebase" and a long note for the humans...` took the exemption. The MCP path now matches the raw body. The Bash path had a fixture against this shape; reconstructing syntax to reuse a matcher is what reopened it. False claims corrected - "there is no single-file scope to get wrong" is false: `--no-globs` is in markdownlint-cli2's own `--help` and gives exactly that (573 files versus 1, measured). The union half was right. Named the flag, since it removes the contamination the weaker remedy works around. - The `.user.type` check gave only the issues endpoint, which 404s on a review-thread comment -- a surface the fragment names in scope two paragraphs above. Both routes now given, with what the 404 means. - The scope list said "an issue filed on the user's behalf" while three other sites in this diff say issue BODIES stay unattributed. Narrowed to comments. - tool-mappings' COMMENT_DISCUSSION row said no MCP tool exists; `mcp__github__discussion_comment_write` does, and this hook covers it. - The MCP_POST_TOOLS comment cited tool-mappings as the source of its five-tool list; that file names four. Said so rather than implying derivation. - The README row described one of the three verdicts the hook emits, omitting the unreadable-body one, which is the commoner in this corpus. Checks: 41/41 hook suites (77 in this one, up from 70), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin, codex wrappers re-synced, and `bash -n` parses all 17 marker-carrying shell blocks.
Ten findings on a9ff133. All addressed. A generated file, edited at the wrong end tool-mappings.md is generated from tool-mappings.yml, so the previous round's edits to it were silently reverted by the next sync -- AGENTS.md's "canonical sources vs generated output" rule, broken while adding rows to the registry CLAUDE.md calls the single source of truth. The marker note now lives in tool-mappings.yml and in the generator's own preamble, and survives a regen. The COMMENT_DISCUSSION row also said no MCP tool existed; mcp__github__discussion_comment_write does. A fix applied to one of two sites carrying the same defect This round's own docstring explains why `[^\n;|&]*` had to go: it cannot cross a backslash line-continuation. The identical bound survived in the `gh pr review` lookahead, so a review whose body flag sat on a continuation line -- the shape skills/ard uses everywhere -- went silent. The body-flag test now runs over the whole segment, where the continuation is already absorbed. A false premise, twice cited The fragment and the hook both claimed the review handle is "never spelled contiguously in a source file, because a diff view would summon the bot". False: 242 occurrences across this corpus's markdown, including AGENTS.md, CLAUDE.md and README.md, none of which has dispatched a run -- and memories/mention-triggers.md states the gate over comment, review and issue BODIES, not file contents. The real reason the command-shaped grep misses ardi's site is mundane: that site is prose. The passage now says so and gives the second query plainly. Six properties no test pinned Removing any of these passed 77/77: three of the five MCP tools, the /notes and /discussions targets, the env-var prefix in the anchor, and the ANSI-C `$'...'` branch a prior round added as a fix. All are now pinned, and all eight mutations are confirmed CAUGHT by re-running the suite against each mutant. The first /discussions fixture was itself masked -- its path also contained /comments -- so it uses GitLab's create-a-thread route, where /discussions is the only marker. Removed rather than fixed The GraphQL early return was justified by a body-as-variable case that does not occur here: every GraphQL comment site supplies `-f body='...'`, so the branch decided nothing while classifying any command that merely NAMED the mutation as a post. Deleting it passed the suite unchanged and removes a false positive. Smaller - `-F key=value` is a typed field, not a body-file; narrowed so a review reply's `-F in_reply_to=5` no longer downgrades a visible body to the weaker note. - `--body=`/`--message=` equals forms read as inline rather than unreadable. - claim-pr's canonical matcher carried the release collision its own consumers document and fix: `paws off released` matches `paws off`, so a released PR read as claimed in every reader that follows the fragment rather than post-merge. - "One exemption" was stated in three always-loaded places while the fragment defines two; the bot-identity one is now named in all of them. - The issue-body pointer cited a scope line that omitted issue bodies. - A duplicated eight-line comment block in the new hook. Checks: 41/41 hook suites (92 in this one, up from 77), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin, codex wrappers re-synced from the yml.
Fourteen findings on de50078. All addressed. This round's differential over all 804 fenced code blocks in the tree showed zero verdict changes from the previous head, so the guard's behaviour on what the corpus actually writes is stable; the findings are gaps rather than regressions. Invisible to the guard - The QUOTED whole-argument field form. `API_BODY_FIELD_RE` required `body=` to follow whitespace directly, so `-F "body=@<file>"` -- the registry's own canonical reply command, and the line this change annotates -- was not merely weakened to the "cannot read" note but completely silent. The corpus writes the quoted spelling for sibling flags too. - `-F "in_reply_to=5"` still downgraded a visible body. The optional quote gave the engine a backtracking path: skip the quote, fail to find `key=` at `"`, satisfy the negation. Matched as a token with no `=` in it instead. Removed rather than kept - `/replies` is dead. GitHub's reply route is `.../comments/<id>/replies`, so it always contains `/comments` -- which is why two attempts at a fixture for it were masked by their own paths. An alternative no fixture can isolate implies a route that does not exist. - Three `UNREADABLE_RE`/`ANY_BODY_FLAG_RE` alternatives were subsumed when `HAS_INLINE_BODY_RE` gained `[^\s"'$]`, and their comments described behaviour the code no longer had. Restored, with a real discriminator The GraphQL branch was deleted last round because "the branch decided nothing" -- a claim about the corpus, applied to a detector that sees whatever an agent writes. A mutation carrying its body in the query text or an `--input` file was silent. It is back, keyed on `mutation` beside the name, so executing one is distinguished from a comment mentioning one. The registry, again - COMMENT_DISCUSSION's new MCP mapping omitted the required `method`, on the one operation where the registry is the only path for a remote session. - Three sibling discussion rows were stale in exactly the way this round corrected a fourth: LIST_DISCUSSIONS, VIEW_DISCUSSION and ANSWER_DISCUSSION all said no MCP tool existed. Verified against the server's tool list, which is what the hook's own docstring says to do. Claim readers `gi` and `claim-pr`'s GitLab sweep were left on the claim invariant alone, against the rule written four files away -- so `... done --- paws off released.` read as a live claim and `gi` would skip a released issue permanently. All six claim readers now exclude release terms. Untested properties, and two that could not be tested Six more survived deletion with the suite green: the `systemMessage` key (which `check-hook-output-shape.py` checks for Stop hooks only), `MARKER_RE`'s attribution prefix, `strip_heredocs`'s opener tail, `_ANCHOR`'s `(`/`{`/backtick positions, `verdict_mcp`'s isinstance guard, and `/replies`. Five are now pinned and confirmed CAUGHT by mutation; `/replies` was removed instead. False and unsupported claims - The published derivation grep put `--include` after `--`, so both flags were read as file operands and the search ran unfiltered -- returning its own test fixtures as apparent sites, in the passage whose thesis is that one query does not derive the set. - "242 times" was a count from a tree predating the sentence, with no deriving command beside it. Re-derived at this commit and the command published. - The previous commit claimed to have removed a duplicated comment block; it had not. Removed now. - AGENTS.md's exclusion list omitted issue bodies while its inclusion list named issue comments -- and non-Claude agents read AGENTS.md instead of CLAUDE.md. Prose and budget - Em dashes on lines authored this round converted to ASCII, EXCEPT inside quoted literal comment bodies: `<lane> done - claim released.` is the text that reaches GitHub, and changing it desynchronizes the corpus from the string its matchers agree on. - CLAUDE.md restated the whole mechanism beside the fragment carrying it; trimmed to the rule, the marker, and the emoji constraint (1516 -> 1171 bytes). AGENTS.md keeps the longer copy deliberately, since agents reading it never load the fragment. The always-loaded pool remains over its advisory budget, which predates this change. Checks: 41/41 hook suites (105 in this one, up from 92), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin, and tool-mappings.md is idempotent under a second regen.
… dead Seven findings on 781d286, two of them blocking. All addressed. A "dead code" deletion that was not dead Round 6 removed the short-flag `$`-interpolation clause on the reasoning that HAS_INLINE_BODY_RE had taken it over, and narrowed the field-form clause so it could no longer cross a quote or a space. Neither held: that pattern rejects only a value BEGINNING with `$`, so a MID-STRING expansion fell through and reported a missing marker over a body the check never read -- an assertion beyond what it observed, and the opposite verdict from `--body` on the identical body. UNREADABLE -> MISSING gh pr comment 5 -b "pre $VAR post" UNREADABLE -> MISSING gh pr comment 5 -f body="pre $VAR post" UNREADABLE -> MISSING gh pr comment 5 -F "body=pre $VAR post" The fixture that licensed the deletion was named "-b with an expanded variable is unreadable" and only exercised `-b "$BODY"`, the whole-value form, which still passed. The name generalized past the check. Both clauses are restored, four mid-string fixtures added, and deleting either clause now goes red. A count that refuted itself The figure was published beside a `git grep` command that is itself an `@claude` occurrence in a markdown file, so shipping the command incremented the number it measured -- wrong at round 5 and wrong again at round 6, in opposite directions, and failing on first contact for a reader invited to re-run it. The count now excludes this file, in both the sentence and the command, so it is stable under edits to the file that states it. Verified: the sentence says 248 and the command returns 248. Contradictions between the corpus and itself - The guard carried the mention-gate rationale that the fragment added in this same branch calls "false twice over" -- shipping the refutation and the refuted claim side by side, with the refuted one in the file a maintainer reads first. The handle stays concatenated for a narrower and true reason, now stated. - "share an id space but not a route" contradicted the 404 two lines below it. If the id space were shared the wrong route would return a different comment, which is a worse failure than the one described. - The guard implements one of the two declared exemptions, and nothing said so. Whether a token belongs to an app or to a person is not in the command text, so no lexical check can decide it; the docstring now names the gap and its one visible consequence. - memories/github-mcp-tools.md carries the same pre-change warning memories/github.md got a pointer for, and had none. - The name-substitution instruction is the one path that can reintroduce the collision the emoji ban prevents: `code review` is a REVIEW_BODY_MARKERS entry, so an agent named for code review would reintroduce it through its own name. Both AGENTS.md and the fragment now say to check the substituted name too. Checks: 41/41 hook suites (109 in this one, up from 105), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin, and tool-mappings regenerates byte-identical.
One blocking finding on 0fff5eb, plus four non-blocking. All addressed. There were three retired claim wordings, not one `ardi` posted `Driving this PR to clean --- back off until done.` since 2026-06-17, and this branch retired it in c3bc43a without adding it to the back-compat alternation -- so the five matchers this branch rewrote all read `hold off|paws off` and were blind to it. `ardi` is the corpus's highest-traffic claim emitter, run on every PR in every repo, so a session claiming a PR through it left a claim that `/push`'s check 3 could not see: the query returns empty, which is indistinguishable from an unclaimed thread, and the session pushes over a live claim. That is verbatim the failure this branch's own added prose describes. `/release-pr` had the mirror failure and would decline to post the release. The prose was false in the same commit that proved it: claim-pr.md said the body "said 'paws off until I'm done' until 2026-08-24", and release-pr.md called `paws off` "the pre-2026-08-24 invariant". Both are true of claim-pr's own body and false of the corpus. All six matchers now carry `hold off|paws off|back off`. The method that missed it, named and replaced Six skills agreed on `paws off`, so enumerating the retired set from the files in front of me returned six confirmations and one silent omission -- the one file that disagreed was the one not consulted. claim-pr.md now publishes a git-log derivation over history instead of a list, verified to return all three, with a Don't naming the enumeration method as the cause. Non-blocking - The migrate-discussion MD049 measurement does not reproduce under the same harness that reproduces the other three exactly. The structural hazard is real and the linter's coverage of it is not established; the passage now says that rather than claiming the linter catches it. - Two count-based self-references ("four steps below", "step 3 below") replaced with stable ones. - A single-quoted `$` is reported unreadable though bash does not expand it. Kept: the `--body`/`--message` clause has always behaved this way, so the short forms match rather than diverge, and it errs toward the weaker note rather than toward asserting something about text never read. Pinned as a test so the choice is visible as a choice. Verification carried over from the review: a differential of verdict() against 781d286 over 7,514 synthetic commands showed 456 differences, all in the intended MISSING -> UNREADABLE direction, and zero across 831 fenced blocks in the tree -- with a negative control showing the corpus sweep fires on 25 blocks, so the zero is a result rather than a detector that never ran. Checks: 41/41 hook suites (110 in this one), markdownlint 0, links, skills, hook-catalog, hook-output-shape, context-closure pin, tool-mappings idempotent.
…d wrong The pre-push review reported 8 lines failing `validate / new-line-breaks`. Running the checker locally returned exit 0, so I nearly rebutted it. The rebuttal would have been wrong, and the way it was wrong is the finding. `.github/workflows/validate.yml` pins the reusable workflow to a SHA in `Morrison-Lab/gha`. That workflow's own step then runs `d-morrison/gha/check-new-line-breaks@v2` -- a different org, a floating tag -- so the pin fixes the caller and nothing about the script that executes. The two differ materially: 340 lines versus 637, no clause-break rule versus one on by default, and on this branch exit 0 versus exit 1 with 8 findings. Reading the pinned copy to predict CI gives the wrong answer confidently, since that artifact exists, is fetchable at the named SHA, and runs clean. That is verify-the-right-artifact's "a checkout for the run" shape, and pr-on-claim already records the same delegation trap for a reusable workflow's trigger conditions. Nothing said it applies to what the workflow RUNS. Filed as #2165, with the measured table and three fix options. The eight lines, all mine, all long with a mid-line semicolon: AGENTS.md 196 and 211, memories/github.md 199, shared/workflow/claim-pr.md 96/114/143, and skills/post-merge/SKILL.md 324/325. Split at their clause boundaries. Most came from my own `scripts/semantic-line-breaks.py --write` runs, which JOIN wrapped lines -- the opposite direction from this check, and it exits 0 either way. So running the local tool to satisfy one convention manufactured failures in the other, and no local signal reported it. Worth knowing before the next reflow pass. Two more surfaced only after the first fix: an `...` inside a quotation reads as a sentence end, so anything following it on the line counts as a second sentence. Rewording the quotations to drop the ellipsis cleared both; a bracketed `[...]` elision did not, since it terminates a sentence too. Also from the same review, non-blocking: - The retired-wording enumeration was wrong in both directions -- `handoff` never said "paws off until I'm done" (it said "still claimed, paws off."), and `gip`, `iterate` and the orchestrator were omitted. Corrected, two lines above the Don't that forbids enumerating from the files in front of you. - "There were three retired wordings" was uncountable: there are two retired two-word invariants, and the third is a wording only if claim SENTENCES are counted, which the next subsection tells the reader not to do. Reworded to name the emitters and the two invariants. Verified with the script CI actually runs (`d-morrison/gha@v2`, NLB_FAIL=true, base = merge-base): "No lines missing semantic breaks", exit 0. Plus 41/41 hook suites (110 in this one), markdownlint 0, links, skills, hook-catalog, hook-output-shape, vendored drift, context-closure pin, tool-mappings idempotent.
…vation One blocking finding on 9565d9f, and it landed in the paragraph about this exact mistake. The correction that repeated the error it corrects The previous commit widened the retired-wording enumeration from six emitters to nine, and one of the three it added was `iterate` -- a twelve-line alias stub whose whole body points at `ardi`. It carries no claim wording at all, and it IS `ardi`, which the next two lines name as the emitter that said something else. So the sentence put the same skill on both sides of its own contrast, inside the passage whose subject is that enumerating from the files in front of you fails silently. `gip` and the orchestrator, the other two additions, are genuine. That commit was billed as a line-break fix. Expanding a factual enumeration inside it is how the error got in without review attention. Also from the same round, non-blocking - "Three retired wordings" against "two predecessors" was a granularity shift between full strings and two-word invariants, never stated, and "All three now read" lost its antecedent when the sentence was split into two groups. Both now count invariants, which is the unit every matcher uses. - The derivation command offered in place of the enumeration missed two of the emitters the prose names: `handoff` opens a multi-line body so its closing quote is on another line, and the orchestrator is a `.py` outside the `skills/*/SKILL.md` pathspec. Widened the pathspec and matched the opening quote only. Verified it now returns both retired invariants. - Filed #2172: `clean-branches` checks for work claims with an EMPTY code block, so it has no matcher and no release-term handling. It was invisible to this branch's sweep by construction -- it names no wording, so a wording-based grep cannot find it -- and its failure direction is a deletion rather than a skip. Verified with the script CI actually runs (`d-morrison/gha@v2`, not the pinned `Morrison-Lab/gha` copy, per #2165): no lines missing semantic breaks. Plus 110/110 hook tests, links, skills, markdownlint 0, context-closure pin, and a grep confirming no emitter anywhere still posts a retired wording.
One blocking finding on 5aa36bb, in the same paragraph as the last two. The derivation offered in place of an enumeration could not reach two of the emitters the prose beside it names. Keying on `--(body|message) "` looks tighter and misses exactly the emitters least like the others: `gip` states its claim as quoted prose inside a worker brief, and the orchestrator builds the body as a Python f-string on a different line from the flag. So a reader following the instruction to derive rather than enumerate would have re-derived a six-emitter set and dropped those two -- the failure the section's own Don't describes. Now keyed on the wordings across the widened pathspec. Verified per emitter: the new pattern returns hits for the orchestrator, `gip`, `pr-on-claim`, `handoff`, `claim-pr` and `ardi`; the old one returned zero for the first two. The cost is that it also returns matcher and prose lines, so it wants a skim rather than a count -- stated in the text rather than left for the reader to discover. Also corrected the two stale "six" counts the enumeration fix left behind: seven skills carried `paws off`, eight emitters counting the orchestrator. `commands/release-pr.md`'s was added by this branch, so it was a new false count rather than an inherited one. Verified with the script CI actually runs: no lines missing semantic breaks. Plus 110/110 hook tests, links, skills, markdownlint 0, hook-catalog, hook-output-shape, context-closure pin.
…ranch's words `main` gained eight commits, one of them a996c0b acting on #2148 --- the issue this branch filed for the three skill files it declined to reflow, precisely because reflowing them inside this PR would have buried the real diff in churn. So the conflict is the expected consequence of having deferred correctly, and it resolves the way that intends: take main's reflow wholesale, then reapply the word-level changes on top. - skills/ardia: main's reflowed bullet, with `a still-live "paws off" claim comment stands` narrowed to `a still-live claim comment stands`. - skills/config-ai: main's reflow, with `claim-pr`'s "paws off" comment` narrowed to `claim-pr`'s claim comment`. - skills/gip: main's reflow, with the claim wording moved to `please hold off` and the agent-disclosure marker line re-added. Verified after resolution: no retired wording survives in any of the three, no conflict markers remain, the real new-line-breaks gate passes (main's reflow is what satisfies it here, not mine), and 110/110 hook tests, links, skills, markdownlint, hook-catalog and the context-closure pin are all green.
This comment has been minimized.
This comment has been minimized.
|
Claude Code CLI (local session) is working on this --- please hold off on pushing to this branch until I'm done. Context from the requesting conversation, so it is visible here rather than only in chat:
Posted by Claude Code (AI agent) --- not written by a human. |
2026-08-24 23:31 UTC |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
/review |
|
Claude finished review — View run Code review — #2131Summary of changes: Two related changes bundled together (per the author's own PR note, both edit the same lines): (1) rewords the "paws off" claim-comment convention to more professional language ("please hold off …") across ~20 skill/doc files, while preserving backward-compatible detection of the retired Verification performed:
Findings: None that meet the high-signal bar (compile/parse errors, definite wrong-result logic, or clear unambiguous CLAUDE.md violations). This diff is almost entirely prose/documentation plus one well-isolated, warn-only hook with unusually thorough self-testing — the PR's own history (11 adversarial review rounds pre-push, per the claim comment) and the round-by-round comments baked into the test file ( Two very minor, non-blocking observations (not filed as findings since neither is a defect):
VerdictReady for merge Reviewed commit: b744d6a |
/#2185 (#2207) * ums: learnings from three review rounds plus a cross-vendor pass on #2185 Five entries, from PR #2131/#2185's review lifecycle: - self-review-fallback.md + .cases.md: a CLEAN same-vendor verdict is a cross-vendor trigger, not only a failed one. Two same-vendor readings (11 pre-push adversarial rounds, and claude-review's "Ready for merge") cleared #2131 at b744d6a; codex then returned 11 findings, 8 blocking. Supplies the missing half of the existing case record, which names its own limit as covering a reviewer that was down rather than clean. - adversarial-self-review.md: a PR's visible review history is rationale you cannot withhold. The claude-review verdict named the prior rounds and the test file's round-N markers in its own justification. - least-flexible-tool.md: third occurrence of the regex-instead-of-lexer class, recorded per ums's recurrence step. Filed as #2189. - algorithmatize-checks.md: two more mutation misdiagnoses, both under outcomes already listed; count recorded, plus when to check an anchor. - fixtures-are-not-evidence.md: where the specification is a written rule, quote its literal text into the fixture. - claim-pr.md: second occurrence of the concurrent-session collision, with different parents rather than matching ones. Two adversarial review rounds ran pre-push; round 1 returned 11 findings and round 2 returned 10, all addressed. * ums: correct the #2185 causal history, the verification count, and three overreaching claims Round-3 adversarial review findings, all addressed: - least-flexible-tool.md: at 10909ff the regex was `-c\s`, which cannot match mid-word. The mid-word false positive was created by the widening to attached/= forms, and three rounds then patched the boundary. Restated as one widening forcing three narrowings, which is the same rule from the other side. - adversarial-self-review.md: nine verification steps, not ten (derived from the comment body, not restated from a reviewer's count); exact quotation where the prose said "in its words"; two bullets reworded to name observable actions rather than mental states. - self-review-fallback.cases.md: the case record asserted "why it probed no further", which the sibling section added in the same commit explicitly retracts; now scoped to the verdict's justification. A third live explanation (a different findings threshold) added. - algorithmatize-checks.md: the anchor-matched-nothing case belongs to the inapplicable-mutation outcome, not outcome four; Don't half added. - fixtures-are-not-evidence.md: cite the rule's file, not a volatile line. * ums: stop narrating a branched commit history, and split the two mutation cases Round-4 adversarial review findings: - least-flexible-tool.md: cf195e4's parent is 10909ff, not 3b8d04e -- the two are concurrent siblings merged by 5c577a5, so no linear 'three rounds then patched' sequence exists. Three attempts at that narrative were each wrong in a new way. Rewritten to the claims that are derivable: the matcher's start state, its end state, the three lookbehind forms, and the count. Drops --request-changes from the false-positive pair, since COMMENT_FLAG_RE gates close/reopen and never sees a gh pr review segment. - algorithmatize-checks.md: the second instance is the parent entry's exception, not an example of it -- the mutation was fine and the suite lacked a discriminating fixture. Split, and the entry's ordering marked as a default the pass-condition entry's identity check settles. - self-review-fallback.cases.md: deduped a cross-reference stated twice in seven lines. - 'denies'/'contradict'/'by construction' softened to what the evidence carries. * ums: derive the order from git rather than advising against it, and drop an inert evidence item Round-5 adversarial review findings: - least-flexible-tool.md: the 'order is unrecoverable' framing was a rationalization of three earlier failed narrations, and contradicted the same passage's own ordered account. The order IS recoverable in one command per commit; the entry now says to run 'git log -1 --format=%P' before narrating a fix history, which is the lesson four rounds actually taught. Count corrected to one writing plus two rewrites, and the intermediate unanchored form named, since the final regex correctly does not match the compliant command the passage cites. - claim-pr.md: '--comment=' was inert -- '--comment\b' already matches '--comment=x' because the boundary sits between t and '='. Executed both hook revisions to confirm. Replaced with the two verified items, and the inert one kept as the reason to execute rather than read both diffs. - self-review-fallback.md: 'depth in one family' imports learn-from-review-findings' term for a converging series' search space to describe a vendor blind spot, which spans families -- the cited record's own round markers span several. Reworded to the vendor's reachable region. - algorithmatize-checks.md: heading no longer calls both rows misdiagnosed, which the body supports for neither; the two discriminating questions are now stated once, in order. - adversarial-self-review.md: dropped an unsourced statistical claim. - self-review-fallback.cases.md: named the referent behind 'it answered twice over', and restored the justification-versus-effort scoping clause. * ums: drop the causal chain the entry never needed, and quote the regex that actually warned Round-6 adversarial review findings. Five rounds each found a factual error in a different narration of which #2185 commit responded to which, while the count and the regex forms survived every round untouched -- so this applies the entry's own thesis to itself and changes the shape rather than tuning the same knob a sixth time. - least-flexible-tool.md: states what each revision WAS and drops what each was a RESPONSE to. The widened form is now 3b8d04e's actual '-c(?:\s|=|\S)', which is the only revision that warns on the compliant command (executed against all four); the previously quoted form shipped already anchored and matches nothing. The six rounds are recorded as the entry's second lesson, with 'git merge-base --is-ancestor' as the instrument, since 'git log --format=%P' reports immediate parents only and cannot separate a sibling from a descendant four commits down. - claim-pr.md: named the two commits instead of theirs/mine, and the Do bullet now says to execute each side rather than read its diff, which is the lesson the paragraph above it actually teaches. - algorithmatize-checks.md: one row was misread, not both; named the mutation instead of a pronoun whose nearest antecedent was the anchor. - self-review-fallback.cases.md: the threshold reading is not the only one with a cheap remedy. * ums: state the four forms without any causal or self-congratulatory framing Round-7 adversarial review findings: - least-flexible-tool.md: 'went in to stop that' and 'a widening forced a run of narrowings' were the same causal claim the entry says it dropped, and git refutes it (cf195e4 branched from 10909ff, not from 3b8d04e). The entry now lists four forms and asserts nothing about which answered which. The self-referential paragraph claimed only the causal chain ever broke and named five prior narrations; both were false and both flattered the fix, so it is cut to the checkable core. Boundary form pinned to 15b63d9 rather than to a moving branch head, and the --is-ancestor bullet now names its operand order, which decides the answer. - claim-pr.md: 'their --comment= alternative' had cf195e4 as its nearest antecedent, which is the side that dropped it; named the commit and the direction. Deduped a clause repeated near-verbatim two lines apart. - self-review-fallback.cases.md: dropped a cost figure not sourced by anything the record links, and which carried no weight. - algorithmatize-checks.md: the new Do now names the sibling Do that prescribes the stronger remedy, and says why a check is still needed. * ums: the eleven rounds did not read the reviewed head, and four edits produced five forms Round-8 adversarial review findings, the first two of them factual errors in the case record's headline: - self-review-fallback.cases.md: 'two same-vendor readings cleared that head' was false of one of them. The claim comment says eleven rounds ran before the FIRST push, and four substantive commits landed after it, three of them changing matcher or derivation logic. Only claude-review read b744d6a. Restated as one verdict at the head plus eleven rounds of same-vendor depth on an earlier state, which is what the record can support. self-review-fallback.md's 'eleven of them and one of them' inherited the same reading and is corrected with it. - least-flexible-tool.md: four edits produce five forms, and the passage said four forms while enumerating five. Separated the two counts. The meta-paragraph claimed the entry attributes nothing, while the sentence seven lines above attributes the quote-boundary form to the false positive its own commit message names -- a linear successor, so the attribution is sound and the meta-claim was the overreach. 'the layer that answers the question' hedged to 'would answer', since #2189 is a proposal and shlex.split raises on unbalanced quotes. --is-ancestor's exit status read three-valued, per errexit-is-not-uniform. - self-review-fallback.md: two framing overreaches, a scope claim over sentences that carry no trigger, and 'no artifact at all' over a case whose primary produced a full verdict comment. * ums: derive the pre-push gap from the branch's own commit messages Round-9 adversarial review findings: - self-review-fallback.cases.md: 'four substantive commits earlier' overstated the gap ~4x, in the record's own favour. The branch's commit messages name the state each round read (594fdce: 'One blocking finding on 5aa36bb'), so the last state read is 5aa36bb -- one branch commit (9 insertions, 4 deletions) plus the origin/main merge from the reviewed head. Recorded as the narrow gap it is, which strengthens the record rather than weakening it: the eleven rounds had substantially read the work codex found eight blocking defects in. Also fixed the cell's other defect, that eleven rounds read eleven different states so no single value could be right; and disambiguated 'readings' from 'explanations'. - least-flexible-tool.md: 'where a token begins' is refuted by the passage's own second edit, which asks what may follow the flag; widened to 'boundaries'. Attribution sentence given its real subject. - self-review-fallback.md: the narrowed scope claim still read as 'every trigger-naming sentence prescribes cross-vendor', which is false of three paragraphs above it that prescribe a self-review. - algorithmatize-checks.md: named ANY_BODY_FLAG_RE instead of a pronoun whose nearest antecedent was POST_RE. - claim-pr.md: 'no merge in it at all' is false one commit back on the 3b8d04e side; scoped to between the two fixes. * ums: weaken three sentences to the claims their arguments actually need Round-10 adversarial review findings. The reviewer's closing observation is the fix: across ten rounds each repair to a sentence broke it on a new axis, because the sentences were carrying more precision than the argument needed. - least-flexible-tool.md: 'linear successor' is false -- every path from cf195e4 to 9336348 runs through the 5c577a5 merge, which the same paragraph names. Ancestry is what the attribution needs and what the entry's own Do bullet tests; --is-ancestor exits 0. Also disambiguated 'the preceding quote', which read as a quotation given earlier. - self-review-fallback.md: dropped the four-item appositive, which omitted the 'no review workflow configured' failure mode and listed 'contradicting itself', which the passage above names in order to exclude it. The head claim is true and self-sufficient. - claim-pr.md: 'no merge between the two fixes' is still false -- ed03e06 is a main-merge inside the divergence on the 3b8d04e side, and is why the parents differ. 'Matching parents nowhere in sight' is the contrast the section actually draws. - self-review-fallback.cases.md: 'reading' carried a review-pass sense and an interpretation sense five lines apart; the latter is now 'explanation' throughout. Named the noun behind 'Its remedy'. And the codex inference is now derived rather than hedged: every file codex raised a blocking finding in is byte-identical at 5aa36bb and b744d6a. * ums: derive the untouched-file set instead of typing it out Round-11 adversarial review finding. The hand-typed list of files codex raised blocking findings in did not match what #2177 records, in both directions: it omitted shared/workflow/disclose-agent-authorship.md and skills/rescue-closed/SKILL.md, and included AGENTS.md, which #2177 names as the authority being violated rather than as a site. The conclusion happened to survive because both omitted files are also unchanged, which is a wrong derivation reaching a true conclusion by luck -- the exact shape the entry around it warns about, and the class round 10 was weakening sentences to eliminate. Replaced with the intersection, published as the command that computes it: the files #2185 changed, against the files that moved between the last state the rounds read and the branch's own last commit. Empty. Only the origin/main merge touches any of them, and only hooks/hooks.json, from another PR's work rather than this branch's. * ums: describe the derived set as the superset it is Round-12 finding: 'the files #2185 changed to address those findings' is a restrictive clause that is false of at least one member -- #2185 also carries an unrelated preferences.md learning. The set is #2185's whole diff, which is deliberately wider, and width is the conservative direction when the result is an empty intersection. Said so. Also strengthened the merge leg's warrant. It rested on authorship ('somebody else's work'), where what the conclusion needs is that no line the rounds read changed. 'git diff 5aa36bb b744d6a -- hooks/hooks.json' removes zero lines, which supplies it directly. * style: break eight added lines at their mid-line semicolons CI's check-new-line-breaks runs with NLB_CLAUSE_BREAKS=true and NLB_CLAUSE_MIN_LENGTH=80, which flags a line of 80+ characters carrying a mid-line semicolon. scripts/semantic-line-breaks.py does not implement that rule and in fact wants these eight lines rejoined, so the local tool cannot be run over this diff without turning the check red again. CI is the authority here; validate.yml runs check-new-line-breaks against content and runs semantic-line-breaks.py only through its own unit tests. * ums: scope the COMMENT_FLAG_RE claim to the unmerged branch, and fix a wrong direction claude-review round 1 on ff5a9da, both findings verified against the repo: - least-flexible-tool.md: the entry opened with a present-tense claim that COMMENT_FLAG_RE decides the close/reopen comment gate. It does not exist on main -- 'git grep COMMENT_FLAG_RE origin/main' returns nothing, and its introducing commit 10909ff is not an ancestor of main. It lives only on #2185, still open. Once this PR merges, a reader grepping the codebase for the construct the entry describes finds nothing. Scoped to that branch and dated, per timestamp-volatile-claims. - self-review-fallback.cases.md: 'in the section linked below' pointed forward at nothing -- the file ends three sentences later, and the adversarial-self-review link it meant sits two paragraphs above. Named the fragment and the direction, per forward-references.
Closes #2130
Two changes to what agent-posted forge comments say. They are one PR because they
edit the same lines --- every claim and release body takes both --- so splitting
them would guarantee a conflict on each one.
1. Professional claim wording
paws off until I'm donebecomesplease hold off ..., and the release markerpaws off releasedbecomesclaim released.Every detector matches all three wordings, not just the new one. There were
two retired invariants, not one: seven skills plus the orchestrator said
paws off, andardi--- the corpus'shighest-traffic claim emitter, run on every PR in every repo --- said
back off,since 2026-06-17. A claim expires on activity rather than age, so claims under
either retired wording can be live right now, and a matcher narrowed to the new
phrase returns nothing on them --- indistinguishable from an unclaimed thread,
which licenses exactly the parallel session the convention prevents.
All six claim readers now match
hold off|paws off|back offand exclude releaseterms, the exclusion being necessary because
... done --- paws off released.contains
paws off.2. Every agent-posted comment says an agent posted it
A comment posted through
gh/glabunder the account holder's credentialscarries their login, avatar and
MEMBERassociation, and reads astype: User. Nothing in the API distinguishes it from a comment they typed ---memories/github.mdalready recorded auditors making that mistake. The forgecannot say it, so the body does:
The marker deliberately avoids the robot emoji.
check-pr-fully-clean.pymatches that emoji as a
REVIEW_BODY_MARKERSentry, so a disclosed claim commentwould be admitted into the fully-clean verdict scan as a review --- and a claim
carries no findings, so it would scan as a clean one. Verified with a
negative control: the marker returns
has_review_body_marker()Falseon claim,unclaim, release and bare-marker bodies; the emoji form returns
True.Two exemptions: a body another machine parses as a command (
@dependabot rebase), and a comment posted under a genuine bot token, where the forge alreadyreports
type: Bot.Ships as
shared/workflow/disclose-agent-authorship.md, sections inAGENTS.mdand
CLAUDE.md, the marker at ~20 comment sites,tool-mappings.ymlrows, andhooks/require-agent-disclosure.py--- a warn-onlyPreToolUseguard registeredunder both
Bashandmcp__github__.*, with 110 tests.It warns rather than blocks because a missing marker is cheap to repair while a
blocked
gh pr commentinterrupts the action that makes a claim visible, andbecause
--body-file,--editorand$BODYput the text where a literal scancannot reach --- so it reports those as unreadable rather than as missing,
never asserting more than it observed.
Review
Eleven adversarial review rounds before the first push. Findings that mattered:
as dead code that was not, and an emitter list "corrected" to include an alias
stub for the very skill it was contrasted against.
new-line-breaksCI check delegates from a pinned SHA to a floating@v2in another org, so reading the pinned copy predicted a pass on a branch the
real check failed. Filed as A SHA-pinned reusable workflow delegates to a floating @v2 in another org, so the pin does not reach the script that runs #2165.
emitters least like the others. Rekeyed on the wordings.
Follow-ups filed: #2148 (three files hard-wrapped rather than semantically
broken), #2165 (the pin that does not reach the script), #2172
(
clean-branchesreads claims with an empty code block, so it has no matcher).