Story-2840 · functions lane · launch adapter

The app can launch Mira over HTTP and reuse the durable clerk rail

The new POST /app/agents/{agent_key} route admits Wave-1 Mira launches, resolves the default matter chat session, optionally attaches an existing socket, applies the same perimeter and consent gates as WebSocket launch, and enqueues onto the existing clerk-run queue only when the run is actually admitted.

What the new route does

POST /app/agents/mira
  |
  +-- validate HTTP request DTO
  +-- optionally attach socketId to an existing ws connection
  +-- resolve default Mira session for (firm, matter, user)
  +-- build internal agent_key = mira_matter_chat
  +-- reserve run id and apply perimeter + consent gates
  +-- on admit: enqueue to clerk rail with ADR-124 trace context
  +-- on block: persist terminal blocked truth, no queue write

The public key is still mira; the adapter translates it to the internal mira_matter_chat rail contract before enqueue so the worker path and run persistence stay unchanged.

Shared launch core and transport boundary

ConcernOwner after Story-2840
HTTP parsing and response DTOrouter_agents.py
Queue selection and enqueue helperagent_launch_core.py
Run-id reservation and blocked-run truthagent_launch_core.py
Mira default-session resolutionagent_launch_core.py
Socket attach for HTTP launchesagent_launch_core.py
Existing WebSocket seamsrouter_ws.py delegates to the shared core without breaking test patch points

This keeps the transport adapters thin while preserving one launch policy for consent gating, perimeter gating, blocked-run persistence, and clerk-rail routing.

Accepted and rejected launch outcomes

Input conditionResult
agent_key = mira, valid matter launch, admitted gates202 accepted; run reserved; queued on mira_matter_chat
Known socket id owned by the callerSocket is attached and the run can stream onto it
Unknown or foreign socket id404 via pramaan.agent_launch.socket_not_found
Consent missing or perimeter rejects202 with blocked truth in agents.agent_run; no queue write
Any non-mira key on this route404 via pramaan.agent_launch.agent_not_found

Observability note

ADR-124 trace propagation now covers this HTTP launch path too. The shared queue client injects W3C trace context into SQS message attributes, and the HTTP route wraps the enqueue in the same producer span shape used by WebSocket launch so traces render the app-adapter to clerk-worker edge consistently.

Acceptance evidence

Focused unit teststests/unit/test_router_agents.py proves accepted enqueue and blocked truth responses for the new HTTP adapter.
WebSocket regression coveragetests/unit/test_router_ws.py stays green after the shared-core extraction, proving the existing launch rail behavior is preserved.
DB-backed route evidencetests/integration/test_app_agents_launch.py covers socket attach, blocked truth, and unknown-socket rejection when DB env is present.
OpenAPI contractscripts/generate_openapi.py updates the committed route and DTO snapshot.

Scope

This Story adds the Wave-1 HTTP launch entrypoint and the shared launch core only. It does not add a new queue, new worker, new cloud capability, or any compatibility bridge to a legacy launch rail. The existing clerk queue remains the only durable egress path.

Governing issue: pramaan-spec#2840.