feat(database,web): an agent with no plan is reached through its own workspace, and the tools that need a plan refuse it

Merged
DA
6d ago
+238-68

Step 3 of the automations plan (docs/proposals/automations-scope.md). It builds on #2637 (merged), which gave coding_agent its own organization_id and made plan_id optional.

What changes

  • Agents are reached through their workspace, not their plan. agentReachableBy now checks membership of the agent's workspace: its own organization_id, or its plan's on a row the backfill hasn't reached. An agent on a plan is reached exactly as before, because #2637's plan move carries the agent's workspace along. An agent an automation starts with no plan (from step 4 on) is reached by every member of its workspace and by no one else. CodingAgent now carries organizationId, and planId is string | null.
  • eve admits a planless agent's session. namedByReachableWork (recorded-session.ts) gates an agent's conversation on the same agentReachableBy rule, now kept in workspace-store.ts beside planReachableBy. eve's ownership check and turn admission therefore take a planless agent's session for any member of its workspace.
  • The workspace hops follow the agent. agentWorkspace resolves the installation used for checkout, the published PR read, repository skills, and steer: the plan's workspace for an agent on a plan, otherwise the agent's own workspace while the caller is still a member.
  • Tools that need a plan refuse a planless agent:
    • edit_plan answers its existing NO_PLAN.
    • The Inbox tools and show_page answer a new PLANLESS refusal that tells the agent what to do instead.
    • Step 5 gives planless agents their Inbox, and the Inbox refusal goes away then.
  • Nothing on the wire changes yet. Summaries, the plan's agent list, and the agent lines in the plan list keep naming a plan, so planless agents are left out of them. reachedAgent refuses a planless agent as not found before any agent route acts, so nothing lands only to answer 404. Both hold until step 7 makes the wire's planId optional. No planless agent exists until step 4, so v0.12.0 and v0.13.0 see no difference.

Tests

  • coding-agent-store.test.ts: a planless agent is read by a teammate who didn't start it, and reads as none for an account outside the workspace. This fails before the change, because the old reach required a plan row.
  • The same test asserts conversationReachableBy is true for the starter and a teammate and false for an outsider. It fails without the recorded-session.ts change.
  • coding-agents-app.test.ts: a planless agent's message and transcript routes answer 404, and eve is sent nothing. This fails without the agent-reach.ts change.
  • Existing coder-host, steer, terminal, and Inbox tests pass unchanged.

pnpm check:affected passes. This change is server-only; CI cannot verify the Mac UI, and ./scripts/verify.sh was not run (LUKE-159 stands in before the first release).

๐Ÿค– Generated with Claude Code