Put the seat side of the room behind three JSON calls - #51
Open
andreisavu wants to merge 2 commits into
Open
Conversation
The seat side becomes a client of the room. wire.ts names what crosses: the seat calls view, commit and lease; the room calls wake, which carries the line a running activation is steered with when a message caused it. Every shape is plain JSON, and survives a round trip through JSON.stringify unchanged. seat/activation.ts is one activation over a view. seat/hands.ts holds say, summarise and seat. seat/seat.ts holds the routing rule, SeatActor and inProcessTransport. The actor takes a wake, claims the lease, reads the view, builds the Pi Agent with the hands the view names, runs it, renews the lease while it runs, and releases it when it stops. A steer that lands before the model has been asked is held until it is, so a steer follows the request it lands during and never joins it. room/lease.ts derives an activation's id from the record. room/assistant.ts keeps what the assistant is, and what the room holds of it while it runs. session.ts answers the three calls from a lease table it keeps in memory: a lease ends when the seat releases it or the room revokes it. A seat is live from the wake that was sent until that lease ends. The runtime gains Transport and the optional transport option. The scenarios run over a transport that serializes every request and response, and fail on a value that would not survive the wire. seat.test.ts drives the actor by hand over a room the test plays. This is PR 3 of the stack SPLIT_PLAN.md cuts from #48. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJjjR3S6iwqEbDXsRDJgiq
… loop A steer still queued on the agent when a run ends says the record moved, beside a renewal whose lastSeq is past what the activation heard. Main rebuilt on the queue alone; over the wire the renewal's lastSeq is read before a message that lands during the same tick, so the activation checks both, and nothing awaits between the check and the release. A seating changes the roster inside the log's landed callback: before the message routes, and once per key. A repeated seated commit hands back the message that landed and changes nothing twice. The renewal loop stops for good when the activation ends: a renewal in flight arms nothing when it comes back. EndReason loses `revoked`, which nothing writes yet. The docs say what ends a lease: the seat side releases it, and the room cuts an activation it ends. Held loses a field nothing read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SJjjR3S6iwqEbDXsRDJgiq
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR 3 of the stack that
SPLIT_PLAN.mdon #48 cuts from that branch. Branches frommainafter #50. The plan says nothing in the branch is this PR alone, so it is built by hand from the final tree.The one idea
The seat side becomes a client of the room, over three calls whose every shape is plain JSON.
wire.tsnames what crosses. The seat callsview,commitandlease; the room callswake. A request and its response survive a round trip throughJSON.stringifyunchanged.seat/activation.tsis one activation over a view.seat/hands.tsholdssay,summariseandseat, every one committing through the room'scommitcall.seat/seat.tsholds the routing rule,SeatActorandinProcessTransport: the actor takes a wake, claims the lease, reads the view, builds the PiAgentwith the hands the view names, runs it, renews the lease while it runs, and releases it when it stops.room/lease.tsderives an activation's id from the record (activationId,draftId,parseId,seatOf).room/assistant.tskeeps what the assistant is and what the room holds of it while it runs.session.tsanswers the three calls from a lease table it keeps in memory. A seat is live from the wake that was sent until the lease it claims ends, sosettled(),quiet()and the exchange's close read the same facts they read before. The table is scaffolding: PR 5 moves it onto the log, and the wire does not change.host/runtime.tsgainsTransport,RunningRoomas the wire sees it, and the optionaltransport.Two decisions the plan left open
Wake.steerlands here, not in PR 7. The room decides who is at work, and a seat at work has to hear the message somehow. The plan's alternative was a direct call on the actor, which is not on the wire. The wake carries the rendered line instead, so every shape stays JSON now, as the plan's "watch for" asks. PR 7 still moves the decision to the seat side and addsheard.mainthe prompt started in the same tick as the routing and never hit that window; over the wire it did, and two assistant tests hung on it. The seat side owns the stream function it hands Pi, so it marks the moment the model is asked and releases held steers then. The rule is deterministic across transports: a steer follows the request it lands during, never joins it.Trimmed per the plan
cuton the port and the race intake(PR 9), the deadline branch ofrenewUntil(PR 10),heardon the lease and thetakenseq (PR 7), theoverflag and the queue as an array (PR 8; one slot, asmainholds one activation), the row types on the wire (PR 4, PR 5),Message.activationIdandwakes(PR 5). Two things the plan did not list:test/support/transport.tsbringsserializingnow, because it is the test that pins this PR's claim in a real run; andpersistTurnsmoves fromlog/log.tstoseat/activation.ts, where the plan keeps it.The tests that pin it
test/wire.test.ts: every request and response round-trips unchanged, andassertWirerefuses what would not survive.test/matrix.test.ts: every scenario on both storages runs over a transport that serializes every request and response, and fails on a violation.test/seat.test.ts: the actor claims, runs and releases; a steer goes into the activation that runs and starts none beside it; a wake that queued behind an activation runs next, and one sent twice runs once.What a later PR adds on top
room/fold.ts,room/view.ts.decide,resumeSession; the in-memory table goes.wakesnaming every seat,heardon the lease, retries.overflag.cuton the wire.Checks
pnpm format && pnpm checkis green, 138 tests. Every doc link resolves. The build's three TS4023 lines fromtest/live/support.tsare onmaintoo.🤖 Generated with Claude Code
https://claude.ai/code/session_01SJjjR3S6iwqEbDXsRDJgiq
Generated by Claude Code