Repository navigation
Description
Implementation issue — aggregator slice 1
Design sources: #851 (quota + throughput view) and #852 (needs-you attention
view). The aggregator package (pm-stack/aggregator/) already renders both
views from fixtures; slice 1 wires them to real fleet data and deploys the
page on the laptop with the stack. Read-only: display + classification only —
no starting/stopping/messaging sessions.
Data path chosen
Attention (#852) — SessionFeedEntry rows built by a shared mapper from
OpenCode v2 server signals (session outcome/time, message-level
error/finish, tool-part states, pending permission requests, open
session forms, consecutive identical errors), classified by the existing
classifyAttention in src/derive.ts into Drew's six states,
attention-ordered machine-first:
- Laptop sessions: live from the laptop's OpenChamber-managed OpenCode
server (port + password read from
~/.config/openchamber/managed-opencode/state.jsonat request time),
45 s in-process cache. - Sandbox sessions: from the fleet feed (below) — the sandbox server
binds its own loopback and is not reachable from the laptop. - Inclusion: sessions in the active set, sessions with a pending permission
request, and sessions updated within 12 h.jules-review-state.json
supervision entries annotate matching sessions as evidence.
Quota (#851) — a fleet feed file (fleet-feed.json) produced by a
collector shipped in this package (scripts/collect-fleet-feed.ts) that runs
on the sandbox host, where the sources of record live:
provider-quota-registry.md(provider facts + dated observations),- the stewardship quota view's dated reads,
provider-usage-snapshot.json(check-usage output),- the sandbox OpenCode server API (session burn attribution).
The collector emits clean QuotaView pools; the aggregator renders the feed
and computes the model-choice guidance (headroom / burn-before-reset /
do-not-start) from pool state + reset times. Per-pool extractors fail soft:
a pool whose read pattern stops matching renders with its identity and a
"no current read" note instead of vanishing or inventing numbers.
Pool map (corrected 2026-10-25): exactly ONE Meta pool — the Muse API
High Usage plan, reached only via the meta provider. There is no
"muse plan" pool and the assistant's Power subscription is never a pool.
Refresh cadence
- Laptop attention: live, 45 s cache.
- Sandbox attention + quota: collector cadence, ~5 min (collector + scp
push over the tailnet proxy route, same pattern aslaptop-ssh.sh).
Feed age is shown in the page footer/provenance; a stale feed keeps
rendering with its age stated, fixtures only if no feed exists at all.
Network exposure — Drew's decision (2026-10-25 09:19 CDT), on the record
LAN/tailnet accessibility is fine. A public endpoint is acceptable only
behind a password (Cloudflare Access or equivalent) — never open.
Applied in this slice:
- The aggregator binds loopback + the laptop tailnet address (like
Pad/Grist); no public hostname in slice 1. - ready-for-agent (6056) gets the same treatment in this pass: it is
laptop-loopback-only today; its binary takes a single--host(bare
--host= all interfaces), so the unit binds all interfaces, which
covers loopback + tailnet + LAN per Drew's decision. Verified answering
on the tailnet address after the change.
Deferred to slice 2
- Projector-ized quota/attention feeds (per-source live usage APIs from
design(aggregator): quota + throughput view — pooled quotas, resets, burn rates, model-choice support #851's source list) replacing the collector's registry extraction. - Throughput burn segments + token-rate history (slice 1 shows live
per-pool active-session token attribution only). - Session → PR join via the projector link registry; Jules sessions in the
attention feed; any action affordances (poke/switch) — the view stays
read-only. - A public, password-gated hostname if Drew wants one.
Acceptance
- Page serving on the laptop on loopback + tailnet;
/attentionand
/quota(and their/api/*.json) render real current data, with
provenance stating each facet's source + feed age. - ready-for-agent answering on the tailnet address.
- Tests green (existing suite + new mapper/extractor tests); PR merged.
Activity
developing-today commentedon Oct 25, 2026
CODE-138
feat(aggregator): live quota + attention facets via fleet feed and la…
feat(aggregator): deployment — laptop user unit, collector timer, rea…
fix(aggregator): resolve bun on the minimal systemd PATH in the feed …
fix(aggregator): run the feed collector as the sandbox user
fix(aggregator): pin HOME in the collector unit (sandbox runs as root)
style(aggregator): treefmt formatting (biome/prettier/shfmt)
dezren39 commentedon Oct 25, 2026
Slice 1 is built, deployed, and verified live. PR #892 merged 2026-10-25T14:59:51Z (merge commit d096f92); all checks green (treefmt, Nix flake evaluation, CodeRabbit).
What shipped (per this issue's data path)
Needs-you (#852) —
src/sources/opencode.tsmaps OpenCode v2 server signals (active set, pending permission requests, open forms, last-turn errors from idle-message errors / finish=error / errored tool parts, consecutive identical errors, long-running tool calls) intoSessionFeedEntryrows classified by the existingclassifyAttention. Laptop sessions are read live from the OpenChamber-managed server (port + password from its state file at request time, 45 s cache); sandbox sessions arrive via the fleet feed; jules-review-state.json supervision entries annotate matching sessions.Quota (#851) —
scripts/collect-fleet-feed.tsruns on the sandbox (~5 min systemd timerfleet-feed-collector, runs from a dedicated sparse clone~/git/pm-feednow on main) and pushesfleet-feed.jsonto the laptop over the tailnet proxy route. Per-pool extractors (src/quota-extract.ts) read the stewardship quota view's dated reads; fail-soft per pool (identity + 'no current read' note); guidance computed at collection (src/quota-guidance.ts); burn attribution from the sandbox server's active sessions. Corrected pool map honored: exactly ONE Meta pool (High Usage,metaonly door) — it rendered walled until today's 19:00 CDT reset in verification.Network (Drew 2026-10-25) — aggregator binds loopback + tailnet only (
PM_AGG_HOSTS, user unitpm-aggregator, env rendered from the laptop SOPS file byaggregator/install.sh). ready-for-agent got the same pass: bare--hostin its unit —ssshows 0.0.0.0:6056, loopback + tailnet both 200 (backup of the old unit kept on the laptop).Verification (2026-10-25 ~15:00 UTC, after redeploy from merged main)
http://127.0.0.1:4870andhttp://100.99.113.103:4870: /, /quota, /attention, /roadmap, /api/quota.json, /api/attention.json all 200.live (fleet feed · fresh · collected 2026-10-25T15:00:01Z); attentionlive (sandbox via fleet feed (fresh) + laptop live); board live (Kaneo), roadmap live (Grist); items/comments fixture-backed because the projector is not running (PM_PROJECTOR_URL=off, as before).bun test40 pass,bunx tsc --noEmitclean; CI treefmt initially failed on formatting — fixed with the repo's own treefmt (biome/prettier/shfmt) in a follow-up commit.Deferred to slice 2 (unchanged from this issue)
Projector-ized live per-source quota/attention feeds replacing registry extraction; throughput burn segments + token-rate history (slice 1 shows live per-pool active-session attribution only); session → PR join; Jules sessions in the attention feed; action affordances (view stays read-only); a public password-gated hostname if wanted.
One operational note: the collector timer is a sandbox system unit installed from
aggregator/systemd/; the sandbox has no user bus. If the sandbox is rebuilt from scratch, re-install from those unit files.