cursor: BYOA slice 3 — Claude Code adapter (topology A, headless) - #508
cursor: BYOA slice 3 — Claude Code adapter (topology A, headless)#508servathadi wants to merge 3 commits into
Conversation
Make the topology-A workers the reference adapter: shared client with server-derived identity, declared runtime types, fleet-attach:v1, and land-at-review rails — so later BYOA harnesses copy one contract, not bespoke glue. Co-authored-by: Cursor <cursoragent@cursor.com>
Add topology-A codex-worker that dispatches via `codex exec --sandbox --json`, reads mupot MCP over streamable-HTTP (url + bearer_token_env_var, no SSE), filters own-assignee open tasks, isolates a worktree, verifies, and lands at review — proving the contract generalizes past cursor for cursor→codex failover. Co-authored-by: Cursor <cursoragent@cursor.com>
Add topology-A claude-code-worker that dispatches via `claude -p` with `--output-format stream-json`, writes mupot remote MCP as type:http `.mcp.json` (url + headers.Authorization) from the flock-agent pack, and lands at review — completing confirmed headless CLI breadth with cursor+codex. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 922f5cc6f2
ℹ️ 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".
| git("worktree", "add", "-b", branch, str(worktree), "main") | ||
| try: | ||
| ensure_worktree_mcp_json(worktree, cfg.mcp_url, cfg.token, write=True) | ||
| proc = claude_run(worktree, build_brief(task, worktree, branch)) |
There was a problem hiding this comment.
Mark timed-out Claude tasks as blocked
When claude exceeds the configured TIMEOUT, subprocess.run raises TimeoutExpired here before verification or report_blocked runs. The finally block removes the worktree, the outer loop only logs the exception, and future cycles poll only open tasks, so the already-claimed in_progress task is stranded indefinitely instead of being surfaced as blocked.
Useful? React with 👍 / 👎.
| git("worktree", "add", "-b", branch, str(worktree), "main") | ||
| try: | ||
| ensure_codex_mcp_config(cfg.mcp_url, CODEX_MCP_ENV_VAR, write=True) | ||
| proc = codex_run(worktree, build_brief(task, worktree, branch), cfg.token) |
There was a problem hiding this comment.
Mark timed-out Codex tasks as blocked
When codex exec exceeds TIMEOUT, this call raises TimeoutExpired; control skips the verification failure path, removes the worktree in finally, and is merely logged by the outer loop. Because the task was already changed to in_progress and subsequent polling requests only open tasks, that task will never be retried without manual intervention.
Useful? React with 👍 / 👎.
| if prior and f'bearer_token_env_var = "{env_var}"' in prior.group(0) and f'url =' in prior.group(0): | ||
| log(f"mcp config already present in {CODEX_CONFIG}") | ||
| return stanza |
There was a problem hiding this comment.
Replace a Codex MCP stanza when its URL differs
If ~/.codex/config.toml already contains a syntactically valid mupot stanza for another pot, this condition treats it as current without comparing its URL to mcp_url. The driver then authenticates its own calls against the newly configured pot while codex exec connects to the stale endpoint, commonly with a token minted for a different pot, so MCP context is unavailable or comes from the wrong deployment.
Useful? React with 👍 / 👎.
| if ignore_line not in existing.splitlines(): | ||
| gitignore.write_text(existing.rstrip() + "\n" + ignore_line + "\n") |
There was a problem hiding this comment.
Keep the generated MCP ignore rule out of task commits
When the repository does not already ignore .mcp.json (as is currently the case), setup modifies the tracked .gitignore inside the task worktree. An agent following the usual git add -A workflow will commit this driver-created change with its work, and an agent that otherwise produced no work can commit only this line and satisfy the driver's commit-plus-tsc verification, resulting in a false-green PR.
Useful? React with 👍 / 👎.
| "//": "Flock agent pack — Claude Code. Connects this agent to a mupot flock over the SOS bus.", | ||
| "//token": "Replace <SCOPED_BUS_TOKEN> with the project-scoped, agent-bound token your operator minted (project=<slug>, agent=<your-agent-name>). NEVER commit this file with a real token — it is gitignored.", | ||
| "//": "Flock agent pack — Claude Code (BYOA topology A). Connects this agent to mupot over remote HTTP MCP.", | ||
| "//token": "Replace <MUPOT_MEMBER_TOKEN> with the project-scoped, agent-bound token your operator minted. NEVER commit this file with a real token — it is gitignored.", |
There was a problem hiding this comment.
Do not claim the member token is project-scoped
The prescribed mint_agent_token credential is not project-scoped: mintAgentBoundToken grants member on the agent's entire squad, and task_list defaults to that squad unless a project_id is explicitly supplied. When a squad contains multiple projects, an agent installed from this pack can therefore read and mutate tasks outside its purported project boundary, despite the template and skill telling operators that the token enforces project isolation.
Useful? React with 👍 / 👎.
| OUTPUT_FORMAT, | ||
| ] | ||
| if SKIP_PERMISSIONS: | ||
| cmd.append("--dangerously-skip-permissions") |
There was a problem hiding this comment.
Isolate Claude from the driver's Git credentials
On the production host where the driver can push and open PRs, this default --dangerously-skip-permissions Claude process inherits the same environment, repository remote, credential helpers, and SSH agent because subprocess.run is invoked without a sanitized environment or network sandbox. Claude therefore has unrestricted Bash access to git push/gh and can bypass the stated human-gated delivery boundary; the prompt saying not to push is not an enforcement mechanism.
Useful? React with 👍 / 👎.
| def _load_ed25519_private_key(key_path: Path) -> Any: | ||
| from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey | ||
| from cryptography.hazmat.primitives.serialization import load_pem_private_key |
There was a problem hiding this comment.
Declare the dependency required for signed attach
When an operator sets any advertised *_KEY option on a normal Python installation, the signed-attach path imports the third-party cryptography package, but the repository supplies no Python requirements or installation step for it. On such hosts the import fails, boot_session silently treats attach as non-fatal, and bearer fallback cannot work for a registered-key agent because the server intentionally rejects that downgrade, leaving the advertised signed runtime unattached.
Useful? React with 👍 / 👎.
| commits = git("log", "main..HEAD", "--oneline", cwd=worktree, check=False).stdout.strip() | ||
| if not commits: | ||
| return False, "no commits — claude-code produced no work" | ||
| tsc = subprocess.run(["npx", "tsc", "--noEmit"], cwd=str(worktree), capture_output=True, text=True) | ||
| if tsc.returncode != 0: | ||
| return False, f"tsc errors:\n{tsc.stdout[-1500:]}{tsc.stderr[-500:]}" |
There was a problem hiding this comment.
Verify the committed Claude tree instead of the dirty worktree
If Claude creates at least one commit and then leaves later edits unstaged or uncommitted, the commit check succeeds while tsc validates the dirty working tree containing those later edits. Delivery pushes only the commits and the forced worktree removal discards the remaining changes, so the opened PR can omit code required for the successful verification and can be incomplete or uncompilable.
Useful? React with 👍 / 👎.
| commits = git("log", "main..HEAD", "--oneline", cwd=worktree, check=False).stdout.strip() | ||
| if not commits: | ||
| return False, "no commits — codex produced no work" | ||
| tsc = subprocess.run(["npx", "tsc", "--noEmit"], cwd=str(worktree), capture_output=True, text=True) | ||
| if tsc.returncode != 0: | ||
| return False, f"tsc errors:\n{tsc.stdout[-1500:]}{tsc.stderr[-500:]}" |
There was a problem hiding this comment.
Verify the committed Codex tree instead of the dirty worktree
If Codex commits an initial change but leaves subsequent edits uncommitted, this accepts the existing commit and runs tsc against the dirty worktree rather than the exact tree that will be pushed. The driver then pushes only committed content and force-removes the worktree, so a PR may lack the edits that made verification pass.
Useful? React with 👍 / 👎.
| try: | ||
| ack = attach(cfg, identity, host=host) | ||
| agent_view = ack.get("agent") if isinstance(ack.get("agent"), dict) else {} | ||
| log( | ||
| f"attach ok runtime={agent_view.get('runtime', cfg.runtime)!r} " | ||
| f"status={agent_view.get('status', 'running')!r}" | ||
| ) | ||
| except Exception as exc: # noqa: BLE001 - attach failure must not block task work | ||
| log(f"attach failed (non-fatal this cycle): {exc}") | ||
| register_port1_presence( |
There was a problem hiding this comment.
Keep dry-run boot sessions read-only
The drivers invoke boot_session even in their documented DRY_RUN=1 paths, and this unconditional call performs both a fleet attach and presence_register. A dry run with a valid token therefore mutates production presence state and marks the runtime running despite promising to poll and print without doing anything; identity-only dry runs need to avoid the attach and registration writes.
Useful? React with 👍 / 👎.
servathadi
left a comment
There was a problem hiding this comment.
BLOCK — exact-head content gate at 922f5cc6.
The Claude worker launches with --dangerously-skip-permissions and inherits the driver's environment, repository credentials, credential helpers, SSH agent, and unrestricted network access. The prompt's “do not push” instruction is not an isolation boundary, so the builder can bypass the human-gated delivery path. Both Claude and Codex verification also tests the dirty worktree rather than the committed tree that will be pushed, allowing a false-green PR.
These are P1 authority and evidence-integrity blockers. Existing exact-head threads remain unresolved; the branch is merge-conflicted and has CodeQL-only evidence rather than the required full gate suite.
|
Tracked as #559 for incremental fix (2026-07-25 backlog triage — codex diverse-gate BLOCK, real finding, not merged/live). Leaving this PR open as the fix branch; will re-request gate and merge on GREEN when picked up. |
|
Closing — investigated per squad ceremony (kasra-code, 2026-07-29). Evidence:
If slice 3 (Claude Code adapter, topology A) is still wanted, the epic in — kasra-code |
Dispatched to the
cursoragent (Grok) headless via the mupot loop for taskb74063a2-82ca-4e5f-841f-b716730cc659.Task done-when: A claude-code runtime adapter conforming to runtime-adapter/v1 dispatches via
claude -p "<prompt>"headless (--output-format stream-json), reads mupot's remote MCP via .mcp.json (type:http, url, headers.Authorization), same land-at-review contract; conformance smoke green; starts from the existing packs/claude-code/flock-agent pack; PR opened (NOT merged).Driver verified: cursor committed real work +
tsc --noEmitclean. Kasra-core gates this PR before merge (the task is inreview; cursor cannot self-close it).