Skip to content

impl(aggregator): slice 1 — live needs-you + quota panels (fleet feed + laptop server) #890

Closed

Description

@dezren39

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.json at 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 as laptop-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; /attention and
    /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

  1. developing-today commented on Oct 25, 2026

    @developing-today
    Contributor
  2. dezren39 commented on Oct 25, 2026

    @dezren39
    MemberAuthor

    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.ts maps 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) into SessionFeedEntry rows classified by the existing classifyAttention. 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.ts runs on the sandbox (~5 min systemd timer fleet-feed-collector, runs from a dedicated sparse clone ~/git/pm-feed now on main) and pushes fleet-feed.json to 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, meta only 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 unit pm-aggregator, env rendered from the laptop SOPS file by aggregator/install.sh). ready-for-agent got the same pass: bare --host in its unit — ss shows 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:4870 and http://100.99.113.103:4870: /, /quota, /attention, /roadmap, /api/quota.json, /api/attention.json all 200.
    • Provenance: quota live (fleet feed · fresh · collected 2026-10-25T15:00:01Z); attention live (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).
    • Attention at capture: 90 sessions merged (50 sandbox + 40 laptop), errored ordered first; zero in-motion was truthful — both servers' active sets were empty at capture (the in-motion path is covered by the mapper's stub-server test).
    • bun test 40 pass, bunx tsc --noEmit clean; CI treefmt initially failed on formatting — fixed with the repo's own treefmt (biome/prettier/shfmt) in a follow-up commit.
    • Captured HTML evidence: kept with the fleet records (agg-evidence/quota.html, attention.html).

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions