* docs: Phase 5 close-prep — README § Plan Usage + supported-provider matrix + live E2E artifact
Pre-close documentation pass per ADR 0012 § Exit gate items 3 + 9.
v0.5.0 close PR (package.json bump + CHANGELOG promotion + tag) is
maintainer-triggered per CLAUDE.md release_kit.phase_close_trigger.
What this PR ships:
(1) README § What you get — added Plan-usage probe bullet with anchor to
the new § Plan Usage section.
(2) README § Supported Providers — table gained "Quota probe (v0.5.0+)"
column. Anthropic ✅ live (13 anthropic-ratelimit-unified-* headers,
opt-in via quota_probe_enabled). Codex ❌ no public quota API.
Mistral ❌ per D84 spike 2026-05-26 (no /v1/usage at docs.mistral.ai/api).
Others TBD (Phase 8+).
(3) README new § Plan Usage (live quota probe) between § Configuration and
§ API Endpoints. Documents: how the probe works (POST /v1/messages
with max_tokens:1 + headers-only parse), opt-in config block, OAuth
token sources (env / .credentials.json / macOS Keychain), olp doctor
integration, schema-drift protection (Path A `strings` over compiled
binary + Path B live probe diff), full ADR cross-refs. Embeds the
dashboard screenshot below.
(4) README § API Endpoints — updated /dashboard / /v0/management/dashboard-data
/ /v0/management/quota rows to reflect Phase 5 changes:
- /dashboard: Claude.ai-style Plan Usage section + 60s auto-refresh + manual button
- /v0/management/dashboard-data: new quota_v2 field alongside legacy quota
- /v0/management/quota: mirrors dashboard-data quota_v2 for scripted monitoring
(5) docs/img/dashboard-v0.5.0.png — dashboard screenshot rendered from
live MacBook server JSON (utilization_5h: 36%, utilization_7d: 34%,
representative_claim: five_hour, overage: rejected) merged into D83
dashboard.html. Identifying IPs / hostnames / Tailscale nodes redacted
from rendered output per public-repo hygiene rule (cc-rules AGENTS.md).
(6) docs/exit-gates/phase-5-e2e.json — sanitized record of the Live
MacBook E2E verification (exit-gate item 9). Confirms quota_v2 shape
produced live, anthropic probe returned 12/13 fields (overage-reset
absent per audit memory — only fires on active overage), opt-in
mechanism verified end-to-end (config flag flipped → server probed
→ dashboard renders). Post-test cleanup noted: temp owner key
revoked, config restored to baseline, test server terminated.
Remaining exit-gate items for the v0.5.0 close PR (maintainer-triggered):
- CHANGELOG.md "Unreleased" → "## v0.5.0 — <date>" promotion
- package.json 0.4.4 → 0.5.0
- CLAUDE.md release_kit.phase_rolling_mode: Phase 5 → Phase 6,
0.5.0-phase5 → 0.6.0-phase6
- Tag v0.5.0 push (triggers .github/workflows/release.yml auto-release)
Authority cited:
- ADR 0012 § Exit gate items 3 + 9
- ADR 0012 Amendment 1 (D84 NO-GO rationale in Mistral row)
- ADR 0002 Amendment 8 + ADR 0013 (the constitutional context for the
Plan Usage section's "how it works" framing)
- D80 commit 82d2e1c (probe producer cited in API Endpoints table)
- D81 commit 5288493 (quota_v2 shape producer)
- D82 commit a41420d (dashboard UI consumer)
- D83 commit 2b07a3b (test coverage)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs: PR #56 fold-in — 3 maintainer findings (F1 doctor kind / F2 Mistral / F3 SPOT)
Maintainer review of PR #56 surfaced 3 valid accuracy issues. Folding in.
F1 [P2] — README claim "fix_oauth on 401/403, fix_provider on 429/network"
is wrong. The anthropic.quota_probe_reachable check has category: 'provider'
(anthropic.mjs:999), so deriveKind() at lib/doctor.mjs:464 maps ALL failures
to fix_provider regardless of underlying HTTP status. Furthermore, _probeOnce
collapses 401/403 → null before reaching the doctor aggregate layer
(anthropic.mjs:451), so the HTTP status isn't observable to deriveKind at
all. README would have steered AI-repair loops toward an unreachable
discriminator.
Fix: README now accurately says all probe failures discriminate to
kind: fix_provider, with the actionable text inside human_steps[] being
auth-aware (re-login recipe for OAuth failures, wait-and-retry for
rate-limit). Note that splitting the check across the provider/auth
category boundary would let fix_oauth fire for OAuth-class failures
specifically; deferred to v1.x pending consumer-reported ambiguity.
F2 [P2] — README + ADR 0012 Amendment 1 said Mistral has "no programmatic
quota API; only web console". Maintainer pointed to Mistral's Admin API
(https://docs.mistral.ai/admin/security-access/admin-api) which DOES
expose Billing and Usage queries — just gated to org-admin-scoped keys.
The D84 NO-GO conclusion is still correct: OLP's deployment posture
(maintainer's personal Le Chat Pro account, trusted-LAN per ADR 0011)
uses Vibe / Le Chat member / La Plateforme keys, NOT org-admin keys.
Provisioning + storing an org-admin token raises the credential-scope
ceiling beyond the trusted-LAN design.
Fix: tightened wording in 3 places (README Supported Providers table,
README Plan Usage provider coverage table, ADR 0012 Amendment 1) to
say "no public quota endpoint accessible to Vibe / Le Chat member /
La Plateforme API keys" + acknowledge the Admin API surface + cite it
as a forward re-entry point if OLP scope expands to org-admin context.
F3 [P3] — Supported Providers table claims it's re-generated from
models-registry.json, but the new "Quota probe (v0.5.0+)" column has
values for OpenAI/Mistral/TBD that aren't in the registry (registry only
had quota_probe.anthropic block before this PR).
Fix: closed the SPOT drift formally by adding quota_probe.openai and
quota_probe.mistral entries to the registry with {status, reason,
re_entry_point} for each, plus an admin_api_reference for Mistral. Also
added explicit "status": "live" field to quota_probe.anthropic for
symmetry. Tightened README's source-of-truth statement to name BOTH
the providers.<key> block (model metadata) and quota_probe.<key> block
(probe status/reason/source).
This makes the column fully derivable from the registry — when D84 ever
becomes GO (Mistral Admin API integration, or OpenAI publishes a quota
endpoint), the registry is the single edit point.
Test impact: 756/756 still pass. Suite 37g (registry presence + 13 fields
for anthropic) unchanged; the new openai/mistral quota_probe entries are
additive and don't break any consumer.
Authority:
- F1: lib/doctor.mjs:464 deriveKind logic + anthropic.mjs:999 category
- F2: https://docs.mistral.ai/admin/security-access/admin-api + ADR 0011
trusted-LAN deployment context for the "out of scope" framing
- F3: CLAUDE.md release_kit overlay § "Supported Providers table sourced
from models-registry.json" — same SPOT discipline applies to the new
D81 column
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
16 KiB
ADR 0012 — Phase 5 Charter: Provider Quota Probes + Dashboard Enrichment
Status: Accepted (Phase 5 open as of 2026-05-26) Date: 2026-05-26 D-day: D79 (charter + ADR 0002 Amendment 8 + ADR 0013 land together as the constitutional layer of Phase 5)
Amendments
Amendment 1 — 2026-05-26: D84 Mistral probe NO-GO (post-D79-close spike)
The D-day table originally listed D84 as "optional, depends on D79-close 30-min Mistral docs spike". The spike completed 2026-05-26 with verdict NO-GO — Mistral does not expose a programmatic quota/usage endpoint accessible to Vibe / Le Chat member / La Plateforme API keys (the key tier OLP uses for spawning the vibe CLI):
docs.mistral.ai/api(the public API spec) covers Chat, FIM, Embeddings, Classifiers, Files, Models, Batch, OCR, Audio, Events, Beta (Agents/Conversations/Libraries/Workflows/Observability). No usage/quota/credits/billing/limits endpoint accessible to a member API key.- Direct probe
https://api.mistral.ai/v1/usagereturns 404. - Mistral's "Limits and Usage" help article documents limit viewing via the
admin.mistral.ai/plateforme/limitsweb console. - No
x-ratelimit-*response headers documented on/v1/chat/completions. (Third-party summaries mentioning these headers are unsourced — appears to be OpenAI-convention extrapolation.) - OLP
lib/providers/mistral.mjsalready records this independently — DL-7 comment: "If quota/budget API surfaces in Le Chat Pro, pin the endpoint here."
Out-of-scope but worth pinning for future revisit. Mistral's Admin API DOES expose programmatic "Billing and usage queries", and the Usage limits docs describe usage/cost queries via that surface. The Admin API requires an org-admin scoped API key (separate from the member key OLP uses). For OLP's family-tier deployment posture (a maintainer's personal Le Chat Pro / La Plateforme account, not an organization's admin console), provisioning + storing an org-admin token raises the credential-scope ceiling beyond what the trusted-LAN deployment context (ADR 0011) was designed for. The NO-GO at v0.5.0 is therefore "out of scope for OLP's current deployment posture", NOT "Mistral has no programmatic surface". If the deployment posture expands to an org-admin context (e.g., a small-business multi-user deployment), this decision should be re-evaluated.
Disposition:
- D84 row dropped from D-day plan (struck through below).
- Mistral dashboard row in D82 UI shows "spend tracking only" badge sourced from
audit-query.mjsaggregates (request count, estimated cost fromestimateCost()). DL-7inmistral.mjsis the documented re-entry point if Mistral ever publishes a usage endpoint.- Phase 5 total D-day budget revised: ~5 D-days (down from ~6).
Context
Phase 4 (ADR 0010) shipped OLP's operator + client UX layer — bin/olp operator CLI, olp doctor framework, olp-connect zero-config IDE wiring, OpenClaw /olp slash commands, anonymous-key deployment-context limits, SSE heartbeat. v0.4.4 is the current shipped state. Phase 4 closed every gap on the OCP-feature-parity matrix EXCEPT one: live quota / plan-usage surfacing.
Today lib/providers/anthropic.mjs:445 has a stub quotaStatus() returning null (D4 placeholder). The OLP dashboard's quota panel renders "—" for all providers. OCP, in contrast, exposes a live "39% session / 30% weekly" panel — the maintainer uses this multiple times per day to decide when to throttle voluntary claude -p traffic away from interactive sessions. OLP cannot become an OCP successor in practice (vs. just feature-parity-on-paper) until quota surfacing works.
A pre-flight institutional-knowledge audit (2026-05-26 — see ~/.cc-rules/memory/learnings/anthropic_plan_usage_probe_schema_2026_05_26.md) confirmed:
- The OCP probe still works today — Anthropic returns the same
anthropic-ratelimit-unified-*headers on everyPOST /v1/messagescall. Tested live 2026-05-26 from PI231 OAuth credentials. - Schema added 3 fields since OCP's 2026-04 capture —
5h-status,7d-status(per-window status),overage-reset(only on active overage). No fields removed or renamed. - Verification protocol has shifted — Claude Code v2.1.x is now a compiled binary (Mach-O / ELF), not bundled JS. OCP's "grep cli.js" approach no longer applies; the replacement protocol is
stringsagainst the binary + periodic live probe diff. - OAuth refresh path unchanged —
platform.claude.com/v1/oauth/token+9d1c250a-...client_id + 60s-3600s exponential backoff.
The audit makes Phase 5 implementation low-risk: this is a port of a working OCP function, not a re-derivation. The work is mechanical + adapter-layer plumbing into OLP's plugin contract.
A parallel maintainer request (2026-05-26, with reference screenshot of claude.ai/settings/usage) asked for Claude.ai-style dashboard enrichment: per-row utilization bars, reset countdown, 1-minute auto-refresh, manual refresh button. P5-1 (probe) + P5-2 (dashboard) together unlock both: the data plus the surface. v1.x roadmap #8 is closed by P5-2.
Decision
Phase 5 scope is Provider quota probes + dashboard enrichment. The phase opens 2026-05-26 with D79 (this charter + ADR 0002 Amendment 8 + ADR 0013 OAuth READ-ONLY consumption rules). Phase 5 close ships v0.5.0; per CLAUDE.md release_kit.phase_rolling_mode, the close PR is maintainer-triggered.
In scope — Phase 5 D-day plan (~6 D-days)
| D-day | Deliverable | Authority | Estimate |
|---|---|---|---|
| D79 | This charter ADR 0012 + ADR 0002 Amendment 8 (direct-API READ-ONLY) + ADR 0013 (OAuth READ-ONLY consumption rules + schema-drift mitigation) + package.json current_pre_release_identifier → 0.5.0-phase5 + CLAUDE.md release_kit.phase_rolling_mode.current_phase → Phase 5 |
This charter + audit memory | 0.5d |
| D80 | lib/providers/anthropic.mjs:quotaStatus() ported from OCP server.mjs:842-1109 — full probe with macOS-keychain auth read added (existing OLP reader only handles env + .credentials.json) + 5min cache + 60s-3600s refresh backoff + stale-cache-on-429 + all 13 headers parsed (including new 5h-status / 7d-status / overage-reset) |
Port OCP probe + ALIGNMENT.md Rule 2 exemption per ADR 0002 Amendment 8 + audit memory | 2d |
| D81 | lib/audit-query.mjs + /v0/management/dashboard-data extended to surface the new quota shape per provider (utilization, reset, representative-claim, fallback-percentage, overage-status). Audit-query stays in-memory scan per ADR 0008 Lane 2 = A (no SQLite). Schema migration documented in ADR 0008 § Amendment |
ADR 0008 + this charter | 1d |
| D82 | dashboard.html Claude.ai-style restructure — per-provider rows replace the current single Quota panel; each row: provider badge, model placeholder, utilization bar (5h + 7d), reset countdown ("Your limit will reset at HH:MM AM/PM" format from the user-shared claude.ai screenshot), status badge, representative-claim hint. 1-minute auto-refresh via setInterval with document.visibilityState guard. Manual refresh button calls /v0/management/dashboard-data directly |
v1.x roadmap #8 + maintainer reference screenshot | 1.5d |
| D83 | Test coverage — Suite 38 quota-probe unit tests (mock HTTP server returning the 13 headers; assert parse + cache + backoff + stale-on-429); Suite 39 dashboard rendering smoke (curl /dashboard after pre-seeding mock quota cache; assert HTML contains expected utilization strings); update Suite 33 doctor checks for new anthropic.quota_probe_reachable check |
Test convention from existing suites | 1d |
quotaStatus() port — depends on D79-close spikeaudit-query.mjs aggregates. DL-7 hook point in mistral.mjs already marks the location for future upgrade if Mistral ever publishes a usage endpoint. Codex permanently skipped (no public API). |
n/a (dropped) | 0d | |
| close | v0.5.0 release PR — package.json 0.4.4 → 0.5.0, CHANGELOG promotion, release_kit.phase_rolling_mode.current_pre_release_identifier advance to Phase 6 token |
CLAUDE.md release_kit overlay |
maintainer-triggered |
Out of Phase 5 scope (with explicit triggers)
X-OLP-Cost-USD per-request response header
Status: Deferred to Phase 6. Was listed in ADR 0010 § Out-of-scope as "Phase 5 prerequisite". The prerequisite (provider-cost weights table) is non-trivial — needs per-(provider, model) input_cost_per_1k_tokens / output_cost_per_1k_tokens / cache_read_discount data sourced from each provider's published pricing page. Phase 5 already pulls in two new ADRs; adding a third data-onboarding ADR is scope creep.
Re-open condition. Phase 6 unless a maintainer reports a cost-attribution debugging need that warrants pulling forward.
context_window_exceeded fallback trigger (LiteLLM prior-art)
Status: Deferred. ADR 0010 listed this as opportunistic-in-Phase-5 unless the trigger fires sooner. The trigger has not fired in Phase 4 production traffic. Continue to defer.
per-(provider, model) live stats Map (replacing audit-query scan)
Status: Deferred. Current scan latency is ~20ms at 7-day depth. Acceptable until volume grows (>100k requests/day). Re-evaluate at Phase 6 if dashboard latency degrades.
Anthropic interactive-mode P0 (ADR 0009)
Status: Still trigger-gated on Anthropic's 2026-06-15 billing-split rollout. Phase 5 does NOT depend on P0 — the quota probe reads anthropic-ratelimit-unified-* headers regardless of which billing pool the spawn path consumes. If P0 succeeds Phase 7+ Phase 5's probe code remains unchanged; if P0 fails Phase 5's probe code remains unchanged. The probe is billing-pool-agnostic because the headers are subscription-pool metadata, not Agent-SDK-Credit metadata.
/v1/messages Anthropic-shape entry surface
Status: Still deferred per ADR 0010 § Out-of-scope. No change in Phase 5.
v1.x roadmap #3 / #5 / #6
Status: Still trigger-gated per docs/v1x-roadmap.md. None has fired. Continue to defer.
Opportunistic Phase 5 micro-additions (not blocking)
Items small enough to land alongside a planned D-day without scope creep, if encountered:
- README § Dashboard screenshot update (post-P5-2 enrichment) — capture from MacBook test path per
~/.cc-rules/memory/feedback/mac_mini_never_for_testing.md. olp usageCLI subcommand (bin/olp.mjs) surfaces the parsed quota shape in terminal form. Already partially exists (cmdUsage in bin/olp.mjs); confirm payload alignment after D80.- Add
claude_code_oauth_client_idconfig override in~/.olp/config.jsonso power users can override the hardcoded9d1c250a-...UUID without env-var fiddling. Mirrors compiled binary'sCLAUDE_CODE_OAUTH_CLIENT_IDenv support. docs/provider-audits/anthropic.mdre-capture with currentclaude --version(v2.1.142 MacBook / v2.1.150 PI231) + binary distribution layout note.
Exit gate — v0.5.0 close criteria
- D79 — D84 all merged with fresh-context opus reviewer APPROVE per Iron Rule 10.
- CI green on every D-day merge commit and on the v0.5.0 release commit head.
alignment.ymlblacklist re-confirmed (no new hallucinated tokens introduced). - README § Quota / Plan Usage section present with screenshot of the enriched dashboard. README § Supported Providers table updated to note "quota probe: anthropic ✅, mistral ⚠️/✅ (D84 outcome), codex ❌ (no public API)".
- ADR 0012 (this charter) + ADR 0002 Amendment 8 + ADR 0013 (OAuth READ-ONLY consumption) on disk.
CHANGELOG.md "Unreleased"promoted to"## v0.5.0 — <date>"with D79 — D84 entries.package.jsonbumped to0.5.0.CLAUDE.md release_kit.phase_rolling_mode.current_phaseadvancesPhase 5 → Phase 6;current_pre_release_identifieradvances0.5.0-phase5 → 0.6.0-phase6.- Standing autopilot grant covers D-day-by-D-day execution; v0.5.0 close PR is maintainer-triggered.
- Live MacBook E2E verification — dashboard renders enriched panel with real quota data (probe live, not mocked).
Consequences
Positive.
- OLP finally has the load-bearing observability OCP had — maintainer can see live "39% session / 30% weekly" and decide whether voluntary
claude -ptraffic stays or moves. - Family members on the LAN see real reset times instead of "—", which makes the "wait 2 hours" guidance concrete vs. abstract.
- The institutional-knowledge audit captured the schema in a memory file pinned with date stamps — future ports (mistral, future provider) re-use the verification protocol without re-deriving.
- v1.x roadmap #8 (Dashboard enrichment per Claude.ai-style usage page) closes inside Phase 5 rather than waiting for a separate phase.
- Compiled-binary-distribution awareness ("no more cli.js to grep") is now codified in OLP governance; the next time Anthropic ships a major CC version, the verification protocol is already written.
Negative.
- ADR 0002 gains another amendment (Amendment 8). The constitution surface area for
anthropic.mjsgrows. Counter-pressure: the alternative (probe lives inserver.mjs, like OCP) violates the plugin-architecture principle that per-provider knowledge stays inlib/providers/. Amendment 8 is the smaller violation. - The probe makes one
/v1/messagescall per 5min cache miss. That's ~12 calls/hour worst case across the whole proxy (probe is per-credentials, not per-key). Withmax_tokens: 1the cost is < $0.01/day at family-scale traffic. Negligible but not zero. - Schema-drift risk over the long horizon. Anthropic could rename or remove headers in a future version. The mitigation protocol (strings + live probe diff) is in place, but it's a manual check — needs to be invoked by the maintainer or scheduled.
- Dashboard refactor introduces a breaking-change risk for the existing dashboard.html consumers (none today, but conceptually). Bumping to v0.5.0 signals this clearly.
Neutral.
- Phase 5 has more ADR work than Phase 4 (3 governance docs vs. 2). The constitutional layer is deliberately heavier because direct-API access is the single biggest authority decision since the plugin contract itself.
Authority + cross-references
- Iron Rule 11 (IDR) — Phase 5 ships across 6 D-days, each a minimum reviewable unit. The governance trio (this ADR + Amendment 8 + ADR 0013) lands at D79 as a single coupled commit (reviewing them separately cannot verify consumer-producer alignment), per ADR 0002 Amendment 7's precedent.
- Iron Rule 12 (prior-art search) — discharged via the audit memory at
~/.cc-rules/memory/learnings/anthropic_plan_usage_probe_schema_2026_05_26.md. Memory committed prior to D80 implementation. - ALIGNMENT.md Rule 1 (citation) — D80 commit must cite compiled-binary
stringsevidence per audit memory § Path A (Claude Code v2.1.x has no traditional§ sectionstructure because it is a Mach-O / ELF compiled binary) plus the audit memory file path. Live-probe transcript MUST be included in the commit body. - ALIGNMENT.md Rule 2 (provider-CLI-as-authority) — direct-API access bypasses the spawn-binary contract. Amendment 8 is the explicit exemption. Without Amendment 8, the D80 commit is unalignable.
- ALIGNMENT.md Rule 5 (CI alignment.yml) — must continue to pass.
api.anthropic.com/v1/messagesis NOT on the blacklist (correct — that's the real endpoint). The hallucinated/api/oauth/usageIS on the blacklist (transitive from OCP) and must remain. - ADR 0002 Amendment 8 — companion ADR. Direct-API access scoping; READ-ONLY constraint; opt-in via config flag (default off).
- ADR 0013 — companion ADR. OAuth credentials shared between spawn path + probe path; refresh backoff; schema-drift mitigation protocol.
- CLAUDE.md release_kit — Phase boundary triggers maintainer-led version bump. D-day commits within Phase 5 stay under "Unreleased".
0.5.0-phase5is the pre-release identifier during the phase.