Skip to content

Session: no single join for a turn's post-turn writers #2470

Open

Description

@santoshkumarradha

Nothing in a session joins all the writes that happen after a turn ends. Tests that measure "nothing changed after this turn" can see a late write, and quit or teardown have the same gap: there's no single point after which a finished turn will write nothing more. Low priority. Today this shows up as a rare test flake (TestAppIdleReleaseI1I2OpenViewKeepsItsFence, about 1 in 200 under load), when the setup turn's pace record is captured and published after the test starts counting.

Writers that can run after a turn has ended (app/v3 at 2c78de1):

  1. Deferred turn-journal writes. internal/session/loop.go:971 defers journalTurnPace (appendPace, sessionfile.go:3277-3281), and loop.go:936/954 defers journalMarkRead. They run after sealTurn (loop.go:1835) has settled and announced TurnEnded (loop.go:1855-1856).
  2. Naming. intentname.go:257-260 registers titleJobs, :288-290 writes intent_name, and the second-chance drain is at :305-307, :408 and :420, started from agent.go:2222. title.go:186-198 and :380 cover ordinary naming and publishing the title.
  3. Memory and knowledge. loop.go:1365 starts learnFromTurn after EventTurnDone. memory.go:867-893 runs under memoryJobs. The standing scanner is at knowledge.go:129/251, and job compilation is at loop.go:348-352 and knowledge_usage.go:60/96/133.
  4. Turn cleanup writes.
    • agent.go:2156-2283 hands back tasks, drains the queue and retires questions, and queues team traffic after closing done (:2235).
    • Team traffic is written at teamevent.go:92/126, joined at :184.
    • placemeta.go:126-138 SettleWrites joins six metadata writers.
    • Provider receipt accounting is tracked separately at usage_pending.go:31.
  5. Capture scheduling. sessionfile.go:3449-3450 onWrite and loop.go:1863-1865 Tail.Wrote feed the trailing timer at internal/cellstore/sealwatch.go:176-189, which runs OnTail after a quiet period (:205-213). That can start a new capture after cellstore.WaitCapture has returned.

Why each existing signal falls short:

  • EventTurnDone (loop.go:1355) and TurnEnded fire before naming, memory, pace and mark-read.
  • Agent.WaitIdle waits on done, which closes at agent.go:2235 without joining titleJobs or memoryJobs, and before team cleanup.
  • Turn stream closure (agent.go:2155, remote/server.go:1635-1651) covers synchronous defers but not titleJobs, memoryJobs or the trailing seal timer.
  • Agent.SettleWrites covers its six owners only, not turn completion, naming or memory.
  • waitForTitle (title.go:455) is a shutdown join with a timeout. WaitReceipts covers only accounting already admitted.
  • cellstore.WaitCapture (pipeline.go:77-94) joins one existing capture and doesn't prevent a later one.
  • SealWatch quiet is time-based, not writer completion. OnTurnEnd runs before pace.
  • Close and StopWork cancel the work instead of letting it finish.

Proposed seam (not implemented): WaitPostTurnWrites(ctx, turn) in internal/session, next to startTurnLocked, reachable from tests through the test transport, since the agent runs in another process. It would, in order:

  1. join the turn goroutine through its last cleanup;
  2. join that turn's titleJobs and memoryJobs, plus any admitted receipt or knowledge work that writes to the journal;
  3. drain SettleWrites;
  4. flush SealWatch's pending tail through OnTail, so every capture has been admitted.

Only after that are WaitCapture and a peer-head comparison a sound "turn fully settled" check. The same join would give quit and teardown a definite end to a turn's writes. A wait for a single record kind, for idle, or for a quiet timer is not enough, because the next post-turn writer reopens the gap.

Found while fixing the idle-release tests (#2467). Low priority.

🤖 Generated with Claude Code

Activity

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