Skip to content

perf: bound the daemon's warm mirror to recent history (#167) - #168

Closed
yonidavidson wants to merge 2 commits into
feat/160-ackfrom
perf/167-bounded-mirror
Closed

yonidavidson wants to merge 2 commits into
feat/160-ackfrom
perf/167-bounded-mirror

Conversation

@yonidavidson

Copy link
Copy Markdown
Owner

Closes #167. Stacked on #166#165 (rebases to main as those merge).

The daemon mirrored the entire bus — every key and every body — and refreshed it on every poll. That read every blob in the store each time: pending mail, the roster, the whole 30-day archive, and every telemetry batch. The cost of a poll grew with all history, forever, on a bus where nothing but the last few messages is hot.

What changed

  • src/daemon.ts — a warm-window policy: agents/ re-read every poll (records mutate), inbox/ always warm, read/ and events/ warm only inside AGENTCOMM_MIRROR_HISTORY_MS (default 7 days). Aged-out history keeps its key, so list, log, purge and channel discovery see the whole store exactly as before; the body loads on demand through the existing get passthrough. Immutable blobs already held are never re-read, so steady state is "the roster plus whatever is new".
  • src/types.ts / src/backends/git.tsSnapshottable.snapshot(prefix, {bodies}) now takes a body filter and returns {keys, bodies}. Listing names (ls-tree) is cheap; cat-file over every object is not.

Tests

  • test/git.e2e.test.ts: a snapshot reads only the requested bodies while still reporting every key, and a skipped body is still one get away.
  • test/daemon.e2e.test.ts: a month-old archived message is outside the window, yet log still returns its body and purge --dry-run still counts it.

`peek` shows messages without consuming; `inbox` consumes what it shows.
With only those two there was no way to mark mail read that arrived any
other way, so an agent that had read all five messages via `peek` and
replied to every one still showed five unread — and the stop guard blocked
every turn for fifteen turns demanding it read them.

`agentcomm ack <id…>` / `ack --all` archives pending messages by id. It
works off the KEYS (the id is in the key), so nothing is re-fetched, and
ids that are not pending for you are reported with a non-zero exit instead
of passing silently — acking someone else's mail is not a quiet no-op.

`peek` now closes with the one-liner that clears what you handled, the
stop guard says the same thing, and the guidance file teaches peek+ack as
the non-destructive read path.
The daemon mirrored the entire bus — every key AND every body — and
refreshed it on every poll. On a snapshot-capable backend that is one round
trip, but it read every blob in the store each time: pending mail, the
roster, the whole 30-day archive, and every telemetry batch. The cost of a
poll therefore grew with all history, forever, on a bus where nothing but
the last few messages is hot.

The mirror now holds what is worth holding. `agents/` is re-read every poll
(records mutate); `inbox/` is always warm; `read/` and `events/` are warm
only inside a window (AGENTCOMM_MIRROR_HISTORY_MS, default 7 days). Older
history keeps its KEY, so list, log, purge and channel discovery see the
whole store exactly as before, and a cold body loads on demand through the
`get` passthrough. Message blobs are immutable, so a body already held is
never re-read either: steady state is "the roster plus whatever is new".

`Snapshottable.snapshot` grew a body filter and now reports the keys it saw
alongside the bodies it read — listing names is cheap, reading blobs is not.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant