fix(review): declare allowed-tools so quality-gate survives headless gh preflight denial (#290) - #658
fix(review): declare allowed-tools so quality-gate survives headless gh preflight denial (#290)#658kyle-sexton wants to merge 2 commits into
Conversation
…gh preflight denial (#290) The quality-gate skill's Pre-computed context block injects dynamic context via the !`<command>` syntax, which runs during prompt expansion before the model turn — so the permission gate sits above the shell. In a headless session the gh pr list preflight was permission-denied during that preprocessing and the whole slash invocation aborted with empty output and exit 0. The in-command `|| echo "unknown"` guard cannot catch this: the denial happens a layer above the shell, so the fallback never runs. Declare allowed-tools authorizing every segment of the three compound pre-computed lines (git branch, git status, head, echo, gh pr list) — the documented canonical mechanism for dynamic-context bash, matching the pressure-test and wayfind precedents. Existing `|| echo` fallbacks are retained; they cover a different failure mode (gh missing / unauthenticated / no PRs). Closes #290 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 51s —— View job PR Review: fix(review) — quality-gate headless preflight denial
SummaryThis is a correct, well-motivated fix. The root-cause analysis in the PR body is technically sound — Segment coverage — verified ✓All five rules cover every segment of the three compound pre-computed lines:
No segments are missing; no rules are unreachable. Precedent alignment — ✓The pressure-test precedent uses: allowed-tools: ["Bash(git branch:*)", "Bash(git status:*)", "Bash(head:*)", "Bash(echo:*)"]The quality-gate addition is a strict superset with only Findings1. The pre-computed line calls 2. Minor format inconsistency with The What is correct
VerdictReady to merge. The fix is structurally correct, the implementation matches the established pattern, segment coverage is complete, and the scope is appropriately bounded. The one breadth note on |
Post-green review classification
No blocking findings. CI green (17/17). Flipping to ready for review. |
|
Claude finished @kyle-sexton's task in 2m 1s —— View job PR Review: fix(review) — quality-gate headless preflight denial
Segment coverage — verified ✓Cross-referenced the
All segments covered. No rule is unreachable. Precedent alignment — ✓Inspected all Findings1.
2. CHANGELOG The entry says the fix matches "the What is correct
VerdictReady to merge. The fix is structurally correct, segment coverage is complete, the implementation is byte-identical in form to confirmed headless-working precedents, and scope is appropriately bounded. Both findings are non-blocking: the |
Round 2 review — classification (closing the coverage gap)The prior classification reply landed ~18s before the second review round posted, so it only covered round 1. Recording the round-2 delta here:
No blocking findings across either round. |
Resolves the review plugin CHANGELOG conflict against merged #673: this PR's entry is re-homed under 0.14.6, above main's 0.14.5, and plugin.json is bumped to 0.14.6 to keep the two fixes on distinct versions.
|
Claude finished @kyle-sexton's task in 1m 56s —— View job PR Review: fix(review) — quality-gate headless preflight denial
Segment coverage — verified ✓Cross-referenced the five
No segment is uncovered; no rule is unreachable. Precedent alignment — ✓The five-element JSON inline array with Findings1. CHANGELOG line 30 makes a false precision claim about rule scope — moderate / informational The CHANGELOG entry says: "the The rule itself is accepted convention (matches 2. The pre-computed command uses 3. CHANGELOG The entry says this matches "the Version bump — ✓
What is correct
VerdictReady to merge with one recommended CHANGELOG fix. The functional change ( |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ccc80e566c
ℹ️ 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".
| argument-hint: "[mode] (e.g., /review:quality-gate, /review:quality-gate self, /review:quality-gate security, /review:quality-gate slice <name>)" | ||
| user-invocable: true | ||
| disable-model-invocation: false | ||
| allowed-tools: ["Bash(git branch:*)", "Bash(git status:*)", "Bash(head:*)", "Bash(echo:*)", "Bash(gh pr list:*)"] |
There was a problem hiding this comment.
Restrict the git branch grant to --show-current
When quality-gate is active, this rule pre-approves every git branch subcommand, not just the pre-computed read. That includes mutating commands such as git branch -D <branch>, so a prompt-injected instruction from reviewed repository content can delete local branches without the normal permission gate. Scope this to git branch --show-current (and only its necessary arguments) instead.
Useful? React with 👍 / 👎.
|
Superseded by the signed twin PR (identical tree, single signed commit): #658's first commit is unsigned and required_signatures blocks the merge; a history rewrite on the shared branch is operator-gated, so the tower took the additive supersede path instead. Digest verdict (MERGE-READY at this exact tree) carries over. Root fix for the class: #631 (register laptop signing keys). |
…runs (#290) (#682) ## Summary Signed supersede of PR #658 (identical tree, single signed commit). #658's original lane commit was unsigned and the org ruleset's required_signatures blocks it; the branch content — digest-verified MERGE-READY — is unchanged here. ## Fix Declares the five allowed-tools rules on the review quality-gate skill so its pre-computed dynamic-context lines survive headless runs (issue #290 candidate 3). Includes the 0.14.6 version bump + CHANGELOG entry (re-homed above #673's 0.14.5) and the digest's wording soften (four rules read-only; `Bash(git branch:*)` scoped in practice to `git branch --show-current`). ## Verification Carried from the digested branch at identical tree: validate-plugins.sh green, validate-plugin-contracts.mjs green (1793 files), standards-binding.test.sh PASS=8 FAIL=0, 17/17 CI checks green pre-supersede. CI re-runs here. ## Related Closes #290 Supersedes #658 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FM1RfM3jHkgenpdbMv4o64 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… retro rubric (signed supersede of #489) (#746) ## Summary `session-flow` baked a fixed workflow stage taxonomy (workflow's 8 stages) and a fixed 5-dimension retro scoring rubric as universal defaults, but never documented the override boundary. Per the plugin extensibility contract, a consumer must be told how to override without editing the plugin — this closes that documentation gap. ## Fix Make the boundary explicit in the two skills that own the fixed structure, documenting the existing mechanism rather than inventing one: - `workflow/SKILL.md` — new **Override boundary** bullet in "Consumer conventions": the stage set is fixed plugin identity, there is no seam to swap in a different taxonomy, and what adapts (execution, gate commands, review criteria) flows through the conventions already named in that section. - `retro/SKILL.md` — new "What this skill does NOT do" bullet: the five scoring dimensions are fixed plugin identity with no swap seam; what adapts is what each dimension scores *against* (the consumer's conventions, session-type calibration). Placed in `SKILL.md` rather than `context/session.md` because the dimensions surface across multiple mode context files, not just `session` mode. The honest boundary is "taxonomy/rubric is fixed; only execution, gates, and scoring criteria adapt" — the existing seam is the consumer's own tracked instruction files, already referenced by the adjacent conventions. No behavior change. Version bumped `0.10.1` -> `0.10.2` (docs = patch) with a matching CHANGELOG entry. ## Verification Ran the repo-pinned gates on all changed markdown (`workflow/SKILL.md`, `retro/SKILL.md`, `CHANGELOG.md`): - `markdownlint-cli2` v0.23.0 (schema-pinned in `.markdownlint-cli2.jsonc`), config `.markdownlint-cli2.jsonc` -> `Summary: 0 error(s)` - `typos` config `_typos.toml` -> exit 0, no findings - `editorconfig-checker` config `.editorconfig-checker.json` -> exit 0 `plugin.json` version single-homed (marketplace entry carries no version field). This PR supersedes #489: #489's branch tip (`fix/433-session-flow-override-boundary`) carries an unsigned merge commit (`92ef6cf9`, verification reason `unknown_key`) that the org's required-signatures ruleset permanently blocks from merging — the #631 known-issue class. Per the sanctioned playbook (precedent: #682 superseding #658), the existing branch's history was never rewritten; instead #489's net content vs `main` was materialized byte-identically and re-committed as a single signed commit on a fresh branch off current `main`. Closes #433 ## Related - #489 (superseded by this PR) - #631 (known issue class: unsigned merge commits blocked by required-signatures ruleset) - #682 (signed-supersede playbook precedent) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
claude -p "/review:quality-gate …"in a non-interactive session produced empty output and exit 0 — total silent failure, no error, no model output. This fixes that by declaringallowed-toolsfrontmatter on thequality-gateskill so its pre-computedghpreflight is authorized during headless prompt expansion.Fix
The skill's Pre-computed context block injects dynamic context via the
!`<command>`syntax. Per the skills docs, each!`<command>`is preprocessing that runs before the model turn — during prompt expansion, with the permission gate sitting above the shell. In a headless session thegh pr listpreflight was permission-denied there, and the whole slash invocation aborted before producing any model output.The existing in-command
|| echo "unknown"guard is structurally incapable of catching this: the denial happens a layer above the shell, so the shell string (and its||fallback) never runs. Prose invocation ("use the quality-gate skill…") degraded gracefully only because it has no dynamic-context preprocessing — the model issuesghas an ordinary Bash tool call whose denial returns a handleable result. This is why candidate fix #1 (a better in-command guard) cannot work, and why #2 (dropgh) was rejected — it would discard the pre-computed PR list thatpr/self/slicemodes and diff-base resolution consume.Chosen fix (candidate #3): add
allowed-toolstoplugins/review/skills/quality-gate/SKILL.md, the documented canonical mechanism for dynamic-context bash, matching two in-repo precedents (prototype/pressure-test,planning/wayfind):Claude Code evaluates each segment of a compound command independently, so all five rules are load-bearing — the three pre-computed lines are compound (
… || echo,… | head … || echo). The existing|| echofallbacks are retained: they cover a different failure mode (ghmissing / unauthenticated / no PRs) thatallowed-toolsdoes not touch. These are narrow, non-interpreter subcommand rules that carry into auto mode — not the interpreter-wildcard anti-pattern; four are read-only, andBash(git branch:*)is scoped in practice to its actual use,git branch --show-current, though the wildcard form also matches mutating subcommands.Scope-honest residual
allowed-toolsfixes the reported scenario (default headless-p) and ordinary auto mode. It does not cover a session withautoMode.classifyAllShell: true, which suspends even narrow shell allow rules; that narrow edge would need candidate #2 (droppinggh), at the cost of the pre-computed PR list. Known edge, not a reason to prefer #2 as the default.Verification
A faithful live headless permission-denial repro is not cleanly reproducible in this environment because it allow-lists
gh: a spawnedclaude -pinherits that grant and never reaches the default-headless denial that triggers the bug. UnsettingGH_TOKENwould exercise the wrong path — it letsghactually run and fail on auth, firing the|| echofallback that already worked on both old and new versions, so it proves nothing. Rather than fabricate a repro, verification rests on structural proof, the repo's own validators, and an exact shipping precedent (mirroring the issue triage note's honest caveat).Segment coverage — every segment of all three compound pre-computed lines is authorized (extracted from the committed SKILL.md):
git branch --show-current … || echo "unknown"git branch,echoBash(git branch:*),Bash(echo:*)git status --porcelain … | head -20 || echo "unavailable"git status,head,echoBash(git status:*),Bash(head:*),Bash(echo:*)gh pr list --json … --limit 10 … || echo "unknown"gh pr list,echoBash(gh pr list:*),Bash(echo:*)Repo validators (all green against the change):
The new
allowed-toolsvalue parses as a valid JSON/YAML array, and the frontmatter is byte-identical in form to theprototype/pressure-testskill that already ships this exact rule set and runs headlessly — direct precedent that the mechanism authorizes dynamic-context bash in non-interactive sessions.Closes #290
Related
!`gh`/!`git`commands but no matchingallowed-toolsfrontmatter may share this same latent silent-death bug. The triage note recommends a separate item — grep skills for!`dynamic-context commands lacking corresponding allow rules, and consider extendingclaude-config:audit-permission-grantsto flag them. This PR stays quality-gate-only per that guidance.🤖 Generated with a Claude Code implementation subagent (issue #290)