Register `claude-fable-5-1` in the CLIProxy model catalog, the dashboard
catalog, and the usage pricing registry.
Pricing is taken from Anthropic's official pricing page:
- Fable 5.1 base rates are $10/$50 per MTok with a $12.50 5m cache write,
matching Fable 5.
- Cache hits bill at 0.025x base input ($0.25/MTok) rather than the
standard 0.1x multiplier. Anthropic applies that reduced rate only to
Fable 5.1 and Mythos 5.1, so this entry cannot derive its cache rates
from CACHE_READ_MULTIPLIER.
This also corrects Claude Sonnet 5 from $3/$15 to $2/$10 (cache write
$2.50, cache read $0.20). The launch introductory rate became the
standard price and the increase scheduled for 2026-09-01 was cancelled,
so the previous entry over-reported Sonnet 5 usage cost by 50%.
Thinking on Fable 5.1 is always on and can only be steered through
effort levels, so the catalog entry exposes the same `low`..`max` level
surface as Fable 5 and Opus 5 instead of a manual token budget.
The GitHub Copilot catalog is deliberately left unchanged, since Copilot
availability for Fable 5.1 is not verified; surfacing it there would
offer a model the backend may reject.
Regenerates docs/reports/hardening-inventory.{json,md} so the ci-parity
gate matches the source tree.
createFileNoReplace and publishCanonicalContent carried the same
open-wx / fchmod / write / fsync / close block and the same cleanup
handler. Extract writeDurableTempFile and discardTempFile so each
publisher is left with only what distinguishes it: a no-replace link, or
the compare-and-swap guard and the rename.
Also spell out in publishCanonicalContent that its guard is read-then-act
rather than atomic. POSIX has no compare-and-swap rename, so the window
is narrowed from the ~100 ms the old claim-and-republish path left open
to two adjacent syscalls, not closed - and the pre-image sidecar is what
keeps that last outcome recoverable. Worth stating so the guard is not
mistaken for a strict guarantee later.
Built [OnSteroids](https://onsteroids.ai)
Adoption moved the canonical settings.json aside with rename() and left
the path empty until publication, roughly 100 ms later. Claude Code or a
second `ccs` starting inside that window found no file and seeded an
empty placeholder; publication then failed with EEXIST because link() is
no-replace, and the rollback published a backup and unlinked the claim,
destroying the only remaining copy of the user's settings. Recovering
meant digging through sidecar files by hand.
Publish by replacement instead: write a temp file next to the canonical
inode and rename() it over the target, so the path always holds a regular
file and no placeholder can be seeded. A compare-and-swap guard on
(ino, mtime, size) runs immediately before the rename and refuses to
publish when the canonical inode changed since it was read, so a writer
that got there first is still never clobbered. The pre-image backup is
published before the replacement, keeping the old content recoverable if
publication is interrupted.
Drops the canonical claim entirely along with restoreCanonicalClaim, and
folds the two identical sidecar publishers into one helper.
recoverOrphanedCanonicalClaim stays, since claims written by older
versions may still be on disk.
New tests cover both writers seen in the incident: Claude Code seeding
`{}` with a trailing newline, and a second `ccs` seeding the 2-byte
variant from shared-dir-linker. Four tests that pinned the claim-based
design were rewritten, among them `preserves a canonical write that
lands during no-replace publication`, whose intent is now enforced by
the CAS guard instead of by an EEXIST from a no-replace link.
Built [OnSteroids](https://onsteroids.ai)
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.