Skip to content

impl(pm-stack): browser-dependent quota watchdog core — disk state, failure classes, repair ladder #6032

Open

Description

@dezren39

Implements the watchdog core from the design in #6031 (browser-dependent quota watchdog). Scope is the generic machinery only — no provider specifics; Mistral is adapter #1 on top of this (openchamber issue, linked from #6031).

What to build

In pm-stack (this repo), deployed on the laptop beside the gatherer:

  1. Watchdog oneshot + timer (pm-stack/systemd/): runs on the gatherer's 15-minute rhythm, offset (e.g. *:3/15) so it judges fresh producer documents. Oneshot discipline like the existing laptop watchdog family (laptop-backups-watchdog code#1841, codex-remote-control-watchdog code#1845).
  2. State file ~/.local/state/pm-aggregator/quota-watchdog-state.json — atomic tmp+rename writes; per-source health record: lastAttemptAt, lastSuccessAt, consecutiveFailures, currentClass, sanitized lastDetail, cooldownUntil, repairLog (last 20), incident refs. Nothing decision-relevant lives in process memory — this is code#5928's lesson applied from day one (cross-tick cooldown + failure counts on disk).
  3. Failure-class evaluation: read each registered source's ~/atlas/<provider>-export/latest.json + the gatherer heartbeat; classify into session-expired / cookie-missing / endpoint-changed / browser-down / cdp-unreachable / rate-limited / unknown (producer pre-classifies its own failures; the watchdog owns classes the producer cannot see, e.g. missing document, browser process state).
  4. Repair ladder executor with per-rung attempt caps (max 2 per incident) and a persisted repair log, cheapest first: (0) rate-limit cooldown honored from disk; (1) retry next tick (no-op rung); (2) CDP session re-validation against the live laptop browser (cookie presence by name; in-page fetch re-run — the cookie value is not read into the process; extraction only for adapters that declare a header-token path, value → fleet secrets by registered name only); (3) browser re-login driver via the provider's own SSO flow using the browser's standing Google session — stops dead at a human-only challenge and marks the incident human-blocked. Rung 4 (escalation) is the sibling escalation issue, invoked from here.
  5. Adapter loader: read per-provider adapter files (pm-stack/exporters/adapters/) — origins allowlist, CDP port, data-path order, cookie names, login hints, class map, thresholds. The core contains zero provider names.
  6. Producer scaffolding: a forkable producer template from the retired Meta producer (~/atlas/meta-quota-export/meta-quota-export.mjs on the laptop — CDP plumbing, tab ownership per code#5121, per-call timeouts, failure-class exit codes, atomic latest.json + history.jsonl), landing producer source in pm-stack/exporters/; plus the gatherer FILE_EXPORTS registration step documented/wired for each adopted source.
  7. Health history: append per-tick per-source observations to ~/.local/state/pm-aggregator/quota-watchdog-history.jsonl (interim); emit into the usage snapshot pipeline once code#4889's laptop path bug is fixed (do not block on it).

Safety bounds (from the design)

Never writes quota data; never changes credentials other than re-storing an extracted session under its registered secret name; browser automation only against adapter allowlist origins, read endpoints + login flow only; no secret value in any log, state field, or document; login attempt caps double as account-lockout protection.

Acceptance criteria

  • Watchdog oneshot + timer installed on the laptop and ticking on the gatherer cadence; state file written atomically each tick and surviving across invocations (demonstrated over ≥3 consecutive ticks).
  • A synthetic broken source (fixture adapter + producer stub) walks the ladder: retry → CDP re-validation → re-login attempt → escalation hand-off, with each rung's attempt recorded in repairLog and caps honored (no rung attempted >2× per incident).
  • A rate-limited fixture sets cooldownUntil on disk and the next tick honors it without any browser action (the quota: claude read needs cross-tick memory — usage-endpoint 429 (proven) + oneshot gatherer defeats the fetcher's in-process cooldown/cache #5928 regression test, in effect).
  • Human-only challenge fixture marks the incident human-blocked and the state carries the surfacing flag the orchestration/stewardship sweeps read.
  • Quiet when healthy: a healthy source produces no output beyond journal/state/history records.
  • Tests name their oracles per the testing doctrine (state-machine invariants for the ladder are a natural PBT/MBT fit).

Related: #6031 (design), #1260 (self-healing umbrella), #5928 (cross-tick state), #4889 (snapshot history path).

Activity

  1. added
    kind/featureNew feature or capability request
    app/pm-stackpm-stack (Pad/Grist) in developing-today/code
    on Nov 5, 2026
  2. developing-today commented on Nov 5, 2026

    @developing-today
    Contributor
  3. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Addendum (design doc updated): the surfacing channel for a failed/escalated source is the aggregator's existing #1148 watchdog mechanism — pm-stack/aggregator/src/watchdog.ts derives deduped watchdog:<input> tier items from input diagnoses, merged into the orchestrator's tiers by derive.ts. Read the human-blocked acceptance criterion as: the source's state is exposed as an aggregator input diagnosis so it surfaces through that channel (adding a quota-source diagnosis to the health layer is in scope if none exists), with the orchestrator's review promoting human-blocked items to Drew. Separately: fleet-watchdog (#1358) watches Muse scheduler jobs only (alert-only) — it is not this watchdog's supervisor and is not a dependency.

  4. added
    queue/queuedHas a queued task in the Supabase agent queue
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 5, 2026
  5. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: queue worker (repo-worker-code), lane code-k
    • Session / ID: task 10398
    • Found via: queue claim
    • Task: Triage issue impl(pm-stack): browser-dependent quota watchdog core — disk state, failure classes, repair ladder #6032 (impl(pm-stack): browser-dependent quota watchdog core — disk state, failure classes, repair ladder)
    • Expectation: execution lane assigned opencode by fleet queue lane for task 10398; the laptop opencode claimant launches the bound session, which drives task 10398 to its terminal state. This lane does not work it directly.
    • Links: n/a
  6. added
    work/assignedQueue worker lifecycle state
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 5, 2026
  7. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Task 10398 (lane code-s) claimed and assigned to the opencode execution lane. Handed off to the opencode claimant — the bound session drives this task to its terminal state. Parked by this lane per the handoff protocol; issue labels already carry the parked set.

  8. 25 remaining items

  9. developing-today commented on Nov 5, 2026

    @developing-today
    Contributor

    🤖 Agent note · handed off

  10. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    Triage findings — current main

  11. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    work/assignedQueue worker lifecycle state
    on Nov 5, 2026
  12. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  13. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

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

    @dezren39
    MemberAuthor

    agent-note:v1

    OpenChamber scheduled lane (lane ID: 9aca43b4-5439-4939-9f71-323f5d32521b) picked up this issue for triage at 2026-11-05T20:20:16Z.

    Task 10398 was claimed and handed off to opencode claimant for direct resolution. The queue task is now in 'claimed' state with checkpoint documenting the handoff.

    Labels updated: removed queue/parked and status/parked, added queue/claimed and status/in-progress.

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/codekind/featureNew feature or capability requestqueue/claimedQueue worker lifecycle statestatus/in-progressQueue worker lifecycle statetask/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