The new helpers added in this PR (inferCodeReviewCadence,
getCodexWindowDisplayLabel) operate on CodexWindowSummary - a Pick subset
of CodexQuotaWindow without usedPercent/remainingPercent/resetAt. They
pass that subset into getCodexWindowKind, which was still typed as
string | CodexQuotaWindow, so tsc -b fails with TS2345 in CI. The
function only reads label/category/cadence, all of which exist on
CodexWindowSummary, so widening the parameter is safe and lets a real
CodexQuotaWindow continue to flow through unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply Copilot review feedback on PR #1113:
- getCodexWindowKind: require cadence alongside category; fall through
to label sniffing when missing instead of silently defaulting to 5h.
- getCodexWindowDisplayLabel (CLI): for category 'additional', fall back
through featureLabel -> window.label -> 'Additional' so partial cached
windows preserve user-visible context.
Mirror the server-side CodexQuotaWindow metadata extension on the UI
side: api-client.ts gains category/cadence/featureLabel; utils.ts
breakdown returns additionalWindows so Spark quota does not pollute
core 5h/weekly badges; quota-tooltip-content renders Spark inline with
prettified labels (GPT-5.3-Codex-Spark -> Codex Spark). Adds breakdown
unit tests and i18n strings for English/Chinese/Vietnamese/Japanese.
The Codex wham/usage API moved GPT-5.3 Codex Spark quota out of
code_review_rate_limit (now null) into a new additional_rate_limits[]
array. Parse the new field and surface it as Spark windows in the quota
view. Adds explicit category/cadence/featureLabel metadata on
CodexQuotaWindow so display logic does not depend on label sniffing.
The legacy label-sniffing path is preserved as a fallback so cached
windows from older versions still render correctly.
Three more issues raised by upstream review after the v1.7 fix push.
Each is encoded both in code and in spec so future drift can't
reintroduce them:
1. Required `storageKey` was the wrong fix for cross-page state bleed.
Forcing every callsite to pass `storageKey` made future
production-page migrations a build-breaking event for no real
safety benefit. Better engineering: keep the prop optional and
default to a key derived from `window.location.pathname` so each
route gets its own localStorage slot automatically. Pathname-
derived defaults are unique by construction; the cross-page bleed
risk only exists for hardcoded shared keys, which the doc warns
against. SSR-safe (falls back to a stable string when window is
unavailable). Pages can still pass an explicit `storageKey` to opt
out of pathname coupling (e.g. when sub-routes should share state).
2. `MaskedInput` `type` was overridable by callers, which defeated
the component's purpose for credentials. Two-layer fix:
- Compile-time: `MaskedInputProps` now extends
`Omit<InputHTMLAttributes, 'type'>` so callers literally cannot
pass `type`. The TS surface enforces the contract.
- Runtime: the `type` attribute on `<Input>` is now placed AFTER
the spread so even an `as`-cast bypass can't override it.
3. The reveal toggle was keyboard-inaccessible. Both `MaskedInput`
and `Field` had `tabIndex={-1}` on the eye-icon button, removing
it from the tab order. Keyboard-only users couldn't show or hide
the secret. Resolution:
- Drop `tabIndex={-1}` so the button joins the natural tab order.
- Add `aria-pressed={revealed}` so screen readers announce toggle
state.
- Add a focus-visible accent ring so the focused state is visible
without a mouse.
Decisions log: v1.8 entry records the rationale for each correction
and explicitly calls out that v1.7's "required storageKey" fix was
worse than the problem it tried to solve.
Validation: typecheck + lint + format + build clean. Tests 519/521
pass (2 pre-existing account-visual-groups failures on dev,
unrelated). Styleguide demos still pass storageKey explicitly so
behavior is unchanged for the only existing callsites.
Three substantive issues raised by upstream review, encoded in code +
spec so future drift can't reintroduce them:
1. Width floor was unenforceable. The previous spec wording said
"form ≥ 360px / json ≥ 320px" but `react-resizable-panels` v3 only
accepts percentage `minSize`. On a 1280px viewport this could let a
user drag a pane down to ~250px — well below the documented floor.
- `Panel minSize` bumped 25 → 30 (≥ 30% of body width after rail)
- Spec rewritten percent-based with the actual 300–360px range
across realistic viewports plus a note on the v3 API constraint
and the `onResize`-clamp escape hatch if hard pixel floors become
necessary later.
2. `storageKey` default caused cross-page state bleed. The previous
default `storageKey="ccs.config-layout"` meant any `<ConfigLayout>`
without an explicit key would share localStorage state with every
other Config page — split ratios contaminating across unrelated
pages.
- `storageKey: string` is now REQUIRED (no default). TypeScript
compile-fails any caller that omits it.
- Spec restated to make the per-page-key contract explicit.
3. Sensitive-field heuristic was too narrow. The previous regex
`AUTH_TOKEN|API_KEY|SECRET|PASSWORD|PRIVATE_KEY` missed common
secret names (ACCESS_TOKEN, REFRESH_TOKEN, BEARER_TOKEN,
CLIENT_SECRET, CLIENT_ID, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY,
GCP/Azure/GitHub/OpenAI/Anthropic variants, JWT, OAUTH, CREDENTIAL,
PAT, WEBHOOK_SECRET, HMAC_KEY, SIGNING_KEY, SSH_KEY).
- New `src/lib/sensitive-label.ts` Single Source of Truth
(`isSensitiveLabel(label)`) with broadened regex; case-insensitive
and tolerant of `_`/`-` separators.
- `Field` imports the shared helper; future consumers do too.
- Spec §5g enumerates the new patterns and points at the SSoT.
Decisions log: v1.7 entry records the rationale and the connections
between spec wording and library API constraints, so the next reviewer
sees the trail rather than re-discovering it.
Validation: typecheck + lint + format clean; build clean; tests
519/521 pass (2 pre-existing account-visual-groups failures on dev,
unrelated). Styleguide demos already pass storageKey explicitly.
Adds a page-level design system spec for CCS dashboard Config pages,
without migrating any production page yet. Pure-spec PR: docs +
primitive treatments + DEV-only /_styleguide route. Production page
adoption is intentionally left for follow-up work, one page at a time.
Spec — `ui/docs/design-system.md`:
- §0 Layout invariants (NEVER VIOLATE): two-column shell, no second
horizontal strip below the global topbar, sibling-pane top alignment,
cliproxy as canonical reference, user-resizable form/json split, and
content-fit unified rail (`w-fit min-w-[240px] max-w-[360px]`)
- §1 identity-strip patterns: HeroBar (home), rail-anchored (cliproxy),
PageHeader (Monitor-only), Bespoke (health)
- §2 body archetypes: Config (rail + form + optional json) and Monitor
- §4 anti-patterns with rejected code samples (PageHeader-over-
ConfigLayout, tab-bar offsetting json pane, redundant description
band, blank vertical band)
- §5 color & accent rules using the existing Pampas/Crail palette only:
1-accent-dot rule, sibling-pane differentiation, status pill spec,
three-tier intensity ladder, sensitive-field treatment per §5g
Decisions log — `ui/docs/design-decisions.md`:
- v1.6: content-fit rail (replaces fixed 260px)
- v1.5: color & accent treatment for body panes
- v1.4: resizable form/json split
- v1.3: layout invariants and rail-anchored default for Config
Primitives — `ui/src/components/config-layout/`:
- ConfigLayout: rail rendered as content-fit `<aside>` within the
unified envelope; form↔json split via react-resizable-panels (already
in deps) with autoSaveId-driven per-page localStorage persistence
- FormPane: 1px Crail top strip on header + soft from-card gradient +
inset bottom shadow for scroll-depth cue; body wash bg-muted/20 so
FormSections read as elevated cards; footer bg-muted/40 anchors save
- FormSection: 2px Crail leading-edge stripe with three-tier intensity
(default 30% / hover 70% / focus-within solid + ring + shadow) + 1.5px
accent dot prefixing each title
- JsonPane: bg-muted/30 shell visually recesses the read-only pane vs
the active form pane (bg-card); accent dot + status pill in header;
inner <pre> sits inside an inset-shadow well; content stays plain
(a dedicated JSON viewer with its own color coding will replace it
later — pages MUST NOT add inline JSON tints in the meantime)
- ListPane: selected row uses three-tier intensity ladder (no stripe at
rest, accent/30 stripe on hover, solid Crail stripe + bg-accent/10
row tint + accent-tinted icon and count badge on selection)
Sensitive input primitive — `ui/src/components/ui/masked-input.tsx`:
Upgraded to match §5g: lock glyph prefixing label, "sensitive" accent
pill on the label row, accent focus ring (the only place the default
ring is overridden), accent-tinted reveal toggle on hover. Existing
consumers (settings-dialog, websearch, friendly-ui-section, env-editor-
section) inherit the treatment without code changes.
Shared Field — `ui/src/components/forms/field.tsx`:
Tiny self-documenting Field component referenced by /_styleguide demos.
Auto-detects sensitive labels (AUTH_TOKEN | API_KEY | SECRET | PASSWORD
| PRIVATE_KEY) and applies §5g treatment. Production pages can adopt
this as a follow-up; no production code currently consumes it.
Live styleguide — `ui/src/pages/_styleguide.tsx`:
DEV-only route (gated by import.meta.env.DEV in App.tsx) showcasing
every primitive in isolation plus composed Config + Monitor archetype
demos. Intro callout surfaces the §0 invariants. §1b/§2a/§2b demos are
canonical rail-anchored examples authors can copy. Demo content uses
realistic Anthropic-style env field sets so the visual density mirrors
the cliproxy reference.
What this PR does NOT do:
- No production page rewrites — `pages/{api,accounts,codex,copilot,
cursor,droid,shared,updates,analytics,claude-extension,cliproxy-ai-
providers}` are unchanged
- No test selector updates — the existing test suite passes (excepting
the 2 pre-existing account-visual-groups failures already on dev)
- No HTML preview reports — the canonical visual reference is the live
/_styleguide page
- No new dependencies — `react-resizable-panels` was already declared
Validation: typecheck + lint + format + build clean. Test suite green
(519/521 — 2 pre-existing dev failures unrelated to this PR).
Bun's `mock.module()` is process-wide and is NOT undone by `mock.restore()`.
Two test files used module-level mocks that leaked across test runs,
silently breaking unrelated suites that imported the mocked modules
transitively:
- tests/unit/cliproxy/version-checker-stale-cache.test.ts mocked
binary/version-checker, contaminating cliproxy-stats-routes-install
and cliproxy-stats-routes-model-update suites that import a route
module which transitively imports version-checker.
- tests/unit/cliproxy/service-manager-startup.test.ts mocked 8 modules
at top level (binary-manager, stats-fetcher, etc.), contaminating any
later file that imports them.
CI happens to be green because file ordering on the GitHub runner places
victim files BEFORE contaminators in `bun test`. On Linux locally the
order is reversed, producing 6 reproducible test failures on every
`bun run test:fast` run. A change in OS, bun version, or new test files
that import the mocked modules could silently flip the CI runner's
order and surface these failures unexpectedly.
Replaced module-level mocks with explicit dependency-injection seams in
production code, following the existing pattern in version-checker.ts
and version-cache.ts (`fetchJsonFn?`, `fetchLatestVersionFn?`):
- src/cliproxy/types.ts: `BinaryManagerConfig.checkForUpdatesFn` (optional)
- src/cliproxy/binary/lifecycle.ts: route through new fn with default
- src/cliproxy/service-manager.ts: `ensureCliproxyService(deps?)` with
optional `ensureBinaryFn`, `detectRunningProxyFn`,
`configNeedsRegenerationFn`, `withStartupLockFn`
All deps fields are optional with real-implementation defaults, so
production callers are unchanged. Tests now inject deterministic stubs
through call-site parameters instead of module-level mocks.
Verified locally: \`bun run validate\` is now 2540 pass / 0 fail
(previously 2534 pass / 6 fail at the same upstream/main HEAD).
Built [OnSteroids](https://onsteroids.ai)
PR-Agent flagged two more real regressions vs the original health page:
1. Stale timestamp (HealthStatusRibbon): formatRelativeTime reads
Date.now() during render, but nothing forces a re-render after mount
so the 'last scan' label froze and never advanced to '1 minute ago',
'2 hours ago', etc. Restore the per-second tick (matching the
original health.tsx behavior) so the label behaves like a real
relative timestamp.
2. Hidden copy action (HealthPriorityCard): the fix-copy button was
gated by group-hover, making it invisible to keyboard users and
unreachable on mobile (no hover state). Make it always visible with
an aria-label so screen-reader and touch users can copy the fix
command.
PR-Agent flagged two real bugs at score 84:
1. Accessibility (HealthPriorityCard): the priority card header was a
clickable <div> with onClick, making expand/collapse mouse-only.
Promote it to a <button type="button"> with aria-expanded reflecting
state and a contextual aria-label, and a focus-visible ring. The
inner content is extracted into a shared fragment so the button and
the no-content fallback <div> render the same markup.
2. Time display (HealthStatusRibbon): formatRelativeTime lost the hours
branch from the original health.tsx, so a 2-hour-old scan showed as
'120 minutes ago' instead of '2 hours ago'. Restore the hours
rollover with health.hoursAgo i18n key.
After the v1.1 restructure, health was further redesigned per a separate
brief and went bespoke -- new components (HealthStatusRibbon,
HealthPriorityCard, HealthPriorityList, HealthAuditSection), no
PageShell/PageHeader/MonitorLayout, custom severity-driven hierarchy.
Bring the docs in line:
- design-system.md: add §1d Bespoke as a fourth identity-strip pattern
with health as canonical reference; remove health from PageHeader
and Monitor canonical-reference slots (now TBD)
- design-decisions.md: v1.2 revision row capturing the redesign and
the resulting doc cleanup
- screenshots-phase2/after-03-health.png: replace stale Monitor-archetype
screenshot with the actual final redesigned page (Privacy mode ON)
- design-system-phase2-preview.html: regenerate; describe the four
patterns, embed the new screenshot, update the file change list
Self-contained verification report at ui/docs/health-redesign/report.html
showing four states of the redesigned health page (initial, loading,
expanded issue, expansion fix). Screenshots at the same level so the
report's relative image refs resolve when opened directly.
Captures the design exploration delivered against the handoff brief
(plans/reports/handoff-260425-1417-health-page-redesign.md).
Iteration on top of the health redesign:
- layout.tsx: drop backdrop-blur transparency on the global header
(interfered with the new health page colored backdrop)
- health.tsx: switch from fixed full-viewport background to scoped
absolute positioning; replace unreliable external grain SVG (vercel
app) with an inline data URI that always loads
- health-priority-card.tsx: refine spacing, expansion behavior, and
visual hierarchy
The v1.1 design contract names three identity-strip patterns but the
styleguide previously only demoed PageHeader. Future contributors
visiting /_styleguide had no live preview of HeroBar or rail-anchored
identity, leaving the contract under-documented.
Add:
- 1a. HeroBar demo (canonical: home pattern) -- single-row dense hero
with logo + title + version badge + 4 inline stat tiles, dotted
background pattern, anonymized data
- 1b. Rail-anchored identity demo (canonical: cliproxy pattern) -- a
Config 3-pane layout where the left rail carries brand + Quick Setup
CTA + provider list + footer status, with NO top chrome
- 1c. PageHeader demo (existing, retitled with health pattern reference)
All demos use anonymized data (Provider A/B/C, Demo Dashboard, fake
metrics) so screenshots are safe to publish without Privacy mode.
home and cliproxy were reverted to their original designs; before/after
screenshots would show identical content. Keep only health migration
screenshots and refocus the HTML report on the design-system docs
restructure (three identity-strip patterns).
User feedback caught a Phase 2 mistake: forcing home and cliproxy into
a one-size-fits-all PageShell + PageHeader chrome regressed density on
both pages.
- home: original 1-row hero (logo + title + version + 4 inline stats)
was split into PageHeader + KpiRow, doubling vertical footprint
- cliproxy: original rail-anchored brand was duplicated by a top
PageHeader, stealing ~80px from the 3-pane body
Resolution: revert both pages to dev-branch state, then restructure
ui/docs/design-system.md to extract THREE identity-strip patterns from
the canonical references rather than imposing one:
1. HeroBar (1-row dense) -> home pattern
2. Rail-anchored (no top chrome) -> cliproxy pattern
3. PageHeader (title + description) -> health pattern
Health stays migrated -- the Monitor archetype + PageHeader is a real
improvement there because the gauge + KPI row + group cards don't fit
the HeroBar pattern.
Future page migrations adapt to whichever identity-strip pattern fits
the page's content shape, not the other way around. design-decisions.md
gets a v1.1 revision row capturing the rationale.