Skip to content

unify(quota-plane): one quota read path — server endpoint, CLI, and aggregator gatherer must share the direct credential-store read #2490

Open

Description

@dezren39

Leg of epic #1149 (quota-plane Stage 2). Filed at Drew's direction, 2026-10-29: "there was supposed to be a unification in the 3 quota read methods." There wasn't — three independent quota readers exist today, they resolve credentials three different ways, and they disagree. This issue is the unification leg of #1149; it cross-links the legs it absorbs or depends on rather than replacing them.

The three readers

  1. OpenChamber web server /api/quota/* — resolves credentials through the running OpenCode server's GET /api/credential projection (openchamber server/index.js:1647; projection server/lib/opencode/auth.js:91, 203-230, active rows only).
  2. openchamber quota CLI — server-first, with a direct credential-store fallback (openCodeLocalCredentialSource, reading opencode.db read-only). Built 2026-10-27 under fix(quota-plane): opencode-serve boot verification + quota CLI per-provider resilience (no wholesale transport failure) #1251, which deliberately switches to the store ONLY on transport failure and stamps each row credentialSource: server|local.
  3. PM-stack aggregator gatherer (pm-stack/aggregator/scripts/gather-quota.ts:10-19, 116-149) — reads opencode.db directly, by design, because the server-API path is known to diverge (fix(quota-plane): opencode-serve boot verification + quota CLI per-provider resilience (no wholesale transport failure) #1251), replicates the server's projection itself, and additionally ingests bespoke per-provider file exporters (Meta: pm-stack/meta-exporter/meta-quota-export.mjs). Writes quota-current.json on a ~15-minute systemd tick.

Divergence evidence (2026-10-29, same evening, same pools)

Meanwhile #2135 unified only the consumers — the steward went snapshot-first onto quota-current.json — but the dispatcher still live-probes reader 1, so consumers still disagree about the same pool at the same moment.

Why it matters

The fleet measurement gate forbids placement on a pool without a fresh read ≤60 min old. Every reader disagreement therefore closes a primary pool: Antigravity is the Gemini Flash pool's ONLY source and carries the Claude seats' clean read; Meta is a primary pool. The cost is burn-by-expiry — Antigravity's weekly (4% used on the Gemini tiers) resets Sat Oct 31 8:11 AM CDT largely unspent while OpenCode Go's monthly sits at ~96%. Three readers also means three places for the same bug family to recur (cf. the exporter's sibling defects #2460/#2461/#2470).

Proposed target state

One canonical read module: the direct credential-store read (the semantics reader 3 already implements and reader 2 falls back to) plus the shared provider fetchers, consumed by:

  • the OpenChamber server endpoint (no more server-projection-only resolution),
  • the CLI (no server-first/store-fallback split — one path, one provenance),
  • the gatherer, which keeps its 15-minute tick and file-export ingestion but imports the same read module instead of carrying its own projection logic.

Exception providers fold into the same path where their integration plans allow (#1167/#1301 for Meta, #1169 for Google/Antigravity — this issue absorbs #1169's read-path half; the login/token-capture half stays with #1169). Per-provider error states stay distinct end-to-end: not-configured vs store-read-failure vs provider-error are different facts and must never collapse into one another.

Acceptance criteria

  • All three surfaces (server endpoint, CLI, gatherer/hub) return identical windows for the same pool in the same tick, demonstrated on Antigravity, Meta, and one ordinary pool (e.g. ClinePass or OpenCode Go).
  • No surface can report not-configured for a credential the direct store read resolves.
  • Dispatcher, steward, babysitter, and the /quota UI consume one source with identical semantics (the Pool quota steward: consume the compiled hub numbers — probe only stale pools, follow-ups daily #2135 snapshot-first rule extends to the dispatcher's gate).
  • A credential-store read failure surfaces as its own error state on every surface, distinct from not-configured (the fix(id-world): manifest plan leaves omitted grants/ceiling/schedules/profile alone (#817) #858 resolver swallow is gone or subsumed).
  • The Meta exporter path either folds into the shared module or its parser contract is enforced by the same validation the shared path uses — a single bad field can no longer poison a whole pool's export.

Related

Activity

  1. added
    task/triageQueue packet kind: verify, reproduce, and route this item
    source/creatorFiled by the new-issue-creator job
    priority/highImportant and time-sensitive
    queue/queuedHas a queued task in the Supabase agent queue
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    on Oct 30, 2026
  2. developing-today commented on Oct 30, 2026

    @developing-today
    Contributor
  3. added
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Oct 30, 2026
  4. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    Agent: quota-unification-filer
    State: filed
    Found via: Drew, 2026-10-29 — "there was supposed to be a unification in the 3 quota read methods"

    Filed at Drew's explicit direction (2026-10-29 ~20:19 CDT). This is the read-path unification leg of epic #1149: the OpenChamber /api/quota endpoint, the openchamber quota CLI, and the pm-stack aggregator gatherer each resolve credentials differently and disagree about live pools — Antigravity blind via the server projection (developing-today/openchamber#858), Meta poisoned in its private exporter (#1301), OpenDesign absent until deployed (developing-today/openchamber#536).

    Enqueued as a triage task on repo-health-code-pilot. Triage should confirm the shared-module shape (direct credential-store read as canonical, per pm-stack/aggregator/scripts/gather-quota.ts), rule on how #1169's read-path half folds in, and route the phased fix packets. Nothing self-assigned.

  5. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Scope extension + fresh evidence — 2026-11-01 (Drew: "unify 1 quota path for all")

    Drew re-issued the unification order tonight after the Command Code meter dispute. #2490 already covers the three readers; tonight's failures show the unification must also cover the consumer side, or the readers can agree and the surfaces still disagree:

    1. Aggregator prose extraction is a fourth, shadow read path. pm-stack/aggregator/src/quota-extract.ts regex-parses dated prose out of the stewardship quota view and renders it as current pool state. On 2026-11-01 it presented a week-old Command Code read as current (defect filed separately as Quota reads: Command Code reports plan windows, not the top-up balance (extractor renders stale read) #3410). Under the unified design, the aggregator consumes the canonical record (quota-current.json / its successor) only; prose extraction is retired as a data path — the view stays a human log.
    2. Feed mapping diverges from the gatherer. The same gatherer tick (2026-11-01T06:45Z) reads OpenCode Go Personal/Lab monthly at 100%/100% in quota-current.json, while the fleet feed's pool mapping shows 84%/87%. One canonical record, one mapping — consumers never re-derive percentages.
    3. Plan-not-balance is a canonical-record invariant (Drew's ruling, 2026-11-01, Quota reads: Command Code reports plan windows, not the top-up balance (extractor renders stale read) #3410): the record for each pool carries the plan windows (used, limit, percent, resetsAt, measuredAt, source). A credit/top-up balance, where a provider exposes one, is a separate labeled field and is never a window. Concrete case: OpenChamber's packages/web/server/lib/quota/providers/command-code.js builds its monthly figure from /alpha/billing/credits as a balance window (usedPercent: null); only 5h/weekly come from windowLimits as real plan windows. The canonical module needs a plan monthly window for Command Code (console-format: monthly limit % of the $10 included credits).
    4. Steward/hub reporting follows the record. The Oct 30–31 "credits 0.1, walled-in-effect" Command Code reporting came from quoting the balance field outside the record. Consumers (stewardship, quota-check, orchestration, dispatcher) read the canonical record only — no side-channel figures.

    Acceptance addition: with the unified path live, one timestamped read per pool exists at any moment, and the dashboard, feed, steward view, and quota-check all show the same figures for the same pool at the same time (spot-check Command Code + both Go workspaces).

  6. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Drew prioritized this in main chat 2026-11-01 (~02:01 CST) after tonight's Command Code / Go mapping disputes. Queue task 3721 moved from priority 90 (the last band — lower claims first) to priority 10, verified head of repo-health-code-pilot. The 2026-11-01 scope-extension comment above (consumer-side unification, plan-not-balance invariant, #3410) is part of the job.

  7. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  8. 35 remaining items

  9. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 7327 (triage, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  10. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 7327 (triage, queued, priority 50) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  11. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Direction update (Drew ruling, 2026-11-03 11:48 CST — ask the OpenCode server, not a proxy/direct read): the aggregator gatherer leg of this unification is now server-first. PR #5062 (merged, e628bee) wires the gatherer with the CLI combined-source shape — openCodeCredentialSource (GET /api/credential on the standing fork server) primary, openCodeLocalCredentialSource only as transport-failure fallback (#1251 preserved, #1268 retention untouched). OpenDesign cannot follow yet: filed precisely as #5060.

  12. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  13. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  14. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · triage findings (queue task 7327, 2026-11-03)

    Verdict: server-first path landed where supported; OpenDesign remains an explicit exception

    Fresh state: #2490 is OPEN, with no hard-stop labels. Its original direct-store-canonical framing was superseded by Drew's 2026-11-03 ruling to ask the OpenCode server first. I verified current source/PR state, but did not run a live quota tick or same-tick pool comparison in this triage.

    #2490 stays open until the OpenDesign exception is resolved or explicitly accepted and the remaining parity acceptance is verified. The code paths are merged, but no runtime/same-tick verification was performed here.

  15. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: done (queue task 7327)

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

Metadata

Metadata

Assignees

Labels

app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/lLarge: multi-day or open-ended; expect staged check-inskind/designA design proposal (design-architect): multi-step, phased, decision-bearingpriority/highImportant and time-sensitivesource/creatorFiled by the new-issue-creator jobtask/triageQueue packet kind: verify, reproduce, and route this item

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions