feat(claude-config): give audit-permission-grants a denominator, and make audit-prompting-postures' contract agree with itself - #2408
Conversation
…h itself Eight coherence gaps in `audit-prompting-postures`, all one kind: the skill's own contract disagreed with itself. - CC-F3: P7 blesses a deny-by-default hook or script gate as presence evidence while Phase B inventories instruction text only, so the one evidence form P7 names was the one Phase B could not see. Split the two ideas — the inventory bounds what may produce a finding, not what counts as evidence — and made Phase C look for the gate before judging P7 MISSING. Tightened `destructive-capable` from "can delete/reset/force-push" (which matches every component with a shell) to what the body has the model DO, per the classification section's own opening line. - CC-F5 + CC-F10, written as one edit because both rewrite the Phase A fetch contract: the best-practices page is fetched every run and its failure ABORTS (it is the single non-negotiable input; ten `wording-unverified` postures is a report shaped like an audit that audited nothing), while model subpages are fetched lazily in Phase C per applicable row and fail locally. The published verdict vocabulary now has four tokens including `info`, and `wording-unverified` / `(unverified)` are named as markers that ride alongside a verdict rather than replacing one. Fifth column renamed to "Proposed addition or pointer", which is what it already carried. - CC-F6: the surface set is named here instead of inherited by reference from a sibling that versions independently — which is how `output-styles` came to be inventoried by this skill and unnameable by its own filter. `output-styles` is now a scope token and appears in the description. - CC-F7: P8 carries the model condition its own gotcha mandates. The pointed-at section scopes context awareness to Sonnet 5 / 4.6 / 4.5 and Haiku 4.5 (re-fetched 2026-08-12; the issue marked this leg unverified). - CC-F4: `disallowed-tools: Edit, NotebookEdit` makes the read-only contract a property of the tool set rather than of model obedience. `Write` stays for the mandated persist. Frontmatter semantics re-fetched 2026-08-12. - CC-F8, with its stated mechanism corrected: the issue says the token appears nowhere in the sibling. At HEAD it appears once, in a state-key aside at `audit-instructions/SKILL.md:392` — so the grep claim is false and the substantive claim holds, because the sibling's Scope-boundary route-out never named this skill. Added that route-out line, plus an eval whose prompt carries no slash invocation so description-driven selection is exercised, and one pinning the CC-F3 mechanical-gate rule. - CC-F11: the state key stops overwrites, not reaping — the uninstall sentence is quoted with `--keep-data`, re-fetched 2026-08-12. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…release 0.36.0
`No fragile permission grants found.` printed identically whether the run parsed
forty `allowed-tools` blocks and found them healthy or parsed none at all, and
`SKILL.md` told the operator to take that string at face value. Every run now
ends with a coverage block, and a run whose denominator is zero prints
`NOTHING TO AUDIT` instead of a clean bill.
The denominator counts what was NOT read as well as what was — a denominator
built only from successes would be the same defect in a new spelling. Two
fail-open paths found while instrumenting it are folded in:
- a settings file present but not valid JSON was skipped by a silent
`|| return 0`, so its rules were never read and the run still reported clean;
- both `find` walks discarded stderr, which this script's own header already
argued against ("a swallowed permission error was indistinguishable from a
clean bill").
The `vendor/` exclusion moved from the `find` predicate into the loop so its
count can be reported — same predicate, same result set, but an exclusion whose
count is printed cannot suppress silently. (A5)
Also:
- A11 — `$PERMISSION_HYGIENE_SCAN_ROOT` is the sanctioned name for the one
scoping lever, with `$PERMISSION_HYGIENE_FIXTURE_DIR` kept as a
back-compatible alias. #2249 made that variable the documented remedy for the
exit-2 refusal while its name still said "test fixture".
- A15 — consumer-declared exemptions must disclose themselves, may widen the
fragile set but never delete a finding, and a run where everything is exempted
says so rather than printing a clean bill. The audited repo authors those
declarations, which is the threat model the skills page names directly.
- A16 — the scope filter is documented as what it is: a filter over one full
detector run. The filed remedy (detector flags) is declined with its
measurement — since #2249 the root is bounded, and the two `find` walks over
this repository take 0.49 s and 0.41 s.
A8 is NOT taken and is filed as #2406 with its rationale corrected. It reasons
from `vendor/`'s "not loadable" justification to `node_modules/`, but the skills
page says nested `.claude/skills/` directories load once Claude touches a file in
that subdirectory — so a `node_modules/` exclusion would make an `error`-tier
username-leak check blind to a live grant.
Tests: 76 -> 99 checks, all passing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 726aa9e569
ℹ️ 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".
…ed-tools on both siblings Two defects found in self-review of the change itself. 1. `audited` summed only frontmatter blocks and allow rules, so a root whose only auditable surface was plugin settings.json files — all parsed, all clean — printed "NOTHING TO AUDIT: … this run has no denominator" two lines above the coverage line reporting `plugins: 2 manifest(s); 2 settings.json parsed`. It had a denominator; the code had not counted it. That is A5's own defect in a new spelling, inside the fix for A5. All three axes now count, the zero-denominator message enumerates all three, and case 10bb pins it — case 10a uses a genuinely empty root and could never have caught it. 2. Declaring `disallowed-tools` on `audit-prompting-postures` alone opened a fresh instance of the sibling divergence CC-F6 is about, in the release that fixes CC-F6. `audit-instructions` states the identical report-only contract and names neither Edit nor Write anywhere in its body, so it carries the same declaration now. `audit-pass` states the contract too and is NOT touched here — PR #2403 owns that file — so it is filed as #2415. Also qualifies the CHANGELOG's CC-F11 claim: only the uninstall half is actioned, `when_to_use` is still unused, and the description grew 1,290 -> 1,305 chars to carry `output-styles`, which is the opposite direction from that row's headroom note. Tests: 99 -> 102 checks, all passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…config-audit-contracts # Conflicts: # plugins/claude-config/.claude-plugin/plugin.json # plugins/claude-config/CHANGELOG.md # plugins/claude-config/skills/audit-permission-grants/scripts/permission-rule-check.test.sh
|
Claude finished @kyle-sexton's task in 2m 11s —— View job Security Review — PR #2408Reviewed all 10 files changed in this PR against FindingsIMPORTANT (confidence: medium) — Both skills now declare Reviewed and found no issues
|
…e requires CI's `hygiene` job failed on SC2249 against the `vendor/` exclusion's `case`. The finding is info-severity, which my local `shellcheck -S warning` did not surface; the repo's `.shellcheckrc` enables `add-default-case` explicitly and CI lints at info. Re-verified with the exact CI invocation (`shellcheck --rcfile .shellcheckrc -S info`), now clean. Worth recording how it surfaced: `hygiene` reports failure in the aggregate while every step reads success, so `--log-failed` shows nothing. The cause is only in `CHECK_RESULTS` (`shellcheck=failure`) in the full log. Also renames the coverage block's exemptions line. It read "exemptions applied by consumer declaration: none", which is true of the DETECTOR and is not the answer to the question it appears to answer — the skill reads consumer declarations, the detector never does, and a reader copying that "none" into a report would assert exactly what A15 requires the skill to establish itself. Tests: 112 checks, all passing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 15s —— View job Security Review — PR #2408
Reviewed the full diff ( FindingsIMPORTANT (confidence: medium) — Both skills now declare Reviewed, no issues found
One non-security note for completeness, already raised by Codex inline and not re-litigated as a finding here: an unreadable (but |
|
Claude finished @kyle-sexton's task in 5m 9s —— View job Code Review — PR #2408
Reviewed per this repo's Finding posted inlineOne correctness finding on I independently verified this by reading the code paths (not executing the script — this sandbox's Bash tool declined to run the test suite or a direct invocation of Reviewed, no other issues found
|
…config-audit-contracts # Conflicts: # plugins/claude-config/.claude-plugin/plugin.json # plugins/claude-config/CHANGELOG.md
|
Claude has reviewed this PR 2 times. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
|
Claude finished @kyle-sexton's task in 2m 38s —— View job Security Review — PR #2408
Reviewed the PR's actual diff ( FindingsIMPORTANT (confidence: medium) — Unchanged from the prior two security passes on this PR, so restating rather than re-discovering: both skills declare Reviewed, no security issues found
Everything above is a repeat confirmation of the prior two security passes' conclusions — this push introduced no new source-visible changes to review, only a version/changelog reconciliation from the |
|
Claude finished @kyle-sexton's task in 6m 17s —— View job Code Review — PR #2408
Reviewed per this repo's Findings posted inline
Reviewed, no other issues found
Everything else in the Test plan and repo-gate output in the PR body (the 112-case suite run, |
…nce, and drop a false assurance claim Five P2 review threads, four distinct defects. Two of them are this change's own defect class turned on this change. **The denominator was still incomplete, in the direction the PR exists to fix.** `find` needs only directory-traversal permission to report a file as `-type f` — it does not need read permission on the file. So a frontmatter candidate that exists but cannot be read (mode 000, restrictive ACL, a mount that denies reads) was enumerated, failed inside `awk`, wrote its error to the real stderr rather than to WALK_ERR, and was counted in NO bucket at all — while the coverage block promised to disclose exactly that input. That is the third instance of "a real surface examined and counted nowhere" in this one change, after the P3 axis. Three instances means the invariant was being asserted per-site, so it is now derived once: every enumerated candidate lands in exactly one of four buckets (vendor-excluded, unreadable, no allowed-tools block, parsed), and `reconcile_frontmatter` checks the buckets sum to the enumeration on every run — printing DENOMINATOR BUG and naming itself as the defect when they do not. A negative test deletes a bucket increment from a copy of the script and asserts the check fires, so this cannot decay into a check that can no longer fail. Extraction stderr now joins the walk's, and a run that audited nothing AND could not open its own inputs says so distinctly. **A false assurance claim, in a skill whose subject is auditing assurance.** `audit-prompting-postures` claimed `disallowed-tools: Edit, NotebookEdit` made "never edits a component" a property of the tool set. It does not. `Write` is retained for the mandated persist and Phase B has already read every audited component, so a retained Write can overwrite one; `Bash` is retained for the state key, and a shell mutates files too. Both this skill and `audit-instructions` now state the honest posture — instruction-held with a narrowed accident surface — and both forbid telling an operator the skill *cannot* edit their files. The CHANGELOG records that the earlier claim was wrong rather than quietly softening it. **P7 missed script gates.** The catalog blesses a deny-by-default hook OR script gate, while Phase C searched only permission rules and hook config — so a component delegating its destructive step to a gating script got a false MISSING, the expensive false positive P7 exists to avoid. Phase C and the catalog row now name all three places. **The P3 thread is already fixed at HEAD** (it is marked outdated): commit dd540d7 folded `plugin_settings_parsed` into `audited`, and a P3-only clean root now prints a clean bill. Verified again before replying. Tests: 112 -> 118 checks, all passing. The unreadable-candidate arm announces an explicit skip where the platform still grants the owner a read after chmod 000 (Windows/Git Bash, root); it exercises on POSIX CI. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 35s —— View job Security Review — PR #2408
This is the fifth automated pass on this PR (fourth security-lane pass). Rather than re-walk the whole diff, I diffed the current HEAD ( Prior finding — now resolvedThe
This closes the access-control-adjacent risk: an operator reading the current text can no longer be misled into believing New in this diff, reviewed — no security issues
Not re-reported (out of scope for this lane)A documentation self-consistency question (whether refuted findings are always demoted to Conclusion: no security issues found in this PR's current state. The one finding this lane raised on every prior pass is fixed as of |
…hree axes
Two more review threads, both correct.
**The formula counted "produced a finding" on two axes and "examined
successfully" on the third.** `plugin_settings_parsed` incremented for every
settings.json that parsed, clean or not — but `fm_with_block` only counted files
carrying a non-empty allowed-tools block, and `allow_rules_read` only counted
extracted rules, so a SKILL.md with no allowed-tools and a settings.json with an
empty allow array contributed nothing despite being read and found to grant
nothing. That is the fourth spelling of "a denominator that counts only
successes" in this change.
The reviewer flagged it as a static read they could not execute. Reproduced
against the committed HEAD before fixing:
NOTHING TO AUDIT: 0 allowed-tools block(s), 0 allow rule(s) ...
frontmatter: 0 allowed-tools block(s) parsed from 2 candidate file(s)
reconciled: 2 candidate(s) = ... + 2 without an allowed-tools block + 0 parsed
— the refusal printed directly above the count of the files it had just read,
exactly as predicted. After:
No fragile permission grants found.
DENOMINATOR = 3 input(s) successfully examined: 2 frontmatter file(s) +
1 settings scope(s) + 0 plugin settings.json.
Rather than patch a third counter, the rule is stated once and printed on every
run: the unit on every axis is an input SUCCESSFULLY READ AND EXAMINED, never an
input that produced something. `scopes_read` replaces `allow_rules_read` as the
settings unit so all three axes count files and are commensurable; rule counts
stay in the coverage text where they inform without deciding.
**Phase D contradicted the verdict schema this PR added.** Phase D said refuted
findings are "dropped OR demoted to `info`" — a disjunction with no criterion —
while the new schema line called `info` "kept for the record". A dropped finding
is kept under no verdict. Reconciled toward keeping: a refuted finding is always
demoted and stays a row carrying its refutation, because deleting it erases the
evidence that Phase D ran and disagreed. This was a residual instance of the
defect class the issue is about, introduced by its own fix — recorded as such.
Tests: 118 -> 121 checks, all passing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
|
Warning Automated review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
Re-run the job to retry the review. A new push re-triggers this lane only if the caller's |
|
Warning Automated security review did not complete — this is an infrastructure failure, not a review verdict. Treat any Claude comment on this PR (including a placeholder like "I'll analyze this and get back to you") as incomplete, not "no findings."
The check is green on purpose, and it is not evidence. It certifies that a security pass ran, and this one did not complete — but the cause is outside this PR's control, so merging is deliberately left unblocked rather than locking every merge for the length of the outage. Nothing was reviewed at this head. Where this check is required, it is satisfied without that evidence; a human should review security-sensitive changes here before merging. Re-run the job to retry the review; a new push also retries it only if the caller's Re-running does NOT help for every class:
|
…grate P4/P2b Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
Closes #2281
Closes #2283
Summary
Two
claude-configskills whose contracts contradicted themselves. Released together as 0.37.0(minor: new output on every detector run, new env-var surface). #2382 (0.35.3), #2403 (0.35.4) and
#2396 (0.36.0) all merged while this was in flight; 0.37.0 sits above all three and the CHANGELOG
order gate is green.
audit-permission-grants— reports a clean bill with no denominator (#2283)A5 — the headline.
No fragile permission grants found.printed identically whether the runparsed forty
allowed-toolsblocks and found them healthy or parsed none at all. Every run now endswith a coverage block, and a run whose denominator is zero prints
NOTHING TO AUDITinstead.The denominator counts what was not read, because one built only from successes is the same
defect in a new spelling. Establishing that turned up two fail-open paths that were not in the
issue, both folded in:
|| return 0. Its ruleswere never read and the run still printed a clean bill — and an unparsable rules file is exactly
where a fragile grant would sit unexamined. Now reported per scope as
NOT VALID JSON — its rules were not read.findwalks discarded stderr. This script's own header already argues against that: "aswallowed permission error was indistinguishable from a clean bill." Unreadable paths are now
captured and counted.
The
vendor/exclusion moved out of thefindpredicate into the loop so the run can report howmany files it removed. Same predicate, same result set — but an exclusion whose count is printed
cannot suppress silently.
--countkeeps the bare integer on stdout (the machine contract) and putsthe block on stderr.
A11.
$PERMISSION_HYGIENE_SCAN_ROOTis now the sanctioned name for the one scoping lever, with$PERMISSION_HYGIENE_FIXTURE_DIRkept as a back-compatible alias (new name wins when both are set).#2249 made that variable the documented operator remedy for the exit-2 refusal while its name still
told them it was a test seam;
reference/criteria.md, which never mentioned it at all, now sanctionsit explicitly.
A15. Consumer-declared exemptions must disclose themselves, may widen the fragile set but
never delete a finding, and a run where every finding is exempted says so instead of printing a clean
bill. The audited repo authors those declarations — the threat model the docs name directly ("Review
project skills before trusting a repository, since a skill can grant itself broad tool access",
fetched 2026-08-12). The report schema grows an
Exempt?column to hold it.A16 — the filed remedy declined, with the measurement. The row says the four scope filters are
"advertised but not implemented". They are implemented, as a presentation filter, and
SKILL.mdsays so at
:70-71. The real defect is that the argument hint reads like a scan-scope. The filed fix(detector flags) rests on a cost claim that no longer holds: since #2249 the root is a git toplevel,
$CLAUDE_PROJECT_DIR, or an explicitly named directory — never an unbounded sweep — and I measuredthe two walks over this repository at 0.49 s and 0.41 s. Flags to skip half a second would
buy nothing and add a second place for scope to be defined. Fixed the wording instead, adopting the
formulation both sibling audit skills already ship.
A8 — declined, filed as #2406. It reasons from the
vendor/exclusion's own justification ("notloadable, so the grant never takes effect") to
node_modules/, worktrees and marketplace mirrors.That step is false. https://code.claude.com/docs/en/skills, fetched 2026-08-12:
So
node_modules/<pkg>/.claude/skills/<name>/SKILL.mdis loadable, and the exclusion would makeP2 — an
error-tier check whose whole subject is a leaked username — silently blind to a live grant.Same failure shape as the
//path exemption on #2382. The defensible half (every exclusion reportsits own count) ships here; the rest needs a loadability model, which #2406 specifies.
audit-prompting-postures— contract disagrees with itself in eight places (#2281)All eight rows taken (CC-F11 partial — see its bullet). Prose-only, so there is no behavioral test to
write and I am not inventing one — same posture #2403 took on the sibling skill.
prose", while Phase B inventories instruction text — so the one evidence form P7 names was the
one form Phase B could not see, on the posture whose false MISSING is most expensive. Split the
two: the inventory bounds what may produce a finding, not what counts as evidence, and Phase C now
looks for the gate before judging P7 — in all three places the catalog blesses: settings rules,
hook configuration, and, after review caught the procedure searching only the first two, the
script the component delegates the destructive step to, followed and read. A component whose
destructive action runs through a gating script is gated and nothing in its own text says so.
Tightened
destructive-capablefrom "can delete, reset, force-push" — which matches everycomponent with a shell — to what the body has the model DO, per the classification section's own
opening line.
separately would ship a Phase A that disagreed with itself. The best-practices page is fetched
every run and its failure aborts (single non-negotiable input; ten
wording-unverifiedpostures is a report shaped like an audit that audited nothing). Model subpages are fetched lazily
in Phase C per applicable row and fail locally — which is what the observed run already did and the
wording forbade. The verdict schema was closed at three tokens while the body mandated two more; it
now carries four verdicts including
info, withwording-unverified/(unverified)named asmarkers that ride alongside a verdict rather than replacing one.
that versions independently — the coupling that let
output-stylesbecome inventoried here andunnameable by this skill's own filter.
output-stylesis now a scope token.unverified, re-fetched by me 2026-08-12: the pointed-at section scopes context awareness to
"Claude Sonnet 5, Claude Sonnet 4.6, Claude Sonnet 4.5, and Claude Haiku 4.5".
disallowed-tools: Edit, NotebookEditis declared, but an earlier draft of this PR claimed it made "never edits acomponent" a property of the tool set. That was false and is removed rather than softened.
Writeis retained for the mandated persist and Phase B has already read every audited component,so it can overwrite one;
Bashis retained for the state key, and a shell mutates files too. Thedeclaration narrows the accident surface, it does not enforce the contract — both skills now say
so, and both forbid telling an operator the skill cannot edit their files. A skill whose subject
is auditing assurance must not ship a false assurance claim about itself; the CHANGELOG records
that the earlier claim was wrong instead of quietly shipping the corrected text. Second
unverified leg, re-fetched 2026-08-12 — the frontmatter reference's semantics, including "The
restriction clears when you send your next message", which is the right lifetime: whoever accepts
a proposal can apply it.
says
grep -c "audit-prompting-postures" .../audit-instructions/SKILL.md→ 0, "the tokenappears nowhere in the sibling". At HEAD it returns 1, at
audit-instructions/SKILL.md:392, ina state-key aside. The grep claim is false; the substantive claim survives, because a mention in an
aside is not a route-out —
audit-instructions' Scope boundary section still never named thisskill. Added that route-out line. Also added two evals: one whose prompt carries no slash
invocation, so description-driven selection is exercised for the first time (all five existing
cases invoke explicitly), and one pinning CC-F3's mechanical-gate rule. The
description-drives-discovery claim was itself an unverified leg — third one re-fetched
2026-08-12 (
skills.md:259,:425-426).state key stops overwrites, not reaping, with the sentence quoted and
--keep-datanamed(re-fetched 2026-08-12). The row's other two observations are recorded, not fixed — and one
moved the wrong way.
when_to_useis still unused, and the description grew from 1,290 to1,305 of its 1,536-char cap to carry
output-stylesfor CC-F6, which is the opposite directionfrom the row's headroom note. That trade is deliberate (a scope token that is unnameable is the
actual defect; 231 chars of headroom is not), but "all eight rows taken" would have been the wrong
sentence to leave standing.
CC-F9 is correctly not touched: the issue records it as falsified, and it is.
One divergence this PR would otherwise have created
CC-F4 declares
disallowed-tools: Edit, NotebookEditonaudit-prompting-postures.audit-instructionsstates the identical report-only contract ("There is no--fix… never bythis skill") and names neither
EditnorWriteanywhere in its body — so declaring it on one ofthe pair and not the other would have opened a fresh instance of exactly the sibling divergence
CC-F6 is about, in the release that fixes CC-F6. It is declared on both, in the file this PR was
already editing.
audit-passstates the contract too and is not touched: PR #2403 owns thatfile right now, so it is filed as #2415 rather than collided with.
Test plan
Fail-before / pass-after for A5. Three roots that
origin/main(5ea4f87f) describes with oneidentical string, run against the old and new detectors:
The
--countline is the point restated: stdout is still0in both cases (the machine contract isunchanged), and stderr now separates them.
The denominator got the same defect wrong four times, and the fourth is why it is now structural.
Recorded in full because the pattern is more useful than any one instance:
auditedomitted the P3 axis, so a root of clean pluginsettings.jsonprintedNOTHING TO AUDITtwo lines aboveplugins: 2 manifest(s); 2 settings.json parsed. Caught in self-review.findcan list but the process cannot read —findneeds only directorytraversal to report
-type f— reachedawk, failed, wrote to the real stderr rather thanWALK_ERR, and was counted in no bucket, while the coverage block promised to disclose exactlythat input. Caught by two reviewers independently.
auditedcounted "produced a finding" on the frontmatter and settings axes but "examinedsuccessfully" on P3's, so a
SKILL.mdwith noallowed-toolsand asettings.jsonwith an emptyallowarray contributed nothing despite being read and found to grant nothing.Three instances means the invariant was maintained by convention at each
continue, so it is nowderived once. Every enumerated candidate lands in exactly one of four buckets — vendor-excluded,
unreadable, no
allowed-toolsblock, parsed — andreconcile_frontmatterchecks they sum to theenumeration on every run, printing
DENOMINATOR BUGand naming itself as the defect when they donot. A check that cannot fail is not a check, so a negative test deletes a bucket increment from a
copy of the script and asserts the reconciliation fires. And the unit is stated once for all three
axes — an input successfully read and examined, never one that produced something — and printed:
Extraction stderr now joins the walk's rather than escaping to the terminal, and a run that audited
nothing and could not open its own inputs says so distinctly instead of reporting an empty tree.
Suite: 76 → 102 on this branch's own base, then 121 after the review round, all passing.
26 of those are this branch's, across the denominator (including the P3 axis), the
unparsable-settings skip, the
--countstdout/stderr split, the exclusion disclosure, and thescan-root rename; the rest are #2382's, which landed mid-flight.
Merge note — and the trap it walked into. #2382 (0.35.3), #2403 (0.35.4) and #2396 (0.36.0) all
merged into
mainwhile this was in flight, twice leaving the PRCONFLICTING. A conflicted mergeref suppresses the
pull_request-triggered runs entirely, and the PR then displayed 3 checks,all passing instead of 33 — nothing distinguishes that from a real green except counting the rows.
Caught by comparing against #2382's 34, not by reading the failure count.
Resolved with merge commits rather than repeated rebases, and verified marker-free across the whole
tree before each commit:
plugin.json— 0.37.0, because feat(claude-config): complete the permission-state skill and add the auto-mode authoring lane #2396 took 0.36.0 (the version this PR originally claimed) andalso rewrote the description to "Nine configuration-health skills". Theirs kept, version raised.
CHANGELOG.md— 0.37.0 / 0.36.0 / 0.35.4 / 0.35.3 / 0.35.2;--check-ordergreen.permission-rule-check.test.sh— both sides pure additions, both kept.permission-rule-check.shandcriteria.mdauto-merged with no conflict: fix(claude-config): tighten permission-rule-check P1/P2 precision #2382's hunks arethe P2 pattern block and P2's criteria section, neither of which this PR touches.
lib/permission-patterns.sh, which this detector sources — so the suite wasre-run against the merged library, not just against this branch's own base. Still passing.
The A16 measurement, since it is what declines the row:
Repo gates:
check-listing-budgetwas run and reports the aggregate already over budget onmain(97537/8000) — advisory-only, and this PR's contribution is +15 chars (description 1290 → 1305
for
output-styles). Not introduced here and not resolvable here.Related
the issue's own "Not in this issue, on purpose" section requires.
drops nothing.
distinguish.
see the merge note in the test plan. feat(claude-config): complete the permission-state skill and add the auto-mode authoring lane #2396 took 0.36.0, so this PR is 0.37.0.
$PWDfallback and added the exit-2 refusal. This is the residueit named: a resolved root with nothing in it still reported clean.
(uninstall still reaps the directory).
audit-prompting-postures/SKILL.md; every anchorin fix(claude-config): audit-prompting-postures' contract disagrees with itself in eight places #2281 past
:78was re-derived at this HEAD before editing.Inbox items:
20260811-021645-plugin-audit-four-components-and-guard-deadlock-ownership(#2281),20260811-024628-claude-config-audit-permission-grants-defects-and-fleet-grant-hygiene(#2283).Ledgers:
.work/handoff-inbox-batch-4/ledgers/I9-021645-four-components.md§ Lane A;.work/handoff-inbox-batch-4/ledgers/I10-permission-grants-fleet.md§ A5, A11, A15, A16.