Goal
Let an agent (Claude Code via the Murmur MCP server) drive a single Murmur run end-to-end without picking up unrelated stale or concurrent work that's also in the queue.
Why
The post-demo workflow on jobseek will let any user request a company be added; each request creates a Murmur run that sits in the queue waiting for an agent. When a user prompts their own Claude session "add Stripe to jobseek", that agent must claim only that run's subtasks. Without a run_id scope, pull_task returns the oldest ready row globally — likely some other user's pending request.
Also unblocks the demo: rehearsals leave stale claims in the queue that are FIFO'd ahead of fresh runs, breaking audience-facing dry-runs.
Scope
- New SQL
CLAIM_BY_RUN_SQL in src/api/agent/sql.ts: same shape as CLAIM_SQL plus AND run_id = ? in the inner SELECT.
GET /work/next accepts an optional ?run_id=... query param. When set, uses CLAIM_BY_RUN_SQL; otherwise legacy global FIFO.
pull_task MCP tool gets an optional run_id: string arg. Description updated to explain the parameter. Forwards to GET /work/next?run_id=....
Interfaces
CLAIM_BY_RUN_SQL: string (export from sql.ts)
app.get("/next", ...) — reads c.req.query("run_id")
PULL_TASK_INPUT_SHAPE = { run_id: z.string().min(1).optional() }
Verification
Definition of done
Out of scope
- Listing runs or filtering by
initial_input (separate issue).
- Per-user ownership / authz on run access (post-demo).
Goal
Let an agent (Claude Code via the Murmur MCP server) drive a single Murmur run end-to-end without picking up unrelated stale or concurrent work that's also in the queue.
Why
The post-demo workflow on jobseek will let any user request a company be added; each request creates a Murmur run that sits in the queue waiting for an agent. When a user prompts their own Claude session "add Stripe to jobseek", that agent must claim only that run's subtasks. Without a
run_idscope,pull_taskreturns the oldest ready row globally — likely some other user's pending request.Also unblocks the demo: rehearsals leave stale claims in the queue that are FIFO'd ahead of fresh runs, breaking audience-facing dry-runs.
Scope
CLAIM_BY_RUN_SQLinsrc/api/agent/sql.ts: same shape asCLAIM_SQLplusAND run_id = ?in the inner SELECT.GET /work/nextaccepts an optional?run_id=...query param. When set, usesCLAIM_BY_RUN_SQL; otherwise legacy global FIFO.pull_taskMCP tool gets an optionalrun_id: stringarg. Description updated to explain the parameter. Forwards toGET /work/next?run_id=....Interfaces
CLAIM_BY_RUN_SQL: string(export fromsql.ts)app.get("/next", ...)— readsc.req.query("run_id")PULL_TASK_INPUT_SHAPE = { run_id: z.string().min(1).optional() }Verification
GET /work/nextwith norun_idclaims oldest ready row globally (legacy).GET /work/next?run_id=r_Xclaims oldest ready row whoserun_id == r_X, ignoring older ready rows from other runs.GET /work/next?run_id=r_unknownreturns{ok:true, data:null}.pull_taskwith no args still works (legacy).pull_task({run_id: 'r_X'})claims only that run's work.src/mcp/server.test.tsstill pass.Definition of done
pnpm typecheck && pnpm lint && pnpm test && pnpm grep:allclean.run_idfilter.Out of scope
initial_input(separate issue).