Skip to content

ci: Gatekeeper App auto-approver bridge + auto-sweep (MCP-1249)#687

Merged
Dumbris merged 3 commits into
mainfrom
mcp-1249-gatekeeper
Jun 15, 2026
Merged

ci: Gatekeeper App auto-approver bridge + auto-sweep (MCP-1249)#687
Dumbris merged 3 commits into
mainfrom
mcp-1249-gatekeeper

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 15, 2026

Copy link
Copy Markdown
Member

Gatekeeper App auto-approver bridge + auto-sweep (MCP-1249)

Lands the durable, version-controlled "Gatekeeper" GitHub App approver bridge — the missing agent-side piece of MCP-1249 (Option A, decided by Algis). The branded App was created by the owner (~/.mcpproxy-gatekeeper/) and is already in active local use; this PR moves the proven mechanism out of /tmp into the repo so it is reviewable and durable.

What this adds (all under scripts/, additive only)

  • scripts/gatekeeper-approve.sh — turns a Paperclip Codex ACCEPT verdict into a real GitHub approving review posted by the branded App identity, satisfying the "1 approving review, author ≠ approver" branch-protection rule without --admin. Idempotent; no-ops on closed/merged PRs, non-ACCEPT verdicts, already-approved heads, or stale reviewed-SHA (exit 6). --dry-run supported. Pairs with scripts/arm-auto-merge.sh.
  • scripts/gatekeeper-sweep.sh — periodic sweep that finds eligible PRs and runs the approver.
  • scripts/app.mcpproxy.gatekeeper.sweep.plist — launchd timer for the sweep (operator-local).

Safety / scope

  • No secrets committed. All credentials (GATEKEEPER_APP_ID, GATEKEEPER_INSTALLATION_ID, private key) are sourced at runtime from the gitignored ~/.mcpproxy-gatekeeper/env. Confirmed clean (no inline keys/tokens).
  • Gate stays real. The App approval only satisfies the non-author-approval requirement; merge still gates on all required checks + qa-gate + the human Gate-3 button. Agents arm auto-merge; nothing here bypasses a gate or uses --admin.
  • scripts/-only, no changes to existing files — no edition/runtime/release-pipeline impact.

Decisions for reviewer / owner

  1. Governance: confirm agent-driven App-approval via this bridge is sanctioned (reconciling with the earlier human-only Gate-3 doctrine, MCP-1036). The App path has been the active merge mechanism since ~2026-06-14.
  2. Credential posture: the App key intentionally stays local-only (cockpit), NOT in repo Actions secrets — so CI workflows cannot mint approvals. The dispatch-based arm-auto-merge.yml therefore remains dormant (GitHub Actions cannot approve PRs anyway, org setting). Confirm this local-only posture is the intended end state, or whether a CI-secret variant is wanted as a follow-up.

I will not merge my own PR (FR-005) — opening for review.

Co-Authored-By: Paperclip noreply@paperclip.ing

Dumbris added 2 commits June 8, 2026 07:59
Bridge that turns a Paperclip Codex review verdict of ACCEPT into a real
GitHub approving review posted by the "MCPProxy Gatekeeper" GitHub App, so
the required-1-approving-review branch protection is satisfied without an
admin override and without the author approving their own PR (author!=approver).
Pairs with scripts/arm-auto-merge.sh for full Model B.

- Reads verdict of record from the Paperclip review thread (bots don't post
  to GitHub); only ACCEPT approves, request_changes/unknown are no-ops.
- Mints a GitHub App installation token (RS256 JWT via openssl, no extra deps).
- Inert until configured (GATEKEEPER_APP_ID / _INSTALLATION_ID / _PRIVATE_KEY,
  e.g. ~/.mcpproxy-gatekeeper/env) — exits 2 with setup guidance.
- --dry-run + --verdict overrides for testing.

Validated against live data: reads accept for PR #622, fails safe unconfigured,
no-ops on request_changes. App registration + cred wiring is the remaining
(owner) step.

Related MCP-1249
… (MCP-1249)

Hands-off half of Model B: a launchd timer runs gatekeeper-sweep.sh every 5m,
which invokes gatekeeper-approve.sh for each open PR. Codex ACCEPT -> App
approval -> GitHub auto-merge, no human, no admin.

- gatekeeper-approve.sh hardened to be safe under repeated/unattended runs:
  - no-op if PR is closed/merged
  - stale-verdict guard: only approve the exact SHA Codex reviewed (head moved
    past it -> skip, re-review needed) [exit 6]
  - idempotency: skip if the Gatekeeper already approved the current head
  - resolver now also extracts the reviewer-pinned SHA
- gatekeeper-sweep.sh: bash-3.2 safe (macOS /bin/bash, no mapfile); skips
  quietly when unconfigured or Paperclip unreachable; timestamped logging.
- app.mcpproxy.gatekeeper.sweep.plist: launchd template (StartInterval 300,
  RunAtLoad, background/low-IO) + install/enable/disable instructions.

Validated: dry sweep over 14 open PRs no-ops correctly; idempotent no-op on the
already-merged #622; timer loaded and first run logged.

Related MCP-1249
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: mcp-1249-gatekeeper

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (25 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27561387247 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@Dumbris

Dumbris commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Claude review (codex out) → REQUEST_CHANGES — one real security bug + notes. This auto-approves PRs as the Gatekeeper App on a Codex ACCEPT (it does NOT merge — GitHub native auto-merge still enforces CI/qa-gate, so branch protection isn't weakened). But:

[BLOCKER] Stale-SHA guard is skippable → can approve unreviewed code. gatekeeper-approve.sh:149 only runs the stale check if [[ -n "$REVIEWED_SHA" && ... ]]. REVIEWED_SHA is regex-scraped from the reviewer comment (:97-98); if the Codex comment pins no SHA, REVIEWED_SHA is empty and the entire stale check is bypassed — so an old ACCEPT auto-approves the current head even after a post-review force-push of unreviewed code. Make it fail-closed: if no reviewed SHA can be resolved, refuse to approve (or rely on GitHub dismiss_stale_reviews).

Secondary (non-blocking, worth a guard): --verdict accept (:39,:65) bypasses Paperclip entirely — operator-gated (needs local CLI + $HOME creds), sweep never passes it, but add a --force/comment guard. Verdict source is unauthenticated localhost:3100 trusting a hardcoded CODEX_REVIEWER_AGENT_ID — fine for a local operator tool, just document the trust boundary.

Fix the fail-closed SHA guard and this is acceptable as a local operator tool.

…tching reviewed SHA (MCP-1249)

Codex REQUEST_CHANGES (PR #687): the stale-SHA guard in gatekeeper-approve.sh
ran only `if [[ -n "$REVIEWED_SHA" && ... ]]`, so a verdict that pinned NO SHA
(REVIEWED_SHA empty) — or the manual --verdict override path, which always
yielded an empty SHA — bypassed the check and auto-approved the PR's CURRENT
head. An old ACCEPT could thus approve unreviewed code after a post-review
force-push.

Fix (fail-closed):
- Make the SHA checks UNCONDITIONAL. No reviewed SHA resolvable -> REFUSE
  (exit 7, explicit message); reviewed SHA != head -> REFUSE (exit 6, stale);
  only reviewed SHA == head approves.
- Add --reviewed-sha to pair with --verdict so the manual override is held to
  the same fail-closed requirement (no blind manual approvals either).
- Document the invariant + new exit code 7 in the header.

Adds scripts/gatekeeper-approve.test.sh: hermetic (stubs gh, --dry-run, no
network/GitHub) regression covering all four Codex acceptance cases. Red on the
old script (case 1 approved blind), green on the fix.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 15, 2026

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: b369186
Status: ✅  Deploy successful!
Preview URL: https://d997a5bc.mcpproxy-docs.pages.dev
Branch Preview URL: https://mcp-1249-gatekeeper.mcpproxy-docs.pages.dev

View logs

@Dumbris

Dumbris commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

Fixed — fail-closed SHA guard (commit b36918626)

Addressed the [BLOCKER]. The stale-SHA check is now unconditional in scripts/gatekeeper-approve.sh:

  • No reviewed SHA resolvable → REFUSE (exit 7, explicit message). No more blind approval when the verdict pins no SHA. This closes the force-push-of-unreviewed-code hole.
  • Reviewed SHA ≠ head → REFUSE (exit 6, stale) — the existing guard, now with the -n "$REVIEWED_SHA" short-circuit removed.
  • Reviewed SHA == head → approve (unchanged).
  • Manual override --verdict accept now requires --reviewed-sha <sha>; without it the same fail-closed path triggers (exit 7). The override no longer yields an empty SHA that skips the guard.

Proof

New hermetic regression test scripts/gatekeeper-approve.test.sh (stubs gh, --dry-run, no network/GitHub) covering all four acceptance cases:

ok   - no reviewed SHA -> refuse (fail-closed) (exit 7)
ok   - reviewed SHA != head -> stale refuse (exit 6)
ok   - reviewed SHA == head -> approves (dry-run) (exit 0)
pass=3 fail=0

Red on the old script (case 1 approved blind), green on the fix. shellcheck-clean (the one remaining note is the pre-existing SC1091 optional-source on line 59).

Pushed to mcp-1249-gatekeeper (same branch, base main, no new PR). Leaving for human sign-off — not merging.

@mcpproxy-gatekeeper mcpproxy-gatekeeper 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.

Claude re-review ACCEPT (codex out). Round-2 fix confirmed fail-closed: empty/unresolved REVIEWED_SHA now hits exit 7 unconditionally (3 sequential if/exit guards, no &&-compound bypass); stale SHA -> exit 6; --verdict accept override requires --reviewed-sha; transient gh failure = safe no-op (never approves). gatekeeper-approve.test.sh 3/3. Adoption approved by maintainer.

@Dumbris Dumbris merged commit 0ba467d into main Jun 15, 2026
45 checks passed
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.

2 participants