Matrix mouth: read receipts on ack, presence from the brake #13

Merged
heimdall merged 5 commits from heimdall/mouth-receipts-presence into main 2026-11-08 16:26:11 +00:00
Owner

Spec gamecult-chat:cut_spec:cut-mouth-receipts-presence.r1 (Bifrost only; no wire change).

  • An ack posts her public m.read per room at the newest covered event, fire and forget (never awaited; one log line on failure; no retry; no receipt state).
  • syncOnce reads the brake once per round, passes it to answerInvites (its own brake read is gone), sends set_presence=online|offline on /sync, and makes one PUT presence offline on a released-to-braked transition (first braked round after restart included; retried next round if it fails). Brake-only per ruling 2026-11-08 16:01Z (mouth-presence-source); no heartbeat lease.
  • Header ear paragraph rewritten; docs/bridge.md gains the two sentences.

Tests: 94 pass (node --test tests/persona-matrix-mouth*.test.mjs, CultLib built from cultlib-pr1 in a scratch dir). Hand mutants (not committed) killed: drop readUpTo, await readUpTo, lowest-seq receipt, pending posts receipt, failed receipt breaks ack, repeated braked PUT, lastPresence set before PUT succeeds, lastPresence starting offline, no set_presence, online never recorded, wrong user in presence path, invites ignore brake.

Merge order: cut-album-share-mouth.r1 edits the same file; whichever merges second rebases. Not deployed; not merged.

🤖 Generated with Claude Code

Spec gamecult-chat:cut_spec:cut-mouth-receipts-presence.r1 (Bifrost only; no wire change). - An ack posts her public m.read per room at the newest covered event, fire and forget (never awaited; one log line on failure; no retry; no receipt state). - syncOnce reads the brake once per round, passes it to answerInvites (its own brake read is gone), sends set_presence=online|offline on /sync, and makes one PUT presence offline on a released-to-braked transition (first braked round after restart included; retried next round if it fails). Brake-only per ruling 2026-11-08 16:01Z (mouth-presence-source); no heartbeat lease. - Header ear paragraph rewritten; docs/bridge.md gains the two sentences. Tests: 94 pass (node --test tests/persona-matrix-mouth*.test.mjs, CultLib built from cultlib-pr1 in a scratch dir). Hand mutants (not committed) killed: drop readUpTo, await readUpTo, lowest-seq receipt, pending posts receipt, failed receipt breaks ack, repeated braked PUT, lastPresence set before PUT succeeds, lastPresence starting offline, no set_presence, online never recorded, wrong user in presence path, invites ignore brake. Merge order: cut-album-share-mouth.r1 edits the same file; whichever merges second rebases. Not deployed; not merged. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
An ack posts her public m.read per room at the newest covered event, fire and
forget (never awaited, failure logged once, no retry). syncOnce reads the brake
once per round, passes it to answerInvites, sends set_presence on /sync and one
presence PUT on a released-to-braked transition (first braked round included).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- answerInvites takes a fresh brake read after the long-poll
- readUpTo reads the brake when each receipt would post; held brake drops it, one log line
- the brake read and offline presence PUT precede the full-backlog early return
- a throwing logger cannot reject the unawaited receipt call
- tests: fake keeps the raw path and honours abort signals; the braked-round carve-out is offline presence PUTs only

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Author
Owner

Fix batch per Heimdall's review, head 8938dbd: (1) fresh brake read at the invite join after the long-poll; (2) no m.read receipt while braked, read fresh when each receipt posts, one log line, nothing queued; (3) receipt path encoding pinned (fake keeps the raw path); (4) receipt timeout pinned (fake honours abort); (5) receipt failure log guarded against a throwing logger; (6) brake read and offline presence PUT precede the full-backlog return; (7) test carve-out narrowed to offline presence PUTs. 100/100 tests pass; each fix has a test and a killed hand mutant. Note: a released Mind at the pending cap makes no /sync, so it asserts no online presence. Report: cut_report attempt 2.

🤖 Generated with Claude Code

Fix batch per Heimdall's review, head 8938dbd: (1) fresh brake read at the invite join after the long-poll; (2) no m.read receipt while braked, read fresh when each receipt posts, one log line, nothing queued; (3) receipt path encoding pinned (fake keeps the raw path); (4) receipt timeout pinned (fake honours abort); (5) receipt failure log guarded against a throwing logger; (6) brake read and offline presence PUT precede the full-backlog return; (7) test carve-out narrowed to offline presence PUTs. 100/100 tests pass; each fix has a test and a killed hand mutant. Note: a released Mind at the pending cap makes no /sync, so it asserts no online presence. Report: cut_report attempt 2. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Author
Owner

Second fix batch, head 94606d0: answerInvites reads the brake fresh before every join (two-invite test, brake lands during the first join); presence PUT path encoding pinned on the raw path. 101/101 tests pass; both fixes have a killed hand mutant. Not touched, filed separately: failed m.direct write after a join, receipts out of order under concurrent acks. Report: cut_report attempt 3.

🤖 Generated with Claude Code

Second fix batch, head 94606d0: answerInvites reads the brake fresh before every join (two-invite test, brake lands during the first join); presence PUT path encoding pinned on the raw path. 101/101 tests pass; both fixes have a killed hand mutant. Not touched, filed separately: failed m.direct write after a join, receipts out of order under concurrent acks. Report: cut_report attempt 3. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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/Bifrost!13
No description provided.