release(v0.5.1): hotfix — quota probe cache/backoff/schema-drift correctness (codex review) (#58)

* release(v0.5.1): hotfix — quota probe cache/backoff/schema-drift correctness (codex review)

Addresses three production-quality findings from codex's post-v0.5.0 review (codex review on
PR #57, reproduced with local mocks):

F1 [P1] — Doctor bypass of cache + backoff (ADR 0013 Rule 3)
  `anthropic.quota_probe_reachable` called `_probeOnce(auth)` directly, bypassing
  `quotaProbeState.backoffUntil`. Successive `olp doctor` invocations within a backoff
  window each hit upstream — violating ADR 0013 Rule 3 (backoff is mandatory for ALL
  consumers). Fix: doctor now routes through `quotaStatus()`. ADR 0013 Rule 3 clarified:
  "All consumers of `quotaStatus()`, including `olp doctor` checks, MUST route through
  `quotaStatus()` and MUST NOT call `_probeOnce()` directly."

F2 [P2] — 200 with empty ratelimit headers cached as live data (ADR 0013 Rule 5)
  A 200 OK with zero `anthropic-ratelimit-*` headers was cached as `stale: false` (live).
  Minimum-viable-schema gate added to `_probeOnce`: requires 5h-utilization + 5h-reset +
  7d-utilization + 7d-reset present; absence → `failureKind: 'schema_drift'`, backoff
  scheduled, result NOT cached. ADR 0013 Rule 5 updated with the gate specification.

F3 [P2] — Dashboard-data loses failure detail (ADR 0013 Rule 6)
  `aggregateProviderQuota()` collapsed all failure modes into `status: 'unavailable'`
  ("no public quota api or probe disabled") — same as providers with no API at all.
  Fix: `quotaStatus()` v0.5.1 contract — `null` ONLY for opt-in-off; failures return
  `{ probe_status: 'unreachable', failure: { kind, message, backoff_until? } }`.
  New `failure_kind` enum: no_credentials | auth_failed | rate_limited | schema_drift |
  network | other. Dashboard renders `unreachable` with red border + failure detail.

Authority:
  ADR 0013 Rules 3, 5, 6 (cache + backoff + schema-drift + failure transparency)
  ADR 0008 Amendment 2 (richer quota_v2 shape; new unreachable status)
  ADR 0002 Amendment 8 unchanged (constitutional permission for the probe)
  Codex review findings F1–F3 (codex on PR #57)

Changes:
  - lib/providers/anthropic.mjs: quotaProbeState gains lastError + failureKind;
    _probeOnce: min-field gate + failureKind population; quotaStatus(): v0.5.1 contract
    (null=disabled only; probe_status:live/stale/unreachable); doctorChecks routes
    through quotaStatus(); reset functions updated
  - lib/audit-query.mjs: _normalizeAnthropicQuota handles probe_status field;
    aggregateProviderQuota emits failure/failure_kind/backoff_until; unreachable status
  - dashboard.html: unreachable CSS classes + render path + footer v0.5.1
  - test-features.mjs: 38f/j/l updated for v0.5.1 shape; 38r refactored for F1;
    38g/k gain probe_status assertions; 38u/v/w new regression tests; 756→759 tests
  - docs/adr/0008: Amendment 2 (richer ProviderQuotaEntry + quotaStatus contract)
  - docs/adr/0013: Rule 3 clarification (doctor must use quotaStatus);
    Rule 5 min-viable-schema gate specification
  - package.json: 0.5.0 → 0.5.1
  - CHANGELOG.md: v0.5.1 hotfix entry promoted from Unreleased
  - README.md / AGENTS.md: Phase 5 closed at v0.5.1; Phase 6 next

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs+code: PR #58 fold-in — reviewer Nits #1 + #2 (ADR doc-drift + opt_in_off enum)

Fresh-context reviewer (PR #58) verdict: APPROVE_WITH_MINOR, 0 blocking,
4 nits. Folding in #1 + #2 (both 1-line cosmetic-but-correct fixes).
Deferring #3 (test-only edge case in module-level seam restore) and #4
(pre-existing codex F4 — bin/olp.mjs + olp-plugin still read legacy
quota shape; separate PR planned).

Nit #1 — ADR 0002 Amendment 8 documentation drift.

Amendment 8 at v0.5.0 line 20 said "the function returns `null` rather
than throwing", and line 33 said "stale-cache-on-failure (`null` is
returned only when no cache entry exists; if a stale entry exists it's
returned with a `stale: true` marker)". v0.5.1 refined this contract:
`null` is now reserved STRICTLY for opt-in-off, and all failure modes
return `{ probe_status: 'unreachable' | 'stale', failure: {...} }`.

The substantive idempotent-failure constraint (no throw to caller) is
unchanged. The operational description in Amendment 8 was stale — fixed
to cross-reference ADR 0008 Amendment 2 + ADR 0013 Rule 6 for the
v0.5.1 contract refinement. Also references Suite 38 (38u/38v/38w) as
the regression coverage producing the new shape.

Nit #2 — `failureKind: 'opt_in_off'` declared but never produced.

The enum value was listed in both the code comment (anthropic.mjs:250)
and ADR 0008 Amendment 2 (line 30) but never actually assigned —
because when opt-in is off, `quotaStatus()` returns the literal `null`
BEFORE any state mutation happens. The enum value was dead.

Fix: removed `opt_in_off` from both enum declarations + added an
inline note explaining that consumers (audit-query, doctor) distinguish
opt-in-off by checking `quotaStatus() === null`, not via failureKind.

Deferred:

- Nit #3 (test-seam restore edge case): if a caller pre-sets
  `_quotaAuthReadFnForTest` AND passes a non-default `_authReadFn`,
  the finally block restores to null clobbering pre-set value.
  Test-only impact, no production risk. Pure hygiene; defer.

- Nit #4 (codex F4): bin/olp.mjs cmdUsage and olp-plugin/index.js
  still read legacy `body.quota` field, never consume `quota_v2`.
  Reviewer confirmed neither crashes — both gracefully fall through
  to "no quota api" branch. Out of scope for this hotfix per the
  hotfix dispatch contract; separate PR will migrate them.

Tests: 759/759 still pass post-fold-in. No test changes needed.

Authority: PR #58 review thread + ADR 0013 Rule 6 (failure transparency)
+ ADR 0008 Amendment 2 (ProviderQuotaEntry v0.5.1 shape).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: dtzp555 <dtzp555@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
dtzp555-max
2026-05-27 09:33:47 +10:00
committed by GitHub
co-authored by taodeng Claude Sonnet 4.6
parent 65681ed7d2
commit bddf2cba1e
11 changed files with 635 additions and 119 deletions
+40
View File
@@ -6,6 +6,46 @@ All notable changes to OLP land here. Per `CLAUDE.md` release_kit overlay, this
(empty — Phase 6 entries land here once Phase 6 opens)
## v0.5.1 — 2026-05-27
**Hotfix — Quota probe cache/backoff/schema-drift correctness (codex review findings F1F3).** Three production-quality bugs in the v0.5.0 quota probe, reproduced by codex with local mocks, are corrected. 756 → 759 tests (3 new regression tests); 4 existing test assertions updated to reflect the v0.5.1 return-shape contract.
### Fixes
- **F1 [P1] — Doctor bypass of cache + backoff (ADR 0013 Rule 3).** `anthropic.quota_probe_reachable` doctor check called `_probeOnce(auth)` directly, bypassing the module-level `quotaProbeState.backoffUntil` check. Successive `olp doctor` invocations within a backoff window each hit upstream — violating ADR 0013 Rule 3 (60s-3600s exponential backoff is mandatory for all consumers). **Fix:** doctor check now routes through `quotaStatus()`, which enforces cache + backoff. ADR 0013 Rule 3 clarification added: "All consumers of `quotaStatus()`, including `olp doctor` checks, MUST route through `quotaStatus()` and MUST NOT call `_probeOnce()` directly."
- **F2 [P2] — 200 with empty `anthropic-ratelimit-*` headers cached as live data (ADR 0013 Rule 5).** `_probeOnce` treated any 200 OK (regardless of header content) as a successful probe, caching it with `stale: false` even when zero `anthropic-ratelimit-*` headers were present. A proxy stripping headers, a schema change, or a mock returning `{}` would silently appear as "LIVE" on the dashboard with all bars empty. **Fix:** minimum-viable-schema gate requires these 4 fields non-null: `5h-utilization`, `5h-reset`, `7d-utilization`, `7d-reset`. Any absence → `failureKind: 'schema_drift'`, backoff scheduled, result not cached. ADR 0013 Rule 5 updated with the gate specification.
- **F3 [P2] — Dashboard-data loses failure detail (ADR 0013 Rule 6).** `aggregateProviderQuota()` collapsed all non-null failure modes (no credentials, auth failure, rate limit, schema drift, network error) into `status: 'unavailable', reason: 'no public quota api or probe disabled'` — the same string as providers with no quota API at all. Operator could not tell what to fix. **Fix:** `quotaStatus()` v0.5.1 return contract: `null` reserved for opt-in-off only; probe failures return `{ probe_status: 'unreachable', failure: { kind, message, backoff_until? } }`. `aggregateProviderQuota()` emits new fields `failure_kind`, `failure`, `backoff_until` per row. `status: 'unreachable'` distinguishes "probe failed" from `status: 'unavailable'` ("no API or disabled"). Dashboard renders `unreachable` with a red border + failure.message + backoff countdown.
### Backwards-compat notes
- `quotaStatus()`: `stale: false` → now also includes `probe_status: 'live'` (additive). `stale: true` → now also includes `probe_status: 'stale'` + `failure: {...}` (additive). `null` → NOW RESERVED FOR OPT-IN-OFF ONLY (breaking for callers that relied on `null` to detect "no credentials" or "probe failed" — use `probe_status: 'unreachable'` instead).
- `ProviderQuotaEntry.status`: gains `'unreachable'` as a new value (additive). Existing `'live'`, `'stale'`, `'unavailable'` semantics unchanged.
- `ProviderQuotaEntry` gains new fields `failure`, `failure_kind`, `backoff_until` (additive, null when not applicable).
- `dashboard.html`: handles `unreachable` row (no existing row had this status; additive render path).
### Test changes
- 38f, 38j, 38l: updated assertions from `null` to `probe_status: 'unreachable'` (F3 shape change).
- 38r: refactored to seed cache + manually expire it + set backoff (F1 — doctor now routes through `quotaStatus()`). Added F1-regression assertion: HTTP call counter stays at 1 after two doctor calls within backoff.
- 38g, 38k: added `probe_status` + `failure` assertions (verify new fields present on live/stale shapes).
- **38u** (new): F1 regression — successive doctor calls within backoff window → HTTP counter stays at 1.
- **38v** (new): F2 regression — 200 + empty ratelimit headers → `probe_status: 'unreachable'` + `failure_kind: 'schema_drift'` + cache stays null.
- **38w** (new): F3 regression — `lastError` + `failureKind` propagate through `quotaStatus()` shape for all failure modes (rate_limited / auth_failed / schema_drift / no_credentials).
### ADR changes
- **ADR 0013 Rule 3** clarification: doctor checks route through `quotaStatus()`, not `_probeOnce()` directly.
- **ADR 0013 Rule 5** update: minimum-viable-schema gate specification (4 required fields; absence = schema_drift signal).
- **ADR 0008 Amendment 2**: richer `ProviderQuotaEntry` shape with `failure`/`failure_kind`/`backoff_until`; `probe_status` on `quotaStatus()` return; `unreachable` status semantics; `dashboard.html` unreachable rendering.
### Authority
ADR 0013 Rules 3, 5, 6 (cache + backoff + schema-drift + failure transparency); ADR 0008 Amendment 2; ADR 0002 Amendment 8 (unchanged); codex review findings F1F3 (codex PR review on v0.5.0 close PR #57).
---
## v0.5.0 — 2026-05-26
**Phase 5 — Provider Quota Probes + Dashboard Enrichment.** OLP gains live subscription-quota observability for Anthropic Pro/Max subscribers, surfaced through a Claude.ai-style Plan Usage panel on the owner-only dashboard. The probe is opt-in, READ-ONLY, idempotent on failure, and 5-min-cached with 60s→3600s exponential backoff. Six D-days, seven PRs, zero blocking reviewer findings, no flaky tests; 720 → 756 total tests.