Skip to content

feat(pi): provision the pi permission-guard extension (pi permission parity) - #171

Open
alex-mextner wants to merge 4 commits into
mainfrom
feat/pi-permission-guard
Open

feat(pi): provision the pi permission-guard extension (pi permission parity)#171
alex-mextner wants to merge 4 commits into
mainfrom
feat/pi-permission-guard

Conversation

@alex-mextner

Copy link
Copy Markdown
Owner

What

Provisions the pi permission-guard extension so pi-harness repos get the same deny-dangerous / ask-risky command belt the other harnesses get. pi ships no built-in permission system (it runs bash with the user's own privileges); its extension API can intercept the bash tool pre-execution, which is the sanctioned parity mechanism.

This is the rig half. The extension itself lives in agent-tools:
➡️ Cross-repo dependency: alex-mextner/agent-tools#294 (the permission-guard extension). This PR installs that extension into pi's extensions dir and writes the policy it reads; it needs the extension present in the agent-tools catalog. Do not merge before #294.

The engine path (one engine, two front-ends — unchanged)

  • catalog (riglib/catalog.py): discovers pi-extensions/<name>/ (requires an index.ts entry) → category pi_extensions.
  • plan (riglib/plan.py): for permissions.kind: pi (or a pi harness), emits a provision_pi_extension action carrying the extension carrier, the install dir, the policy-file path, and the resolved policy document (baseline in the extension's flag-anywhere dialect: deny raw PR merges / force pushes / hook-bypass commits / root file removal / screencapture; ask before broad process kills and hard resets).
  • runner (riglib/actions/runner.py): _do_provision_pi_extension copies the extension into pi's extensions/ dir and writes the rig-owned policy JSON — idempotent (skip-if-identical), backup-on-conflict, error surfaced over backed_up.
  • drift (riglib/drift.py): _check_pi_extension reports missing/modified for both the extension dir and the policy file.
  • permissions (riglib/permissions.py): pi is no longer an N/A skipharness_permission_extension("pi") + pi_policy_document() (deep-copied baseline). config.validate now accepts permissions.kind: pi.
  • harness_skills (riglib/harness_skills.py): adds pi_agent_dir()/pi_user_path() (honor PI_CODING_AGENT_DIR) and fixes pi's global instruction path (~/.pi/agent/AGENTS.md, not the wrong ~/.config/pi/AGENTS.md).

Proof it works end-to-end in real pi

  • Extension unit tests: 32 matcher + handler-wiring tests green.
  • rig unit tests: 21 new (tests/test_pi_extension.py), full suite 1933 passed (only the pre-existing tolerated test_tmux_e2e_gating flake fails).
  • RIG_AGENT_TOOLS_SOURCE=… bash tests/smoke.sh --fast green (full real-catalog dry-run, zero unknown items).
  • Real-pi end-to-end: rig's real provision_pi_extension installed the extension + policy into a temp PI_CODING_AGENT_DIR; pi (driven by a deterministic faux provider) auto-discovered the extension and blocked git push --force with isError:true and the exact deny reason — the side-effect marker (only created if the command executes) was absent. Fail-closed (missing/malformed policy → baked baseline blocks) and ask-non-interactive→block also verified live.

Not included

Does not merge. Off-scope matcher limitations (shell-wrapper / substitution / leading-redirect obfuscation) are documented in the extension README on #294 and remain the domain of the deeper argv-level agent-hooks.

🤖 Generated with Claude Code

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca1dc0c4ab

ℹ️ 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".

Comment thread riglib/config.py
Comment thread riglib/plan.py Outdated
@alex-mextner
alex-mextner force-pushed the feat/pi-permission-guard branch from 6de9602 to ff99905 Compare July 18, 2026 11:39
alex-mextner added a commit that referenced this pull request Jul 18, 2026
… overrides

Two review findings on the pi permission-guard extension:

1. permissions.kind schema/wizard/JSON-schema choices were missing 'pi' even
   though the validator and planner already accepted/provisioned it — only
   hand-editing rig.yaml could enable the path. Added to riglib/schema.py,
   riglib/config_schema.py, and regenerated schema/rig.schema.json.

2. _build_pi_extension always built the baked default policy, ignoring the
   cascaded permissions.deny/ask overrides — a user declaring deny: [] / ask: []
   (the established 'empty replaces the baseline' convention) silently kept
   getting the full baseline reconciled. Honor an explicit empty override per
   role; a populated override is in claude-code's rule-string dialect (which
   doesn't translate to pi's structured rule dicts) so it's dropped with a
   visible plan note instead of silently diverging from the declared config.
   Also note when the allowlist-only keys (tools/extra/disable/allow) are set
   for pi, which has no additive allowlist.

Addresses review findings on PR #171.
@alex-mextner
alex-mextner force-pushed the feat/pi-permission-guard branch from ff99905 to 294167e Compare July 18, 2026 14:42
alex-mextner and others added 4 commits July 22, 2026 18:20
…parity)

pi ships no built-in permission system — it runs bash with the user's own
privileges. The agent-tools `permission-guard` pi extension delivers deny/ask on
the bash tool, but only if rig installs it and writes the policy it reads. This is
the rig half of pi permission parity.

- catalog: discover `pi-extensions/<name>/` (requires an index.ts entry).
- plan: for `permissions.kind: pi` (or a pi harness), emit a `provision_pi_extension`
  action carrying the extension carrier, the install dir, the policy file path, and
  the resolved policy document (the same deny/ask baseline claude-code encodes, in
  the extension's flag-anywhere dialect): deny raw PR merges, force pushes, hook
  bypass commits, root file removal, screencapture; ask before broad process kills
  and hard resets.
- runner: `_do_provision_pi_extension` copies the extension into pi's extensions dir
  and writes the rig-owned policy JSON — idempotent (skip-if-identical), backup-on-
  conflict, error surfaced over backup.
- drift: `_check_pi_extension` reports missing/modified for both artifacts.
- permissions: pi is no longer an N/A skip — `harness_permission_extension("pi")`
  + `pi_policy_document()` (deep-copied baseline). config.validate accepts
  `permissions.kind: pi`.
- harness_skills: add `pi_agent_dir()`/`pi_user_path()` (honor PI_CODING_AGENT_DIR)
  and FIX pi's global instruction path (`~/.pi/agent/AGENTS.md`, not `~/.config/pi`).

Cross-repo: the extension lives in agent-tools PR #294
(alex-mextner/agent-tools#294); this PR is the rig
provisioning half and depends on that extension being in the agent-tools catalog.

Closes #170

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When both the extension dir and the policy file conflict, ActionResult carries one
structured backup slot but neither restore path is lost — both appear in the result
detail. Adds the missing double-conflict coverage (review follow-up).

Closes #170

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… overrides

Two review findings on the pi permission-guard extension:

1. permissions.kind schema/wizard/JSON-schema choices were missing 'pi' even
   though the validator and planner already accepted/provisioned it — only
   hand-editing rig.yaml could enable the path. Added to riglib/schema.py,
   riglib/config_schema.py, and regenerated schema/rig.schema.json.

2. _build_pi_extension always built the baked default policy, ignoring the
   cascaded permissions.deny/ask overrides — a user declaring deny: [] / ask: []
   (the established 'empty replaces the baseline' convention) silently kept
   getting the full baseline reconciled. Honor an explicit empty override per
   role; a populated override is in claude-code's rule-string dialect (which
   doesn't translate to pi's structured rule dicts) so it's dropped with a
   visible plan note instead of silently diverging from the declared config.
   Also note when the allowlist-only keys (tools/extra/disable/allow) are set
   for pi, which has no additive allowlist.

Addresses review findings on PR #171.
@alex-mextner
alex-mextner force-pushed the feat/pi-permission-guard branch from db993e9 to 144a178 Compare July 22, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant