feat(mcp): Sprint B — on-chain tools (submit_pr) — GHB-187#102
Merged
Gastonfoncea merged 21 commits intoMay 19, 2026
Merged
Conversation
Linear issues (spec, plan, code, migrations, everything tied to the issue) must live on the issue's feature branch and reach main only via a merged PR. Never commit issue work directly to main. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Design spec para Sprint B: submit_pr + submissions.list + hard role gating + fix GHB-182. Resuelve decisión central de signing model via Privy delegated server-signing (Opción A). Defense-in-depth para PR ownership (MCP pre-check + relayer post-check). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TDD-style bite-sized tasks for: agent_delegations migration, verifyPrOwnership shared lib, role/delegation guards, Privy delegated-signer, submit_solution tx builder, submissions.list + submissions.create MCP tools, frontend consent UI, relayer post-check, smoke runbook, and PR handoff. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the agent_delegations table to track which users have granted server-side Privy wallet signing consent for the MCP submit_pr flow. Includes FK to profiles(user_id) ON DELETE CASCADE, a partial index on active delegations (revoked_at IS NULL), and an RLS policy for end-user session reads. Migration ready for human-applied db:migrate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Gaston must run pnpm db:migrate against devnet before opening the PR. Without it, agent_delegations doesn't exist and Sprint B fails at runtime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements buildSubmitSolutionTx helper that builds the submit_solution Anchor instruction and packs it into an unsigned v0 VersionedTransaction with gas station as fee payer, ready for Privy signing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full auth → role guard → delegation guard → idempotency → PR ownership → build tx → Privy sign → gas station submit → DB mirror insert flow. Adds SolanaGasStation singleton wrapper, registers the tool, and covers happy path + 4 guard cases with vitest. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add 4 missing error-case tests: mcp_status suspended → Forbidden, no active delegation → Forbidden, bounty not found → NotFound, and Privy delegation_revoked → Forbidden with DB update verification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /api/agent-delegation handles delegate (upsert) and revoke (set revoked_at) actions; GET returns the caller's current delegation row. Auth via Privy JWT (same pattern as stake/api-keys routes). Core logic extracted to lib/agent-delegation-route-core.ts with 10 vitest tests. Also adds agent_delegations table types to db.types.ts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds defense-in-depth ownership verification to the relayer's submission handler (GHB-182). Before calling the analyzer, the handler now calls verifyPrOwnership (from @ghbounty/shared) with the solver's GitHub handle and the bounty's repo URL looked up from DB. Definitive failures (author_mismatch, repo_mismatch, pr_not_found, invalid_url) → auto_rejected; transient failures (rate_limited, upstream_error) → skip poll cycle, retry. Also fixes @ghbounty/shared's internal imports to use .js extensions so it works for both bundler and NodeNext module resolution consumers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Privy's signTransaction expects the internal wallet id, not the on-chain pubkey. Resolve walletAddress → walletId via getWalletByAddress before signing; map 404 to delegation_revoked. Rename SignInput.walletId → walletAddress throughout signer, caller, and tests (5 tests total). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…GHB-187 Task 14 added .js extensions to @ghbounty/shared internal imports to make NodeNext resolution happy when the relayer started consuming the package. That undid the GHB-180 fix that made the package consumable by Next.js (Turbopack), breaking both MCP and frontend builds on Vercel. Revert the .js extensions in the shared package and align the relayer's tsconfig to use moduleResolution: bundler (same pattern as packages/shared/tsconfig.json). tsx handles either mode at runtime; the workspace tests + typecheck still pass for all packages. Verified locally: pnpm --filter @ghbounty/mcp build → ok pnpm --filter frontend build → ok pnpm typecheck → ok (7/7 packages) pnpm test → ok (641 tests pass) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Sprint B (GHB-187): the MCP server's first write-capable tools that close the AI-agent loop — find bounty → resolve → submit PR on-chain autonomously.
submissions.create(a.k.a.submit_pr) — new dev-only toolsubmissions.list— new dev-only toolrequireRole) on all on-chain tools@privy-io/node/app/credentials(AgentDelegationCard)agent_delegations(migration0026)Spec:
docs/superpowers/specs/2026-05-18-mcp-sprint-b-onchain-tools-design.mdPlan:
docs/superpowers/plans/2026-05-18-mcp-sprint-b-onchain-tools.mdRunbook:
docs/superpowers/runbooks/2026-05-18-sprint-b-smoke.mdRelated: closes GHB-114; mitigates GHB-182 off-chain (on-chain redeploy stays in scope of GHB-182).
Key design decisions
delegateWallet; subsequentsubmit_prcalls have Privy sign on their behalf without a browser popup. Dismissed alternatives: agent-owned wallet (changes data model, breaks authz), two-step client signing (AI agents don't have wallets natively), per-action browser handoff (defeats the point of an agent).pr.author/pr.base.repobefore signing; relayer re-checks and marksauto_rejectedif MCP bypassed. No Anchor redeploy.SolanaGasStationfrom@ghbounty/shareddirectly (not via HTTP). Removed a hop + a second auth path.submit_pr. Triple key(solver, issue_pda, pr_url); retries after timeout return the existing submission, including when the bounty has since closed.opus_report_hashstays zeros. Same as the web app — relayer never commits a hash on-chain in v1.State of devnet
0026_agent_delegationsapplied (Gaston, 2026-05-19)drizzle.__drizzle_migrationswas empty before this PR (all prior migrations had been applied manually). Reconciled by inserting marker rows; documented as tech debt in memory for0023/0024(those remain outside Drizzle's tracking, not introduced here).Test plan
Unit / integration (already verified via pre-commit hook)
submissions.create: 13 tests (happy + idempotency + role guard + delegation guard + bounty not open + PR ownership failure + Privy 404 + revocation flow + transient GitHub errors + pr_url length)submissions.list: 2 tests (happy + role guard)role-guard: 2 testsdelegation-guard: 3 testsdelegated-signer: 5 tests (includinggetWalletByAddress404 path)build-submit-solution-tx: 2 tests (length validation + v0 + 2 signature slots)verifyPrOwnership(happy + each fail reason + network error)agent-delegation-route-coreManual smoke (post-deploy, Gaston runs)
Full runbook:
docs/superpowers/runbooks/2026-05-18-sprint-b-smoke.md/app/credentials→agent_delegationsrow appearssubmissions.listreturns a row for an existing submissionsubmissions.createwith a real PR → returns{ submission_id, status: "pending", tx_signature, submission_pda }submit_solutioninvocationsubmissions.getreturnsstate: "scored"Forbidden: PR ownership check failed: author_mismatchForbidden: Wallet delegation requiredForbidden: This tool requires \dev` role.`(bounty_id, pr_url)twice → samesubmission_id,idempotent: trueVerification of the central assumption
walletIdin Privy is the internal Privy wallet ID, NOT the on-chain pubkey. Fixed in commit2671b1d:signSolanaTransactionnow callsclient.wallets.getWalletByAddress({ address })to resolve the pubkey to the internal id before signing. The smoke test will verify this works against real Privy.Known follow-ups (not blocking this PR)
0023/0024migration journal gap. Pre-existing tech debt — these SQL files are applied to devnet but absent from_journal.json/__drizzle_migrations. Doesn't break current/future migrations.project_privy_pricing_2026_05_19. No action needed for v1; revisit when approaching MAU cap.getWalletByAddresslookup. Currently +1 HTTP roundtrip persubmit_pr. If it becomes a hot path, cache the mapping pubkey → walletId in Redis with TTL.Files changed
44 files, +10,566 / -310 (excluding lockfile churn from Privy SDK install).
🤖 Generated with Claude Code