Skip to content

Fold automated message bursts into one compact list at the delivery layer - #224

Merged
johnnyelwailer merged 1 commit into
mainfrom
fix/automated-message-burst-fold
Sep 12, 2026
Merged

johnnyelwailer merged 1 commit into
mainfrom
fix/automated-message-burst-fold

Conversation

@johnnyelwailer

Copy link
Copy Markdown
Owner

The bar

"any burst like that, for any kind of automated source... should probably be replaced with one folded message, that contains a super compact list, with instructions on how to read individual contents on demand IF necessary at all"

So the fold is a delivery-layer concern, built ONCE beside the digest framing + the restart-hold summary, not inside each notifier. Every automated source inherits it by passing through the shared renderer — no per-notifier copies that could drift.

What this changes

New t3team-actorBurstFold.ts: one compact renderer (renderAutomatedBurstBlock) + the fold decision (splitAutomatedBurst). Two call sites:

  • Digest (buildActorReactionDigestInput): a batch of more than the threshold of non-urgent entries renders as ONE compact list — a header naming the count, one line per item (id · «sender» · thread · urgency — short subject), and a t3team_read_message pointer.
  • Restart-hold (buildActorRestartHoldSummary): the held-messages section folds the same way.

Invariants:

  • Dedup runs FIRST (the Fix duplicate terminal-state notifications: epoch-scoped durable dedup #222 terminal-notify ledger), so the fold renders an already-deduplicated set — it never re-collapses repeats on its own.
  • Urgent bypasses the fold: urgent entries keep their own full block so a hard blocker is never buried in a compact list.
  • Human messages never fold: the actor mailbox is inter-agent-only (human turns go through the normal user path), so this is structural.
  • Sub-threshold and single-entry batches keep the full per-entry shape byte-exact — so the restart-rehydrate matcher (B4 invariant, actor.messageIds + single-entry bases) is unaffected.

Every automated source folds — none left behind

All automated sources publish to the actor mailbox and reach the recipient through exactly these delivery surfaces:

  • Normal (idle boundary) → startActorReaction → the digest (folded).
  • Held at restart → startActorRestartHoldSummary → the restart-hold summary (folded).
  • User engaged → buildActorReactionCompressedInput, which is already a compact grouped list.

So silence watches, abnormal stops, completion notices, and workflow steps all fold. No automated source is left unfolded.

Measured on real rows (thread fbdb583b shape)

32 raw silence-watch messages → 12 distinct after the #222 dedup. Reconstructed 12 "stopped" notices with the real buildSilenceMessageText body + the emit's sender summary:

Surface Before After Saved
Digest (12 notices) 2616 B 1163 B 1453 B (56%)
Restart-hold (12 notices) 2531 B 1511 B 1020 B (40%)

After shape (digest):

[Inter-agent digest: 12 message(s)]

[Inter-agent burst: 12 messages folded — deduplicated; read any in full with t3team_read_message]
- id r-1 · «Fix A» · thread fbdb-1 · normal — Watched thread stopped: Fix A
- id r-2 · «Fix B» · thread fbdb-2 · normal — Watched thread stopped: Fix B
...

Verification

  • 68 delivery-layer tests pass (15 new fold tests: burst→folded list, sub-threshold full, urgent bypass, single-entry base byte-shape, dedup-before-fold, item-retrievable-by-id, real-row byte savings; plus the existing digest/restart-hold/rehydrate/reaction suites).
  • Additive guard, comm-diffed vs pristine main fe484ad8c: 0 new violations, 0 new warnings (the new module is 89 LOC, well under the cap).
  • No typecheck regressions in touched files; the hard typecheck errors in-tree are pre-existing in unrelated files.
  • t3team-actorReactionInput.ts was already over the 200 cap on main (255, from the overhaul); this change adds 16 lines to it. t3team-actorMessageReaction.ts (233) is untouched.

…ayer

When a burst of non-urgent inter-agent messages lands in one delivery, fold
them into a single compact list (count + one line per item + a
t3team_read_message pointer) instead of one verbose block each. One compact
renderer (t3team-actorBurstFold.ts) beside the digest framing and the
restart-hold summary, so every automated source (silence watches, abnormal
stops, completion notices, workflow steps) inherits the fold by passing
through it - no per-notifier copies.

Urgent entries bypass the fold (their own full block). Sub-threshold and
single-entry batches keep the full per-entry shape byte-exact, so the restart
rehydrate matcher (B4 invariant) is unaffected. Human messages never fold
(the actor mailbox is inter-agent-only). Dedup runs first (the #222
terminal-notify ledger), so the fold renders an already-deduplicated set.

Measured on the thread fbdb583b shape (12 distinct 'stopped' silence-watch
notices after the #222 dedup): digest 2616B -> 1163B (56% smaller),
restart-hold 2531B -> 1511B (40% smaller).
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Sep 12, 2026
@johnnyelwailer
johnnyelwailer merged commit d31e01d into main Sep 12, 2026
9 of 16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant