Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
9c704ed
docs(topics): lock the permission-model plan and design resolution
kyle-sexton Aug 10, 2026
025d351
docs(topics): discharge the permission-model fresh-docs mandate
kyle-sexton Aug 10, 2026
7f14c14
docs(topics): resolve the three parked permission-model decisions
kyle-sexton Aug 10, 2026
c43ccc9
feat(claude-config): widen the permission checks and extract their sh…
kyle-sexton Aug 10, 2026
09f88c1
fix(claude-config): announce an unresolvable user scope instead of sk…
kyle-sexton Aug 10, 2026
721fee1
feat(claude-config): report which permission scopes exist and what ea…
kyle-sexton Aug 11, 2026
d269c41
fix(claude-config): end the managed-registry search at the first key …
kyle-sexton Aug 11, 2026
434d24f
feat(claude-config): compute the permission set in force, with each r…
kyle-sexton Aug 11, 2026
837d503
fix(claude-config): stop reporting rules for a tool that whole-tool d…
kyle-sexton Aug 11, 2026
d8f75f2
Merge origin/main into feat/audit-permission-state
kyle-sexton Aug 12, 2026
64bb24a
merge: integrate main into feat/audit-permission-state
cursoragent Aug 12, 2026
0e158f6
fix(claude-config): prune contract slice, fix CI blockers, address re…
cursoragent Aug 12, 2026
1740735
fix(claude-config): hygiene — exec bits, typos, portable test paths
cursoragent Aug 12, 2026
2413b6b
fix(claude-config): mark audit-permission-state shell scripts executable
cursoragent Aug 12, 2026
9386daa
fix(claude-config): preserve exact rule text in permission-merge comp…
cursoragent Aug 12, 2026
797f2eb
fix(claude-config): restore exec bit on permission-merge scripts
cursoragent Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/CATALOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ plugin manifests and kept in sync by CI — never hand-edit it; the category voc

- [`desktop-notification`](../plugins/desktop-notification) — Alert you when Claude Code needs input — an audible terminal bell, an OSC 9 terminal notification, and an OS-native toast (macOS/Linux) on permission and idle prompts.
- [`playbooks`](../plugins/playbooks) — Doctrine and knowledge playbooks as on-demand skills, plus a maintainer-facing update skill. boris — Boris Cherny's Claude Code workflow tips (howborisusesclaudecode.com); skill-authoring — Anthropic's internal skill-authoring playbook; fable-5 — Claude Fable 5's operating doctrine (self-authored, no upstream). The boris and skill-authoring packs vendor a verbatim upstream baseline; /playbooks:update drift-checks and syncs those baselines centrally (maintainers).
- [`claude-config`](../plugins/claude-config) — Seven configuration-health skills (plus setup) for a repo's Claude Code configuration: audit (settings.json / .mcp.json / hooks / plugins / permissions drift), audit-automation-gaps (evidence-gated verdicts on automation gaps), audit-permission-grants (allow-rule / allowed-tools grants for auto-mode durability and portability), audit-instructions (locally-owned instruction surfaces vs current model capability — proposes removals/rewrites of instructions the model no longer needs, and detects cross-surface instruction conflicts), audit-prompting-postures (the additive lane — posture guidance the prompting guide says a component's purpose needs but the component does not carry), audit-pass (one coordinated, ordered, resumable pass over a named target — three-scope inventory, run-time-derived exclusion set, stable finding identity, suppression memory, resume, one human gate — delegating every check to the plugin that owns it), and unhobble (the empirical bare-baseline experiment: reversibly strip a repo's standing instructions, log real stumbles against the current model, re-add only what evidence earns).
- [`claude-config`](../plugins/claude-config) — Eight configuration-health skills (plus setup) for a repo's Claude Code configuration: audit (settings.json / .mcp.json / hooks / plugins / permissions drift), audit-automation-gaps (evidence-gated verdicts on automation gaps), audit-permission-grants (allow-rule / allowed-tools grants for auto-mode durability and portability), audit-permission-state (which settings scopes exist and what rules each one holds — managed policy, user-global, project, local, and the pre-v2.1.211 start-directory copy), audit-instructions (locally-owned instruction surfaces vs current model capability — proposes removals/rewrites of instructions the model no longer needs, and detects cross-surface instruction conflicts), audit-prompting-postures (the additive lane — posture guidance the prompting guide says a component's purpose needs but the component does not carry), audit-pass (one coordinated, ordered, resumable pass over a named target — three-scope inventory, run-time-derived exclusion set, stable finding identity, suppression memory, resume, one human gate — delegating every check to the plugin that owns it), and unhobble (the empirical bare-baseline experiment: reversibly strip a repo's standing instructions, log real stumbles against the current model, re-add only what evidence earns).
- [`claude-memory`](../plugins/claude-memory) — Keeps a repo's Claude Code memory layer healthy and under your control, against criteria derived from official Claude Code documentation. The audit skill checks the instruction/memory layer (CLAUDE.md, CLAUDE.local.md, .claude/rules/, auto-memory) with a deterministic script-backed spine plus judgment-tier checks. The stateless skill inspects, disables, and (confirm-gated) purges Claude-written auto memory across all settings scopes.
- [`claude-ops`](../plugins/claude-ops) — Claude Code operations toolkit. Eight skills: audit-install-state (read-only audit of the machine-scope ~/.claude installation directory and ~/.claude.json — full inventory split into an authored surface and rolled-up bulk trees, product-managed retention vs genuinely unmanaged state, filename-scheme resolution before any process-liveness check, and deliberate/mid-experiment detection; reports, never deletes), observability (read locally captured telemetry — OTEL store, collector, hook-event JSONL, ccusage — with trend reports and store pruning), known-issues (search known Claude product GitHub bugs, check service health, maintain a persistent tracked-issue registry), changelog (ingest Claude Code changelog entries and integrate them into the current repo), plugins (bring a machine's plugin fleet current on demand — marketplace refresh, effective-scope updates including in-repo project/local installs, new-plugin install per policy, scope-divergence detection and explicit convergence), morning-brief (read-only gh-based operator morning view — queue-label counts, merge-ready PRs, parked decisions with their RECOMMENDED lines, and loop-lane telemetry freshness), lanes (start/restart/stop/status loop lanes as named background Claude Code sessions seeded from canonical prompt files, with per-lane model/effort, a repo-pull + marketplace-refresh launch step, and a consume-restarts action — an OS-schedulable reader that relaunches stopped lanes whose telemetry carries a restart_request), and a re-runnable setup action that settles where the known-issues registry lives. Plus a family of seven advisory *-audit telemetry-emitter hooks (API errors, config changes, instruction loads, permission denials, pre-compaction, skill usage, tool failures) that emit the shared hook-telemetry envelope, and a reference sink that maps envelopes into the hook-events.jsonl the observability skill reads.
- [`rate-limit-guard`](../plugins/rate-limit-guard) — Shared rate-limit guard for loop lanes: a statusline wrapper tees the subscription rate-limit windows to a fixed machine-scope file, a StopFailure hook records rate-limit stops reactively, and a reader contract fixes how consuming sessions pause and resume.
Expand Down
1 change: 1 addition & 0 deletions docs/SKILL-CHEAT-SHEET.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ owned by [docs/CATALOG-TAXONOMY.md](CATALOG-TAXONOMY.md).
| [`/claude-config:audit-instructions`](../plugins/claude-config/skills/audit-instructions/SKILL.md) | `claude-config` | Find instructions current models no longer need across CLAUDE.md, rules, and skill bodies |
| [`/claude-config:audit-pass`](../plugins/claude-config/skills/audit-pass/SKILL.md) | `claude-config` | Run one coordinated, resumable audit pass over a repo with a single human gate |
| [`/claude-config:audit-permission-grants`](../plugins/claude-config/skills/audit-permission-grants/SKILL.md) | `claude-config` | Audit permission grants for portability and auto-mode durability |
| [`/claude-config:audit-permission-state`](../plugins/claude-config/skills/audit-permission-state/SKILL.md) | `claude-config` | Report which permission scopes exist and what rules each one holds |
| [`/claude-config:audit-prompting-postures`](../plugins/claude-config/skills/audit-prompting-postures/SKILL.md) | `claude-config` | Find posture guidance the prompting guide says a component needs but does not carry |
| [`/claude-config:unhobble`](../plugins/claude-config/skills/unhobble/SKILL.md) | `claude-config` | Strip instructions to a bare baseline, log real stumbles, re-add only what evidence earns |
| [`/claude-memory:audit`](../plugins/claude-memory/skills/audit/SKILL.md) | `claude-memory` | Audit CLAUDE.md, rules, and auto-memory against the official-docs checklist |
Expand Down
4 changes: 2 additions & 2 deletions plugins/claude-config/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "claude-config",
"version": "0.32.0",
"description": "Seven configuration-health skills (plus setup) for a repo's Claude Code configuration: audit (settings.json / .mcp.json / hooks / plugins / permissions drift), audit-automation-gaps (evidence-gated verdicts on automation gaps), audit-permission-grants (allow-rule / allowed-tools grants for auto-mode durability and portability), audit-instructions (locally-owned instruction surfaces vs current model capability — proposes removals/rewrites of instructions the model no longer needs, and detects cross-surface instruction conflicts), audit-prompting-postures (the additive lane — posture guidance the prompting guide says a component's purpose needs but the component does not carry), audit-pass (one coordinated, ordered, resumable pass over a named target — three-scope inventory, run-time-derived exclusion set, stable finding identity, suppression memory, resume, one human gate — delegating every check to the plugin that owns it), and unhobble (the empirical bare-baseline experiment: reversibly strip a repo's standing instructions, log real stumbles against the current model, re-add only what evidence earns).",
"version": "0.33.0",
"description": "Eight configuration-health skills (plus setup) for a repo's Claude Code configuration: audit (settings.json / .mcp.json / hooks / plugins / permissions drift), audit-automation-gaps (evidence-gated verdicts on automation gaps), audit-permission-grants (allow-rule / allowed-tools grants for auto-mode durability and portability), audit-permission-state (which settings scopes exist and what rules each one holds — managed policy, user-global, project, local, and the pre-v2.1.211 start-directory copy), audit-instructions (locally-owned instruction surfaces vs current model capability — proposes removals/rewrites of instructions the model no longer needs, and detects cross-surface instruction conflicts), audit-prompting-postures (the additive lane — posture guidance the prompting guide says a component's purpose needs but the component does not carry), audit-pass (one coordinated, ordered, resumable pass over a named target — three-scope inventory, run-time-derived exclusion set, stable finding identity, suppression memory, resume, one human gate — delegating every check to the plugin that owns it), and unhobble (the empirical bare-baseline experiment: reversibly strip a repo's standing instructions, log real stumbles against the current model, re-add only what evidence earns).",
"author": {
"name": "Melodic Software",
"email": "info@melodicsoftware.com"
Expand Down
58 changes: 58 additions & 0 deletions plugins/claude-config/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,64 @@
All notable changes to the `claude-config` plugin are documented here. Format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this plugin uses semantic versioning.

## [0.33.0]

### Changed

- **`audit-permission-grants` check P1 now sees user-global allow rules.** It scanned project and
local settings only, so an interpreter-wildcard rule in
`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json` was invisible to it — and that is the scope
Claude Code's own "Always allow" path writes to, so it is where the broad rules auto mode drops
actually accumulate. Expect new findings on a repository whose own configuration did not change.
The user scope resolves through `CLAUDE_CONFIG_DIR` before `$HOME`, and a finding names the
resolved absolute path rather than `~/.claude/settings.json`, which would name the wrong file
whenever the config root has been relocated.
- **`audit`'s structure check now reports a start-directory `settings.local.json`.** A pre-v2.1.211
Claude Code wrote the file to the directory the session started in, and the current one still
reads what an earlier version left there: the repository-root copy wins on a shared key, but
permission rules from **both** files stay in effect. The row appears only when the start directory
genuinely differs from the project root and a copy is there, so the same file is never counted as
two rule sources.
- **`audit`'s structure check names the managed surfaces it does not read.** On Windows the
`HKLM`/`HKCU\SOFTWARE\Policies\ClaudeCode` policy keys, on macOS the `com.anthropic.claudecode`
managed-preferences domain. A file-only reader that stays silent about them lets an absent
`managed-settings.json` read as "no managed policy deployed" while a policy is in force.

### Added

- **`audit-permission-state`** — a new skill reporting which permission rules are actually in effect
and where each comes from. `/permissions` lists your rules and the file each came from, but it does
not resolve which of two conflicting rules wins, cannot distinguish a scope that was empty from one
it could not read, and exists only inside a live session — there is no `claude permissions`
subcommand and no machine-readable export. The reader discovers managed policy, user-global,
project, local, and any
pre-v2.1.211 start-directory copy, and inventories each scope's `allow`/`ask`/`deny` rules with its
source named. Every scope and every managed surface emits a record on every OS, so a surface that
was never attempted can never be mistaken for one that is genuinely empty: `absent` means looked and
found nothing, `skipped` means could not look. Server-managed settings are disclosed as having no
local path rather than assumed absent. Report-only, and managed policy is read-only by construction.
A second pass merges those scopes into the set actually in force, each rule naming every scope that
contributes it and the documented mechanic that put it there. Permission rules merge across scopes
rather than override, so a rule written at two scopes has no winner and is never reported as one;
what a rule can lose is its kind, because deny is evaluated before ask and ask before allow from any
scope in either direction — a user-level deny blocks a project-level allow just as the reverse. The
beaten entry is reported as inert alongside the rule that beat it, which is the answer to "why is my
allow rule ignored". A rule that is a bare tool name reaches every call of that tool: a whole-tool
deny removes the tool from context entirely, so every other rule naming it is inert — including
other denies, which are moot rather than weakened — and a whole-tool ask prompts for every call, so
no scoped allow for that tool applies. `EndConversation` is exempt from removal, as documented. Every run states the two bounds on the claim: the command-line scope
(`--settings`, `--allowedTools`, `--disallowedTools`) outranks the files and has none to read, and
rules are compared by exact text, so a narrow allow blocked only by a broader deny pattern is still
reported effective — the error direction is over-reporting allow, never over-reporting blocking.
- **`lib/permission-patterns.sh`** — the auto-mode drop vocabulary (blanket, wildcarded-interpreter,
package-manager-runner, and script-glob rule shapes, plus the top-level tool-token grammar) as a
define-only library. It was inline in the P1 detector, which self-executes and cannot be sourced,
so a second consumer had no way to reuse it without copying.
- **`lib/managed-scope.sh`** — the per-OS managed-policy surface enumeration (base JSON file,
`managed-settings.d/` drop-in directory, Windows policy registry keys, macOS preferences domain).
`claude-memory` carries a byte-identical copy, registered as a cross-plugin shared-source cluster
so the two cannot drift.

## [0.32.0]

Two behavior changes, hence the minor: `permission-rule-check.sh` refuses an unresolvable scan root
Expand Down
28 changes: 26 additions & 2 deletions plugins/claude-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ different question about the same surface:
| `/claude-config:audit` | Are the configuration FILES (`settings.json`, `settings.local.json`, `.mcp.json`, hooks, plugins, permissions) correct against upstream truth? |
| `/claude-config:audit-automation-gaps` | Is the configured automation SET the right set — are there genuine gaps, judged against the enforcement hierarchy? |
| `/claude-config:audit-permission-grants` | Are the permission GRANTS (`allowed-tools`, `permissions.allow`) portable and durable — do they survive auto mode, work across machines, and live where they can take effect? |
| `/claude-config:audit-permission-state` | Which permission rules are actually IN EFFECT, and where does each one come from — across managed policy, user-global, project, local, and the pre-v2.1.211 start-directory copy? |
| `/claude-config:audit-instructions` | Are the INSTRUCTIONS you wrote (CLAUDE.md, rules, skill bodies, agents, hooks, output styles) still earning their context cost against current model capability, or is prior-model scar tissue holding the model back? |
| `/claude-config:audit-pass` | Can all of that run as ONE ordered, resumable pass over a named target — every scope inventoried before any check, one reconciled findings artifact, one human gate — instead of several separate runs whose results nobody reconciles? |
| `/claude-config:unhobble` | What does the CURRENT MODEL actually still need — measured, not reasoned: reversibly strip the project's standing instructions to a bare baseline, log real stumbles, and re-add only what the evidence earns back? |
Expand Down Expand Up @@ -59,8 +60,11 @@ Audits permission GRANTS (not file correctness — that is `audit`) for the fail
make a grant silently do nothing: interpreter-wildcard / blanket rules that Claude Code drops on
entering auto mode, hardcoded absolute machine/user paths (Bash rules match literally, no expansion),
and inert plugin self-grants. A deterministic detector scans skill/command/agent frontmatter
`allowed-tools` and `settings.json` / `settings.local.json` `permissions.allow`, and recommends the
bare-command-on-PATH pattern. The principle and citations live in the marketplace
`allowed-tools` and the project, local, and user-global `permissions.allow` arrays, and recommends the
bare-command-on-PATH pattern. The user-global file
(`${CLAUDE_CONFIG_DIR:-$HOME/.claude}/settings.json`) is where Claude Code's own "Always allow" path
writes, so on a long-lived machine expect it to carry most of the findings — and their remediation is
the operator's, since no skill can write that file. The principle and citations live in the marketplace
[permission-rule-hygiene convention](../../docs/conventions/permission-rule-hygiene/README.md).
Report-only.

Expand All @@ -70,6 +74,26 @@ Report-only.
/claude-config:audit-permission-grants settings # permissions.allow only
```

### audit-permission-state

Reports the permission state actually in effect. Claude Code exposes no `claude permissions`
subcommand and no machine-readable export, so "where is this rule coming from" has meant reading five
files in five places and hoping you knew all five. A deterministic reader discovers every settings
scope — managed policy, user-global, project, local, and any pre-v2.1.211 copy left in the session's
start directory — and inventories each one's `allow` / `ask` / `deny` rules with its source named.

The status vocabulary is the point: `absent` means looked and found nothing, `skipped` means could not
look. The managed scope is four surfaces per OS, not one file; the Windows policy registry keys and
the macOS managed-preferences domain are optional platform integrations that degrade visibly while the
portable core (the JSON file and its `managed-settings.d/` drop-ins) still reads. Server-managed
settings have no local path and are disclosed as invisible rather than assumed empty. Report-only —
it writes nothing in any scope, and managed policy is read-only by construction.

```shell
/claude-config:audit-permission-state # scopes + rule inventory
/claude-config:audit-permission-state --scopes # which scopes exist and which were readable
```

### audit-instructions

Audits instruction *content* against current model capability — a different question from the
Expand Down
Loading
Loading