Development - #14
Merged
Merged
Conversation
added 27 commits
August 13, 2026 02:40
Genuine usage.jsonl read/stat/schema failures now respond 503 with a
{ error: read_failed, range, surface } body instead of a 200 zeroed
summary. Missing log files still return 200 with a zeroed summary via
readUsageSnapshotForManagement. Tests updated and extended to assert the
503 contract and that success responses carry estimatedCostUsd,
pricedRequests, unpricedRequests, and unmeteredRequests.
The Usage page now validates GET /api/usage success bodies before any cache write: error envelopes and malformed summaries (missing or non-finite required summary fields, including estimatedCostUsd/pricedRequests/ unpricedRequests/unmeteredRequests) throw a typed UsageReportValidationError and are never persisted. A defined zero cost renders $0.00 while a genuinely missing legacy field renders "Unavailable". Cold failures keep the failed-cold Notice with retry, and failed refreshes retain last-known- good data with the stale/error banner. Shared validator lives in gui/src/usage-report-validation.ts for reuse by the upcoming domain store.
…alignment
- .select-trigger > span now ellipsizes inside the pill instead of spilling
past it (global rule matching the dashboard sidecar pattern).
- Roster/library/fallback/matrix model IDs wrap with overflow-wrap:anywhere
instead of clipping mid-ID; name spans carry title tooltips.
- Run Policy grid: align-items start (kills the 30px label staircase),
guidance spans 4 columns + save cell pinned to column 5 (kills the void
next to Save changes), helper text uses --text-label, 150px tracks widened
to 170px, save cell magic min-height removed.
- Library filter chips wrap instead of clipping at the right edge.
- Notice tone=err now announces with role=alert; ok/warn stay status.
- Rail IDs on Models/ClaudeCode/Storage/Combos get title tooltips.
- Drop redundant style={{width:'100%'}} on policy selects (CSS owns it).
…nology - subagents-classic/busy-race tests assert the Configured Roster rename (heading + add/remove aria-labels); the aria-labels were never regressed. - Copy updates: no-preferred-model hint, shortened fallback hint, rewritten V2 compatibility notices (drop lidge-jun#92 and off-topic clauses), consolidated save/timing instructions (sub.policy.saved is now one line; timing lives in the details note), de-jargoned Usage subtitle, V1/V2 capitalization, 'protocol' as the single word for the collaboration selector, and 'configured roster' terminology (en/de/ja/ko landed with the usage validation commit; ru/zh follow here). - models-empty-provider test expects 'Reliable V1'.
Run under tsc --noUnusedLocals --noUnusedParameters: 356 diagnostics in src/server/management were stale imports left by earlier route consolidations. Removed unused bindings (compiler-API driven, verified per file), dropped unused destructured ctx fields, and normalized the blank lines left behind. No behavior change: bun run typecheck clean, management route tests (395) and privacy:scan pass.
Cross-client grep: no GUI or other runtime client calls /api/disabled-models or /api/key-providers anymore; only tests, docs, and structure notes reference them. Add deprecation notes pointing at the replacements (PUT /api/model-visibility, GET /api/providers). Routes stay live for script compatibility.
Sync English guide copy with the GUI labels: Reliable V1, Concurrent V2, V1/base/V2 mode descriptions, and prose references. CLI command syntax (ccx v2 mode v1, /api/v2) stays lowercase. Translated docs had no label strings to contradict.
Introduces gui/src/usage-report-store.ts, a keyed zustand domain store for
GET /api/usage reports (${apiBase}:${range}:${surface}) with
client-resource-shaped snapshots, singleflight per key, AbortController
cancellation, and sessionStorage persistence of validated reports plus a
timestamp only (never errors or in-flight state). Rehydrated seeds are
marked seedNeedsRevalidate so the first subscriber quiet-revalidates.
Usage.tsx and the Dashboard now select the same 30d/all entry, collapsing
the previous three independent usage fetches into one. The dashboard usage
client-resource poll and USAGE_CACHE_PREFIX session-cache are removed;
fetchDashboardUsage is deleted. The page-loading contract pin now accepts
Usage's classifyDataSurface wiring through the shared data-surface state
machine. Adds store unit tests, a rehydration test file, and updates
usage-validation and dashboard-contract tests.
Introduces gui/src/provider-quota-store.ts, a zustand domain store keyed by
apiBase for GET /api/provider-quotas. ProviderWorkspaceShell now routes its
quota fetch through the store (quotaRefreshEpoch/quotaForceRefresh map to a
refresh({ force }) action that adds the ?refresh=1 TTL bypass), keeping its
strict capacity-aggregation display filter on top of the store's permissive
ingest so reference-window-only reports stay representable for the upcoming
Dashboard Plan & quota section.
Privacy invariant: only quota reports (provider/label/source/quota/updatedAt/
aggregation) plus a timestamp are persisted — never account emails or ids;
stray identity fields on the wire are projected away. Stale (>30 min)
rehydrated seeds are rejected. Store tests cover force-refresh, singleflight,
last-good retention, stale-seed rejection, and the privacy invariant.
Also fixes global hygiene in the store test files: the default bun test
runner shares one global scope across files, so each test file now restores
every installed global (sessionStorage/window/document/fetch) after its run.
Extends UsageSummary30d with estimatedCostUsd/pricedRequests/unpricedRequests/ unmeteredRequests and shows a 30-day estimated cost stat (API list-price equivalent, never billed spend) with a request-coverage line in the dashboard overview head ($0.00 for a defined zero, em-dash when absent). Adds a Plan & quota section fed by the provider-quota store (same apiBase-keyed entry as the Providers workspace shell): per-provider plan, 5h/week/month windows via ProviderCapacityQuota, and observed reference spend vs published caps (referenceWindows) — all labeled provider-reported/estimates. Styling reuses existing dashboard tokens/classes; no new stylesheet. All new strings land in all 6 locales (lint:i18n green). Tests cover the cost stat (estimate, zero, absent), the Plan & quota render from a provider-quotas fixture, and the privacy invariant that the section never persists account identities to sessionStorage.
…ve button - sub.pageSubtitle now reads 'Choose which models sub-agents may use, which model guides them, and the fallback order.' (all 6 locales) — 'advertised' was jargon per the designer audit (C1). - Policy save cell right-aligns the button so it sits at the card edge (17px from the grid edge vs 423px void before; measured at 1380px). - Visual verification (playwright + mock API): labels all at 17px (no 30px staircase), select labels ellipsize with zero spill at 1380/1024/390/360, roster/library IDs fully visible (no mid-ID clip), filter chips wrap with no overflow at 360px.
- management-api.md: GET /api/usage now documents the 503 read_failed envelope; missing log file stays 200/zero. - cli/agents.md: ccx usage reports the read failure instead of a zeroed report; missing log still prints empty. - web-dashboard.md: Dashboard summary includes the 30-day estimated cost; the cost/Plan & quota notes clarify provider-reported estimates are never billed spend.
…freshness
Addresses the post-review findings:
- usage-report-store: a failed quiet revalidation of a rehydrated seed restores
seedNeedsRevalidate (next subscriber retries) and seeds read as hasSucceeded
(last-known-good, not never-succeeded). refresh() gains replace:false so the
Dashboard can quiet-revalidate on a 60s interval (singleflight, pause-when-
hidden) without reintroducing a client-resource poll.
- usage-report-validation: days/models/providers array elements are now validated
(finite numeric fields, string ids) instead of cast wholesale; a malformed
element rejects the whole report so nothing unvalidated is cached.
- provider-quota-store: quota/aggregation rows are deep-projected onto the known
keys at ingest, so stray identity fields can never reach sessionStorage;
ensure({force:true}) now always fetches.
- ProviderWorkspaceShell: mount uses ensure() (healthy-entry short-circuit, never
aborts shared in-flight fetches) with a StrictMode guard; explicit quota
revision bumps still use refresh({force}).
- logs-usage-routes: the 503 read_failed catch is narrowed to the file-read
machinery (revision stat + snapshot read); derivation/cache-layer errors
propagate as ordinary server errors.
- Dashboard: 30-day cost stat uses a locale-aware zero formatter, hides the
coverage line when all request classes are 0; Usage cold-failure Notice keeps
localized text and moves raw error detail to the retry button title.
Tests: element-level validation unit tests, malformed-element store rejection,
HTTP 503 cold-failure page test, hostile identity fields inside quota/aggregation,
seed-semantics assertions, and strengthened $0.00 (no tilde) assertions.
…oken swap
- OpenCode config-destination <code> now carries title={full path}; it
ellipsizes inside .integration-facts (verified truncated=true with title
set via mock-API harness). Grok model names already had titles (no-op).
- styles-integrations.css: min-height 34px -> var(--control-md); off-scale
gaps 18/10/7px and 14px paddings -> nearest space tokens (16/8/6/12px);
the 6px/28px/4px chip values were already exact tokens.
Swap hardcoded paddings/gaps/margins to the nearest design token where the delta is <=2px and imperceptible (18->space-4, 14->space-3, 10->space-2, 9->space-2, 7->space-1-5, 3->space-1, 31->space-8, 6->space-1-5, 34->control-md, 11px->text-caption). Font-size 10.5px, line-height 1.45, font-weight 550, and the select/trigger pill box-shadow 0 2px 8px are deliberate tuning -> marked /* deliberate */ instead of swapped. Verified visually at 1380/390 via the mock-API harness; full GUI suite 791/0 green.
…logy Sweep the 8 guide/reference files: lowercase v1/v2 in prose that refers to the collaboration protocol becomes V1/V2 (code literals, config values, CLI syntax, /api/v2 paths, plaintext/encrypted options, multi_agent_v2 flag stay untouched). Align wording with the GUI labels: 'collaboration protocol' for the three-state selector (was 'surface mode'), V2 thread limit, and keep Configured Roster / Agent Library / quick picks / catalog nouns consistent. 503 and Est. cost/Plan & quota prose reviewed in context. Docs build: 46 pages.
…, chip tokens - styles.css: grok-model-names 10.5px comment corrected (value sits between --text-micro and --text-caption, not below micro). - styles-integrations.css: client-apps-flow-chip padding 4px 9px + gap 6px + min-height 28px now use tokens (space-1/space-2/space-1-5/control-sm). - sub-agent-surface.md: v1/v2 mode bullets -> V1/V2 (prose labels). - cli/agents.md: active V1/V2 thread limit row. - configuration/agents.md: V1/V2 developer guidance row. - Deferred (ledger): out-of-scope pages codex-app-models.md / how-it-works.mdx terminology, and remaining off-grid values outside the story's enumerated sweep list.
…der overview
Parity with the Mac app: /api/provider-quotas availability rows are now
projected into the shared store (in-memory only — the persisted slice stays
reports + timestamp) and rendered where a provider has no report:
- Store: quotaAvailabilityFromResponse (provider/status/reason/checkedAt
only; unknown reason codes dropped) + unavailableQuotaProviders selector
(status !== available AND no report, sorted). Refresh populates; failed
fetch retains last-known-good. authAttention flow untouched.
- Dashboard: full-width 'Quota unavailable' strip below the Plan & quota
grid listing '{provider} — {reason copy}' with a shared Retry
(refresh(force) -> ?refresh=1); hidden when nothing is unavailable.
- Provider Overview: quota slot shows a compact warn notice with reason copy
+ Retry when the selected provider has no report and is unavailable;
quota card unchanged when a report exists. Threaded shell -> Providers ->
ProviderDetails -> Overview.
- Copy matches the Mac app summary: reauth_required -> Sign in required,
local_cli_refresh_required -> Login needs refresh, else Temporarily
unavailable. 7 new keys in all 6 locales (lint:i18n green).
- Tests: store projection/selector/privacy/failure-retention, dashboard
strip render + Retry-disappears + hidden cases, overview notice + Retry.
Full GUI suite 804 pass / 0 fail; visual check via mock harness at
1380/390 (strip full-width, grid unchanged).
…ason-copy tests - useProviderQuota uses module-level EMPTY availability/reports so derived memos don't recompute while the entry is absent (restores zero lint warnings; was 2 react-hooks/exhaustive-deps). - quotaAvailabilityFromResponse allowlists status to available/stale/ unavailable; hostile status strings are dropped at ingest. - Tests: selector covers stale-without-report inclusion; projection covers hostile status; dashboard strip covers the reauth_required reason copy (Sign in required) next to upstream_unavailable. - Full GUI suite 804 pass / 0 fail; typecheck + lint clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.