Skip to content

feat(handoff): expose explicit ACK, deadline, and unknown outcome for unanswered requests #181

Open

Description

@abruption

Reddit feedback

A reader asked whether the sender can tell when a target closes before replying and whether a pending request is safe to retry. Source: https://www.reddit.com/r/codex/comments/1wotij6/i_built_sessionpeer_to_message_live_codex_and/

Current v1.0.0 boundary

A posted Claude inbox write or queued Codex message is submission evidence, not a read/completion receipt. Session exit or an absent reply does not prove non-delivery. Native local/SSH adapters do not implement wait or acknowledgement (see docs/architecture/agent-transports.md); a prior reply-wait proposal #31 was closed without turning native submission into an ACK.

Proposed work

  • Design an opt-in handoff correlation ID, explicit receiver reply/ACK, and deadline/result states that distinguish submitted, acknowledged, timed_out_unknown, and definite pre-submission refusal.
  • Specify what evidence is available for Claude, Codex, local, and SSH, and what remains unknowable. Never infer consumption from a queue result or process exit.
  • Default to no automatic resend after an ambiguous outcome. Do not claim exactly-once delivery.
  • Evaluate a privacy-preserving observation mechanism before reading agent transcripts; document unsupported cases.
  • Test no reply, delayed reply, target exit/restart, duplicate ACK, and reply with the wrong correlation ID.

This is a design/implementation issue, not evidence that a native read receipt already exists.

Current follow-up scope after v1.1.0 — 2026-10-09

The original proposal above is retained as historical context. User-authorized release accounting keeps this issue OPEN and moves the remaining acceptance work from v1.1.0 to v1.2.0.

Shipped in v1.1.0

Experimental same-machine POSIX Claude receipt handling and correlation-only local Codex / one-destination POSIX source-streamed SSH submission are shipped. The joint frozen design remains the authoritative contract.

Merged implementation: #290. Published release, exact source 8ca77cf31254c8287a2593fe484bb30a7dba2727; integrated main CI and PyPI publication/verification succeeded. Publication does not establish operational rollout or new live-agent acceptance.

Remaining acceptance — not completed by the release

  • Implement and validate body-free, precisely correlated Codex injection observation and explicit ACK; turn completion and queue removal are not ACK evidence.
  • Validate installed reverse-SSH receipt-handler/bootstrap and remote preparation before enabling remote receipts; required unsupported waits continue to refuse before effect.
  • Complete Windows bounded native pipe/process cleanup, paired-device handoff and explicit opt-in fanout acceptance independently.
  • Complete cross-boot retention, restore/reconcile and stale collector lifecycle tooling; retained sender fencing is not general receiver deduplication or exactly-once delivery.
  • Require actual supported native/platform acceptance in addition to synthetic fixtures. investigate(delivery): extend request-ID deduplication safely to ordinary local and SSH sends #182 remains a separate long-term receiver-dedup investigation, not a blanket prerequisite or retry permission.

This is backlog/state accounting only. No new runtime implementation, skill merge/release, operational change, native experiment or publication is authorized by this reassignment.

Activity

  1. added this to the v1.1.0 milestone on Sep 24, 2026
  2. abruption commented on Sep 29, 2026

    @abruption
    OwnerAuthor

    Research: what each agent lets a sender observe (2026-09-29)

    Read-only investigation to ground this design. No live delivery tests were run for this comment.

    Claude Code

    • The documented inbox socket (cross-session messaging docs) accepts one JSON line and defines no response, acknowledgement or status query. The receiver decides Delivered, Held or Refused. A held message expires after the dialog timeout; only a reachable Claude sender is told. A raw-socket sender cannot observe any of these.
    • posted therefore means only that the socket write completed (session_peer_core/claude.py ignores any response).
    • notify_when_idle is limited to a local main Claude conversation and reports idleness, not receipt.
    • The receiver's local transcript does record inbound peer messages (queue enqueue/dequeue operations and a user record marked as peer-originated, followed by the assistant turn). This format is undocumented and internal. Reading it requires same-user access to the receiver's full conversation history.

    Codex

    • codex queue has no status or listing option, and the queue row carries no status. It is deleted when consumed, so deletion alone cannot distinguish consumption from a manual removal. Stale unconsumed rows can persist for weeks, which is a concrete example of queued ≠ consumed.
    • Each queued submission carries a client message ID. The thread history store records that ID on the injected user message, and records turn status and completion time. Injection and turn completion can therefore be observed by identifier and status alone, without reading message bodies.
    • The experimental app-server v2 protocol exposes queue add/list/delete with a caller-chosen client message ID, plus queue-changed, turn-started/completed and item-completed notifications. This avoids the race of reading a row after codex queue returns.

    Observability by stage

    Stage Claude Codex
    Submitted yes (posted) yes (queued + queue ID)
    Accepted / pending at receiver local transcript only (internal format) queue row / app-server queue list
    Injected into the conversation local transcript only (internal format) client message ID on the thread item
    Turn completed local transcript only turn status / turn/completed
    Acknowledged explicit correlated reply only explicit correlated reply only

    "Turn completed" still does not prove the model understood or acted on the request.

    Suggested direction

    1. State model: submitted → delivered (injection observed) → acknowledged (correlated reply), with timed_out_unknown when not observed before the deadline. Keep no automatic resend and no exactly-once claim.
    2. Codex first, opt-in: submit through the app-server with a sender-chosen client message ID, then observe injection and turn status read-only from the home that received the queue. Where the app-server path is unavailable, fall back to the current codex queue and report observation as unsupported.
    3. Claude: keep observation unsupported by default, because there is no public API and the transcript format is internal. A local, opt-in probe that extracts only a correlation token match and peer-origin markers, never message text, could be evaluated separately.
    4. Explicit correlated replies remain the portable, privacy-preserving acknowledgement path for both agents.

    Implementation is out of scope for the v1.0.x maintenance line and would target v1.1.0.

  3. fujiezee commented on Oct 1, 2026

    @fujiezee

    Thanks for the stage-by-stage table; having each of Claude and Codex spelled out for what the sender can actually observe makes the design much easier to discuss. The state model, no automatic resend, and the correlation ID are already a solid base. Two small things that might be worth considering, both about what happens after timed_out_unknown:

    1. Since the Reddit question was whether a pending request is safe to retry, the correlation ID could also serve as the receiver's dedup key. Then a deliberate resend (approved by a person, not automatic) would be recognised as the same request instead of being injected twice. The test list covers duplicate ACKs and a wrong correlation ID in a reply, but I didn't see a duplicate delivery of the request itself.
    2. The state could say who decides next. timed_out_unknown returned to the sender with the options spelled out (keep waiting, resend with the same ID, give up) would make the "default to no automatic resend" rule actionable for whoever is running the sender.
  4. abruption commented on Oct 1, 2026

    @abruption
    OwnerAuthor

    Thanks, @fujiezee — I agree with both suggestions and am accepting them as design/acceptance follow-ups.

    For #181, the timed_out_unknown result should make the sender/operator the decision owner and explain the available next actions:

    • Keep waiting / reconcile: retain the original correlation ID and distinguish later injection evidence or an explicit correlated ACK from submission alone.
    • Deliberate resend with the same ID: describe this as retry-safe only where a verified receiver-side dedup contract covers the original destination/generation and payload. A correlation ID alone is not idempotency, and human approval alone does not prevent duplicates. Until that capability exists for a transport, do not present resend as a safe recovery action.
    • Give up waiting: this abandons observation; it does not cancel or retract the original queued/submitted request, which may still be processed later.

    The design should expose retry eligibility and the evidence/reason behind it rather than simply returning an unexplained timeout. No automatic resend remains the default. A late or duplicate ACK must attach to the original request without triggering another delivery.

    I will track duplicate request handling and its durable receiver contract in #182, separately from this issue's duplicate/wrong-ID ACK tests. #181 can provide useful deadline/decision guidance even when dedup is unsupported; it must honestly report that limitation rather than promise a safe retry.

    Thank you for pointing out the gap. This comment records planned design coverage, not an implemented feature or an exactly-once guarantee. #181 remains in v1.1.0 and #182 remains the separate long-term investigation; no milestone or release scope is changed here.

  5. modified the milestones: v1.1.0, v1.2.0 on Oct 9, 2026
  6. added
    priority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.
    on Oct 13, 2026
  7. abruption commented on Oct 15, 2026

    @abruption
    OwnerAuthor

    Owner-authorized allocation — correlation PY120-REMAINING-DELEGATION-20261015.

    Implementation/preparation request submitted to the session-peer-claude session; consumption and acceptance are not yet confirmed.

    First map remaining acceptance to shipped behavior, implementable offline slices and native/upstream/live-test gates. Prepare a separate isolated, contract-preserving candidate for safe implementable lifecycle/retention/reconcile or negative-fixture work. Codex observation/ACK stays unsupported without the required body-free/original-generation proof. Declare the unmerged #311 dependency where relevant; do not reintroduce its storage bypass.

    The coordinating Codex session owns independent-cli-review, finding verification, PR creation and exact-head CI checks after receiving frozen commits. No new live model/session experiments, operations, merge, release or publication are authorized here. Keep the issue OPEN until its actual acceptance gates are met; the 2026-10-18 target does not waive them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority:P2Planned reliability, acceptance or feature work; priority does not authorize implementation.

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions