Skip to content

What the last save says when a chat closes never reaches the window #2527

Closed

Description

@AbirAbbas

What happened

On dev@b5b7dead1, a sentence produced by the last save of a chat that is closing never reaches the window that closed it. This was already true before #2509.

On the default engine road the engine stops forwarding sentences to the window before the conversation closes, and the final drive save runs after that. If the final publish discovers that another computer took the chat, or keeps turns on a branch, the sentence it says is dropped.

Replication

Deterministic (no model).

Setup used by the replications below (no provider key, no real home):

make build                                   # bin/codeaf
go build -o /tmp/o/modelstub ./test/remote/stub
/tmp/o/modelstub -addr 127.0.0.1:8804 &      # keyless scripted model; `PROBE-ECHO <text>` gets a reply
(cd relay/hosted && npm ci && npx wrangler dev --local --ip 127.0.0.1 --port 8803 --persist-to /tmp/o/relay-state) &
# every codeaf command below runs with:
export HOME=/tmp/o/a CODEAF_HOME=/tmp/o/a      # or /tmp/o/b for the second computer
export CODEAF_SYNC_URL=http://127.0.0.1:8803 CODEAF_TELEMETRY=off DO_NOT_TRACK=1 CODEAF_NO_UPDATE_CHECK=1
export OPENROUTER_API_KEY=stub CODEAF_BASE_URL=http://127.0.0.1:8804/api/v1

Pair the two homes: codeaf pair with home b, then codeaf pair approve <token> with home a. Storage is off for the new account by default.

Approve the account first, so storage is on (codeaf-relay-admin approve <request code> --relay http://127.0.0.1:8803 --key <throwaway key>, with the relay started with --var CAF_ADMIN_KEYS:<its public key>), or start the relay with --var 'CAF_LIMITS:{"storageOpen":true}'.

  1. On A, codeaf chat --model stub/scripted, run one turn, and leave the window open.
  2. On B, continue the chat and run a turn there.
  3. Before A's window notices, run a turn on A and quit A at once.
  4. A's final publish finds the chat held by B. The displacement sentence it says appears neither in the window nor in the quit receipt.

Where

The close path in internal/remote/server.go (search stopRenewals), and the final drive save in cmd/codeaf/engine.go (search FinishSaving), which runs after the notice pump has stopped.

The fix

Sentences said by a closing chat's final save reach the window before it lets go:

  • one chat closing: the window notes them;
  • app quit: the quit receipt includes them once;
  • --no-host: behaves the same.

Older and newer builds keep talking to each other unchanged.

Acceptance

  • e2e: through the real remote client and server: a final publish that discovers displacement makes the closing window show the displacement sentence exactly once.
  • e2e: control: a clean final save shows nothing new; an older peer without the new receipt field behaves as today, in both directions.
  • The manual page that covers closing a chat quotes the wording, and the change entry's invalidates names it.

Activity

  1. added a commit that references this issue on Nov 9, 2026
    35d38c5
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions