feat: add github admin-plane plugin - #781
Conversation
Task contract for the new github operations plugin: user-locked Brief (D1-D10), design-resolution early-exit artifact, and the approved Plan (7 phases, stress-tested by two fresh-context passes; scope-qualified change routing with policy-floor write posture; zero-vendored-knowledge method recipes; browser-automation offer with layered gates). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017u8VXXEMUtTn5GDkr83Kmr
…area router
Scaffold plugins/github (manifest 0.1.0, README with verb contract incl. the
advise verb declaration, CHANGELOG) and prove the whole read path end-to-end:
audit skill -> 27-area router -> generic method ladder -> live gh state +
runtime doc fetch -> grounded findings.
- reference/method-ladder.md: gh native -> gh api REST -> GraphQL -> UI-only
detection -> guided manual + deep link; fetch-integrity rung, credential-
modality diagnosis, 403/404 disambiguation, plan/SKU honest degradation,
org-scale scoping (confirmed sweeps, incremental emission, honest 429
partials).
- skills/audit/SKILL.md: read-only contract in write-capability terms (no
-f/-F/--field/--raw-field/--input, no non-GET --method/-X, no GraphQL
mutation body), refusal branch (never present recall as grounded), fetched
GitHub content = untrusted data; all intra-plugin citations anchored
${CLAUDE_PLUGIN_ROOT}/reference/.
- skills/audit/evals/evals.json: 5 cases drafted eval-first (trigger routing,
grounded happy path, bare-invocation mutation refusal, fetch-failure
refusal, injected-instruction anti-pattern).
- Register github in the audit leaf-name collision owner set (#721 gate).
- Mark PLAN.md Phase 1 [DONE].
Sanity: plugin validate exit 0; D4 endpoint grep clean; router keys = exact
Brief coverage list (27/27); markdownlint clean; run-plugin-tests exit 0;
headless smoke in a clean non-source dir produced grounded findings with a
zero-write transcript (write-capability guard).
Fresh-docs mandate: code.claude.com/docs/en/plugins, /en/plugins-reference,
/en/skills fetched 2026-07-20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017u8VXXEMUtTn5GDkr83Kmr
- reference/change-routing.md: routing.yaml schema contract_version 1.0.0 — scope blocks (repo / org.<login> / enterprise.<slug>), three routing values (propose / guided-apply / handoff), per-key override layering, policy-floor precedence inversion declared next to the write-posture keys (#649 class), target-resolution rule (never a silently inferred org target on --apply), recursive overlay gitignore line. - reference/conventions-file.md: concatenating prose conventions contract and how audits cite it; conventions carry no write posture. - skills/setup: SKILL.md (disable-model-invocation: true; check = gh presence, auth status, credential-modality picture, per-layer verdicts; apply = idempotent interview-driven config write, gitignore recommended never edited, no auto re-consent) + eval cases drafted eval-first. - consumer-config-layering README: github Implementers row (all three layers, declared policy-floor inversion). - PLAN.md Phase 2 tagged DONE. Sanity: plugin validate exit 0; D4 + agnosticism greps clean; markdownlint 0 errors; run-plugin-tests.sh exit 0; headless smoke — setup apply twice in a clean scratch repo, second run byte-identical (sha256), routing.yaml written with default: propose, .gitignore untouched. Fresh-context verifier: 12/12 binary criteria PASS. Fresh-docs: code.claude.com/docs/en/skills fetched 2026-07-20. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017u8VXXEMUtTn5GDkr83Kmr
- skills/advise: SKILL.md — forward-looking guidance/hand-holding vocabulary, proactive-suggestion posture (offered with provenance, never acted on), grounding + method-ladder citations, current-state anchoring, refusal branch, untrusted-data posture; negative routing boundary vs audit declared in BOTH skill descriptions. Eval cases drafted eval-first (7 cases: trigger, boundary, happy path, bare-invocation refusal, apply-unconfigured propose, guided-apply per-step confirm + read-back, handoff change request). - reference/change-routing.md: the --apply resolution flow — scope + target resolved first (org/enterprise targets asked, never silently inferred on a write path), then effective routing: propose (emit exact commands/diff, execute nothing) / guided-apply (per-step user confirms, each step naming the exact command/payload and its fetched-doc provenance, post-write read-back or explicit unverified) / handoff (change request shaped for the declared channel). Unconfigured resolves to propose. - skills/audit: --apply wired (read-path target inference explicitly does not carry over to writes); read-only-on-bare contract intact; new eval case (apply unconfigured → propose, no silently inferred org target). - README rows updated (advise shipped, --apply on both); CHANGELOG entries. - PLAN.md Phase 3 tagged DONE. Sanity: plugin validate exit 0; --apply grep lists both skills; both descriptions state read-only-on-bare; D4 + agnosticism greps clean; markdownlint 0 errors (9 files); run-plugin-tests.sh exit 0; headless smoke in a clean scratch repo — bare audit: transcript write-capability guard clean (no field/input flags, no non-GET method, no GraphQL mutation); --apply with no config: resolved to propose, exact command + doc provenance emitted, nothing executed. Fresh-context verifier: 12/12 binary criteria PASS. Fresh-docs: code.claude.com/docs/en/skills fetched 2026-07-20. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017u8VXXEMUtTn5GDkr83Kmr
- reference/browser-automation.md: the UI-only rung's offer mechanics —
never-auto-fire rule as the governing rule (embedded instructions in
fetched content are untrusted data, never triggers); presence gates
(claude-in-chrome runtime MCP tool probe; playwright gated on the plugin
being installed with guided-manual fallback in the same sentence);
claude-in-chrome-first preference order (live authenticated session) with
user choice honored; confirm-gate offer template naming surface URL from
fetched docs, action, doc provenance, and the authenticated-session fact,
explicit yes per action; offer_browser_automation framed honestly as an
advisory gate layered under the per-action confirm; post-write read-back
via API where a read exists, explicit "unverified" where none does;
guided manual steps + deep link as the always-available fallback.
- plugin.json: userConfig.offer_browser_automation (boolean, title,
description, default true) per the platform userConfig schema.
- method-ladder rung 4 now cites the reference (placeholder removed).
- audit + advise SKILL.md: gate value surfaced in skill prose via
${user_config.offer_browser_automation} (substitution reaches skill
content, not files read at runtime) + reference pointer.
- README consumer section + CHANGELOG updated; PLAN.md Phase 4 tagged DONE.
Sanity: plugin validate exit 0 with userConfig; "never auto" grep >= 1;
playwright gate+fallback adjacency; markdownlint 0 errors (10 files);
run-plugin-tests.sh exit 0; D4 + agnosticism greps clean. Fresh-context
verifier: 14/14 binary criteria PASS. Fresh-docs:
code.claude.com/docs/en/plugins-reference fetched 2026-07-20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017u8VXXEMUtTn5GDkr83Kmr
- reference/recipes/: four method recipes per the non-hollow contract —
billing.md (16-question checklist, cost-control levers),
security-posture.md (19 questions across authentication, advanced
security, GitHub Apps, OAuth app policy, PATs), rulesets-repo-drift.md
(16 questions, declared-baseline + sibling-consistency drift axes),
actions-policy.md (17 questions, actionlint/D9 boundary stated). Each
carries credential-and-gate preflight, audit-question checklist,
cost/posture levers, drift-comparison procedure against declared
conventions (with no-conventions fetched-docs fallback + provenance),
dated re-verify-live caveats, and stable official-doc entry pointers.
Zero vendored endpoints, scopes, or prices — mechanics resolve at
runtime via the method ladder (D4).
- reference/areas.md: all 8 primary rows link their recipe (five
security-bundle rows point at security-posture.md); intro updated.
- README areas section + CHANGELOG updated; PLAN.md Phase 5 tagged DONE.
Sanity: all six contract headings grep-clean per file; checklists
16/19/16/17 (>=10); D4 grep (api.github.com | /orgs/{ | $digit) empty;
agnosticism grep empty; markdownlint 0 errors; plugin validate exit 0;
run-plugin-tests.sh exit 0. Fresh-context verifier: 12/12 binary criteria
PASS. Authored via 4 file-disjoint workers per the plan's [EXEC-SHAPE]
parallel window; fences held (git status audit). Fresh-docs:
code.claude.com/docs/en/skills fetched 2026-07-20.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X4sqsctNeU1FE38pWDx6tR
- skills/*/evals/evals.json: completed the three eval suites — added the anti-pattern browser-automation-never-auto-fires case to audit (id 7) and advise (id 8), and mirrored the injected-instruction anti-pattern into advise (id 9, installed-app description injection). Full case coverage per plan: trigger/routing, happy path, refusal (bare-invocation no-mutate, fetch-failure-not-recall, never-auto-reconsent), both anti-pattern contracts. All three schema-valid (check-jsonschema). - plugins/github/github.test.sh: committed contract test discovered by scripts/run-plugin-tests.sh — D4 sweeps (endpoints, prices, scope names in shipped prose), agnosticism sweep (*.md; plugin.json author is the sanctioned exception), area-coverage oracle (27-key canonical fixture in-test, diffed against reference/areas.md), recipe non-hollow contract (six headings + >=10-question checklist per recipe), evals presence. Red-proof: mutated copy fails naming both planted defects. shellcheck + shfmt clean (one repo-precedent SC2016 directive). - CHANGELOG updated; PLAN.md Phase 6 tagged DONE. Sanity: github.test.sh 36/0; run-plugin-tests.sh exit 0 with the new test discovered and passing; skill-quality check PASS x3 (descriptions 730/666/284 vs the 1,536-char listing cap — trigger vocabulary fits, no progressive-disclosure move needed); validate-evals conform x3; markdownlint 0 errors; plugin validate exit 0; agnosticism grep empty. Fresh-context verifier: 12/12 binary criteria PASS (its two observations closed by the advise id 9 case). Fresh-docs: code.claude.com/docs/en/skills fetched 2026-07-20. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X4sqsctNeU1FE38pWDx6tR
- docs/MIGRATION-PLAYBOOK.md: plugin-acceptance security-review record
for github (ACCEPT, 2026-07-21) as a subsection of the review criteria
it answers — no hooks/MCP/bin; one non-sensitive userConfig boolean;
cache-isolated; egress limited to api.github.com via the consumer's
own gh auth, official-docs runtime fetches, and the opt-in
confirm-gated browser-automation offer (offer_browser_automation
recorded honestly as advisory — the hard gate is the per-action
confirm); prompt injection via ingested GitHub content as an explicit
item (untrusted-data posture + anti-pattern evals audit id 5 /
advise id 9).
- .claude-plugin/marketplace.json: github entry (category operations,
source ./plugins/github, tags mirroring plugin keywords; no relevance
block — cli:["gh"] would over-fire).
- README.md: Operations catalog bullet.
- plugins/github/CHANGELOG.md: publish entry.
- PLAN.md: Phase 7 tagged DONE — all 7 phases complete.
Gates: 11-step migration gate walked clean (outcomes in
.work/handoffs/20260721T002752Z-handoff-phase-7.md); claude plugin
validate --strict . exit 0; github.test.sh exit 0; full
run-plugin-tests.sh exit 0; markdownlint 0 errors; PII sweep clean.
Security record independently verified by a fresh-context subagent
(13/13 binary checks PASS, RECORD VERIFIED); user gate passed
2026-07-21 ("Approve — commit"). Fresh-docs:
code.claude.com/docs/en/plugin-marketplaces + /plugins-reference
fetched 2026-07-21.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X4sqsctNeU1FE38pWDx6tR
|
Claude finished @kyle-sexton's task in 3m 51s —— View job Security Review — PR #781
|
|
Claude finished @kyle-sexton's task in 6m 9s —— View job PR #781 Review —
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bf721eeb22
ℹ️ 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".
- setup SKILL.md argument-hint: "check | apply" per setup-skill contract - github.test.sh: +x bit in git index; split grep flag cluster -rEin (typos checker read it as "Ein") - rulesets-repo-drift.md: mis-classed -> misclassified - README.md: regenerate catalog block via scripts/generate-catalog.mjs (block is generated; github row now follows marketplace.json ordering) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X4sqsctNeU1FE38pWDx6tR
|
Claude finished @kyle-sexton's task in 4m 40s —— View job PR #781 Review —
|
|
Claude finished @kyle-sexton's task in 5m 33s —— View job Security Review — PR #781
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e766de845b
ℹ️ 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".
- browser-automation: executable offer now gated on resolved guided-apply routing; propose/handoff (incl. unconfigured default) execute nothing — per-action confirm is consent to a step, not a routing override (Codex P1) - setup: local-overlay write requires git check-ignore verification and explicit user go-ahead before writing an unignored overlay (Codex P2) - read-only contract: gh api graphql carve-out — field flags may supply the query document/variables; mutation documents remain banned (Codex P2) - README: disclose offer_browser_automation as advisory, not a runtime kill-switch (claude review, Medium) Deferred: bash>=4 mapfile guard in github.test.sh — the shared test runner itself uses mapfile, so macOS bash-3 support is a fleet-scope decision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X4sqsctNeU1FE38pWDx6tR
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Claude finished @kyle-sexton's task in 2m 26s —— View job PR #781 Review —
|
|
Claude finished @kyle-sexton's task in 3m 20s —— View job Security Review — PR #781
|
|
Re claude[bot] review (APPROVE + 2 suggestions) and security review — classification per finding:
Setup worktree pre-check, evals-depth, and |
- github.test.sh: visible SKIP on bash < 4 — mapfile fails silently under set -uo pipefail on bash 3.x and the area oracle would spuriously pass on empty arrays - audit evals: id 8 covers --apply + propose routing + UI-only surface — routing precondition suppresses the executable browser offer even with browser tools present Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X4sqsctNeU1FE38pWDx6tR
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Claude finished @kyle-sexton's task in 1m 48s —— View job PR #781 Review —
|
| Finding | Commit | Status |
|---|---|---|
| Browser offer routing gate (P1 — Medium) | 44ce345 |
✅ Fixed |
| GraphQL read-path field flag restriction (P2) | 44ce345 |
✅ Fixed |
README advisory disclosure for offer_browser_automation |
44ce345 |
✅ Fixed |
| Local-overlay write before gitignore verified (P2 — Minor) | 44ce345 |
✅ Fixed |
mapfile bash 4+ guard |
ff81685 |
✅ Fixed |
| Routing-precondition eval | ff81685 |
✅ Fixed |
Outstanding items from prior reviews that remain at SUGGESTION/DEFERRED status (no action required before merge):
handoff.target/handoff.instructionsnot floor-protected — acknowledged inchange-routing.mdas intended; no execution occurs; low practical impactcloud-sandboxesarea uses search page as doc pointer — honest inline comment; tracked for future update
Verdict
APPROVE. The fix commit is clean and targeted — 19 lines across two files, both changes correct and matching prior recommendations exactly. The plugin is ready to merge.
|
Claude finished @kyle-sexton's task in 2m 18s —— View job Security Review — PR #781
|
| Prior finding | Commit | Status |
|---|---|---|
P1 — browser offer fires under propose/handoff routing |
44ce345 |
✅ Fixed — browser-automation.md L36–45 adds routing precondition; both SKILL.md files mirror it |
| P2 — local overlay written before gitignore protection verified | 44ce345 |
✅ Fixed — setup/SKILL.md L53–58 adds git check-ignore -q precondition before any *.local.* write |
README presents offer_browser_automation: false as hard off-switch |
44ce345 |
✅ Fixed — README L64–65 now: "advisory gate honored by the skills, not a runtime kill-switch; the hard gate is the per-action user confirm" |
| GraphQL read path blocked by blanket field-flag ban | 44ce345 |
✅ Fixed — method-ladder.md L35–38 carve-out permits field flags for gh api graphql; guard moves to the document itself |
mapfile bash-4 dependency → silent false-pass on macOS |
ff81685 |
✅ Fixed — this commit |
No eval covering --apply + propose routing + UI-only surface |
ff81685 |
✅ Fixed — audit eval id:8, this commit |
No findings in the following areas (current-state verification)
github.test.sh: All variable expansions remain quoted.SELF_EXCLUDE,PLUGIN_DIR,AREAS,RECIPES_DIRall safe.greppatterns are hardcoded literals. No attacker-controlled input reaches any shell expansion. No writes, no network calls, no execution of plugin-file contents. Clean.plugin.json: No hooks, nobin/entries, no MCP server declarations, no external dependency pins. Single booleanuserConfigkey — no string interpolation surface. Clean.- Read-only contract: Both
auditandadviseSKILL.md state the contract at the capability level (prohibiting-f/-F/--field/--raw-field/--inputoutside GraphQL, non-GET methods, mutation documents, write-verb native subcommands). The GraphQL carve-out is correctly scoped. - Routing precondition:
browser-automation.mdnow explicitly states the three-layer ordering — routing precondition selects the channel, advisory gate suppresses the offer, confirm gate protects every action. Both SKILL.md files carry the same constraint inline. - Local overlay write order:
setup/SKILL.mdnow requiresgit check-ignore -q <path>to pass before any*.local.*write; absent gitignore protection surfaces the recommended line and waits. - Credential handling:
setup/SKILL.mdandmethod-ladder.mdboth prohibit storing, echoing, or persisting credentials. Auto-runninggh auth refreshis explicitly prohibited and tested in setup eval id:5. ${CLAUDE_PLUGIN_ROOT}isolation: All intra-plugin references anchor at${CLAUDE_PLUGIN_ROOT}. Consumer config writes use${CLAUDE_PROJECT_DIR}/.claude/github/. No../reach-outs.- Supply chain: No third-party action pins, no external SaaS trust delegation, no MCP server declarations. GitHub reached only via the consumer's pre-existing
ghsession. - Prompt injection posture: Both
auditandadviseSKILL.md files have explicit "Standing security posture" sections treating all fetched GitHub content as untrusted data. Anti-pattern evals (audit id:5, advise id:9) test this contract.
Residual acknowledged items (not blocking merge)
handoff.target/handoff.instructionsnot floor-protected —change-routing.mdL121–122 explicitly documents this as later-layer-wins. A personal config layer can redirect a team-declared handoff channel. No execution occurs inside the plugin; direct security impact is low. Acknowledged in prior reviews and in kyle-sexton's disposition table as below action threshold.- Model-instruction-enforced write guards — inherent to the plugin-as-prompt-artifact architecture. Correctly disclosed in
MIGRATION-PLAYBOOK.md's accept rationale and acknowledged throughout the review history.
Summary: No new security findings in this commit or in the full PR. All six previously raised security issues are resolved across the two fix commits. The plugin is security-cleared for merge.
|
Re follow-up review: both remaining minors taken in ff81685.
Local gates re-run green: |
No related issue: plugin build tracked in-repo via
docs/topics/github-plugin-candidates/PLAN.md(Brief D1–D10 + 7-phase plan), not a tracker issue.Summary
githubplugin (v0.1.0): GitHub admin-plane audit, advice, and guided setup over the consumer's own authenticatedghCLI — billing/cost control, security posture, rulesets and settings drift, Actions policy, and every other org/repo/enterprise settings area.ghstate and runtime-fetched official docs. Read-only by default; every mutation is user-in-loop and routed per consumer-declared change routing (.claude/github/).--applyrouting → browser-automation offer → primary-tier recipes → evals + QA → gates + publish..claude-plugin/marketplace.jsonentry (categoryoperations), README catalog row, plugin CHANGELOG.docs/MIGRATION-PLAYBOOK.md§Plugin-acceptance security review (2026-07-21), independently verified by a fresh-context subagent — 13/13 binary checks PASS. User approval gate passed.${CLAUDE_PLUGIN_ROOT}isolation, documenteduserConfig, PII sweep, semver, marketplace validation).Test plan
claude plugin validate --strict .exit 0 (re-run after rebase onto latestmain)github.test.shexit 0 (includes repo-agnosticism sweep)scripts/run-plugin-tests.shexit 0*.md)Fresh docs
Per the repo fresh-docs mandate — schema/behavior verified against these pages (fetched 2026-07-21):
Related
Refs #509 — the plugin-acceptance security-review record in this PR feeds the shared security-review gate.
🤖 Generated with Claude Code