Fixes the two collector-side root causes of #1601:
1. Claude per-profile credential reads were file-only, but on macOS Claude
Code stores the OAuth token for an isolated CLAUDE_CONFIG_DIR in a
per-directory Keychain item ("Claude Code-credentials-<sha256(dir)[0..8]>").
The .credentials.json file never exists, so every isolated profile was
parked with needsReauth:true forever. The reader now falls back to that
Keychain item (file-first, same security-CLI read the shipped global
fallback already performs; TTL-gated so it is not on every /summary).
2. Non-default profiles were cache-only forever, so they could never leave
the parked state even with valid credentials. getNativeAccountRows now
gives each surface ONE rotating live slot: the stalest eligible
non-default profile is refreshed per pass, skipping profiles inside
breaker/reauth cooldowns. Every account converges to real quota within a
few polls while the per-pass upstream budget stays constant (<= 2 calls
per surface regardless of profile count). Codex named profiles with valid
auth but sparse payloads now yield an active quota-less row instead of a
false needsReauth row.
Non-default rows keep paused:true (dimmed) even when freshly refreshed so
only the default renders active and rows do not flicker between polls.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ccs bar always forced the dashboard onto port 3000 (first free of a
hardcoded candidate list), which collides with other local dev servers, and
bar.json was rewritten to 3000 on every launch.
- `ccs bar [launch] --port N` runs the server on exactly N: reuses a live
server already on N, moves a running server from another port (SIGTERM via
server.pid, wait for exit), errors clearly when N is busy or the value is
invalid.
- The chosen port is persisted into launch.json args, so the Swift app
self-starts the server on the same port.
- Without --port, launch and serve now try the port recorded in bar.json
first (sticky), so the server keeps coming back on the port the user last
chose instead of reverting to 3000.
- Bare flags (`ccs bar --port N`) route to the launch subcommand; --port is
documented in `ccs bar --help`.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The maintainability metrics artifact aged past the 30-day freshness
gate in scripts/ci-parity-gate.sh, so validate:ci-parity fails for
every contributor branch until the committed copy is regenerated.
Google retired the gemini CLI on 2026-06-18, so the gemini websearch fallback no longer
works. Add agy (Antigravity) as the primary CLI websearch provider end to end: runtime spawn
in websearch-transformer.cjs (agy --dangerously-skip-permissions --print-timeout Ns -p),
detection (agy.ts), config schema and defaults, hook env, status, dashboard persistence, and docs.
The legacy gemini provider stays present but is marked deprecated for enterprise users who
retain access. Image analysis already supported the agy provider (no change). The CLIProxy
gemini quota fetcher (Gemini API OAuth, not the cli binary) is untouched.
Validation: typecheck, lint, and the websearch + config + web-server unit suites pass.
Releasing the macOS bar app was fully manual (run package_app.sh on a Mac, then
gh release upload --clobber), so Swift-side changes never reached users until
someone remembered to rebuild and re-upload the floating ccs-bar-latest asset.
Add a tightly-scoped Bar Release workflow that does it automatically:
- triggers ONLY on push to main touching macos-bar/**, or manual dispatch, so
regular PRs, dev pushes, and non-bar changes never start it
- runs ONLY on the dedicated self-hosted macOS runner (label ccs-bar); the Linux
CI runners never match it and it never competes for them
- least-privilege contents:write, single-flight via concurrency
Version is sourced from a new macos-bar/VERSION single-line file (the workflow
reads it; the asset is always the latest build regardless). package_app.sh now
defaults to that file when no version arg is passed, so the local manual path and
CI share one source of truth. Documents the release process in docs/ccs-bar.md.
Redact StageOptions error payloads and summarize debug launch args.
Propagate request IDs through Cursor daemon and dashboard completion logs.
Mark remaining P2/P3 maintainability targets as partial instead of overclaiming.
Epic P7. Locks in the epic's gains with ESLint gates so the adoption work
does not regress.
- ccs/no-new-throw-error (error): custom flat-config rule that flags NEW
throw new Error(...) outside a generated baseline allowlist. Forces the
typed-error taxonomy (src/errors/error-types.ts). Existing 338 sites are
grandfathered in eslint-rules/throw-error-baseline.json; only NEW violations
error. Rule normalizes the filename to repo-root-relative to match baseline
keys regardless of how ESLint reports paths.
- max-lines (warn, 400, skipBlankLines/skipComments): warns on files over
400 LOC (goal of P5/P6 god-file splits). Currently 51 warnings on the
not-yet-split god-files (P6 territory).
- scripts/generate-throw-error-baseline.js: emits the allowlist from raw
source (superset of real throws; never undercounts). Run after intentionally
grandfathering a site, or quarterly to prune.
- tests/unit/eslint-rules/no-throw-new-error.test.ts: rule logic (flags
off-allowlist, passes on-allowlist/typed/rethrow, line-sensitivity).
- docs/code-standards.md: Lint Enforcement Gates section.
- docs/logging-contract.md: error.code -> ExitCode table (from P4).
validate + validate:ci-parity green.
Epic P4. Migrates plain throw new Error to the typed-error classes in the four
locked subdomains, and makes the taxonomy erasable-syntax-compatible so it can
be adopted across UI-reachable code.
Migration (cliproxy/auth, web-server/routes, auth):
- 21 of 23 throws in the locked subdomains now use typed subclasses
(ProfileError, AuthError, ConfigError, ValidationError, ProviderError).
- Typed adoption in locked subdomains: 0/23 -> 21/23 (91.3%), > 40% target.
- Overall typed adoption: 0.9% -> 8.6%.
- Messages preserved exactly (message-based tests stable). Exit codes now
differentiate via handleError (ProfileError=7, AuthError=4, ConfigError=2,
ProviderError=6). ccs doctor 0/1 contract untouched (outside scope).
Erasable-syntax fix (unblocks the migration in the UI build graph):
- exit-codes.ts: enum ExitCode -> const object + union type (value and type
usage both preserved; no Object.values(ExitCode) consumers).
- error-types.ts: constructor parameter properties -> explicit readonly field
declarations + body assignment.
- The web UI build enforces erasableSyntaxOnly (ui/tsconfig.app.json) and
reaches src/errors via the @shared -> src/auth graph; pre-erasable
error-types blocked the build once profile-registry adopted typed errors.
Compat audit: docs/reports/typed-error-exit-code-compat-audit.md (Q1 resolved:
migrate freely; only documented contract is ccs doctor, which is untouched).
Behavior-lock: src/errors/__tests__/typed-error-migration-exit-codes.test.ts
(taxonomy -> exit-code mapping, instanceof chains, context fields).
validate + validate:ci-parity green (incl. UI build).
Preserves the Gatekeeper quarantine attribute on the installed CCS Bar app so the user makes the right-click to Open trust decision, instead of silently clearing it.
Sets undici headersTimeout/bodyTimeout to request_timeout+30s so the AbortController is the single authority on upstream request lifetime, preventing premature socket closes on slow self-hosted upstreams. Verified undici v5 ProxyAgent object-signature compat.
Reflect the seamless-launch change: the app self-starts the background
server, ccs bar runs it detached, and add the serve/stop/status commands
plus the launch.json/server.pid/serve.log files.
'ccs bar install' previously ended with two manual steps: clearing the
Gatekeeper quarantine by hand and running 'ccs bar' separately.
Install now detects an existing installation and says so before
reinstalling, clears the quarantine attribute itself via execFile with
a graceful fallback to the printed hint when xattr fails, and ends with
a TTY-aware 'Launch CCS Bar now?' prompt (default yes) that hands off
to the existing launch flow. --launch forces the handoff and
--no-launch suppresses it for scripted installs; non-TTY runs skip the
prompt and print the manual command instead.
Closes#1504
Add a 'Get CCS Bar' promo banner + card to the dashboard (mirroring the
OpenRouter promo pattern + design system) so users discover the macOS menu-bar
app, with a macOS-aware install CTA. Add a user-facing docs/ccs-bar.md covering
what it is, install via 'ccs bar install', launch, what it shows, uninstall,
and the loopback requirement -- closing the docs-sync gap for the new ccs bar
command.