Persona first line carries the mouth's room tail (wake-reads-room-tail) #44

Merged
eureka merged 3 commits from eureka-substrate/persona-room-tail into main 2026-11-11 08:43:12 +00:00
Owner

Ruling eureka-body:ruling:wake-reads-room-tail. The mouth side is Bifrost #51 (e8722d0, docs/bridge.md "Room tail"); this PR consumes it. The earlier in-process accumulator (Soul-failed) is deleted.

Deploy safety. ChatPending gains tail: Option<Vec<TailLine>> (defaulted, skipped when none), so a two-field v3/v2/v1 answer and the three-field v3 both decode. Golden bytes: the two-field files are the old mouth's (persona-chat-v5); the three-field files in persona-chat-v6 are Bifrost e8722d0's own golden files, written by its encoder pendingV3Tuple, copied unchanged.

Consumer. Chat::pending returns the ChatPending; carry hands the tail of the poll to the session, and the process's first line renders it:

  • limits: 20 newest per room, 60 in all, at most 24 KiB of the section as rendered (header, markers and lines counted); speakable rooms only (public session: config.rooms; DM session: its own room alone)
  • lines carry room, event, sender, role (derived), form, basis, ts, JSON-quoted text; framed as untrusted DATA, never instruction
  • a tail line whose event id a waiting delivery shows is dropped; lines acked as unaddressed stay (the tail is sent regardless of ack)
  • a marker for dropped lines; a note naming rooms with no line (quiet, or the mouth could not read it); "empty" when all are quiet; "unavailable" when the mouth sent no tail (older mouth, v1 request)
  • a failed first poll fails the start as Failure::Chat again (the earlier degrade-and-continue is removed)

Tests (new). 600 lines of chatter across rooms through a fake that enforces the mouth's caps; quiet-room wake and no-tail mouth markers; unspeakable rooms and DM isolation; dedupe by event id; injection-looking line stays in the frame; byte bound counts framing; failed first poll is Chat with no process started; old and new byte shapes decode (chat_client), v1 request sees no tail (chat_rudp).

cargo test -p mind-body --no-fail-fast in the rust docker image as root on the final tree: all targets ok (lib 862 passed).

Not verified: the live mouth; Bifrost's encoder was not run here (its fixture bytes are used).

🤖 Generated with Claude Code

Ruling `eureka-body:ruling:wake-reads-room-tail`. The mouth side is Bifrost #51 (e8722d0, `docs/bridge.md` "Room tail"); this PR consumes it. The earlier in-process accumulator (Soul-failed) is deleted. **Deploy safety.** `ChatPending` gains `tail: Option<Vec<TailLine>>` (defaulted, skipped when none), so a two-field v3/v2/v1 answer and the three-field v3 both decode. Golden bytes: the two-field files are the old mouth's (`persona-chat-v5`); the three-field files in `persona-chat-v6` are Bifrost e8722d0's own golden files, written by its encoder `pendingV3Tuple`, copied unchanged. **Consumer.** `Chat::pending` returns the `ChatPending`; carry hands the tail of the poll to the session, and the process's first line renders it: - limits: 20 newest per room, 60 in all, at most 24 KiB of the section as rendered (header, markers and lines counted); speakable rooms only (public session: `config.rooms`; DM session: its own room alone) - lines carry room, event, sender, role (derived), form, basis, ts, JSON-quoted text; framed as untrusted DATA, never instruction - a tail line whose event id a waiting delivery shows is dropped; lines acked as unaddressed stay (the tail is sent regardless of ack) - a marker for dropped lines; a note naming rooms with no line (quiet, or the mouth could not read it); "empty" when all are quiet; "unavailable" when the mouth sent no tail (older mouth, v1 request) - a failed first poll fails the start as `Failure::Chat` again (the earlier degrade-and-continue is removed) **Tests (new).** 600 lines of chatter across rooms through a fake that enforces the mouth's caps; quiet-room wake and no-tail mouth markers; unspeakable rooms and DM isolation; dedupe by event id; injection-looking line stays in the frame; byte bound counts framing; failed first poll is Chat with no process started; old and new byte shapes decode (`chat_client`), v1 request sees no tail (`chat_rudp`). `cargo test -p mind-body --no-fail-fast` in the rust docker image as root on the final tree: all targets ok (lib 862 passed). Not verified: the live mouth; Bifrost's encoder was not run here (its fixture bytes are used). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Matrix is the record and the mouth its only reader: the tail is assembled from the deliveries and contexts the mouth already sends (no new endpoint, no stored copy). Newest 60 lines / 24 KiB, oldest dropped with a marker; public sessions read the rooms she may speak in, a DM session its own room alone; a first poll that fails degrades to a tail-unavailable note instead of failing the wake.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
eureka changed title from Persona processes open with the tail of her rooms (wake-reads-room-tail) to Persona first line carries the mouth's room tail (wake-reads-room-tail) 2026-11-11 08:24:24 +00:00
ChatPending gains a defaulted Option tail (old two-field mouths still decode), Chat::pending returns it, and a process's first line renders it: speakable rooms only, 20 per room and 60 in all, byte bound counting framing, quoted untrusted data, dedupe by event id against waiting deliveries, a marker for dropped lines and for rooms with none, and an unavailable note when the mouth sends no tail. A failed first poll fails the start as Chat again.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
eureka merged commit cc0709039f into main 2026-11-11 08:43:12 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Huginn!44
No description provided.