A second implementation of session-storage reclamation was drafted
uncommitted on the control root's dev checkout (SessionDataArchive).
This folds its distinct ideas into the committed implementation, which
stays the base (per-directory locking, partial-then-rename with the
directory-wins invariant, funnel-covered transparent restore, tests):
- deleteSession is now truly permanent: it removes sessions/<id>/, the
compressed <id>.tar.gz if the sweep packed it, and any local
Carbon-mirror copy — previously every deleted chat leaked its
transcript directory forever. Project deletion purges the same per
chat; deleteEphemeralSession drops its now-redundant own removal.
- The worktree-cleanup and storage sweeps source candidates from
database.loadAllSessions() instead of persistedSessionRecords, which
only holds active projects' sessions — an archived project's chats
could otherwise never be reclaimed (a permanent worktree leak).
- New opt-in retention policy (Settings ▸ Chats ▸ "Reclaim archived
chat data", Never by default): an archived, non-favorite chat past
the window is deleted outright on an Optimized-storage Mac, but on an
All-Copies Mac it is compressed in place through the same guarded
compression path — full storage never deletes. Off by default because
discarding chat data must be an explicit opt-in.
- The archiver verifies the fresh archive is listable (tar -tzf)
before the source directory is removed, so a truncated stream is
caught at compress time, not restore time.
Co-Authored-By: Claude Fable 5 <[email protected]>
At hundreds-to-thousands of sessions Nucleic's storage balloons into the
hundreds of gigabytes across worktrees and per-session storage. Two changes:
1. "Delete archived chat worktrees" now reclaims only chats whose work is
Done (a finished run, or a last turn classified/marked .completed) — a
chat archived mid-conversation keeps its checkout. Moved-away tombstones
remain reclaimable regardless: their work lives on another Mac, and the
transfer's own discard is best-effort. The Done predicate is the new
Session.isCompleted, shared with isChatDone so the definitions can't
drift, and the Settings ▸ Chats picker documents the narrowed behavior.
2. Idle-chat session-storage compression for Macs in Carbon's "All Copies"
role — the machines carrying the mesh's full storage burden. A new
SessionStorageArchiver packs sessions/<id>/ (transcript, render
sidecars, agent home) into a sibling <id>.tar.gz via the system tar
(~10x on this JSONL-heavy data), with a partial-then-rename protocol
and a "directory wins over any archive beside it" invariant so every
crash point degrades safely. A sweep on the auto-archive loop packs at
most 4 chats per pass, and only ones with no live controller, no
hydration in flight or queued, not open, not pinned, not transferring,
and idle past the threshold. Restore is transparent at every read
funnel: prepareSession (open/hydration/dashboard repair), the open-chat
preview streamer, and the mesh transcript server. The idle window is a
new Settings ▸ Carbon dropdown (default: after 7 days), resolved
against the storage role at launch and on change; Optimized-role Macs
and headless hosts stay off.
Co-Authored-By: Claude Fable 5 <[email protected]>
Bring NucleicRemote closer to desktop parity in two areas (the core
sync loop was already at parity — shared protocol, control scope).
Transcript fidelity (iOS): a client-side TranscriptProjection coalesces
streaming text by messageID and folds each tool call's lifecycle
(start/deltas/complete/result/fileChange) into one expandable card —
fixing the duplicate started+completed rows. Adds Markdown bubbles, the
gold Orchestra card for Task/Agent spawns, and the previously-dropped
usage/cost, rate-limit, file-change, turn-boundary and session-started
rows, plus a context-window % header badge.
Mid-session controls + model catalog (protocol/host/iOS): project the
host ModelCatalog over the wire as WireModelCatalog (in Welcome); add 5
control-scope setters (setSessionModel/Effort/Auto/AutoShip/ShipBranch)
backed by the existing AppStore.mutateSession + SessionController hooks;
enrich WireSessionSummary with model/effort/auto/autoShip/shipBranch/
contextInputTokens (all forward-compatible). The composer gains a model
picker and a catalog-driven effort menu (per-backend caps: Codex→xhigh,
Grok→auto), and the session header gains a model/effort/auto/autoship
control bar.
Tests: CBOR round-trips for the new messages, Welcome.modelCatalog, the
new summary fields, forward-compat decode of old bytes, and the setters
reaching the host. Verified in the Simulator (NUCLEIC_DEMO=1).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Replace the streaming-json CLI + generated PreToolUse-hook Grok adapter with grok's native ACP mode (JSON-RPC 2.0 over stdio). Adds GrokACPBackend/GrokACPDecoder/GrokACPDecisionMapping (a near-twin of the Codex app-server adapter); generalizes CodexJSONRPC into a shared JSONRPCConnection (jsonrpc-header flag, default off so Codex is byte-identical, plus endNotifications); deletes GrokBuildBackend, GrokStreamDecoder, GrokHookConfig and strips the /grok-hook HTTP bridge from MCPApprovalServer. Claude and Codex are left native. Preserves conflict-lock parity, native resume (session/load), and the hermetic fake-grok contract test. Full suite: 525 tests green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Orchestra is a Nucleic Control capability now:
- Core: SessionController withholds the fan-out consent unless the session's project
is under Control (resolvedEffort still maps the sentinel to xhigh, so a stray
selection never reaches a backend verbatim). Covered by a new SessionController test.
- macOS: the effort menu shows Orchestra disabled with a 'Requires Nucleic Control'
note + tooltip off-control; effectiveEffort demotes a carried-over selection so the
label/glow never show it active where it can't run.
- Home composer themes lavender (the Control accent) for a Control project — the
Auto/Merge accents and a persistent ring on the field, tracking the project picker.
- Remote: WireProject carries isNucleicControlled (decode-tolerant) so the iOS effort
picker gates Orchestra the same way.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Orchestra already worked end-to-end on Codex (resolvedEffort maps it to xhigh,
which Codex accepts, and CodexAppServerBackend forwards appendSystemPrompt as
developerInstructions); it was only held back by a deliberate Claude-only UI gate.
Drops supportsOrchestra and the menu gating so the mode is offered for every model,
and simplifies clampedEffort to always preserve Orchestra (it's an orchestration
mode, not an API level).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Renames the orchestration mode's user-facing brand and all code identifiers
(UltracodeStyle→OrchestraStyle, isUltracode→isOrchestra, AppTheme.ultracode→
.orchestra, etc.). The canonical effort sentinel becomes "orchestra"; the legacy
"ultracode" token is still recognized so a persisted/in-flight session keeps
working. Comment referencing Claude Code's own ultracode mode kept accurate.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Adds the ultracode orchestration mode. Resolved effort-menu conflicts with the
per-model effort rework on dev (efforts(for:)/effortNoun/clampedEffort): ultracode
is offered only for Claude models and preserved by clampedEffort there.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Both Opus 4.8 SKUs now display as 'Opus 4.8', distinguished only by the
grayed picker badge: '1M' for the [1m] variant, '256K' for the standard.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Put the 'claude-opus-4-8[1m]' SKU back in the picker and as the app
default. Standard Opus 4.8 now sizes to a 256K context window and shows
a grayed '256K' badge beside it in the model pickers.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Opus 4.8 now ships a 1M context window natively, so the separate
'claude-opus-4-8[1m]' SKU is redundant. Remove it from the picker,
revert the default back to 'claude-opus-4-8', and size the session
context-usage indicator (5h/weekly bars) to 1M for any opus-4-8 SKU.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
The idle window is still user-configurable in Settings (Chats > Archiving);
this only moves the out-of-the-box default from 30 minutes to 1 day, which is
a less aggressive default for sweeping completed chats into the archive.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adds a background sweep that archives chats whose work is done (a finished
run, or an interactive chat whose last turn classified `.completed`) once
they've sat untouched past a configurable idle window. Favorites, the open
chat, and anything still awaiting the user (a question, a pending approval,
or an unclassified turn) are left alone.
A new "Archiving" setting in Chats picks the window: 30 minutes (default),
1 hour, 3 hours, 1 day, 1 week, or Never. Persisted via AutoArchivePolicy;
the AppStore only sees the resolved interval (nil = Never).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- Home: size the usage card to the activity chart's measured height (the
card stretches to match) and make the chart a touch taller, so the two
columns line up. Trim the card to the 5-hour + weekly windows.
- Composer: add session context-window usage (latest turn's input tokens vs
the model's window) as the left-most metric, and trailing-inset the row so
the text aligns with the chat box's right edge rather than the send button.
- Replace the "5h"/"wk" text with clock/calendar symbols and use a document
symbol for context usage; each metric is colored by its own utilization.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
New chats can default to autoship via a Settings toggle in New chat
defaults. Enabling it implies Auto mode, mirroring the per-session
control. Persisted under nucleic.defaultAutoShip and loaded into the
store on launch.
Co-Authored-By: Claude Opus 4.8 <[email protected]>