Skip to content

feat(omp): add acknowledged durable live guidance #899

Description

@tomdps

Parent: #864

Depends on: #866

Objective

Add durable ordered guidance and acknowledged live steering.

Scope

  • Bump the task-store to schema version 6 with nullable cluster_id TEXT, agent_id TEXT, live_guidance_base TEXT, and accepted_guidance_through TEXT, mapped exactly to task fields clusterId, agentId, liveGuidanceBase, and acceptedGuidanceThrough. Both cursors are canonical unsigned decimal strings and start at the task's captured pre-prompt ledger high-water; acceptedGuidanceThrough advances only contiguously. Existing rows migrate to NULL; an OMP live-steer attempt requires all four values and exact agreement with the active ompSessionOwnership.owner. Persist guidance before effects by appending the original USER_GUIDANCE_* row with metadata.delivery.status:'pending', then serialize delivery per agent. If an older queued sequence creates a cursor gap, return queued reason guidance-order-gap without writing steer.
    For USER_GUIDANCE_AGENT, the original row carries {delivery:{status:'pending',reason:null,method:null,taskId,timestamp}}. For USER_GUIDANCE_CLUSTER, it carries {delivery:{summary:{injected:0,queued:<agent-count>,total:<agent-count>},agents:{<agentId>:{status:'pending',reason:null,method:null,taskId}},timestamp}}. Preserve caller metadata beside, never instead of, this closed delivery object. Publishing null because the ledger is closing aborts delivery before any provider write.
  • Extend the length-prefixed attach wire with exact messages {type:'stdin',requestId:'input_<32-lower-hex>',data:'<base64>',guidanceSequence?:'<canonical-decimal>'} and {type:'input_result',requestId,ok:boolean,error:string|null}; add INPUT_RESULT:'input_result' to MessageType. createStdinMessage generates or validates the request ID, bounds decoded input to the OMP 1 MiB outbound-frame limit, and accepts guidanceSequence only from the cluster path. Existing PTY attach replies only after write() succeeds; sendInput decodes frames and waits for the exact request ID rather than treating its socket write as acceptance.
  • Map cluster guidance to correlated OMP steer. After OMP accepts, atomically CAS the active task's contiguous acceptedGuidanceThrough, insert-or-observe the immutable receipt below, and only then return success or allow terminal task completion. A stale task/session owner fails before steer.
  • Add constant USER_GUIDANCE_DELIVERY = 'USER_GUIDANCE_DELIVERY' but deliberately exclude it from GUIDANCE_TOPICS. Append one receipt per (clusterId,guidanceSequence,agentId,taskId) with ID msg_guidance_delivery_<sha256>, where the digest is SHA-256 over UTF-8 zeroshot:user-guidance-delivery:v1\0 followed by canonical JSON of that four-string tuple. The message is exactly {cluster_id:clusterId,topic:USER_GUIDANCE_DELIVERY,sender:'orchestrator',receiver:agentId,content:{data:{guidanceSequence,agentId,taskId,outcome,method,reasonCode}}}. outcome is accepted|rejected|indeterminate; method is always rpc; reasonCode is null only for accepted, provider-rejected for rejected, or one of ack-lost|task-changed|cursor-persist-failed|process-failed for indeterminate. Never copy guidance text. Insert-or-observe returns the byte-equivalent existing receipt or fails closed on collision.
  • Preserve public return shapes: accepted RPC maps to {status:'injected',reason:null,method:'rpc',taskId}. Provider rejection, post-write indeterminacy, cursor gap, and Docker unsupported map to the existing {status:'unsupported',reason,method:null,taskId} shape with reason provider-rejected|indeterminate-delivery|guidance-order-gap|docker-live-guidance-unsupported; cluster summaries remain injected|queued. Receipt messages never enter mailbox content.
  • Keep ledger history append-only. Add a resolver that reads legacy inline metadata.delivery or folds all new receipts in canonical ledger sequence over pending, exposing the latest attempt without overwriting earlier rejection/indeterminate evidence; matching deterministic-ID retries must observe byte-equivalent content or fail closed. Update guidance status/export surfaces to use it. The mailbox still returns only original guidance rows, so old ledgers remain readable and no migration rewrites history.
  • Advance the reusable provider-session guidance cursor through accepted live guidance only when that exact task commits logical/schema/hook success. Rejection or task failure leaves the original sequence queued for safe-point delivery. A crash after OMP acceptance but before durable task acknowledgement is explicitly indeterminate and may replay; the contract is at-least-once with no silent loss, not impossible cross-process exactly-once.
  • Once steer may have been written, a missing/ambiguous correlation or failure to persist the accepted cursor/receipt is a failed task commit barrier: record indeterminate when possible, issue bounded abort, fall back to owned termination, and move the partition to cleanup-required. A correlated negative response is instead a definitive rejected receipt and the task may continue. In either case the original guidance stays queued for a later safe point; external effects on an indeterminate path may therefore repeat.
  • Direct standalone zeroshot task attach input has no cluster ledger sequence: send correlated steer with requestId, return the exact provider acknowledgement, and make no durable-guidance or exactly-once claim. It never advances a cluster guidance cursor or emits USER_GUIDANCE_DELIVERY.
  • Keep Docker live injection unsupported in this epic; adding it requires a separately scoped authenticated host/container bridge.
  • Update AGENTS.md with durable guidance ordering, receipt folding, commit barriers, and acknowledged attach semantics here.

Primary files

  • OMP RPC session driver and pipe watcher
  • src/attach/protocol.js, src/attach/send-input.js, src/attach/attach-server.js
  • src/agent/agent-input-injector.js, src/agent/agent-lifecycle.js, src/orchestrator.js
  • src/guidance-topics.js, src/ledger.js, src/message-bus.js, and guidance status/export/receipt handling
  • task-store guidance provenance/cursor transitions
  • guidance/attach/crash-reconciliation tests and AGENTS.md

Acceptance

  • Every guidance effect has a prior durable sequence. OMP rejection records an immutable rejected receipt and leaves the sequence queued; a successful steered task advances a contiguous cursor and does not replay in the next context; task failure leaves it queued.
  • Concurrent/gapped delivery, crash-before-write, crash-after-write-before-ack, cursor-before-receipt, deterministic-receipt replay/collision, and receipt-reconciliation vectors prove the stated ordered at-least-once/no-silent-loss semantics.
  • Missing or ambiguous post-write correlation fails the task and retains cleanup ownership; it cannot be reported as accepted.
  • Standalone OMP attach reports accepted/rejected steer acknowledgements without claiming durable delivery; legacy PTY input receives an acknowledgement only after a successful write and otherwise behaves unchanged.
  • Docker continues to return unsupported and queues guidance for the next safe point.

Verification

npm run test:omp plus the focused guidance/attach/receipt/crash-window tests introduced by this issue.

Source contract

OMP RPC v2 steer/cancel contract

Delivery constraints

Keep this issue independently mergeable and OMP opt-in. Do not flip the default here unless this is issue I. Fix only this slice; do not add a second harness registry or retain compatibility shims. Validators inspect files and run the commands above directly; never put Git commands in validator prompts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions