Story 2038: Broker lands a Mira command as a proposed frame version

WHY this exists. Two different agents want to change the same Matter Frame. Vega rewrites it ambiently when documents change; Mira rewrites one field when counsel issues a command ("make the issues bullet points"). If Mira simply saved its whole frame, a Vega build that landed a second earlier would be silently erased โ€” counsel's command would quietly wipe work it never saw. The catastrophe this code prevents is a lost-update: two writers, one frame, no arbiter. The fix is that a command is a proposal, not a write, arbitrated by the frame version it was built against.

๐Ÿ›ฐ๏ธ Satellite: same rail, two doors

Mira does not get a new "save" path. Its command lands on the same proposed → accept/reject rail Vega ambient builds already use: one appended source='agent' version whose changed field the app shows as proposed, and counsel Confirms (the existing accept_field) or Rejects (reject_field). No new ratify surface. The broker is the interactive one โ€” the clerk worker (pramaan-docs ADR-110 T6) โ€” so the landing hangs off the run's terminal turn.completed, exactly where Vega's ambient landing hangs off the framer worker's completion.

โœˆ๏ธ Airplane: the analogy โ€” a git rebase of one field

Think of every frame version as a commit. Mira builds its command against version v5 (its base). By the time the command comes back, a Vega build may have committed v6. The broker does not force-push Mira's v5+edit over v6. It rebases: it takes only the one field the command changed and replays it on top of v6, producing v7. v6's other fields survive; the command's scoped edit still applies. base == current is just the trivial rebase (a fast-forward).

            v5 โ”€โ”€ (Vega) โ”€โ”€โ–ถ v6            command built on v5, scope = "issues"
             โ”‚                 โ”‚
   base โ”€โ”€โ”€โ”€โ”€โ”˜                 โ””โ”€โ”€โ”€โ”€ current (re-base target)
                                          โ”‚
                changed = {issues}  (broker-owned diff: proposed vs v5)
                                          โ–ผ
              v7 = v6 with issues replaced by the command's issues
              (v6.core_dispute preserved โ€” never a silent overwrite)

๐Ÿ”ฌ Microscopic: the landing algorithm

All of it runs under one FOR UPDATE lock on the matter row (allocate_next_frame_version), so two writers queue instead of racing max(version).

MatterFrameService.ingest_command_frame(matter_id, req):
  proposed  = validate(req.envelope.frame)         # the FrameOutput Mira produced
  if matter fully locked        -> skipped_locked
  version   = allocate_next_frame_version(...)      # FOR UPDATE on the matter row
  current   = latest frame                          # the re-base target
  base      = frame at req.base_frame_version        # what the command SAW
  changed   = { f in EDITABLE_FIELDS : proposed[f] != base[f] }   # broker owns the diff
  verify    changed โІ scope                          # ยง5.1 invariant; drift -> typed reject
  landed    = { ...current, **{ f: proposed[f] for f in changed } }   # re-base overlay
  landed    = restore field locks (locked value wins; drop from changed)
  if not changed               -> no_change          # nothing to propose
  append    source='agent' version(landed) + resolution.command{scope,changed,base,rebased}
  emit audit (actor=agent) + domain event matter_frame.updated

What changed

SurfaceChange
MatterFrameService.ingest_command_frame New landing. Broker-owned canonical diff (proposed vs the base version), scope verification, stale-base re-base onto the current frame, field-lock overlay, append as a proposed source='agent' version. Reuses the Vega rail's canonical resolution, audit, and event.
MatterFrameCommandIngestRequest The landing DTO: agent_run_id, base_frame_version (the generation-CAS token, not the matter_frame_jobs lease), scope, command_text, envelope.
clerk_worker/mira_command.py The broker seam: opens the firm-scoped transaction and adapts a launch command + turn.completed into the landing request. Injected as ClerkDeps.land_command_frame (default no-op keeps the loop unit-testable).
clerk_worker/runner.py On a kind='mira_command' terminal turn the frame is landed before it is pushed, so counsel never sees a proposal that failed to persist; a landing error fails the run closed. Per-route cost cap: _effective_cost_cap gives the command route command_cost_cap_usd (default $1.50) while every other route keeps the global $0.50. _invoke_brain builds the command envelope (input passthrough, no atlas coercion).
agent_contracts/invoke.py TurnKind gains mira_command so the broker's agent_invoke.v1 accepts and routes the command turn (requires input, forbids dock_chat โ€” the existing validators already fit).

Acceptance mapping

CriterionWhere it is proven
Command proposal appears as a frame version for Confirm/Rejecttest_command_lands_as_proposed_version_and_reject_discards โ€” lands a version, then reject_field (the existing rail) discards it.
Confirm promotes; Reject discardsSame test + the existing Story-1998 accept_field/reject_field rail (unchanged; the landed version is shaped like a Vega proposal).
Stale base re-based, never silently overwritestest_stale_base_command_rebases_and_never_overwrites_newer_version โ€” command on v1 re-bases onto v2; v2's newer field survives.
Per-route cost cap enforcedtest_command_route_uses_its_own_cost_cap_not_the_global โ€” the same cost aborts under a tight command cap and succeeds under a generous one even when the global cap is tiny.
Empty first-run statetest_command_on_empty_first_run_lands_v1 โ€” a matter with no frame yet lands v1.
Scope drift rejectedtest_command_out_of_scope_is_rejected_never_landed โ€” a change outside scope raises; nothing lands.

Observability (pramaan-docs ADR-124)

The landing reuses the registry-approved ManualSpanName.MATTER_FRAME_INGEST span (the same domain op as Vega's ambient ingest) parented to the command's trace carrier โ€” no new span name, no raw tracer call. Cost/usage stay on the broker's existing stream instrumentation.

Deferred (sibling stories, not this Story)

The Mira runtime that produces the command turn is pramaan-agents Story-C; the CCB that fires the command and the WS agent-key→rail routing are Story-F. This Story is the broker landing: once a mira_command run streams through the clerk worker, its proposed frame is persisted on the existing rail.

Verification

uv run ruff check src tests
set -a; . ./.env.test-db; set +a
uv run pytest tests/integration/test_mira_command_landing.py -q
uv run pytest tests/unit/clerk_worker/test_runner_mira_command.py -q
uv run pytest tests/unit/agent_contracts tests/unit/clerk_worker tests/contract -q
make openapi   # app-api.json: WS kind enum gains "mira_command"

Recall in one sentence: a Mira command is a proposal that the broker rebases โ€” one scoped field replayed onto the current frame and appended for Confirm/Reject โ€” so counsel's edit and a concurrent Vega build never overwrite each other.