feat(claude-permissions): fleet permission floor component, distributed to dotfiles - #210
Conversation
…ed to dotfiles One reviewed allow/deny set for Claude Code agent sessions (Boris step-2 guardrail: pre-approve common safe commands), shipped as data and composed by the consumer, never merged by the sync engine (runner-policy handoff pattern). deny = strictest observed union of the fleet's two substantive deny surfaces (170 rules: destructive git verbs in Bash + PowerShell spellings, gh api DELETE surface, hook-disable prefixes, secret-material reads). allow = 60 safe-everywhere rules: read-only inspection, routine non-destructive working verbs, lint tooling, plus PowerShell read-parity — the one additive gap 2026-07-20 transcript mining surfaced (all 47 PowerShell prompts fired because zero PowerShell() allow rules existed). Data nests under one unique claudePermissions key for the consumer's shared template-data namespace. Machine-layer (managed policy) placement deferred with trigger in the component README. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 3m 26s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4fa0e04923
ℹ️ 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".
Review gaps: Bash(git push *) also pre-approved --delete/-d and --force-with-lease spellings, and Bash(git add *) pre-approved -f/--force staging of ignored files (secret-material .env class). All four families now denied in both shell spellings, bare-flag and mid-command forms; deny wins over the broad allows. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 55s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0c3856e3ca
ℹ️ 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".
…no-verify spellings Review gaps: git checkout -B / git switch -C reset an existing branch under the broad checkout/switch allows, and git commit -n is the short --no-verify spelling the long-form deny missed. All denied in both shell spellings; bare and mid-command forms. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 57s —— View job Code Review —
|
… --dry-run, not a hook bypass Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 28s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e82d3f881
ℹ️ 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".
…eat model Deny additions (both shell forms): git diff --no-index secret reads, forced fetch (--force/-f/+refspec force syntax), terminal-position checkout -f/--force, bare and terminal commit -n. README gains an explicit threat-model boundary: the glob grammar pins common destructive spellings and is one defense-in-depth layer — not a sandbox; flag permutation is structurally unclosable by rules, and the classifier-side command decomposition gap is the recorded structural fix. Observed new spellings land on sight; adversarial enumeration beyond common forms is out of scope. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 38s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af6522491c
ℹ️ 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".
…etches Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 38s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 911decf168
ℹ️ 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".
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 21s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e23b5a5ee8
ℹ️ 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".
…discard flags Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 33s —— View job Code Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a87d4822c0
ℹ️ 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".
… once, never self-apply (#730) ## Summary Ships #495's three fix directions: 1. **Reviewed permissions recipe by pointer**: `reference/permission-preflight.md` cites the standards `claude-permissions` component (the fleet's canonical allow/deny floor, merged today: 60 allow incl. PowerShell read-parity, 244 deny) composed operator-side via the dotfiles chezmoi seam — no restated list to drift. 2. **Trusted worktree-root guidance**: the sibling out-of-tree worktree root plus the matching `permissions.additionalDirectories` entry, root-agnostic. 3. **Loop-start preflight check**: `skills/work/scripts/preflight.sh` reports ONCE, up front — cwd-not-a-repo (note), probed core verbs (`git commit`, `git push`, `gh pr create`, `gh issue comment`) uncovered by any allow rule, worktree root not in `additionalDirectories`. Always exits 0 (report-only), `--count` for scripted gating, no live permission probe. **Never self-applies**: the auto-mode classifier blocks an agent editing its own `permissions.allow` (empirically hit during #495's own remediation attempt), so the check detects and points at operator-side remediation. Wired as the work skill's first loop-start action; babysit-prs applicability noted by pointer (no cross-plugin edit). 28/28 hermetic test cases; shellcheck/shfmt/markdownlint/typos/validate-plugin-contracts/changelog-parity all green. Live smoke on this machine correctly flagged two real gaps. Version 0.17.0 (composed above the concurrently-merged 0.16.0 container-verbs entry). ## Related - melodic-software/standards#210 (the claude-permissions floor this consumes) - melodic-software/dotfiles#242 (the composition seam the remediation points at) - #697 (the mining evidence for the report-only constraint) Closes #495 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## What Adds `gh pr review` comment-form spellings to the `claude-permissions` allow floor, alphabetically between `gh pr ready` and `gh pr view`: - `Bash(gh pr review * --comment *)` - `Bash(gh pr review * --comment)` - `Bash(gh pr review --comment *)` `--approve` and `--request-changes` forms are deliberately NOT floor-allowed — they can satisfy or block required-review gates, so they stay prompted per session. ## Why Component change discipline requires observed-usage evidence for `allow` additions: recurring real-session need to submit PR review comments from agent sessions (recorded in the plugin-batch session handoff, 2026-07-21). Fits the existing "PR and issue CRUD" allow posture; `gh pr merge --auto` remains denied. ## Distribution Reaches the dotfiles consumer (`.chezmoidata/claude-permissions.json`) through the ordinary sync PR; the modify-template union then composes it into `~/.claude/settings.json`. ## Related No linked issue. Component introduced in #210; observed-usage evidence recorded in the plugin-batch session handoff (claude-code-plugins `.work/handoffs/20260721T053034Z-handoff-plugin-batch-complete.md`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01U9zbRxtRuTx1WsQavFc2Gi --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…tomerge-alert (#251) No related issue: caller rollout for merged melodic-software/ci-workflows#213 (issue #210 closed) ## Related ci-workflows#210, ci-workflows#213, ci-workflows#223 ## Summary - standards owns the sync trigger point and already holds the App secrets `sync.yml` uses, so it hosts the scheduled caller for the stuck-automerge watchdog reusable added in melodic-software/ci-workflows#213, rather than medley. - **This PR is a draft, blocked on melodic-software/ci-workflows#223.** The pinned SHA (`dd45dacd7b74dd05f3334b78769e57225f7356d8`) is the reusable as merged in #213, which authors its tracking issue with the caller's ambient `GITHUB_TOKEN` and therefore needs a caller-granted `issues: write`. That combination cannot satisfy `runner-policy`'s write-caller-permissions invariant (identity-passthrough-only secret mapping, no hyphens) against this reusable's kebab-case `app-client-id`/`app-private-key` secret inputs — see #223 for the full diagnosis and the fix (the reusable mints its own App token for issue writes instead). - The workflow file and `policy.json` entry here are already written for the **post-#223 shape**: no caller `issues: write`, no `allowedCallerPermissions` waiver, secrets-only contract. Only the pinned SHA (in both the workflow file and the policy.json key) needs to flip to #223's merge SHA once it lands — no other change. ## Test plan - [x] `npm run test:runner-policy` — 228/228 pass - [x] `npm run lint:runner-policy` — Runner policy passed - [x] `actionlint .github/workflows/standards-sync-stuck-automerge-alert.yml` — clean - [x] `zizmor .github/workflows/standards-sync-stuck-automerge-alert.yml` — no findings - [x] `npx biome check components/runner-policy/policy.json` — no fixes needed - [ ] Flip pinned SHA to ci-workflows#223's merge SHA once it merges, mark ready for review 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01KYvF6bWGqemS9aYFfWJRiW --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ins (#267) ## What Re-derives the `claude-permissions` allow floor against Claude Code auto mode's built-in coverage (fleet default is now `permissions.defaultMode: "auto"`; migration tracked in melodic-software/dotfiles#309, this PR is its P1b phase). Allow floor 90 → 81; deny grows by 14 pull-spelling rows (`--no-verify` and forced-fetch families mirrored onto `git pull`, both shells — the review round that also dropped the rerun grant); the component carries no ask array. An earlier shape of this PR trimmed 45 rows on the premise "auto mode covers read-only commands promptless." That premise was then MEASURED and found false for everything except read-only git (empirical record: the operator-local migration file `.work/auto-mode-defaults/RESEARCH.md`, "Addendum — built-in read-only set, MEASURED" — not repo-reachable, so the decisive figures are inlined here; `claude -p --setting-sources "" --permission-mode default`, v2.1.219): **9/9 git inspection spellings ran promptless** (`status`, `diff`, `log`, `show`, `branch`, `branch --list`, `ls-files`, `merge-base`, `rev-parse`); **16/16 non-git commands were denied** — every `gh` read verb, every third-party linter, even bare `--version`. Membership is command identity, not flag safety, and it is tool-agnostic (git ran promptless through `PowerShell()` too). The trim was re-derived accordingly: - **TRIM 16** — read-only *git* inspection only (10 `Bash()` + 6 `PowerShell()` spellings). The built-in read-only command set runs these promptless in every mode, so floor entries for them are dead weight everywhere, not just under auto. - **RESTORE 29** — the read-only `gh` verbs, third-party linters, and `claude plugin` inspection rows the earlier shape wrongly trimmed. Measured denied without them; they are load-bearing in every non-auto lane (workflow subagents always run `acceptEdits`; headless `-p`/SDK runs fail with nobody to answer a prompt). - **REPLACE 2 dead rows with 4 live spellings** — the bare-wrapper rows `Bash(source-control-babysit-merge *)` / `Bash(source-control-babysit-resolve-thread *)` match nothing today: the plugin `bin/` directory is not on the shell's PATH and the skill invokes each wrapper as `bash "${CLAUDE_PLUGIN_ROOT}/bin/<wrapper>"` (claude-code-plugins `3fc72d351c`). They are dropped, and the wrappers instead carry the same interim path-form convention as the five plugin scripts already on the floor — quoted and unquoted `Bash(bash …${CLAUDE_PLUGIN_ROOT}/bin/<wrapper>…*)` spellings, 4 rows. Bare-name rows return when claude-code-plugins#843 makes the bare name resolve. - **DO NOT PROMOTE the 4 `pytest` rows** the earlier shape added. A test runner executes whatever test files are on disk, and a session that can write files plus a blanket `pytest` grant compose into a general code-execution grant wearing a narrow name — the same refusal melodic-software/dotfiles#315's git entry records three times. Test runs are judged per session. - **PROMOTE 6** — additions with observed-usage evidence or a reviewed unattended-lane need, per the component's change discipline: `git pull` (bare + starred) and four gh write verbs (rationale below). No PowerShell parity rows: the PS-mirror convention covers read-only inspection only, and write verbs have never carried mirrors. ### Promote — issue/PR metadata verbs and CI re-run - `gh issue close *`, `gh issue reopen *`, `gh pr edit *` — the same class as floor incumbents already present (`gh issue comment/create/edit`, `gh pr comment/create/ready`); excluding them was an arbitrary gap in an otherwise-consistent issue/PR metadata-write posture. ### Considered, not promoted (decision record) - `Bash(gh api graphql *)`, `Bash(gh api orgs/*)`, `Bash(gh api repos/*)` — empirical evidence in melodic-software/claude-code-plugins#1235 (gap 2) shows prefix allow rules of exactly this shape did NOT short-circuit the classifier's category safety layer for external writes. They also widen the granted surface in non-auto sessions, which is precisely the posture the floor's rows exist to define. Kept machine-local. - `Bash(gh workflow run *)` — dispatches arbitrary workflows with operator-controlled inputs; a CI-execution trigger is a different risk tier from issue/PR metadata edits, and the melodic-software/claude-code-plugins#697 precedent keeps high-blast-radius write verbs (merge) off the floor. Kept machine-local. - `Edit(//**/github.com/melodic-software/**)` — encodes this machine's checkout layout; a fleet-distributed floor must not carry a machine's directory layout. Kept machine-local. ## Scope limit under auto mode These grants are deterministic only outside auto mode's `classifyAllShell`. That key is NOT yet set fleet-wide: G3a split it out of melodic-software/dotfiles#315 into its own gated follow-up draft PR in dotfiles, which lands only after the operator's prose-grant test (dotfiles#309 P3 test 3) passes. Until then the narrow pre-classifier short-circuit these rows provide still operates in auto-mode sessions for matching spellings. Once the key lands, every shell allow rule is suspended in auto-mode sessions and this floor is the fallback posture for non-auto lanes — which the measurement above shows are exactly the lanes that need it. `deny` is unaffected in every mode and every regime — `classifyAllShell` suspends allow rules only; the 262 deny entries stay pre-classifier and non-overridable, which is exactly why G1 keeps all of them. ## Why - The floor predates auto mode: it was built (#210, #223, #228; lineage melodic-software/claude-code-plugins#695) to pre-approve safe commands so unattended loops never prompt. Auto mode provides that natively for read-only git only — the measured set — so exactly those entries no longer pay for their maintenance surface. - Merge-verb policy is unchanged: `gh pr merge` stays out of the allow floor and `gh pr merge --auto` stays denied, per melodic-software/claude-code-plugins#697; babysit merges keep going through the guarded gate wrapper. - Deny/ask keep-all is a locked decision (dotfiles#309 G1): explicit deny is pre-classifier and non-overridable, while built-in coverage is consent-gated soft_deny — trimming deny would downgrade hard blocks to consent gates. ## Per-entry disposition (reconciles to 90) Baseline 90 = TRIM 16 + DROP 2 + KEEP 72; final 81 = KEEP 72 + PROMOTE 5 + REPLACE 4 (the wrappers' path-form spellings). A review round later dropped the sixth PROMOTE candidate (`gh run rerun *` — a bare run id names any accessible run, so no floor glob holds it to merged workflow code; judged per session until a guarded wrapper exists). <details> <summary>TRIM — 16 entries (read-only git, built-in-covered, measured)</summary> | Entry | | --- | | `Bash(git branch --list *)` | | `Bash(git branch)` | | `Bash(git diff *)` | | `Bash(git log *)` | | `Bash(git ls-files *)` | | `Bash(git merge-base *)` | | `Bash(git rev-parse *)` | | `Bash(git show *)` | | `Bash(git status *)` | | `Bash(git status)` | | `PowerShell(git diff *)` | | `PowerShell(git log *)` | | `PowerShell(git merge-base *)` | | `PowerShell(git rev-parse *)` | | `PowerShell(git show *)` | | `PowerShell(git status *)` | </details> <details> <summary>DROP 2 / REPLACE 4 — bare wrappers out, path-form spellings in</summary> Dropped (match nothing until ccp#843): | Entry | | --- | | `Bash(source-control-babysit-merge *)` | | `Bash(source-control-babysit-resolve-thread *)` | Added in their place (the real invocation shape, parity with the five script rows): | Entry | | --- | | `Bash(bash "${CLAUDE_PLUGIN_ROOT}/bin/source-control-babysit-merge"*)` | | `Bash(bash ${CLAUDE_PLUGIN_ROOT}/bin/source-control-babysit-merge*)` | | `Bash(bash "${CLAUDE_PLUGIN_ROOT}/bin/source-control-babysit-resolve-thread"*)` | | `Bash(bash ${CLAUDE_PLUGIN_ROOT}/bin/source-control-babysit-resolve-thread*)` | </details> <details> <summary>KEEP — 72 entries</summary> Everything else in the baseline: the non-destructive working verbs, the gh write incumbents, the RESTORED read-only `gh`/linter/`claude plugin` rows (measured load-bearing), the 10 interim `${CLAUDE_PLUGIN_ROOT}` script-path rules (README carries the interim note; end state is bare wrappers on the plugin `bin/` PATH, trigger claude-code-plugins#843), and the PowerShell read-only mirrors outside the built-in git set. </details> <details> <summary>PROMOTE — 6 entries</summary> | Entry | Evidence | | --- | --- | | `Bash(gh issue close *)` | metadata-write parity with floor incumbents | | `Bash(gh issue reopen *)` | metadata-write parity with floor incumbents | | `Bash(gh pr edit *)` | metadata-write parity with floor incumbents | | `Bash(git pull *)` | operator runtime keeper (dotfiles#309 P2 keep set) | | `Bash(git pull)` | operator runtime keeper (dotfiles#309 P2 keep set) | </details> ## Status DRAFT — stays draft pending operator review of this amended body; merges AFTER melodic-software/dotfiles#315 (merging this first would trigger the standards→dotfiles sync bot to rewrite `.chezmoidata/claude-permissions.json` mid-flight under #315). ## Related No linked issue. This is the P1b phase of a cross-repo migration; it closes no issue in this repository. - melodic-software/dotfiles#309 — auto-mode migration (P1b phase) - melodic-software/dotfiles#315 — host-side auto-mode hardening (merge-order dependency) - melodic-software/claude-code-plugins#697 — merge-verb / classifier-tuning policy - melodic-software/claude-code-plugins#843 — bare-wrapper PATH end state - melodic-software/claude-code-plugins#695 — component lineage (introduced in #210) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
New
claude-permissionscomponent — the fleet's reviewed Claude Code permission floor (Boris's step-2 guardrail verbatim: "Pre-approve common safe bash and MCP commands in settings.json") — distributed as DATA to the dotfiles consumer, which owns runtime composition into~/.claude/settings.jsonvia its existing modify-template (the runner-policy consumer-handoff pattern; the sync engine stays byte-exact, no merge primitive touched).Bash()/PowerShell()spellings, bare + starred forms: force-push/hard-reset/clean/discard family, forced branch deletion,--no-verify,gh apiDELETE surface, hook-disable env prefixes, secret-materialRead()patterns.PowerShell()allow rules, so every PowerShell invocation prompted by construction. Merge verbs deliberately excluded (operator-policy, not safe-verb).melodic-software/dotfilesadopts asmanaged→.chezmoidata/claude-permissions.json. Machine-layer (managed policy settings via provisioning) is deferred with a recorded trigger in the component README.sync-manifest.sh validate+ schema validation + full engine test suite (139 PASS) green.Consumer-side composition (dotfiles modify-template unioning these rules into the live settings) lands as the follow-up dotfiles PR; claude-code-plugins#495 (permission preflight) and #697 (classifier tuning) consume this component.
Related
No linked issue in this repository.
🤖 Generated with Claude Code