Skip to content

Quota gatherer: Meta ok-but-null masks last-good read; Command Code needs explicit plan-only ask (routing-disabled) #5704

Open

Description

@dezren39

Root cause (verified 2026-11-04, laptop powered off — source-level, gh api reads only)

Meta — ok-but-null masks the last good read

  • The v1 file-export producer (~/atlas/meta-quota-export/latest.json, dev.meta.ai scrape) was retired 2026-11-03 (PR quota: gather meta + opendesign by asking OpenCode #5040); FILE_EXPORTS in pm-stack/aggregator/scripts/gather-quota.ts is empty. Its 401/session-expired state is a symptom, not the current source.
  • Current source is the explicit module ask (runMetaModule in gather-quota.ts): OpenChamber quota module fetchQuotaForProviderInstances("meta") — the in-process form of openchamber quota meta --json — credentials asked from the OpenCode server (GET /api/credential), then Meta's own plan endpoint POST https://api.meta.ai/muse-code/key (OpenChamber packages/web/server/lib/quota/providers/meta.js).
  • That provider returns ok:true with both windows present and percentages null on a 200 / is_subs_active:true response with no subs_usage (its documented "post-roll empty state"). That shape has been the gatherer row since after the last good read (weekly 100% @ 2026-11-03T21:00:12Z, reset 2026-11-09T00:00Z).
  • Gatherer bug: entryFromResult (src/quota-gather-map.ts) maps that to a fresh state:"ok" entry with null windows, discarding the provider's failureClass/lastSuccessAt. perf(quota-plane): parallel provider fetching + per-provider timeout budget for the 15-min tick #1268 retention only fires on error runs, so it never engages; recordsFromEntries emits nothing for null pct; quota-current.json publishes fresh nulls over the last good percentages, and downstream (quota-check/plan) has to carry the last good read by hand.
  • OpenCode's internal route_attempt history is not a substitute source: it meters tokens/cost per attempt; Meta's plan percentage exists only on Meta's endpoint. OpenCode core QuotaSnapshot.derive is a routing gate derived from the model-plan store + route history, not a provider usage read.

Command Code — routing-disabled, no explicit ask; plan vs balance

  • commandcode is disabled in routing (disabled_providers, definitions kept). Unlike Meta, the gatherer had no explicit ask for command-code: if the provider listing drops it, there is no run at all — no error, no retention, a silently missing row. An explicit ask always works (listing filter governs listing, never a named fetch; same rule the CLI documents).
  • The module's provider (command-code.js) returns PLAN windows (monthly derived from the $10 included-credits bucket, 5h/weekly from windowLimits) and balance windows (purchased_credits, free_credits, label-only). Per Drew's 2026-11-01 rule — Command Code reads use PLAN, not BALANCE — the gatherer must strip the balance windows so no consumer mistakes a balance for a plan reading (and, in OpenCode snapshot terms, a non-windowed credits snapshot is balance:true, percent:null — reading the plan kind from that snapshot is reading the wrong snapshot kind).

Fix (patch prepared, NOT yet applied — laptop is powered off)

Patch against gather-quota.ts + src/quota-gather-map.ts + src/model.ts (prepared in the fleet sandbox at /tmp/gather-quota-fix.patch on 2026-11-04; applies cleanly with patch -p1):

  1. entryFromResult preserves failureClass/lastSuccessAt and names the ok-but-null case (no-data: provider reported ok but no window carried a percentage…) on the entry.
  2. New finalizeRun in the gatherer downgrades any ok-but-no-measured-window run to an error run carrying that reason, so perf(quota-plane): parallel provider fetching + per-provider timeout budget for the 15-min tick #1268 retention replaces it with last-known rows stamped retainedFromFailedTick — stale-with-reason in quota-current.json + heartbeat, never fresh nulls. (CNY-label measurements, e.g. DeepSeek feat(usage-hub): DeepSeek daily balance check + CNY unit in snapshot store #1052, count as measured and do not degrade.)
  3. New runCommandCodeModule explicit ask with Meta's exactly-one-run discipline, plus keepCommandCodePlanWindows stripping purchased_credits/free_credits balance windows from listed and explicit runs alike (raw payload stays on entry.usage).

Verification still owed (laptop off at time of writing)

  • Apply the patch in a laptop worktree of ~/git/code, run the gatherer once, and verify with two independent reads of ~/.local/state/pm-aggregator/quota-current.json (deployed path per aggregator layout) that Meta shows a measured window or a retained stale row with the no-data reason, and Command Code shows a fresh plan read (monthly/weekly/5h, no balance windows).
  • Related: append-usage-snapshots.ts fails on laptop: quota-view.md path ENOENT under ~/workspace #4889 (usage snapshot append broken on laptop, bad path in the snapshot writer) is a separate defect in the same tick's output path.

Cross-link: summary also posted on #1163 (the Claude-adapter issue this investigation was routed through; the 2026-11-04 Claude ruling there is unaffected).

Acceptance criteria

  • A Meta quota read that returns ok-but-null no longer masks the last good read; the gatherer surfaces an explicit unknown/stale state instead.
  • Command Code plan state is handled explicitly (plan-on vs plan-off), not inferred from a null payload.
  • The null-payload case is covered by a test or a reproducible fixture in the gatherer.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 5, 2026
  2. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Intake review

    Related: part of the quota-pipeline batch filed 2026-11-04 23:12–23:18Z (see also the quota recorder move, #5703). Notes: the body already contains a verified root-cause pass, so triage should confirm the module-level fix scope rather than re-investigate. Risk: silently masked stale quota reads feed dispatch decisions — a wrong 'ok' is worse than an explicit unknown. Suggested approach: make ok-but-null an explicit stale/unknown state in the gatherer, add the fixture test, then handle Command Code plan state explicitly.

    Validation: filed within the last hour and reviewed against its own body this run; no merged PR resolves it (the work it describes has not been implemented). Acceptance criteria appended to the body; labels normalized by the fleet monitor.

    🤖 Agent note · intake enrichment

    • Agent: fleet monitor (repo-fleet-monitor)
    • Session / ID: monitor run 2026-11-04 18:12 CST
    • Found via: monitor reconciliation
    • Task: Duty 2 intake enrichment (protocol §5)
  3. added a commit that references this issue on Nov 5, 2026
  4. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Fix applied + verified live — 2026-11-05T00:27:47Z tick

    Applied: the prepared patch (md5 9311e0dbba943f2c22207fe907df1a0e, byte-identical transfer) applied cleanly with patch -p1 in a laptop worktree of ~/git/code at origin/main af0847d; committed a6ee7d0; PR #5719 MERGED 2026-11-05T00:33:39Z. Files: pm-stack/aggregator/scripts/gather-quota.ts, src/quota-gather-map.ts, src/model.ts (+166/−18).

    Verification run: one manual gatherer run from the worktree with the unit's state env. Result line:

    quota-gatherer: meta error 620ms rows=1 retained-last-known error=no-data: provider reported ok but no window carried a percentage (provider lastSuccessAt=2026-11-05T00:27:52.217Z)
    quota-gatherer: command-code ok 1666ms rows=1
    quota-gatherer: tick 2026-11-05T00:27:47.702Z providers=12 ok=10 records=+43 -> ~/.local/state/pm-aggregator/quota-current.json
    

    Two independent reads of quota-current.json (python json + jq) agree:

    • Meta: published entry is the retained last-known row — "retainedFromFailedTick":"2026-11-05T00:27:47.702Z", measuredAt = the last pre-stall tick (2026-11-04T18:30:20Z), NOT fresh nulls stamped as current. Honest nuance: the retained row's own windows are nulls, because the last-known entry on file was itself captured in the ok-but-null era — retention preserves staleness honestly (stamped + dated + reasoned) but cannot resurrect the 2026-11-03T21:00:12Z weekly-100% values, which live only in older snapshots. The reason string is carried on the run record (gatherer stdout/journal), as designed.
    • Command Code: fresh measured entry (measuredAt 2026-11-05T00:27:49Z), windows[] = plan windows only: monthly 6.64% ("0.66 / 10"), 5h 0% ("0 / 3"), weekly 11.07% (resets 2026-11-10T06:52:52Z). Balance windows (purchased_credits) survive only in the raw usage payload, exactly as the patch specifies. (This tick the provider came via the listing, so the explicit ask stood down as designed; the ask + strip apply when the listing drops it.)

    One blocker remains before the timer goes green — separate defect, filed as #5721: every timer tick currently dies at import (auth.openCodeLocalCredentialSource is not a function) because resolveLibDir() resolves to ~/git/openchamber-fork, a checkout parked on queue branch queue/8357-224 whose auth.js predates that export (origin/main has it at auth.js:214). The verification run above used a clean origin/main worktree via OPENCHAMBER_LIB_DIR. The #5704 code is landed; the timer will still fail until #5721 is fixed. Do not "fix" it by cleaning that checkout — it holds another lane's dirty work.

    Fresh quota picture from the same tick (for the record): opencode-go.popesdrew weekly 80.0% / monthly 40.0% (was 40%/20% at 13:24 CST — burn continuing; NO throttling per Drew's ruling, report only); openai.main weekly 41% (resets 2026-11-10T18:32Z); openai.alternate weekly 100% (walled, resets 2026-11-09T17:04Z); opencode-go.drewrypope weekly 100% / monthly 50% (unchanged); claude = session-expired error (Drew's re-sign-in; see also the Phase 5 note — ~/.claude/.credentials.json expiresAt unchanged at the 2026-11-04T22:18:48Z expiry).

  5. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed on Nov 5, 2026
  6. 187 remaining items

  7. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 6, 2026
  8. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  9. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 6, 2026
  10. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  11. dezren39 commented on Nov 6, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

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

Metadata

Metadata

Assignees

Labels

effort/mMedium: expected under 1 day of workkind/bugSomething isn't working (prefixed form of the classic bug label)meta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)queue/parkedQueue task is parked (waiting on CI, time, or an event)status/parkedDeliberately paused with a recorded resume conditiontask/triageQueue packet kind: verify, reproduce, and route this itemwork/assignedQueue worker lifecycle state

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions