Count a canceled bridge stream's terminal from the items written #130
No reviewers
Labels
No labels
Area/ci
Area/cli
Area/client
Area/client-core
Area/daemon
Area/deps
Area/docs
Area/grid
Area/manual
Area/nix
Area/protocol
Area/pty
Area/render
Area/shaping
Area/skills
Area/transport
Area/vt
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Performance
Kind/Security
Kind/Testing
perf-regression-accepted
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Blocked
Status
Need More Info
Status
Upstream
No milestone
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
felis-terminal/felis!130
Loading…
Reference in a new issue
No description provided.
Delete branch "bridge/eof-stream-terminal"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
When a
cancelop or stdin EOF canceled a live bridge stream, the bridge could write a cleanendwhosecountclaimed items it never wrote: e.g.sessions.searchpiped in with stdin closed right after printed{"v":1,"id":1,"event":"end","count":2}and no item lines.Cause.
Link::cancelmarks the stream canceled in the client driver before the Cancel frame leaves, so items the daemon already had in flight decode as dropped-after-cancel. The daemon still answersEnd { count }with its producer's emitted total, and the bridge relayed that count. The same path servedsessions.search,sessions.captureandnotifications.subscribe, under both EOF and an explicitcancel.Fix. The bridge counts the item lines it queues for stdout per op, under the lock that queues items and publishes the terminal, and uses that count in
end. On an uncanceled stream the two counts are equal, so output there is unchanged and the goldens pass. A canceled stream still ends with a cleanend, as thecancel-a-live-streamgolden pins; thecanceledfailing terminal stays for a stream the daemon has not terminated when the EOF shutdown stops waiting.End{count:2}after it reads the Cancel, so the drop is deterministic. The explicit-cancel and EOF tests both require exactlyendwithcount: 0(they fail on main withcount: 2).count: 2with no items before,count: 0after (4/4 each); with stdin held open both items andcount: 2as before.cli.mdbridge contract andCHANGELOG.mdupdated.just checkpasses; reviewed by pi (sol): PASS, both non-blocking notes applied.Fixes #112.
A bridge stream canceled by a `cancel` op or by stdin EOF ended with a clean `end` carrying the daemon's own count while none of the items it counted reached stdout. Link::cancel marks the stream canceled in the client driver before the Cancel frame leaves, so every item the daemon had in flight decodes as DroppedAfterCancel; the daemon still closes the stream with End { count } covering those items, and the bridge relayed that count. A consumer reads a clean terminal with count N as a complete result of N lines, so `printf <search> | felis bridge` reported two matches and printed none, and the count varied with how much of the producer's last slice crossed the cancel. The docs already decide the terminal: closing stdin cancels in-flight operations, and the pinned cancel conversation ends a canceled stream with a clean `end`. So the shape stays and only the count changes: the bridge counts the item lines it queues, under the same lock that publishes the terminal, and reports that. The daemon's count is no longer carried through the link at all. Draining on EOF instead would contradict the documented cancel, and a `canceled` error terminal for every canceled stream would change the pinned explicit-cancel shape. The scripted-daemon tests hold the producer until its Cancel arrives, so the drop is certain rather than timing-dependent. Assisted-by: Claude Code