Skip to content

feat(compass): SEA-1561 swap runner durable-frame ingest to CommitConversationFrame - #45

Merged
mattwilkinsonn merged 1 commit into
mainfrom
compass-runner-1364-sink-ack-swap
Jul 31, 2026
Merged

feat(compass): SEA-1561 swap runner durable-frame ingest to CommitConversationFrame#45
mattwilkinsonn merged 1 commit into
mainfrom
compass-runner-1364-sink-ack-swap

Conversation

@mattwilkinsonn

@mattwilkinsonn mattwilkinsonn commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

What

Swaps the Runner's durable-conversation-frame ingest from the loss-tolerant PublishEvents buffer-accept path to the dedicated unary RunnerService.CommitConversationFrame sink-ack, closing the OQ-3 delivered-or-erred gap (SEA-1561, downstream of #24's OQ-3).

Before: PostConversationFrame acked the agent on a PublishEvents buffer-accept (the shared client-stream Send returned), so any Server-side loss after Send-accept permanently dropped a durable conversation frame while the agent's retry was suppressed.

After: durable conversation frames leave the loss-tolerant Publish spine entirely and commit request/response via the dedicated CommitConversationFrame unary (proto/stub landed in #42, Server handler in #43). Success is returned only after the Server acknowledges the commit; a Server-side loss is a Connect error the agent's sink retries on the same idempotency key.

Design (per #42's Matt-ratified proto)

  • Runner is a pure forwarder (mirrors RelayCommsCall): resolves the session structurally from the socket, sends {session_id, frame, idempotency_key}, asserts no account. The Server resolves session→account fail-closed.
  • The per-session publisher becomes trace/session-only (the Publish path); durable frames no longer ride its RunnerSeq, so the two paths no longer contend on one monotonic counter.
  • The agent-minted idempotency_key is reused verbatim; the Server commits at-most-once per key. An in-process advisory committedKeys LRU short-circuits a retry without a redundant upstream forward — it is not the durability boundary (the store's atomic commit on the same key is), so a Runner crash that loses the map is safe.
  • Error mapping is split by retryability, set Server-side and passed straight through: transient (Unavailable/FailedPrecondition) → retryable, permanent (InvalidArgument/NotFound) → terminal.
  • The agent-facing PostConversationFrameResponse stays empty — the swap is Runner-internal, no agent-facing contract change.

Tests (red-green, -race)

  • TestPostConversationFrameDeliveredOrErred — a commit failure surfaces the Server's Connect error, never a silent success.
  • TestPostConversationFrameAtLeastOnceRetryability (permanent-not-found / permanent-invalid-argument / transient-unavailable) — closes feat(runner): SEA-1364 C2 - Gateway telemetry-ingest handlers #24 OQ-3 item 5: a transient failure surfaces retryable and does not mark the key committed (a retry re-forwards); a permanent failure surfaces terminal.
  • TestPostConversationFrameDedupOnKey / …EmptyKeyNeverDedups / …AdvisorySetLossReforwards — the advisory fast-path is correct and never the boundary.
  • TestConcurrencyNoFalseGap — Publish-only ordering after the durable path leaves the spine.

All gateway tests green under -race; hk gate green (lint 0 issues, vet, vuln, licenses, drift, full test suite).

Refs SEA-1561, SEA-1364. Downstream of #24 (OQ-3); consumes #42 (proto/stub) + #43 (Server handler).

Review (skill://review, one adversarial pass)

0 high, 3 medium, 1 low. Contract verified sound end to end: success marks the key; a transient commit error returns unmarked (agent re-commits the same key); a permanent error returns unmarked (agent drops); a committed-but-response-lost retry short-circuits on the advisory LRU while the store's atomic commit stays the real boundary. No false-hub-gap regression — durable frames left the publisher, so the seq critical section now guards only the serial Publish stream.

  • M1 (doc-drift, gateway.go pub/seq field comments) — fixed in this PR: the comments described the deleted pre-swap shared-publisher mechanism; rewritten to the current model (pub driven only by Publish; seq on the Gateway because the publisher is replaceable via acquirePublisher's session-change reset).
  • M2 (coverage gap, the isConversationFrame fail-closed guard) — fixed in this PR: added TestPostConversationFrameRejectsNonConversationFrame (trace/session, empty-oneof, nil → CodeInvalidArgument with commit-count 0).
  • M3 (api-surface, NewGateway/Serve 6/7 positional args, 3 same interface-typed) — deferred to SEA-1572 (reviewer framed it as a judgment call / legitimate trade-off; the signature shape is a design fork for Matt). Non-blocking; no behavior impact.
  • L1 (below floor, pure-forwarder trusts resp.GetCommitted() it structurally ignores) — left as the strict pure-forwarder shape; safe today per the seam contract (runner.proto surfaces a non-commit as a retryability-split status, never committed=false with nil err).

@linear-code

linear-code Bot commented Jul 30, 2026

Copy link
Copy Markdown

SEA-1561

SEA-1364

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

…versationFrame

PostConversationFrame now commits conversation frames request/response via the
dedicated CommitConversationFrame unary (delivered-or-erred) instead of acking on
a lossy PublishEvents buffer-accept, closing the OQ-3 silent-drop gap; the Publish
spine becomes trace/session-only.

Co-Authored-By: seal <noreply@sealedsecurity.com>
@seal-agent
seal-agent force-pushed the compass-runner-1364-sink-ack-swap branch from b52300a to 517f767 Compare July 30, 2026 20:46
@seal-agent
seal-agent marked this pull request as ready for review July 31, 2026 01:01
@mattwilkinsonn
mattwilkinsonn merged commit 76aec66 into main Jul 31, 2026
2 checks passed
@mattwilkinsonn
mattwilkinsonn deleted the compass-runner-1364-sink-ack-swap branch July 31, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant