system-prompt profiles Wave 1: CLAUDE.md split (~11k→~2.1k tokens), B3+C3 findings, slimmed plan

- B1: CLAUDE.md reduced to orientation only; status timeline, component detail,
  and plugin procedures moved to docs/implementation-status.md (loaded on demand).
  Fresh-session check: ~32k context vs 41.8k baseline (~9.7k/session saved),
  orientation probes pass.
- B3: memsearch SessionStart injection measured (61 sessions, median ~11.7KB);
  size is hardcoded in the hook, no config knob — report in docs/plans/.
- C3: --setting-sources must include "user" or all plugins silently drop;
  ~/.claude/profiles/*.json overlays are disjoint from bin/refresh-plugins.
- Plan docs: Gate G0 cleared (WS2/WS4 complete); scope slimmed — A1/A2 deferred
  behind an interactive /context check, D dropped (pointer kept), C minimized.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uc1HZDxnpzTJ8EYLqruEnY
This commit is contained in:
jared 2026-07-08 13:36:16 -04:00
parent 397145b27b
commit a06431c5fd
6 changed files with 892 additions and 201 deletions

299
CLAUDE.md
View File

@ -1,230 +1,129 @@
# CLAUDE.md # CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. This file provides guidance to Claude Code (claude.ai/code) when working with code in this
repository. It is orientation only — implementation history, component details, and
operational procedures live in [docs/implementation-status.md](docs/implementation-status.md)
(read it on demand, e.g. before touching a plugin or claiming something isn't built yet).
## What this repository is ## What this repository is
`cc-os` is a **design + implementation repository** — it captures the design of a **personal, `cc-os` is a **design + implementation repository** for a **personal, cross-project memory
cross-project memory system for Claude Code** (for a multi-client freelancer) plus the research system for Claude Code** (for a multi-client freelancer), plus a family of global Claude Code
that informs it. The global memory plugin is now partially implemented; markdown specs, ADRs, plugins and the research/evals that inform them. Everything is markdown-as-truth: specs,
and the build plan remain the source of truth for what is being built and what remains. ADRs, and the build plan in `docs/` are the source of truth for what is being built. When
asked to "build," work from the staged tasks in `docs/memory-system/04-build-plan.md`, not ad
Everything is markdown-as-truth. When asked to "build," work from the staged tasks in hoc.
`docs/memory-system/04-build-plan.md`, not ad hoc.
## Directory layout ## Directory layout
Numbered files within a directory are not a required reading order — read the one whose topic Numbered files within a directory are not a required reading order — read the one whose
you need. topic you need.
- **`docs/memory-system/`** — the design of the memory system itself. Go here to understand or - **`docs/memory-system/`** — the design of the memory system itself. `02-system-design.md`
change *what is being built*. `02-system-design.md` is the architecture, `03-architecture- is the architecture, `03-architecture-decisions.md` is the ADR log, `04-build-plan.md` the
decisions.md` is the ADR log (each decision + what was rejected/deferred and why), build outline with step status, `06-graphify-evaluation.md` justifies the Graphify pivot.
`04-build-plan.md` is the build outline, `06-graphify-evaluation.md` justifies the Graphify - **`docs/implementation-status.md`** — the status timeline, full per-component detail for
pivot. Read the specific file relevant to your task; read the whole set only when reworking every shipped plugin (os-vault, os-orchestration, os-status, os-doc-hygiene, os-adr),
the design. eval-harness records, and plugin rename/cache-refresh procedures.
- **`docs/graphify/`** — a verified handbook for the Graphify knowledge-graph tool (the chosen - **`docs/graphify/`** — verified handbook for the Graphify knowledge-graph tool. Skim
knowledge-layer engine). Go here when working with Graphify commands/behavior. Skim `00-README.md` for the model; keep `09-best-practices-checklist.md` open when running it.
`00-README.md` for the model, keep `09-best-practices-checklist.md` open while actually Claims are provenance-tagged (`[github]` trustworthy; `[interview]` / `[unverified claim]`
running it. Claims are provenance-tagged (`[github]` trustworthy; `[interview]` / not).
`[unverified claim]` not).
- **`graphify-interview`, `memory-systems-compared060326`** (repo root) — raw source - **`graphify-interview`, `memory-systems-compared060326`** (repo root) — raw source
transcripts (marketing / video). Only open these to trace where a claim came from; treat as transcripts. Only open to trace a claim's origin; treat as *intent*, not *fact*.
*intent*, not *fact* — they were already corrected against primary sources in `docs/`. - **`plugins/`** — source of the global plugins, symlinked into `~/.claude/plugins/`.
- **`openspec/`** — spec-driven change management (see workflow below). `changes/` holds live - **`openspec/`** — spec-driven change management (see workflow below).
changes, `changes/archive/` completed ones, `specs/` stable specs. - **`.claude/`, `.codex/`, `.pi/`** — **identical** copies of the OpenSpec skills for three
- **`.claude/`, `.codex/`, `.pi/`** — **identical** copies of the OpenSpec skills for three AI AI assistants. Only open when changing a skill — and mirror any change across all three.
assistants. Only open when changing a skill — and mirror any change across all three.
## The current design in one paragraph ## The current design in one paragraph
This is a **work in progress**, not a frozen spec. The paragraph below is the current approach; This is a work in progress, not a frozen spec; **when a conversation changes the design,
treat it as the default you operate from, but **whenever a conversation with the user changes update this paragraph (and the relevant `docs/memory-system/` files + an ADR) to match.**
the design, update this paragraph (and the relevant `docs/memory-system/` files + an ADR) to
match.** Keep it accurate, don't preserve it for its own sake.
Two memory types kept as **separate systems**: **episodic** ("what happened, when") handled by Two memory types kept as **separate systems**: **episodic** ("what happened, when") handled
**memsearch** (Milvus Lite, embedded), and **semantic/knowledge** ("how do we…") handled by the by **memsearch** (Milvus Lite, embedded, single global store for ALL clients by design —
existing **`~/Documents/SecondBrain` Obsidian vault** as the single source of truth. Notes keep ADR-015), and **semantic/knowledge** ("how do we…") handled by the existing
`summary` + six flat, parallel namespaced facets (`type/`/`client/`/`project/`/`domain/`/`tool/`/`convention/`) plus `scope/` as metadata; hierarchy and relationships are expressed via hub notes (`type/hub`), wikilinks, and Graphify graph edges — not nested tag paths. The vault is queried via a **Graphify knowledge graph** (local Ollama SLM for doc extraction, free **`~/Documents/SecondBrain` Obsidian vault** as the single source of truth (ADR-012). Notes
tree-sitter AST for code). Retrieval is hook-injected + on-demand so project repos stay thin; keep `summary` + six flat namespaced facets
freshness is lazy (`type/`/`client/`/`project/`/`domain/`/`tool/`/`convention/`) plus `scope/` (ADR-011);
(write-time hook + SessionStart reconcile, no daemon/cron); the vault syncs to a VPS while hierarchy comes from hub notes, wikilinks, and Graphify graph edges — not nested tags
indexes stay disposable and rebuildable. Ships as a global Claude Code plugin (`os-vault`) with skills. (Graphify is a structure extractor, not a topic clusterer; hubs are author-provided,
ADR-014). The vault is queried via a **Graphify knowledge graph** (local Ollama SLM for doc
extraction, tree-sitter AST for code); Graphify replaced the originally-planned Ruby/SQLite
tag-index CLI (2026-06-03 pivot). Retrieval is hook-injected + on-demand; freshness is lazy
(write-time hook + SessionStart reconcile, no daemon); the vault and memsearch store
auto-sync to private Forgejo repos via SessionEnd hooks. Ships as a global Claude Code
plugin (`os-vault`) with skills. Projects are onboarded one at a time; bulk vault migration
is deferred to last (ADR-013).
**Recent pivot (2026-06-03):** Graphify **replaces** the originally-planned Ruby/SQLite ## Implemented components (inventory)
tag-index CLI and also covers the deferred QMD semantic layer. `04-build-plan.md` and
`06-graphify-evaluation.md` reflect this; if an older doc still describes the Ruby CLI, defer
to those two and fix the stale doc.
**Decisions locked (2026-06-04):** Six-facet tag taxonomy + `scope/` (ADR-011); reuse `~/Documents/SecondBrain` vault rather than creating a new one (ADR-012); build-first / migrate-incrementally — build full system against a fixture set first, defer bulk vault migration to last, onboard projects one at a time (ADR-013). All build-plan steps required for the memory system are complete (2026-06-15). Full detail
for each item is in [docs/implementation-status.md](docs/implementation-status.md) — read it
before modifying any of these:
**Empirical finding locked (2026-06-05):** Graphify is a structure extractor, not a topic clusterer — no emergent hub nodes appear even at `--mode deep`; hub notes + wikilinks must be author-provided during migration (not deferred). Migration scaffolding is now a first-class deliverable. Open question: do facet tags create graph edges? (ADR-014; findings: `docs/memory-system/07-graph-connectivity-findings.md`). - **os-vault** (`plugins/os-vault/`) — vault write/query/reorganize/onboard-project/
design-template skills, SessionStart/End hooks, memsearch + vault git sync. Write-behavior
eval harness in `plugins/os-vault/eval/`.
- **os-orchestration** (`plugins/os-orchestration/`) — injects `ORCHESTRATION.md`
(session-orchestration + delegation-economics rules) into all sessions; this repo carries
no local override. Eval harness in `plugins/os-orchestration/eval/`.
- **os-status** (`plugins/os-status/`) — aggregated deterministic SessionStart checks
(subagent-model env override, ADR system present, vault hub note present). ADR-022.
- **os-doc-hygiene** (`plugins/os-doc-hygiene/`) — stale/bloated-doc monitoring;
check/clean/status/sweep skills.
- **os-adr** (`plugins/os-adr/`) — ADR system: Ruby `lib/adr/` + CLIs, create/init/migrate/
find skills; Eval A/B/C harnesses. Rollout order locked: pilot projects, then the cc-os
retrofit of `03-architecture-decisions.md`, then wider.
- **Graphify** v0.8.31 (`~/.local/bin/graphify`; PyPI package is `graphifyy`, double-y) —
vault graph at `~/Documents/SecondBrain/graphify-out/`, per-project graphs at
`<project-root>/graphify-out/` (gitignored); both disposable/rebuildable.
- **memsearch** v0.4.6 (marketplace plugin) — `~/.memsearch/memory/YYYY-MM-DD.md` daily
files + Milvus index; `/memory-recall`, `/memory-config`.
**Implementation status (2026-06-09):** The global Claude Code plugin is live (`~/.claude/plugins/os-vault/`). Steps 1, 2a, 2b, 3, and 6 of the build plan are complete (including the `onboard-project` skill, previously TODO under Step 6/Part D). Step 1 completed 2026-06-09: vault-conventions.md exists, all 6 fixture notes seeded, and 14 Graphify handbook + memory-system design notes migrated to the vault as migration scaffolding. Step 4 (memsearch) completed 2026-06-09: plugin installed via marketplace, MEMSEARCH_DIR set global (~/.memsearch), Stop hook verified producing daily memory files, search confirmed working. Step 5a (memsearch episodic git sync) completed 2026-06-09: dedicated private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`), whitelist `.gitignore` (memory/*.md only), auto-commit+push via cc-os `session-end.sh` hook (ADR-015; relocated to `memsearch_sync.py` by ADR-016, 2026-06-12). Step 5b (Obsidian vault → VPS sync) remains. See `docs/memory-system/04-build-plan.md` for full step status. **Eval discipline (applies to every harness above):** scenario Task blocks are held-out —
never run them informally; reserve sets are never even read informally. Before designing or
**Decision (2026-06-09):** Single global memsearch store for ALL clients — cross-client commingling is the accepted, intended design (ADR-015 resolved). One private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`), not per-client repos. Forward direction: minimize/eliminate dedicated per-client working directories; work projects locally or use a single general `clients/` dir for non-local client work — memory is captured globally regardless of cwd. No `clients/` directory structure designed yet; open item. running any autoresearch eval, Read
`~/Documents/SecondBrain/howto/running-autoresearch-skill-evals.md`. Wording loops for
**Implementation status (2026-06-12):** os-vault plugin source moved into git at `cc-os/plugins/os-vault/` and bash hooks ported to Python (deep-module architecture: shared `config.py`, `hook_io.py`, `session_state.py`; thin entry-point scripts). Cutover via symlink `~/.claude/plugins/os-vault → cc-os/plugins/os-vault/` and settings.json hook rewrite. memsearch sync split into dedicated `memsearch_sync.py` SessionEnd hook (relocation of ADR-015 behavior, not reversal). Fresh-session test passed 2026-06-12. See ADR-016. os-adr Eval B, os-vault WS2, and os-orchestration WS4 are complete and shipped
(2026-07-04/07/08); their run-sets AND reserves are contaminated for future wording tuning —
**Implementation status (2026-06-15):** Step 5b done and automated — vault (`~/Documents/SecondBrain`) initialized as git repo, pushed to private Forgejo (`ssh://git@forgejo.swansoncloud.com:2222/jared/SecondBrain.git`); 52 files, git chosen over Syncthing. Auto-commit+push wired via `vault_sync.py` SessionEnd hook (third SessionEnd hook, runs after `session_end.py` so the daily journal note is included; push-only — SessionStart pull is an optional future item for multi-machine). Step 2e pilot done — llf-schema (`/home/jared/dev/llf-schema`, PHP 8.2 WordPress plugin) onboarded via `/os-vault:onboard-project` skill: 605 nodes / 930 edges / 52 communities; Ollama doc pass lossy on WordPress docs but AST pass solid. Step 2d closed — live vault `graphify-out/` is the baseline; fixture-only build superseded. next signal is production IRL session audits.
**Implementation status (2026-06-17):** `/os-vault:onboard-project` skill now uses assessment-first onboarding: surveys the repo structure, classifies exclude candidates by TYPE (11 categories: fetched deps, build output, caches, VCS internals, editor/AI-tooling dirs, lockfiles, coverage/logs, bulk data, binaries, secrets, graphify-out/ — illustrative names, not fixed templates), generates a per-project `.graphifyignore`, confirms with the user, then extracts the graph using `qwen25-coder-7b-16k` (config.yaml `ollama_model`; 16k-context build of qwen2.5-coder:7b — larger context window cuts chunk count per doc, the main speed lever). Fixes issue where repos with large dependency trees routed non-code files through the Ollama doc pass (see ADR-017).
**Implementation status (2026-06-30):** Phase 1 of the SecondBrain Content Plan complete (`docs/memory-system/09-sb-content-plan.md`, issues #1#6). `~/Documents/SecondBrain/vault-conventions.md` reconciled to a single authoritative typed frontmatter schema (removed the contradictory block; canonical filenames are slug-only, `source:` is a frontmatter field not a tag, `scope` is a field not a tag). New `/os-vault:design-template` skill added (`cc-os/plugins/os-vault/skills/design-template/SKILL.md`) — routes between template-design (4-step process + injection-economics filter) and new-type creation (9-step lifecycle). Three vault note templates created in `~/Documents/SecondBrain/_templates/` (`howto.md`, `convention.md`, `reference.md`; `reference` carries a four-subtype variant selector — pattern/framework, api-integration, role-definitions, design-rules), each dogfooded against a real note. Four proof-of-concept vault notes patched (cookbook subtype → pattern/framework, glossary de-duped to a wikilink, design-mode scope → project, tags + `last_reviewed` added). Phase 2 (issue #7) remains open as the steady-state migration/onboarding epic (no code). See `docs/memory-system/09-sb-content-plan.md`.
## Implemented Components
**Global os-vault plugin** — `cc-os/plugins/os-vault/` (git-tracked, 2026-06-12); symlinked into `~/.claude/plugins/os-vault`
- Hooks: `hooks/``session_start.py`, `session_context.py` (project graph path only), `post_tool_use_write.py`, `session_end.py` (vault journal), `memsearch_sync.py` (second SessionEnd hook; memsearch auto-commit+push, 30s timeout), `vault_sync.py` (third SessionEnd hook; vault auto-commit+push to forgejo.swansoncloud.com/jared/SecondBrain, 30s timeout; mirrors memsearch_sync.py)
- Shared modules: `config.py` (load_config → frozen Config dataclass), `hook_io.py` (read_input → HookInput dataclass), `session_state.py` (record_touch/read_touches; encapsulates `/tmp/claude-vault-touched-$SESSION_ID` contract)
- Skills: `skills/` — query, write, reorganize, onboard-project (assessment-first onboarding: surveys repo, classifies excludes by type (11 categories), writes per-project `.graphifyignore`, confirms with user, then extracts using `qwen25-coder-7b-16k` — per ADR-017), design-template (routes template-design ↔ new-type-creation: 4-step template-design process + injection-economics filter, 9-step new-type lifecycle; backs Phase 1 of the SB Content Plan — see `docs/memory-system/09-sb-content-plan.md`)
- Config: `config.yaml` — vault path, Ollama model (qwen25-coder-7b-16k), env vars
- Hook wiring: `~/.claude/settings.json` (hook entries invoke `/usr/bin/python3` with absolute paths into cc-os)
- Isolation seam (2026-07-06, WS2): `OS_VAULT_PATH` env var overrides `vault_path` for the write skill and every hook via `config.load_config()`; `OS_VAULT_SKIP_REBUILD` suppresses the SessionStart graphify rebuild. Added for the eval harness; zero production impact when unset.
- Write-behavior eval (2026-07-06, WS2): `plugins/os-vault/eval/` — held-out unprompted vault-write discrimination eval (Eval B/C playbook: ambiguity ladder L1 explicit → L3 conceptual, paired positives/negatives, 6 run-set scenarios on a new `reportgen` Ruby fixture + 6 frozen reserve twins in a different knowledge domain, isolated sandbox vault, headless-only runner, deterministic-first Ruby checker with narrow frozen offer-detection judge fallback stubbable via `OS_VAULT_EVAL_JUDGE_CMD`, model-free `bin/self-test` 21/21). **Scenario Task blocks are held-out — never run informally; the reserve set is never even read informally.** Procedure: `plugins/os-vault/eval/README.md`.
- Untuned baseline grid (2026-07-06, run-set × sonnet/haiku × 3 reps + 1 counted canary, 37 reps, zero harness errors): **positives 1/19, negatives 18/18**. Headline: at L1 (explicit cue) both tiers persist the knowledge every rep but route it to Claude Code's built-in auto-memory (`~/.claude/projects/<slug>/memory/`) instead of the vault; L2/L3 mostly don't persist at all; zero over-triggering anywhere. The WS2 wording loop (step 4, NOT started; run-set still uncontaminated) therefore has two targets: trigger at L2/L3, and route to the vault over auto-memory at L1. TSV: `eval/results/2026-07-06-baseline-grid.tsv`; full analysis: vault note [[os-vault-write-eval-baseline-grid-results]].
- WS2 wording loop COMPLETE (2026-07-07, 4 iterations + confirm + held-out reserve; run-set AND reserve now contaminated): shipped destination-ladder write SKILL.md + SessionStart USAGE_NOTE (mechanical triggers + before-final-reply checkpoint), mechanical vault-root resolution (`${OS_VAULT_PATH:-$HOME/Documents/SecondBrain}` — fixed real-vault breaches found on iteration 1), fixture CLAUDE.md "Cross-project memory" section (**the real-project adoption template**, required to reach haiku at L2/L3; includes ADR-vs-vault disambiguation), candidates 24 unscored, post-reserve YAML-colon quoting rule. Confirm grid sonnet 18/18 / haiku 16/18; reserve (held-out) positives 8/18 strict / trigger-axis 13/18, negatives 17/18 (vs baseline 1/19, 18/18). Residual gaps: YAML-hostile titles, haiku L2 no-trigger, generalization-laundering over-trigger. One checker false-positive fixed+rescored mid-loop (breach regex vs `2>/dev/null`); negative-criterion redesign pre-registered. TSVs: `eval/results/2026-07-07-wording-*.tsv`; loop log: `autoresearch/classic-260707-1253/loop-log.md`; analysis: vault note [[os-vault-write-eval-wording-loop-results]]. Next signal is production IRL audits.
**Global os-orchestration plugin** — `cc-os/plugins/os-orchestration/` (git-tracked, 2026-07-03); symlinked into `~/.claude/plugins/os-orchestration`
- Hooks: `hooks/``inject.py` (injects `ORCHESTRATION.md` as additionalContext to all sessions)
- Behavior: SessionStart hook injects an `ORCHESTRATION.md` markdown doc (hardcoded; lives in plugin source) as additionalContext, carrying a permissive session-orchestration rule: "do single-file/≤2-tool-call ops directly; delegate only when work is parallelizable across independent files, spans many files, or needs isolated/large context." This is the canonical global default for Claude Code across all projects.
- Migration: migrated from a standalone repo (`~/dev/cc-plugins/orchestration/`, 2026-07-03) and integrated into cc-os. Supersedes the per-project copy-pasted orchestration text blocks that previously existed in individual project CLAUDE.md files (including a stricter local override that cc-os had carried — now removed; see ADR-019).
- Session audit (WS1, 2026-07-06): 10 stratified real sessions audited via `audit/bin/extract` (Ruby fact-sheet extractor, dual-use as future eval checker) + parallel sonnet auditors + verified synthesis. Findings + E1E4 eval-scenario backlog: `docs/orchestration-audit/2026-07-06-findings.md`. Headlines: model param was silently overridden to haiku on all 23 verified spawns — root cause found post-audit: `CLAUDE_CODE_SUBAGENT_MODEL=haiku` in `~/.claude/settings.json` env block (set by an earlier session as a cost measure; removed 2026-07-06; the Fable-5 correlation was coincidental timing). Omitted model param inherits the main-loop model (opus/sonnet) — misses cluster in pre-rollout and ops sessions; no over-sharing/full-dump failures anywhere.
- Wording fixes shipped (2026-07-06, post-audit): ORCHESTRATION.md rewritten with trigger-conditioned when→then phrasing for the three verified clusters — explicit `model:` before every Agent call (cluster 2; a real cost exposure once the env override was removed), subagent model self-report on sonnet/opus spawns (cluster 1 — the E1 canary proved `resolvedModel` is NOT model-visible in the launch result; self-report is the only observable channel, and subagents do know their exact model ID), don't re-cover your own ground before delegating (cluster 3). Delegation thresholds unchanged. Cache refreshed via `bin/refresh-plugins`. Deterministic env-override detection (warn on `CLAUDE_CODE_SUBAGENT_MODEL`) is delegated to WS3's status-check design.
- Eval harness E1E3 (2026-07-06): `plugins/os-orchestration/eval/` — scripted headless orchestration-behavior eval scored by the audit extractor (extended with per-segment read targets). 6 run-set scenarios (E1P/N downgrade detection with `CLAUDE_CODE_SUBAGENT_MODEL=haiku` runner stub; E2P/N explicit-model fan-out vs no-delegation-on-trivial; E3P/N delegate-investigation vs orienting-reads-correct) + 6 frozen reserve twins; new Node.js "relaystation" fixture with deterministic sandbox-time log generation; deterministic-first checker (`eval/bin/check`, narrow frozen haiku-judge fallback for E1 language axes only, stubbable via `ORCH_EVAL_JUDGE_CMD`); model-free `bin/self-test` 21/21 green incl. shipped-instruction-compliant conformance transcripts. FAIL axes map only to verified audit misses; tier choice informational. **Scenario Task blocks are held-out — never run informally.** Procedure: `plugins/os-orchestration/eval/README.md`.
- Baseline grid run (2026-07-06, run-set × sonnet/haiku × 3 reps + 2 counted canaries, all counted, zero harness errors): negatives 18/18 PASS both tiers (zero over-delegation, zero false downgrade claims); positives 1/18 on the delegate-at-all axis — but transcript verification shows most positive FAILs are *superior direct strategies* (E2P sonnet: one scripted bulk edit; E3P sonnet: surgical greps over 400KB logs), which the WS1 audit itself scored as justified non-delegation, vs genuine miss shapes (E3P haiku: whole 112KB logs into own context; E2P haiku: 20-call per-file grind). Conditional rules validated where engaged: 14/14 spawns explicit-model; the one delegating E1P rep flagged the stubbed downgrade via self-report. Criterion redesign for E2P/E3P A-axes is pre-registered (anchor thresholds independently, NOT to this grid) before any rescore/tuning; reserve stays frozen. ORCHESTRATION.md gained a scripted-bulk-edit carve-out (a uniform multi-file change one script covers is direct work). TSV: `eval/results/2026-07-06-baseline-grid.tsv`; full analysis: vault note [[os-orchestration-eval-baseline-grid-results]].
- WS4 economics loop COMPLETE + SHIPPED (2026-07-08; run-set AND reserve now contaminated for future wording tuning): ORCHESTRATION.md v3 shipped — cost-asymmetry + tier-conditional threshold opener, symmetric delegate-when/work-directly-when trigger lists (replaces "delegate only when"), batching rule (~58 items per grouped spawn, SendMessage reuse), async rule, Workflow effort dial; explicit-model/self-report/don't-re-cover-ground rules unchanged. Harness extended first (commit b226601): real token econ axes — subagent transcripts persist at `<projects>/<flat-cwd>/<session-id>/subagents/agent-*.jsonl` with per-message usage blocks (solves the mini-audit's instrumentation gap; vault note [[claude-code-subagent-transcripts-and-token-accounting]]) — E5 batching pair + reserve twins (authored by subagent, held-out content never entered the tuning session), fable column, pre-registered E2P/E3P criterion redesign landed (scripted-direct PASS branch; 74KB ingestion anchor + id-keyed root-cause concepts). Results: E5P 0/6 → majority-PASS both tiers incl. reserve twin 5/5 (fable median mltok 43% of baseline, H3 met); negatives clean everywhere (36/36 across confirm+reserve). Residual gaps: E1P sonnet delegate-at-all (fixture too small — needs bigger fixture before meaningful), fable downgrade-flag ~1/4 judge flicker. 15 baseline reps were session-limit-truncated and mechanically excluded+replaced (limit-banner sweep now a standard step, promoted to the autoresearch howto). TSVs: `eval/results/2026-07-08-*.tsv`; loop log: `autoresearch/classic-260708-1039/`; vault note [[os-orchestration-ws4-econ-wording-results]]. IRL re-audit of ~5 real Fable sessions scheduled ~2026-07-22.
**Global os-status plugin** — `cc-os/plugins/os-status/` (git-tracked, 2026-07-06); symlinked into `~/.claude/plugins/os-status`
- Purpose: aggregated deterministic SessionStart status checks for the cc-os plugin family — per-project artifacts (ADR system, vault hub note) and environment hazards (subagent model env override). Built via OpenSpec change `add-os-status-plugin` (supersedes the WS3 plan doc); decision record ADR-022.
- Architecture: checks are **in-process** plain Python functions in `hooks/checks.py` with the uniform signature `check(ctx) -> CheckResult(status, message)` plus one registry entry (no per-plugin subprocess protocol, no per-project code copies — both rejected in ADR-022 after perspective review). Three states: `ok` (silent), `note` (near-zero-token additionalContext line, never snoozed/suppressed), `warn` (aggregated into at most one banner; once-per-day snooze + permanent `suppress-<check>` markers in gitignored per-project `.cc-os/` — state only, never code; `.cc-os/config` holds optional `hub`/`vault_path` key=value overrides).
- Initial checks: `subagent-model-env-override` (env + `~/.claude/settings.json` `env` block; runs outside git projects too — catches the WS1 Cluster 1 incident deterministically at session start), `adr-system-present` (verbatim port of os-adr's hook behavior incl. `.os-adr/suppress`), `vault-hub-note-present` (config slug first, else `type/hub` + `project/<name>` facet-tag scan of the vault; missing → warn naming `/os-vault:write`).
- Tests: `python3 tests/hook_test.py` (36, model-free) incl. byte-identity of PRESENT_NOTE/ABSENT_NOTE against os-adr's source. Invariants in `invariants.md`. Smoke-tested 2026-07-06 (cache==source, real headless session fired the hook, env-override canary warns).
**Global os-doc-hygiene plugin** — `cc-os/plugins/os-doc-hygiene/` (git-tracked, 2026-07-03); symlinked into `~/.claude/plugins/os-doc-hygiene`
- Hooks: `hooks/hooks.json` → SessionStart hook (matcher: startup|resume) runs `scripts/reminder.py` via `${CLAUDE_PLUGIN_ROOT}` (5s timeout), emitting a deterministic zero-token reminder banner
- Behavior: Monitors stale and bloated project documentation per-project under `.dochygiene/` state dir (gitignored). SessionStart reminder is deterministic (no AI tokens, once/day snooze). Skills (verb-first, no `commands/` dispatcher — invoked directly as `/os-doc-hygiene:<skill>`, per [[cc-os-plugin-skill-naming-convention]]): `check` (AI-assisted classification of staleness signals, emits machine+human reports), `clean` (AI-assisted or deterministic patch application with git-safe scoped cleanup), `status` (read-only lifecycle-timestamp read), `sweep` (check then clean in sequence). Reversion-protected via invariants.md + golden-example test fixtures.
- Migration: migrated from standalone repo (`~/dev/cc-plugins/doc-hygiene/`, 2026-07-03) and integrated into cc-os. No content conflicts detected (doc-hygiene scope does not overlap cc-os memory system). Renamed from `doc-hygiene` to `os-doc-hygiene` per cc-os plugin naming convention. Skills renamed `hygiene-check`/`hygiene-clean` → `check`/`clean`, and the `commands/hygiene.md` dispatcher removed in favor of two new skills (`status`, `sweep`), aligning with the `os-vault`/`os-orchestration` pattern of no `commands/` directory (2026-07-03).
**Global os-adr plugin** — `cc-os/plugins/os-adr/` (git-tracked, 2026-07-03); symlinked into `~/.claude/plugins/os-adr`
- Purpose: Architecture Decision Records, consistently in every project — one customized-Nygard template (frontmatter `id`/`date`/`status`/`supersedes`/`superseded-by`/`affected-paths`/`affected-components`; Context/Decision/Consequences/Alternatives-rejected sections), `docs/adr/NNNN-kebab-title.md` one-file-per-decision + fully-regenerated `docs/adr/README.md` index. Built from `docs/adr-system/` (requirements `04`, PRD `05`, eval sketches `06`) via OpenSpec change `add-os-adr-plugin`.
- Core: Ruby OO library `lib/adr/` (`Record`, `Repository`, `Index`, `Template`, `Detector`, `Migrator`, `MigrationReport`, `Finder`; Sandi Metz style, injected paths) behind thin CLIs in `bin/` (`adr-new`, `adr-init`, `adr-detect`, `adr-migrate`, `adr-find`). Tests: `ruby tests/all.rb` (47) + `python3 tests/hook_test.py` (15), all model-free. Behavioral invariants in `invariants.md`.
- Hook: **moved to os-status (2026-07-06)** — the SessionStart existence check now runs as the `adr-system-present` check in the os-status plugin (`hooks.json` here is empty; `hooks/session_start.py` stays as the wording source of record, byte-copied into os-status). Behavior unchanged: present → near-zero-token usage note naming `/os-adr:create` + `/os-adr:find`; absent → once-per-day init/migrate suggestion, silenced permanently by `.os-adr/suppress` (still honored); silent outside git projects.
- Skills (verb-first, no `commands/`): `create` (one-invocation templated+numbered+indexed ADR with mechanical supersession), `init`, `migrate` (non-destructive: detect surveyed shapes → mechanical heuristic fill → LLM fills only manifest-listed interpretive fields via `--apply-fills``migration_confidence` frontmatter + `docs/adr/migration-report.md`; old-system deletion only as an explicit separate user-approved step), `find` (deterministic-first retrieval: affected-paths/components match → Accepted-only filter → one-hop Graphify query-path expansion with graceful degradation → AI judgment over the candidate set only).
- Migration pilot passed 2026-07-03 (sandboxed project copies; gate threshold 25% low-confidence flags): viking-warrior-training-log 0%, delta-refinery 0%, llf-schema 8.3%. Retrieval verified end-to-end against the real llf-schema project graph. Resolved: ADRs need no Graphify tag/edge convention — retrieval layer 3 expands the *query* paths via graph-node `source_file`s.
- Eval A harness (2026-07-03, ADR-021): `plugins/os-adr/eval/` — prompted skill-execution eval across model tiers (haiku/sonnet). Two fixtures (generated with the plugin's own CLIs), six scenarios (S1 create, S2 create+supersede, S3 find/conflict, S4 find/distractor, S5 init, S6 migrate+fills), deterministic Ruby checker (`eval/bin/check`, structural invariants, TSV mode for autoresearch), sandbox + headless-runner scripts, runner-prompt template. Primary run mode: in-session Agent-tool subagents with pinned `model:` (cheaper than `claude -p`); optimized via the `/autoresearch` Classic loop over SKILL.md *wording only* (checker/fixtures/scenarios frozen during a loop). Procedure: `plugins/os-adr/eval/README.md`. Self-tested both directions; grid run 2026-07-03, all scenarios × both tiers passing. Distinct from the held-out Eval B (unprompted behavior) — do not conflate.
- Eval B harness (2026-07-03, OpenSpec change `add-os-adr-eval-b-harness`): `plugins/os-adr/eval-b/` — held-out unprompted-behavior eval (requirements 45). 7 scenarios (W1W3 write-trigger, R1R4 retrieval) authored from the frozen shapes in `docs/adr-system/06-eval-scenarios.md`; dedicated Ruby webhook-relay fixture with a 6-ADR history (Superseded pair + near-miss distractors, generated via the plugin's own CLIs); R4's one-hop graph reach uses a real `graphify update` AST build (model-free, rebuilt via `eval-b/bin/build-fixture-graph`, never committed). **Headless-only runner** (`eval-b/bin/run` — fresh `claude -p` per rep, cwd = sandbox, so the real SessionStart hook fires; in-session subagents are invalid here, unlike Eval A) and a two-axis deterministic-first checker (`eval-b/bin/check`): axis (a) unprompted consultation, mechanical from transcript tool_use blocks; axis (b) correct-ADR citation (R1R4) or new-ADR-file with a narrow frozen-rubric haiku judge fallback (W1W3, `judge-rubric.md`, stubbable via `ADR_EVAL_B_JUDGE_CMD`). `R4-nograph` is the graph-degradation variant (expected FAIL). Self-tested both directions model-free via `eval-b/bin/self-test`. **Scenario Task blocks are held-out — never run them informally.** Procedure: `plugins/os-adr/eval-b/README.md`. Grid run 2026-07-03 (1 rep/cell): haiku 0/8 PASS (never unprompted-consults the ADR system in any scenario — a real gap, not a harness defect); sonnet 5/8 PASS (fails W3 — doesn't propose recording the decision; fails R1 — misses the direct-conflict retrieval). `R4-nograph` FAILed on both tiers as expected (degradation check, only meaningful paired with an R4 PASS — sonnet has one, haiku doesn't). Full results + observations (prompting-issue hypothesis, open question on whether in-session subagents could ever validly substitute for part of this measurement) written to the vault: [[os-adr-eval-b-grid-results-and-observations]].
- **Initial iteration complete (2026-07-03):** plugin build, migration pilot, Eval A grid (clean pass), and Eval B grid (baseline captured above) are all done — this closes the first pass on os-adr. Follow-on work is deliberately deferred to future sessions, not in-flight.
- **Wording experiment complete (2026-07-04):** the follow-up `/autoresearch` loop over Eval B wording ran (5 iterations, checker/fixtures/scenarios/rubric frozen; fixture CLAUDE.md declared a wording surface upfront) and closed the gap — final full grid **sonnet 8/8, haiku 7/8** (from 5/8 / 0/8 baseline; haiku's one miss is a W3 axis-b judge-boundary flicker). Winning wording shipped in `hooks/session_start.py` (PRESENT_NOTE), find/create SKILL.mds, and `eval-b/fixture/project/CLAUDE.md` (new — the trigger-phrased "Architecture decisions" section, the template for real-project adoption). Confirmed mechanisms: trigger-conditioned when→then phrasing beats inventory phrasing on both tiers; each rule must live where its precondition is visible (the reversal→supersede rule in the find skill's act-on-findings step fixed W3); lower tiers need *mechanical* triggers ("before your first edit to any existing file → run `/os-adr:find` on those paths; additions count") — semantic triggers ("architecture-level choice") only reach sonnet. Open: channel ablation never run (hook vs CLAUDE.md redundancy unknown); R4-nograph now passes both tiers, so the graph-degradation check no longer differentiates. Full hypothesis→result mapping: vault note [[os-adr-eval-b-wording-experiment-hypotheses]]. **Before designing or running any autoresearch eval, Read `~/Documents/SecondBrain/howto/running-autoresearch-skill-evals.md`.**
- **Eval B W3 stability check (2026-07-06):** 7 headless reps of haiku × W3 (all reps counted, including two the runner initially excluded, to avoid peeking bias): axis (a) consultation 7/7 PASS; axis (b) recording-offer ~5/7 (~7585%) — one FAIL plausibly infra, one a genuine behavioral miss (consulted, then asked a clarifying question instead of unconditionally proposing the superseding ADR; the conditional-phrasing failure mode iteration 3 fixed on sonnet, improved but not eliminated on haiku). Verdict: intermittent flicker, not a hard gap — grid claim stays **haiku 7/8**, W3 characterized as an ~1-in-4/5 axis-b miss. Haiku cleared for daily use with that caveat. Recorded in the vault good-enough gate: [[os-adr-eval-b-wording-experiment-hypotheses]].
- **Eval C harness built, NOT yet run (2026-07-06):** `plugins/os-adr/eval-c/` — ambiguity-ladder DISCRIMINATION eval (held-out; Eval B is contaminated by the wording loop). 3 levels (explicit → moderate → conceptual framing) × paired positive/negative scenarios; run-set (6, job-execution domain) + frozen reserve-set (6, notifications domain — becomes the measurement set if anyone ever tunes wording against the run-set). New Python fixture (`taskq` async job queue, 6-ADR history generated via the plugin's own CLIs, trigger-phrased CLAUDE.md section copied from eval-b). Scoring: positives = both axes; negatives FAIL **only on unneeded ADR creation** — consultation and truthful ADR citation are informational (`A:yes/no`, `cited-adr:yes/no`), never FAILs, because the shipped wording endorses cheap finds and the negatives sit deliberately in ADR-covered domains (two earlier negative designs that punished instruction-compliant behavior were caught in review and redesigned/fixed). Model-free `bin/self-test` green, including a truthful-citation-must-PASS guard. **Scenario Task blocks are held-out — never run informally.** Methodology: vault notes [[eval-methodology-ladder]] (per-level pass bars, reserve discipline) and [[eval-methodology-irl-feedback-loop]] (post-rollout session-audit backlog); vault also gained an `eval-results` note type + `_templates/eval-results.md` (2026-07-06).
- **Eval C frozen grid run (2026-07-06):** run-set × {sonnet, haiku} × 3 reps/cell (36 headless reps, all counted) — **12/12 cells PASS at ≥2/3 majority; haiku 18/18 reps, sonnet 17/18**. Zero over-trigger ADR creation on any negative at any level; every positive pass was via governing-ADR citation (16) or judge-recognized proposal (1), never duplicate creation. Sonnet's one FAIL rep (P1-L1) is a final-message-citation boundary artifact (implemented the ADR-0002 policy, cited only 0005), not a behavioral miss. Two harness defects fixed on the first live rep, BEFORE the grid, with Task blocks/fixtures/rubric untouched: (1) `bin/run` pipefail bug aborted a cell's reps after the first FAIL; (2) positive axis-b required creation/proposal, contradicting the README's own "consulting and/or recording is correct" — the run-set positives sit in *already-decided* territory, so find→cite→comply is correct; fixed via a mechanical `cited-governing` PASS branch + two new self-test guards (`B-via:` now recorded in TSV). Verdict: the Eval B wording **generalizes** (new fixture/language/domain, decreasing cue explicitness, paired negatives) — grid saturated, ceiling not found; next signal is production (rollout + IRL session audits), not another lab rung. Run-set is now the baseline; wording tuning against it moves measurement to the reserve-set. Full results: vault note [[os-adr-eval-c-frozen-grid-results]].
- **Remaining (locked rollout order):** real-project migration/adoption one at a time via `/os-adr:migrate` — pilot projects first, then the cc-os retrofit (its 19-ADR monolithic file was deliberately excluded from the pilot), then wider. When onboarding real projects, add the trigger-phrased "Architecture decisions" CLAUDE.md section (copy from `eval-b/fixture/project/CLAUDE.md`; candidate: emit it from `/os-adr:init`/`migrate`).
- **Resolved (2026-07-04):** os-adr's skills failed to register on first install due to stale plugin caches — the cache that was installed on 2026-07-03 17:21:48 was missing `hooks/hooks.json`, `bin/adr-detect`, `bin/adr-find`, `bin/adr-migrate`. Root cause: unknown, but the caches were restored via plugin uninstall/reinstall. A parallel issue affected os-doc-hygiene (cache retained deleted `commands/` directory). Fixed by manually refreshing both caches, then created `bin/refresh-plugins` automation to prevent future drift — see "Editing a local plugin (cache refresh)" subsection for refresh procedure. Investigation result: stale caches (not manifest-naming issues) were the cause; slash command registration works correctly once caches are fresh.
**Graphify** — v0.8.31 at `/home/jared/.local/bin/graphify`
- Vault graph: `~/Documents/SecondBrain/graphify-out/` — disposable, structural index rebuilt by SessionStart hook; handles relational/graph queries. Distinct from vault-index.json (planned, Plan B Phase 1): a flat `{tag: [{path, title, summary}]}` lookup rebuilt at SessionEnd, queried by the `/memory-find` skill for fast tag-based SB note discovery.
- Project graph: `<project-root>/graphify-out/` — same pattern; gitignore it in each project repo
- Vault conventions: `~/Documents/SecondBrain/vault-conventions.md` — frontmatter contract + tag taxonomy (canonical name decided 2026-06-09; formerly referred to as CONVENTIONS.md)
**memsearch** — v0.4.6 via Claude Code plugin marketplace (`memsearch@memsearch-plugins`)
- Hooks: Stop, SessionStart, UserPromptSubmit, SessionEnd (ship with plugin)
- Memory store: `~/.memsearch/memory/YYYY-MM-DD.md` daily files (global, cross-project, all clients — one store by design)
- Index: `~/.memsearch/milvus.db` (Milvus Lite, local); embeddings via ONNX bge-m3
- Config: `MEMSEARCH_DIR=~/.memsearch` in `~/.zshrc` for global scope
- Skills: `/memory-recall`, `/memory-config` (ship with plugin)
- Git sync: `~/.memsearch` is a dedicated private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`); whitelist `.gitignore` commits only `memory/*.md` (excludes rebuildable `milvus.db`, model, config); auto-commit+push wired into the cc-os memory plugin's own `memsearch_sync.py` SessionEnd hook (relocated from `session-end.sh` by ADR-016, 2026-06-12; behavior preserved — see ADR-015)
**Obsidian vault git sync** — `~/Documents/SecondBrain` (2026-06-15)
- Remote: `ssh://git@forgejo.swansoncloud.com:2222/jared/SecondBrain.git` (private Forgejo; web: `https://forgejo.swansoncloud.com/jared/SecondBrain`)
- 52 files tracked (notes, journal, templates, vault-conventions.md, CLAUDE.md, .obsidian config); `graphify-out/` excluded via `.gitignore`
- Auto-commit+push via `vault_sync.py` SessionEnd hook in cc-os memory plugin (mirrors `memsearch_sync.py`; push-only — SessionStart pull is optional future item for multi-machine)
**Remaining optional items:** SessionStart vault pull (multi-machine sync; push-only is the current design); additional project onboarding (one at a time, per ADR-013); bulk vault migration. All required build steps complete as of 2026-06-15.
**Plugin renamed `memory``os-vault` (2026-07-03):** directory renamed in git, `~/.claude/plugins/os-vault` symlink and `settings.json`'s `enabledPlugins` updated — but the `local-plugins` marketplace manifest (`~/.claude/plugins/.claude-plugin/marketplace.json`) still declared the old `memory`/`./memory` entry, and `installed_plugins.json` still had a cached `memory@local-plugins` install record. Skills (and therefore slash commands) never registered under the new name as a result; hooks kept working because they're wired by absolute path in `settings.json`, independent of plugin resolution. Fixed by editing the marketplace manifest to `"os-vault"`/`"./os-vault"`, then `claude plugin marketplace update local-plugins`, `claude plugin install os-vault@local-plugins`, `claude plugin uninstall memory@local-plugins`. See ADR-018 and the "Renaming or moving a local plugin" procedure below.
### Renaming or moving a local plugin
Renaming a plugin directory under a directory-source marketplace (like `local-plugins`, whose
source is `~/.claude/plugins` itself) requires updating **three** places, not just the directory
and `settings.json`:
1. **Plugin directory / symlink** — the actual files (or symlink target, for plugins that live in
a git repo like `cc-os/plugins/os-vault/`).
2. **Marketplace manifest**`~/.claude/plugins/.claude-plugin/marketplace.json` (for
`local-plugins`) lists each plugin's `name` and `source` path explicitly; this does **not**
auto-discover from the directory listing. Update the entry to match the new name/path.
3. **`settings.json` `enabledPlugins`** — the `<name>@<marketplace>` key must match the manifest
entry from step 2.
Then refresh the plugin manager's state so it re-resolves the marketplace and drops the stale
install record:
```bash
claude plugin marketplace update <marketplace-name> # re-validates the manifest
claude plugin install <new-name>@<marketplace-name> # creates the new install record
claude plugin uninstall <old-name>@<marketplace-name> # drops the stale one
claude plugin list # verify: new name enabled, old name gone
claude plugin details <new-name>@<marketplace-name> # verify skills/agents/hooks resolved
```
Skipping step 2 is the failure mode to watch for: hooks (wired by absolute path in
`settings.json`) keep working, masking the fact that skills/slash-commands silently stopped
registering under the plugin's new name.
### Editing a local plugin (cache refresh)
Plugin source edits (including SKILL.md wording, hook scripts, or CLI code) do not reach running
sessions until the cache is refreshed. Claude Code caches plugins when installed, copying them
to `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`. To refresh a stale cache after
source edits:
```bash
bin/refresh-plugins [marketplace-names...] # refresh all or specific local marketplaces
```
This uninstalls and reinstalls each plugin, forcing the cache to repopulate. See `bin/refresh-plugins --help` for usage. After refreshing, `claude plugin details <name>@<marketplace>` should show updated skill descriptions and hook counts. Directory-source marketplaces (e.g., `local-plugins` sourced from `~/.claude/plugins`, `cc-plugins` sourced from `~/dev/cc-plugins`) are the only ones that need manual refresh — public marketplaces auto-update.
## Issue tracking ## Issue tracking
Issues (created via `/to-issues`) live on self-hosted Forgejo (`jared/cc-os`), queried with the `tea` CLI — not GitHub/`gh`. See `docs/issue-workflow.md` for token setup, listing, implementing, and closing issues. Issues (created via `/to-issues`) live on self-hosted Forgejo (`jared/cc-os`), queried with
the `tea` CLI — not GitHub/`gh`. See `docs/issue-workflow.md`.
## OpenSpec workflow ## OpenSpec workflow
Changes are managed spec-driven via OpenSpec. Use the matching skills rather than editing spec Changes are managed spec-driven via OpenSpec. Use the matching skills rather than editing
files by hand: `openspec-explore` (think through an idea), `openspec-propose` (create a change spec files by hand: `openspec-explore`, `openspec-propose`, `openspec-apply-change`,
with design/specs/tasks), `openspec-apply-change` (implement tasks), `openspec-verify-change` `openspec-verify-change`, `openspec-archive-change` (slash commands under `/opsx:*`). Live
(validate before archiving), `openspec-archive-change`. Slash commands mirror these under changes in `openspec/changes/`, completed in `openspec/changes/archive/`, stable specs in
`/opsx:*`. Live changes live in `openspec/changes/`, completed ones in `openspec/specs/`. `openspec/config.yaml` uses `schema: spec-driven`; project context for
`openspec/changes/archive/`, stable specs in `openspec/specs/`. OpenSpec artifacts comes from `docs/` and this file.
`openspec/config.yaml` uses `schema: spec-driven`; its `context` block is currently empty —
project context for OpenSpec artifacts comes from `docs/` and this file.
## Conventions specific to this repo ## Conventions specific to this repo
- **Provenance discipline:** when writing about Graphify or anything sourced from the - **Provenance discipline:** when writing about Graphify or anything sourced from the
interview transcripts, keep the inline source tags and never promote an `[interview]` / interview transcripts, keep the inline source tags and never promote an `[interview]` /
`[unverified claim]` to fact without checking a primary source (the GitHub repo is the `[unverified claim]` to fact without checking a primary source (the GitHub repo is the
authority; it was anchored to Graphify **v0.8.30**). authority; anchored to Graphify v0.8.30).
- **Dates are absolute** (e.g. `2026-06-03`), and design docs carry a `_Last updated:_` / - **Dates are absolute** (e.g. `2026-06-03`); design docs carry a `_Last updated:_` / status
status line — keep these current when editing. line — keep these current when editing.
- **Decisions live in ADRs.** Don't silently reverse a locked decision; add or amend an ADR in - **Decisions live in ADRs.** Don't silently reverse a locked decision; add or amend an ADR
`03-architecture-decisions.md` with the reasoning. in `docs/memory-system/03-architecture-decisions.md` with the reasoning.
- The package on PyPI is `graphifyy` (double-y) but the command is `graphify`. - **Keep records current:** when a build step completes, (a) mark it done in
- **Keep this file current:** When a build step completes, (a) mark it done in `docs/memory-system/04-build-plan.md`, (b) add or update a component pointer in the Implemented Components section above, and (c) update the current design paragraph if the design changed. This file is the AI's orientation entry point — accuracy matters more than brevity. `docs/memory-system/04-build-plan.md`, (b) record it in
- **Plugin and skill naming:** Before naming ANY new cc-os plugin, skill, or slash command, Read `docs/implementation-status.md` (timeline entry + component pointer), and (c) update the
`~/Documents/SecondBrain/cc-os-plugin-skill-naming-convention.md` and follow it — do NOT invent design paragraph in THIS file only if the design itself changed. Never append status
a new convention. Rules in brief: plugins are `os-[domain]`; skills are verb-first kebab-case history to this file — it goes in implementation-status.md.
(`query`, `onboard-project`), invoked as `/os-[domain]:[verb]`; no `commands/` dispatcher - **Plugin and skill naming:** before naming ANY new cc-os plugin, skill, or slash command,
directories; **never set a `name:` field in SKILL.md frontmatter** — the directory name is the Read `~/Documents/SecondBrain/cc-os-plugin-skill-naming-convention.md` and follow it. In
skill name, and an explicit `name:` collapses the slash command to a bare unnamespaced form brief: plugins are `os-[domain]`; skills are verb-first kebab-case, invoked as
(`/find` instead of `/os-adr:find`; found and fixed 2026-07-04). After editing any SKILL.md, `/os-[domain]:[verb]`; no `commands/` dispatcher directories; **never set a `name:` field
run `bin/refresh-plugins` (installs cache plugin files; source edits don't reach sessions until in SKILL.md frontmatter** (it collapses the slash command to a bare unnamespaced form).
refreshed). Agent and hook naming are open questions per that note, not yet covered by this - **After editing any plugin source** (SKILL.md, hooks, CLIs), run `bin/refresh-plugins`
rule. source edits don't reach sessions until the cache is refreshed. Rename/move procedure and
cache details: docs/implementation-status.md → "Operational procedures".
**Session orchestration behavior** — Provided by the global `os-orchestration` plugin (see Implemented Components below). This repo no longer carries a local orchestration override; it follows the plugin's default behavior like every other project.

View File

@ -0,0 +1,490 @@
# cc-os implementation status & changelog
_Last updated: 2026-07-08. This is the append-only implementation-status record moved out of
`CLAUDE.md` (2026-07-08, task B1 of
[system-prompt-profiles-tasks.md](plans/system-prompt-profiles-tasks.md)). CLAUDE.md keeps
orientation only; when a build step completes, record it HERE (plus
`docs/memory-system/04-build-plan.md`), and touch CLAUDE.md only if the current design or
conventions changed._
## Status timeline
**Recent pivot (2026-06-03):** Graphify **replaces** the originally-planned Ruby/SQLite
tag-index CLI and also covers the deferred QMD semantic layer. `04-build-plan.md` and
`06-graphify-evaluation.md` reflect this; if an older doc still describes the Ruby CLI, defer
to those two and fix the stale doc.
**Decisions locked (2026-06-04):** Six-facet tag taxonomy + `scope/` (ADR-011); reuse
`~/Documents/SecondBrain` vault rather than creating a new one (ADR-012); build-first /
migrate-incrementally — build full system against a fixture set first, defer bulk vault
migration to last, onboard projects one at a time (ADR-013).
**Empirical finding locked (2026-06-05):** Graphify is a structure extractor, not a topic
clusterer — no emergent hub nodes appear even at `--mode deep`; hub notes + wikilinks must be
author-provided during migration (not deferred). Migration scaffolding is now a first-class
deliverable. Open question: do facet tags create graph edges? (ADR-014; findings:
`docs/memory-system/07-graph-connectivity-findings.md`).
**Implementation status (2026-06-09):** The global Claude Code plugin is live
(`~/.claude/plugins/os-vault/`). Steps 1, 2a, 2b, 3, and 6 of the build plan are complete
(including the `onboard-project` skill, previously TODO under Step 6/Part D). Step 1
completed 2026-06-09: vault-conventions.md exists, all 6 fixture notes seeded, and 14
Graphify handbook + memory-system design notes migrated to the vault as migration
scaffolding. Step 4 (memsearch) completed 2026-06-09: plugin installed via marketplace,
MEMSEARCH_DIR set global (~/.memsearch), Stop hook verified producing daily memory files,
search confirmed working. Step 5a (memsearch episodic git sync) completed 2026-06-09:
dedicated private Forgejo repo (`forgejo.swansoncloud.com/jared/memsearch`), whitelist
`.gitignore` (memory/*.md only), auto-commit+push via cc-os `session-end.sh` hook (ADR-015;
relocated to `memsearch_sync.py` by ADR-016, 2026-06-12). Step 5b (Obsidian vault → VPS
sync) remained at that date.
**Decision (2026-06-09):** Single global memsearch store for ALL clients — cross-client
commingling is the accepted, intended design (ADR-015 resolved). One private Forgejo repo
(`forgejo.swansoncloud.com/jared/memsearch`), not per-client repos. Forward direction:
minimize/eliminate dedicated per-client working directories; work projects locally or use a
single general `clients/` dir for non-local client work — memory is captured globally
regardless of cwd. No `clients/` directory structure designed yet; open item.
**Implementation status (2026-06-12):** os-vault plugin source moved into git at
`cc-os/plugins/os-vault/` and bash hooks ported to Python (deep-module architecture: shared
`config.py`, `hook_io.py`, `session_state.py`; thin entry-point scripts). Cutover via symlink
`~/.claude/plugins/os-vault → cc-os/plugins/os-vault/` and settings.json hook rewrite.
memsearch sync split into dedicated `memsearch_sync.py` SessionEnd hook (relocation of
ADR-015 behavior, not reversal). Fresh-session test passed 2026-06-12. See ADR-016.
**Implementation status (2026-06-15):** Step 5b done and automated — vault
(`~/Documents/SecondBrain`) initialized as git repo, pushed to private Forgejo
(`ssh://git@forgejo.swansoncloud.com:2222/jared/SecondBrain.git`); 52 files, git chosen over
Syncthing. Auto-commit+push wired via `vault_sync.py` SessionEnd hook (third SessionEnd
hook, runs after `session_end.py` so the daily journal note is included; push-only —
SessionStart pull is an optional future item for multi-machine). Step 2e pilot done —
llf-schema (`/home/jared/dev/llf-schema`, PHP 8.2 WordPress plugin) onboarded via
`/os-vault:onboard-project` skill: 605 nodes / 930 edges / 52 communities; Ollama doc pass
lossy on WordPress docs but AST pass solid. Step 2d closed — live vault `graphify-out/` is
the baseline; fixture-only build superseded.
**Implementation status (2026-06-17):** `/os-vault:onboard-project` skill now uses
assessment-first onboarding: surveys the repo structure, classifies exclude candidates by
TYPE (11 categories: fetched deps, build output, caches, VCS internals, editor/AI-tooling
dirs, lockfiles, coverage/logs, bulk data, binaries, secrets, graphify-out/ — illustrative
names, not fixed templates), generates a per-project `.graphifyignore`, confirms with the
user, then extracts the graph using `qwen25-coder-7b-16k` (config.yaml `ollama_model`;
16k-context build of qwen2.5-coder:7b — larger context window cuts chunk count per doc, the
main speed lever). Fixes issue where repos with large dependency trees routed non-code files
through the Ollama doc pass (see ADR-017).
**Implementation status (2026-06-30):** Phase 1 of the SecondBrain Content Plan complete
(`docs/memory-system/09-sb-content-plan.md`, issues #1#6).
`~/Documents/SecondBrain/vault-conventions.md` reconciled to a single authoritative typed
frontmatter schema (removed the contradictory block; canonical filenames are slug-only,
`source:` is a frontmatter field not a tag, `scope` is a field not a tag). New
`/os-vault:design-template` skill added
(`cc-os/plugins/os-vault/skills/design-template/SKILL.md`) — routes between template-design
(4-step process + injection-economics filter) and new-type creation (9-step lifecycle).
Three vault note templates created in `~/Documents/SecondBrain/_templates/` (`howto.md`,
`convention.md`, `reference.md`; `reference` carries a four-subtype variant selector —
pattern/framework, api-integration, role-definitions, design-rules), each dogfooded against
a real note. Four proof-of-concept vault notes patched (cookbook subtype →
pattern/framework, glossary de-duped to a wikilink, design-mode scope → project, tags +
`last_reviewed` added). Phase 2 (issue #7) remains open as the steady-state
migration/onboarding epic (no code). See `docs/memory-system/09-sb-content-plan.md`.
**Plugin renamed `memory``os-vault` (2026-07-03):** directory renamed in git,
`~/.claude/plugins/os-vault` symlink and `settings.json`'s `enabledPlugins` updated — but
the `local-plugins` marketplace manifest
(`~/.claude/plugins/.claude-plugin/marketplace.json`) still declared the old
`memory`/`./memory` entry, and `installed_plugins.json` still had a cached
`memory@local-plugins` install record. Skills (and therefore slash commands) never
registered under the new name as a result; hooks kept working because they're wired by
absolute path in `settings.json`, independent of plugin resolution. Fixed by editing the
marketplace manifest to `"os-vault"`/`"./os-vault"`, then
`claude plugin marketplace update local-plugins`,
`claude plugin install os-vault@local-plugins`,
`claude plugin uninstall memory@local-plugins`. See ADR-018 and the "Renaming or moving a
local plugin" procedure below.
**Resolved (2026-07-04):** os-adr's skills failed to register on first install due to stale
plugin caches — the cache that was installed on 2026-07-03 17:21:48 was missing
`hooks/hooks.json`, `bin/adr-detect`, `bin/adr-find`, `bin/adr-migrate`. Root cause:
unknown, but the caches were restored via plugin uninstall/reinstall. A parallel issue
affected os-doc-hygiene (cache retained deleted `commands/` directory). Fixed by manually
refreshing both caches, then created `bin/refresh-plugins` automation to prevent future
drift — see "Editing a local plugin (cache refresh)" below. Investigation result: stale
caches (not manifest-naming issues) were the cause; slash command registration works
correctly once caches are fresh.
**Remaining optional items:** SessionStart vault pull (multi-machine sync; push-only is the
current design); additional project onboarding (one at a time, per ADR-013); bulk vault
migration. All required build steps complete as of 2026-06-15.
## Implemented components
**Global os-vault plugin** — `cc-os/plugins/os-vault/` (git-tracked, 2026-06-12); symlinked
into `~/.claude/plugins/os-vault`
- Hooks: `hooks/``session_start.py`, `session_context.py` (project graph path only),
`post_tool_use_write.py`, `session_end.py` (vault journal), `memsearch_sync.py` (second
SessionEnd hook; memsearch auto-commit+push, 30s timeout), `vault_sync.py` (third
SessionEnd hook; vault auto-commit+push to forgejo.swansoncloud.com/jared/SecondBrain, 30s
timeout; mirrors memsearch_sync.py)
- Shared modules: `config.py` (load_config → frozen Config dataclass), `hook_io.py`
(read_input → HookInput dataclass), `session_state.py` (record_touch/read_touches;
encapsulates `/tmp/claude-vault-touched-$SESSION_ID` contract)
- Skills: `skills/` — query, write, reorganize, onboard-project (assessment-first
onboarding: surveys repo, classifies excludes by type (11 categories), writes per-project
`.graphifyignore`, confirms with user, then extracts using `qwen25-coder-7b-16k` — per
ADR-017), design-template (routes template-design ↔ new-type-creation: 4-step
template-design process + injection-economics filter, 9-step new-type lifecycle; backs
Phase 1 of the SB Content Plan — see `docs/memory-system/09-sb-content-plan.md`)
- Config: `config.yaml` — vault path, Ollama model (qwen25-coder-7b-16k), env vars
- Hook wiring: `~/.claude/settings.json` (hook entries invoke `/usr/bin/python3` with
absolute paths into cc-os)
- Isolation seam (2026-07-06, WS2): `OS_VAULT_PATH` env var overrides `vault_path` for the
write skill and every hook via `config.load_config()`; `OS_VAULT_SKIP_REBUILD` suppresses
the SessionStart graphify rebuild. Added for the eval harness; zero production impact when
unset.
- Write-behavior eval (2026-07-06, WS2): `plugins/os-vault/eval/` — held-out unprompted
vault-write discrimination eval (Eval B/C playbook: ambiguity ladder L1 explicit → L3
conceptual, paired positives/negatives, 6 run-set scenarios on a new `reportgen` Ruby
fixture + 6 frozen reserve twins in a different knowledge domain, isolated sandbox vault,
headless-only runner, deterministic-first Ruby checker with narrow frozen offer-detection
judge fallback stubbable via `OS_VAULT_EVAL_JUDGE_CMD`, model-free `bin/self-test` 21/21).
**Scenario Task blocks are held-out — never run informally; the reserve set is never even
read informally.** Procedure: `plugins/os-vault/eval/README.md`.
- Untuned baseline grid (2026-07-06, run-set × sonnet/haiku × 3 reps + 1 counted canary, 37
reps, zero harness errors): **positives 1/19, negatives 18/18**. Headline: at L1 (explicit
cue) both tiers persist the knowledge every rep but route it to Claude Code's built-in
auto-memory (`~/.claude/projects/<slug>/memory/`) instead of the vault; L2/L3 mostly don't
persist at all; zero over-triggering anywhere. TSV:
`eval/results/2026-07-06-baseline-grid.tsv`; full analysis: vault note
[[os-vault-write-eval-baseline-grid-results]].
- WS2 wording loop COMPLETE (2026-07-07, 4 iterations + confirm + held-out reserve; run-set
AND reserve now contaminated): shipped destination-ladder write SKILL.md + SessionStart
USAGE_NOTE (mechanical triggers + before-final-reply checkpoint), mechanical vault-root
resolution (`${OS_VAULT_PATH:-$HOME/Documents/SecondBrain}` — fixed real-vault breaches
found on iteration 1), fixture CLAUDE.md "Cross-project memory" section (**the
real-project adoption template**, required to reach haiku at L2/L3; includes ADR-vs-vault
disambiguation), candidates 24 unscored, post-reserve YAML-colon quoting rule. Confirm
grid sonnet 18/18 / haiku 16/18; reserve (held-out) positives 8/18 strict / trigger-axis
13/18, negatives 17/18 (vs baseline 1/19, 18/18). Residual gaps: YAML-hostile titles,
haiku L2 no-trigger, generalization-laundering over-trigger. One checker false-positive
fixed+rescored mid-loop (breach regex vs `2>/dev/null`); negative-criterion redesign
pre-registered. TSVs: `eval/results/2026-07-07-wording-*.tsv`; loop log:
`autoresearch/classic-260707-1253/loop-log.md`; analysis: vault note
[[os-vault-write-eval-wording-loop-results]]. Next signal is production IRL audits.
**Global os-orchestration plugin** — `cc-os/plugins/os-orchestration/` (git-tracked,
2026-07-03); symlinked into `~/.claude/plugins/os-orchestration`
- Hooks: `hooks/``inject.py` (injects `ORCHESTRATION.md` as additionalContext to all
sessions)
- Behavior: SessionStart hook injects an `ORCHESTRATION.md` markdown doc (hardcoded; lives
in plugin source) as additionalContext, carrying the canonical global
session-orchestration rule set for Claude Code across all projects.
- Migration: migrated from a standalone repo (`~/dev/cc-plugins/orchestration/`, 2026-07-03)
and integrated into cc-os. Supersedes the per-project copy-pasted orchestration text
blocks that previously existed in individual project CLAUDE.md files (including a stricter
local override that cc-os had carried — now removed; see ADR-019).
- Session audit (WS1, 2026-07-06): 10 stratified real sessions audited via
`audit/bin/extract` (Ruby fact-sheet extractor, dual-use as future eval checker) +
parallel sonnet auditors + verified synthesis. Findings + E1E4 eval-scenario backlog:
`docs/orchestration-audit/2026-07-06-findings.md`. Headlines: model param was silently
overridden to haiku on all 23 verified spawns — root cause found post-audit:
`CLAUDE_CODE_SUBAGENT_MODEL=haiku` in `~/.claude/settings.json` env block (set by an
earlier session as a cost measure; removed 2026-07-06; the Fable-5 correlation was
coincidental timing). Omitted model param inherits the main-loop model (opus/sonnet) —
misses cluster in pre-rollout and ops sessions; no over-sharing/full-dump failures
anywhere.
- Wording fixes shipped (2026-07-06, post-audit): ORCHESTRATION.md rewritten with
trigger-conditioned when→then phrasing for the three verified clusters — explicit
`model:` before every Agent call (cluster 2), subagent model self-report on sonnet/opus
spawns (cluster 1 — the E1 canary proved `resolvedModel` is NOT model-visible in the
launch result; self-report is the only observable channel), don't re-cover your own ground
before delegating (cluster 3). Delegation thresholds unchanged. Cache refreshed via
`bin/refresh-plugins`. Deterministic env-override detection (warn on
`CLAUDE_CODE_SUBAGENT_MODEL`) is delegated to WS3's status-check design.
- Eval harness E1E3 (2026-07-06): `plugins/os-orchestration/eval/` — scripted headless
orchestration-behavior eval scored by the audit extractor (extended with per-segment read
targets). 6 run-set scenarios (E1P/N downgrade detection with
`CLAUDE_CODE_SUBAGENT_MODEL=haiku` runner stub; E2P/N explicit-model fan-out vs
no-delegation-on-trivial; E3P/N delegate-investigation vs orienting-reads-correct) + 6
frozen reserve twins; new Node.js "relaystation" fixture with deterministic sandbox-time
log generation; deterministic-first checker (`eval/bin/check`, narrow frozen haiku-judge
fallback for E1 language axes only, stubbable via `ORCH_EVAL_JUDGE_CMD`); model-free
`bin/self-test` 21/21 green incl. shipped-instruction-compliant conformance transcripts.
FAIL axes map only to verified audit misses; tier choice informational. **Scenario Task
blocks are held-out — never run informally.** Procedure:
`plugins/os-orchestration/eval/README.md`.
- Baseline grid run (2026-07-06, run-set × sonnet/haiku × 3 reps + 2 counted canaries, all
counted, zero harness errors): negatives 18/18 PASS both tiers (zero over-delegation, zero
false downgrade claims); positives 1/18 on the delegate-at-all axis — but transcript
verification shows most positive FAILs are *superior direct strategies* (E2P sonnet: one
scripted bulk edit; E3P sonnet: surgical greps over 400KB logs), which the WS1 audit
itself scored as justified non-delegation, vs genuine miss shapes (E3P haiku: whole 112KB
logs into own context; E2P haiku: 20-call per-file grind). Conditional rules validated
where engaged: 14/14 spawns explicit-model; the one delegating E1P rep flagged the stubbed
downgrade via self-report. Criterion redesign for E2P/E3P A-axes pre-registered (anchor
thresholds independently, NOT to this grid) before any rescore/tuning; reserve stays
frozen. ORCHESTRATION.md gained a scripted-bulk-edit carve-out. TSV:
`eval/results/2026-07-06-baseline-grid.tsv`; full analysis: vault note
[[os-orchestration-eval-baseline-grid-results]].
- WS4 economics loop COMPLETE + SHIPPED (2026-07-08; run-set AND reserve now contaminated
for future wording tuning): ORCHESTRATION.md v3 shipped — cost-asymmetry +
tier-conditional threshold opener, symmetric delegate-when/work-directly-when trigger
lists (replaces "delegate only when"), batching rule (~58 items per grouped spawn,
SendMessage reuse), async rule, Workflow effort dial; explicit-model/self-report/
don't-re-cover-ground rules unchanged. Harness extended first (commit b226601): real token
econ axes — subagent transcripts persist at
`<projects>/<flat-cwd>/<session-id>/subagents/agent-*.jsonl` with per-message usage blocks
(solves the mini-audit's instrumentation gap; vault note
[[claude-code-subagent-transcripts-and-token-accounting]]) — E5 batching pair + reserve
twins (authored by subagent, held-out content never entered the tuning session), fable
column, pre-registered E2P/E3P criterion redesign landed (scripted-direct PASS branch;
74KB ingestion anchor + id-keyed root-cause concepts). Results: E5P 0/6 → majority-PASS
both tiers incl. reserve twin 5/5 (fable median mltok 43% of baseline, H3 met); negatives
clean everywhere (36/36 across confirm+reserve). Residual gaps: E1P sonnet delegate-at-all
(fixture too small — needs bigger fixture before meaningful), fable downgrade-flag ~1/4
judge flicker. 15 baseline reps were session-limit-truncated and mechanically
excluded+replaced (limit-banner sweep now a standard step, promoted to the autoresearch
howto). TSVs: `eval/results/2026-07-08-*.tsv`; loop log:
`autoresearch/classic-260708-1039/`; vault note
[[os-orchestration-ws4-econ-wording-results]]. IRL re-audit of ~5 real Fable sessions
scheduled ~2026-07-22.
**Global os-status plugin** — `cc-os/plugins/os-status/` (git-tracked, 2026-07-06);
symlinked into `~/.claude/plugins/os-status`
- Purpose: aggregated deterministic SessionStart status checks for the cc-os plugin family —
per-project artifacts (ADR system, vault hub note) and environment hazards (subagent model
env override). Built via OpenSpec change `add-os-status-plugin` (supersedes the WS3 plan
doc); decision record ADR-022.
- Architecture: checks are **in-process** plain Python functions in `hooks/checks.py` with
the uniform signature `check(ctx) -> CheckResult(status, message)` plus one registry entry
(no per-plugin subprocess protocol, no per-project code copies — both rejected in ADR-022
after perspective review). Three states: `ok` (silent), `note` (near-zero-token
additionalContext line, never snoozed/suppressed), `warn` (aggregated into at most one
banner; once-per-day snooze + permanent `suppress-<check>` markers in gitignored
per-project `.cc-os/` — state only, never code; `.cc-os/config` holds optional
`hub`/`vault_path` key=value overrides).
- Initial checks: `subagent-model-env-override` (env + `~/.claude/settings.json` `env`
block; runs outside git projects too — catches the WS1 Cluster 1 incident deterministically
at session start), `adr-system-present` (verbatim port of os-adr's hook behavior incl.
`.os-adr/suppress`), `vault-hub-note-present` (config slug first, else `type/hub` +
`project/<name>` facet-tag scan of the vault; missing → warn naming `/os-vault:write`).
- Tests: `python3 tests/hook_test.py` (36, model-free) incl. byte-identity of
PRESENT_NOTE/ABSENT_NOTE against os-adr's source. Invariants in `invariants.md`.
Smoke-tested 2026-07-06 (cache==source, real headless session fired the hook,
env-override canary warns).
**Global os-doc-hygiene plugin** — `cc-os/plugins/os-doc-hygiene/` (git-tracked,
2026-07-03); symlinked into `~/.claude/plugins/os-doc-hygiene`
- Hooks: `hooks/hooks.json` → SessionStart hook (matcher: startup|resume) runs
`scripts/reminder.py` via `${CLAUDE_PLUGIN_ROOT}` (5s timeout), emitting a deterministic
zero-token reminder banner
- Behavior: Monitors stale and bloated project documentation per-project under
`.dochygiene/` state dir (gitignored). SessionStart reminder is deterministic (no AI
tokens, once/day snooze). Skills (verb-first, no `commands/` dispatcher — invoked directly
as `/os-doc-hygiene:<skill>`, per [[cc-os-plugin-skill-naming-convention]]): `check`
(AI-assisted classification of staleness signals, emits machine+human reports), `clean`
(AI-assisted or deterministic patch application with git-safe scoped cleanup), `status`
(read-only lifecycle-timestamp read), `sweep` (check then clean in sequence).
Reversion-protected via invariants.md + golden-example test fixtures.
- Migration: migrated from standalone repo (`~/dev/cc-plugins/doc-hygiene/`, 2026-07-03) and
integrated into cc-os. No content conflicts detected. Renamed from `doc-hygiene` to
`os-doc-hygiene` per cc-os plugin naming convention. Skills renamed
`hygiene-check`/`hygiene-clean` → `check`/`clean`, and the `commands/hygiene.md`
dispatcher removed in favor of two new skills (`status`, `sweep`), aligning with the
`os-vault`/`os-orchestration` pattern of no `commands/` directory (2026-07-03).
**Global os-adr plugin** — `cc-os/plugins/os-adr/` (git-tracked, 2026-07-03); symlinked into
`~/.claude/plugins/os-adr`
- Purpose: Architecture Decision Records, consistently in every project — one
customized-Nygard template (frontmatter
`id`/`date`/`status`/`supersedes`/`superseded-by`/`affected-paths`/`affected-components`;
Context/Decision/Consequences/Alternatives-rejected sections),
`docs/adr/NNNN-kebab-title.md` one-file-per-decision + fully-regenerated
`docs/adr/README.md` index. Built from `docs/adr-system/` (requirements `04`, PRD `05`,
eval sketches `06`) via OpenSpec change `add-os-adr-plugin`.
- Core: Ruby OO library `lib/adr/` (`Record`, `Repository`, `Index`, `Template`, `Detector`,
`Migrator`, `MigrationReport`, `Finder`; Sandi Metz style, injected paths) behind thin
CLIs in `bin/` (`adr-new`, `adr-init`, `adr-detect`, `adr-migrate`, `adr-find`). Tests:
`ruby tests/all.rb` (47) + `python3 tests/hook_test.py` (15), all model-free. Behavioral
invariants in `invariants.md`.
- Hook: **moved to os-status (2026-07-06)** — the SessionStart existence check now runs as
the `adr-system-present` check in the os-status plugin (`hooks.json` here is empty;
`hooks/session_start.py` stays as the wording source of record, byte-copied into
os-status). Behavior unchanged: present → near-zero-token usage note naming
`/os-adr:create` + `/os-adr:find`; absent → once-per-day init/migrate suggestion, silenced
permanently by `.os-adr/suppress` (still honored); silent outside git projects.
- Skills (verb-first, no `commands/`): `create` (one-invocation templated+numbered+indexed
ADR with mechanical supersession), `init`, `migrate` (non-destructive: detect surveyed
shapes → mechanical heuristic fill → LLM fills only manifest-listed interpretive fields
via `--apply-fills``migration_confidence` frontmatter + `docs/adr/migration-report.md`;
old-system deletion only as an explicit separate user-approved step), `find`
(deterministic-first retrieval: affected-paths/components match → Accepted-only filter →
one-hop Graphify query-path expansion with graceful degradation → AI judgment over the
candidate set only).
- Migration pilot passed 2026-07-03 (sandboxed project copies; gate threshold 25%
low-confidence flags): viking-warrior-training-log 0%, delta-refinery 0%, llf-schema 8.3%.
Retrieval verified end-to-end against the real llf-schema project graph. Resolved: ADRs
need no Graphify tag/edge convention — retrieval layer 3 expands the *query* paths via
graph-node `source_file`s.
- Eval A harness (2026-07-03, ADR-021): `plugins/os-adr/eval/` — prompted skill-execution
eval across model tiers (haiku/sonnet). Two fixtures (generated with the plugin's own
CLIs), six scenarios (S1 create, S2 create+supersede, S3 find/conflict, S4
find/distractor, S5 init, S6 migrate+fills), deterministic Ruby checker (`eval/bin/check`,
structural invariants, TSV mode for autoresearch), sandbox + headless-runner scripts,
runner-prompt template. Primary run mode: in-session Agent-tool subagents with pinned
`model:`; optimized via the `/autoresearch` Classic loop over SKILL.md *wording only*
(checker/fixtures/scenarios frozen during a loop). Procedure:
`plugins/os-adr/eval/README.md`. Self-tested both directions; grid run 2026-07-03, all
scenarios × both tiers passing. Distinct from the held-out Eval B (unprompted behavior) —
do not conflate.
- Eval B harness (2026-07-03, OpenSpec change `add-os-adr-eval-b-harness`):
`plugins/os-adr/eval-b/` — held-out unprompted-behavior eval (requirements 45). 7
scenarios (W1W3 write-trigger, R1R4 retrieval) authored from the frozen shapes in
`docs/adr-system/06-eval-scenarios.md`; dedicated Ruby webhook-relay fixture with a 6-ADR
history (Superseded pair + near-miss distractors, generated via the plugin's own CLIs);
R4's one-hop graph reach uses a real `graphify update` AST build (model-free, rebuilt via
`eval-b/bin/build-fixture-graph`, never committed). **Headless-only runner**
(`eval-b/bin/run` — fresh `claude -p` per rep, cwd = sandbox, so the real SessionStart
hook fires; in-session subagents are invalid here, unlike Eval A) and a two-axis
deterministic-first checker (`eval-b/bin/check`): axis (a) unprompted consultation,
mechanical from transcript tool_use blocks; axis (b) correct-ADR citation (R1R4) or
new-ADR-file with a narrow frozen-rubric haiku judge fallback (W1W3, `judge-rubric.md`,
stubbable via `ADR_EVAL_B_JUDGE_CMD`). `R4-nograph` is the graph-degradation variant
(expected FAIL). Self-tested both directions model-free via `eval-b/bin/self-test`.
**Scenario Task blocks are held-out — never run them informally.** Procedure:
`plugins/os-adr/eval-b/README.md`. Grid run 2026-07-03 (1 rep/cell): haiku 0/8 PASS;
sonnet 5/8 PASS (fails W3, R1). `R4-nograph` FAILed on both tiers as expected. Full
results + observations: vault note [[os-adr-eval-b-grid-results-and-observations]].
- **Initial iteration complete (2026-07-03):** plugin build, migration pilot, Eval A grid
(clean pass), and Eval B grid (baseline) all done — closes the first pass on os-adr.
- **Wording experiment complete (2026-07-04):** the follow-up `/autoresearch` loop over
Eval B wording ran (5 iterations, checker/fixtures/scenarios/rubric frozen; fixture
CLAUDE.md declared a wording surface upfront) and closed the gap — final full grid
**sonnet 8/8, haiku 7/8** (from 5/8 / 0/8 baseline; haiku's one miss is a W3 axis-b
judge-boundary flicker). Winning wording shipped in `hooks/session_start.py`
(PRESENT_NOTE), find/create SKILL.mds, and `eval-b/fixture/project/CLAUDE.md` (new — the
trigger-phrased "Architecture decisions" section, the template for real-project adoption).
Confirmed mechanisms: trigger-conditioned when→then phrasing beats inventory phrasing on
both tiers; each rule must live where its precondition is visible (the reversal→supersede
rule in the find skill's act-on-findings step fixed W3); lower tiers need *mechanical*
triggers — semantic triggers only reach sonnet. Open: channel ablation never run;
R4-nograph now passes both tiers, so the graph-degradation check no longer differentiates.
Full hypothesis→result mapping: vault note [[os-adr-eval-b-wording-experiment-hypotheses]].
**Before designing or running any autoresearch eval, Read
`~/Documents/SecondBrain/howto/running-autoresearch-skill-evals.md`.**
- **Eval B W3 stability check (2026-07-06):** 7 headless reps of haiku × W3 (all reps
counted): axis (a) consultation 7/7 PASS; axis (b) recording-offer ~5/7 (~7585%) — one
FAIL plausibly infra, one a genuine behavioral miss. Verdict: intermittent flicker, not a
hard gap — grid claim stays **haiku 7/8**, W3 characterized as an ~1-in-4/5 axis-b miss.
Haiku cleared for daily use with that caveat. Recorded in the vault good-enough gate:
[[os-adr-eval-b-wording-experiment-hypotheses]].
- **Eval C harness built, NOT yet run as designed (2026-07-06):** `plugins/os-adr/eval-c/`
ambiguity-ladder DISCRIMINATION eval (held-out; Eval B is contaminated by the wording
loop). 3 levels (explicit → moderate → conceptual framing) × paired positive/negative
scenarios; run-set (6, job-execution domain) + frozen reserve-set (6, notifications
domain). New Python fixture (`taskq` async job queue, 6-ADR history generated via the
plugin's own CLIs, trigger-phrased CLAUDE.md section copied from eval-b). Scoring:
positives = both axes; negatives FAIL **only on unneeded ADR creation**. Model-free
`bin/self-test` green, including a truthful-citation-must-PASS guard. **Scenario Task
blocks are held-out — never run informally.** Methodology: vault notes
[[eval-methodology-ladder]] and [[eval-methodology-irl-feedback-loop]]; vault also gained
an `eval-results` note type + `_templates/eval-results.md` (2026-07-06).
- **Eval C frozen grid run (2026-07-06):** run-set × {sonnet, haiku} × 3 reps/cell (36
headless reps, all counted) — **12/12 cells PASS at ≥2/3 majority; haiku 18/18 reps,
sonnet 17/18**. Zero over-trigger ADR creation on any negative at any level; every
positive pass was via governing-ADR citation (16) or judge-recognized proposal (1), never
duplicate creation. Sonnet's one FAIL rep (P1-L1) is a final-message-citation boundary
artifact, not a behavioral miss. Two harness defects fixed on the first live rep, BEFORE
the grid, with Task blocks/fixtures/rubric untouched: (1) `bin/run` pipefail bug; (2)
positive axis-b contradiction fixed via a mechanical `cited-governing` PASS branch + two
new self-test guards (`B-via:` now recorded in TSV). Verdict: the Eval B wording
**generalizes** — grid saturated, ceiling not found; next signal is production (rollout +
IRL session audits), not another lab rung. Run-set is now the baseline; wording tuning
against it moves measurement to the reserve-set. Full results: vault note
[[os-adr-eval-c-frozen-grid-results]].
- **Remaining (locked rollout order):** real-project migration/adoption one at a time via
`/os-adr:migrate` — pilot projects first, then the cc-os retrofit (its 19-ADR monolithic
file was deliberately excluded from the pilot), then wider. When onboarding real projects,
add the trigger-phrased "Architecture decisions" CLAUDE.md section (copy from
`eval-b/fixture/project/CLAUDE.md`; candidate: emit it from `/os-adr:init`/`migrate`).
**Graphify** — v0.8.31 at `/home/jared/.local/bin/graphify`
- Vault graph: `~/Documents/SecondBrain/graphify-out/` — disposable, structural index
rebuilt by SessionStart hook; handles relational/graph queries. Distinct from
vault-index.json (planned, Plan B Phase 1): a flat `{tag: [{path, title, summary}]}`
lookup rebuilt at SessionEnd, queried by the `/memory-find` skill for fast tag-based SB
note discovery.
- Project graph: `<project-root>/graphify-out/` — same pattern; gitignore it in each project
repo
- Vault conventions: `~/Documents/SecondBrain/vault-conventions.md` — frontmatter contract +
tag taxonomy (canonical name decided 2026-06-09; formerly referred to as CONVENTIONS.md)
**memsearch** — v0.4.6 via Claude Code plugin marketplace (`memsearch@memsearch-plugins`)
- Hooks: Stop, SessionStart, UserPromptSubmit, SessionEnd (ship with plugin)
- Memory store: `~/.memsearch/memory/YYYY-MM-DD.md` daily files (global, cross-project, all
clients — one store by design)
- Index: `~/.memsearch/milvus.db` (Milvus Lite, local); embeddings via ONNX bge-m3
- Config: `MEMSEARCH_DIR=~/.memsearch` in `~/.zshrc` for global scope
- Skills: `/memory-recall`, `/memory-config` (ship with plugin)
- Git sync: `~/.memsearch` is a dedicated private Forgejo repo
(`forgejo.swansoncloud.com/jared/memsearch`); whitelist `.gitignore` commits only
`memory/*.md`; auto-commit+push wired into `memsearch_sync.py` SessionEnd hook (relocated
from `session-end.sh` by ADR-016, 2026-06-12; behavior preserved — see ADR-015)
**Obsidian vault git sync** — `~/Documents/SecondBrain` (2026-06-15)
- Remote: `ssh://git@forgejo.swansoncloud.com:2222/jared/SecondBrain.git` (private Forgejo;
web: `https://forgejo.swansoncloud.com/jared/SecondBrain`)
- 52 files tracked (notes, journal, templates, vault-conventions.md, CLAUDE.md, .obsidian
config); `graphify-out/` excluded via `.gitignore`
- Auto-commit+push via `vault_sync.py` SessionEnd hook (mirrors `memsearch_sync.py`;
push-only — SessionStart pull is optional future item for multi-machine)
## Operational procedures
### Renaming or moving a local plugin
Renaming a plugin directory under a directory-source marketplace (like `local-plugins`, whose
source is `~/.claude/plugins` itself) requires updating **three** places, not just the
directory and `settings.json`:
1. **Plugin directory / symlink** — the actual files (or symlink target, for plugins that
live in a git repo like `cc-os/plugins/os-vault/`).
2. **Marketplace manifest**`~/.claude/plugins/.claude-plugin/marketplace.json` (for
`local-plugins`) lists each plugin's `name` and `source` path explicitly; this does
**not** auto-discover from the directory listing. Update the entry to match the new
name/path.
3. **`settings.json` `enabledPlugins`** — the `<name>@<marketplace>` key must match the
manifest entry from step 2.
Then refresh the plugin manager's state so it re-resolves the marketplace and drops the
stale install record:
```bash
claude plugin marketplace update <marketplace-name> # re-validates the manifest
claude plugin install <new-name>@<marketplace-name> # creates the new install record
claude plugin uninstall <old-name>@<marketplace-name> # drops the stale one
claude plugin list # verify: new name enabled, old name gone
claude plugin details <new-name>@<marketplace-name> # verify skills/agents/hooks resolved
```
Skipping step 2 is the failure mode to watch for: hooks (wired by absolute path in
`settings.json`) keep working, masking the fact that skills/slash-commands silently stopped
registering under the plugin's new name.
### Editing a local plugin (cache refresh)
Plugin source edits (including SKILL.md wording, hook scripts, or CLI code) do not reach
running sessions until the cache is refreshed. Claude Code caches plugins when installed,
copying them to `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`. To refresh a
stale cache after source edits:
```bash
bin/refresh-plugins [marketplace-names...] # refresh all or specific local marketplaces
```
This uninstalls and reinstalls each plugin, forcing the cache to repopulate. See
`bin/refresh-plugins --help` for usage. After refreshing,
`claude plugin details <name>@<marketplace>` should show updated skill descriptions and hook
counts. Directory-source marketplaces (e.g., `local-plugins` sourced from
`~/.claude/plugins`, `cc-plugins` sourced from `~/dev/cc-plugins`) are the only ones that
need manual refresh — public marketplaces auto-update.

View File

@ -0,0 +1,91 @@
# B3 — memsearch SessionStart injection cost review
_Reviewed 2026-07-08. memsearch v0.4.6 (installed) / v0.4.11 (also cached, not yet installed). Plugin: `memsearch@memsearch-plugins`._
## What gets injected
`hooks/session-start.sh` (both cached versions, logic unchanged 0.4.6→0.4.11) builds a
`# Recent Memory` block on every SessionStart and returns it as `additionalContext`:
- Finds the daily memory files under `$MEMORY_DIR` (`~/.memsearch/memory/YYYY-MM-DD.md`),
sorted by filename descending, and takes the **2 most recent**.
- For each of those 2 files, greps lines matching `^(#{2,4} |- )` (headings + bullets) and
takes the **first 40 matching lines** (`head -40`).
- Concatenates both files' extracts under one `# Recent Memory` heading and returns it.
This is entirely hardcoded (`head -2`, `head -40`) in the shell script — there is no
per-file/per-line/byte cap that's user-configurable.
## Measurements (cc-os project, last ~7 days of session transcripts)
Extracted the `additionalContext` string from every SessionStart hook event whose payload
contained `# Recent Memory`, across `~/.claude/projects/-home-jared-dev-cc-os/*.jsonl`
(61 SessionStart events found in that window — heavy usage during the WS2/WS4 eval loops,
so this sample is skewed toward busy days, not a quiet baseline).
| Stat | chars | approx tokens (÷4) |
|---|---|---|
| min | 5,976 | ~1,494 |
| median | 11,721 | ~2,930 |
| avg | 11,978 | ~2,995 |
| max | 15,407 | ~3,852 |
This roughly confirms the ~13 KB figure already observed. Size scales with how many
`##`/`###`/bullet lines the 2 most recent daily files accumulated that day — on a day with a
lot of sessions/subagent activity (like the recent eval-loop days), each daily file alone can
hit the 40-line cap, so the injection saturates near its structural ceiling
(2 files × 40 lines × ~150200 chars/line ≈ 12,00016,000 chars), matching the observed range.
Caveat: this is a per-project sample from one heavy-usage repo (cc-os) over 7 days; other
projects' daily files will differ, and this sampling method (string-matching the raw jsonl,
not a documented API) is the best available without deeper memsearch internals — reasonably
solid since the hook's exact output string was recovered from real transcripts, not estimated.
## Config surface found
Read `skills/memory-config/SKILL.md` (both cached versions) and `hooks/session-start.sh`
directly. Findings:
- The **documented** TOML config surface (`plugins.claude-code.summarize`, `.project_review`,
`.user_profile`, `.memory_to_skill`, `[llm.providers.*]`, `[prompts]`) controls
summarization, maintenance artifacts (PROJECT.md/USER.md), skill distillation, and provider
routing — **none of it touches the SessionStart recall injection**.
- No `[plugins.claude-code.session_start]` or equivalent section exists in the documented
schema, and none of the constants used by the injection logic (`head -2` file count,
`head -40` line count, the `^(#{2,4} |- )` grep pattern) appear anywhere in `config.toml`,
`memsearch config list --resolved/--global/--project`, or the SKILL.md. They are hardcoded
in `hooks/session-start.sh` itself.
- The only adjacent, real lever is **memory file density**: since the cap is by line count
(not token count) over headings+bullets, whatever produces terse vs. verbose daily-file
bullets (the Stop-hook summarizer, governed by `plugins.claude-code.summarize.model`,
defaults to haiku) indirectly controls how much of the 40-line budget per file is "real
signal" vs. how many days of content get squeezed out.
## Recommendation
**No first-class cap/trim setting exists in memsearch's config schema for this injection.**
The two concrete options, in preference order:
1. **Workaround, no code change (recommended to try first):** the injection is driven purely
by memory-file content density, not by any setting. If daily files are noisy (e.g. many
short low-signal bullets from busy multi-session days, as in the cc-os sample), the
Stop-hook summarizer producing those bullets is the actual place to add discipline —
tightening what counts as bullet-worthy would shrink the 40-line/file budget's real size
without touching the plugin. This is a memsearch summarizer/prompt-quality issue, not a
session-cost cap.
2. **Direct edit, unsupported (only if (1) is insufficient):** hand-edit the cached
`hooks/session-start.sh` at
`~/.claude/plugins/cache/memsearch-plugins/memsearch/<version>/hooks/session-start.sh`,
lowering `head -2``head -1` (halves injection size, roughly ~68 KB/day instead of
~1215 KB) and/or `head -40` → a smaller value (e.g. 20). Expected savings: cutting to 1
file × 20 lines would drop the typical injection from ~1115 KB (~2,9003,850 tokens) to
roughly ~3 KB (~750 tokens), a ~75% reduction. **Caveat:** this is an unmanaged edit to
plugin-cache source — it will be silently overwritten on the next
`claude plugin update memsearch` / cache refresh, is not upstream-supported, and isn't a
"setting" in the config-surface sense the task asked to identify. Treat it as a stopgap,
not a durable fix; if this repo wants a durable cap, the change belongs upstream in
`zilliztech/memsearch` (file an issue/PR to add a
`[plugins.claude-code.session_start]` `max_files` / `max_lines_per_file` config section)
rather than as a local hack here.
No setting was applied — this is a review/recommendation only per the task.

View File

@ -0,0 +1,114 @@
# C3 spike findings: `--setting-sources` interaction + overlay file placement
_Task: C3 in [system-prompt-profiles-tasks.md](system-prompt-profiles-tasks.md). Feeds B2._
## Method note (important correction mid-spike)
Initial attempts asked the model to self-report whether ORCHESTRATION.md/skills were
injected ("ORCH=yes/no SKILL=yes/no"). This was **unreliable** — a default run that the
transcript later proved *did* receive the orchestration injection had the model answer
`ORCH=no`. Model self-report is not a valid signal for this spike.
Switched to a deterministic signal: the session transcript JSONL at
`~/.claude/projects/<slug>/<session_id>.jsonl` records hook execution as
`attachment.type == "hook_additional_context"` entries (one per SessionStart hook that
returned `additionalContext`) and the full injected skill list as
`attachment.type == "skill_listing"`. Both are populated *before* the model ever sees the
turn, so they're a ground-truth substitute for `/context`.
## Spike (a): `--setting-sources` vs symlinked local plugins + absolute-path hooks
4 headless haiku runs from an empty scratch dir
(`/tmp/claude-1000/.../scratchpad/c3spike`), `claude -p --model haiku --output-format json
"reply OK"`, varying `--setting-sources`. For each, inspected the resulting transcript
JSONL for hook-injected additionalContext count and whether plugin-specific skills
(`os-adr`, `os-vault`) appear in the injected skill listing (built-in skills like
`autoresearch`/`caveman` appear regardless — only plugin skill presence is diagnostic).
| `--setting-sources` value | additionalContext entries (hooks fired) | os-adr/os-vault skills present |
|---|---|---|
| (no flag, default) | 1 (confirmed in first exploratory run) | yes |
| `""` (none) | 0 | no |
| `user` | 1 | yes |
| `project,local` | 0 | no |
Interpretation:
- Hooks in this setup are wired **only** in `~/.claude/settings.json` (a **user**-scope
settings file), by absolute path into `cc-os/`. They fire if and only if the `user`
settings source is included. `--setting-sources ""` and `--setting-sources
project,local` both silently drop them — no error, no warning, just no
`additionalContext`. This is a real footgun for a future `cyolo` profile wrapper: any
profile that passes `--setting-sources` explicitly must include `user` or it silently
loses ORCHESTRATION.md injection, os-status checks, os-vault hooks, and every other
hook in the plugin family.
- Plugin skill registration (the `os-adr:find` style slash-command/skill listing) tracks
the same axis: present with `user` included, absent without it. This makes sense —
`enabledPlugins` lives in the user-scope `~/.claude/settings.json`, so a
`--setting-sources` value that excludes `user` also excludes plugin enablement, not
just hooks.
- Symlinks themselves are a non-issue: once the `user` settings source is included, the
symlinked plugin (`~/.claude/plugins/os-adr -> cc-os/plugins/os-adr`) resolves and
loads identically to the default case — no special-casing needed for the symlink or
for the hooks' absolute paths into `cc-os/`. The failure mode is entirely about which
settings **source** is included, not about symlinks or path resolution.
**Verdict (a):** `--setting-sources` interacts cleanly with symlinked plugins and
absolute-path hooks in the sense that nothing breaks or errors — but it is an all-or-
nothing gate: excluding `user` from `--setting-sources` silently disables every cc-os
plugin (hooks AND skills) with no error surfaced. Any profile wrapper must always include
`user` in `--setting-sources` (or simply not pass the flag) to keep the plugin family
alive; only `project`/`local` inclusion should be varied per profile.
## Spike (b): where should per-profile settings overlay files live?
Read `bin/refresh-plugins` (Ruby). It touches exactly these paths, all read-only except
for the plugin cache itself:
- Reads `~/.claude/plugins/known_marketplaces.json` (marketplace list/config)
- Reads `~/.claude/plugins/installed_plugins.json` (which plugins are installed per
marketplace)
- For each installed plugin under a `directory`-source marketplace, shells out to
`claude plugin uninstall <plugin>@<marketplace>` / `claude plugin install
<plugin>@<marketplace>` — this rewrites `~/.claude/plugins/cache/<marketplace>/
<plugin>/<version>/` (via Claude Code's own plugin manager, not directly) and updates
`installed_plugins.json`'s install record for that plugin.
- Verifies via `diff -rq` between the cache dir and the plugin's source dir under
`source_path` (from marketplace config, e.g. `~/dev/cc-os/plugins` or
`~/dev/cc-plugins`).
It never touches `~/.claude/settings.json`, never touches any file under a
`~/.claude/profiles/` style directory, and never touches arbitrary `--settings <file>`
overlays — its blast radius is strictly `known_marketplaces.json`,
`installed_plugins.json`, and the `plugins/cache/<marketplace>/<plugin>/` tree.
**Recommendation:** put per-profile settings overlay JSONs at `~/.claude/profiles/*.json`
(e.g. `~/.claude/profiles/cc-os-design.json`, `client-dev.json`, `brainstorm.json`),
passed via `claude --settings ~/.claude/profiles/<name>.json`. This path is structurally
disjoint from everything `refresh-plugins` reads or writes (`plugins/cache/`,
`plugins/known_marketplaces.json`, `plugins/installed_plugins.json`) — there is no
plausible future edit to `refresh-plugins` that would need to reach into
`~/.claude/profiles/`, since that script's whole job is cache-vs-source verification for
marketplace-installed plugins, not settings resolution.
Interaction with `enabledPlugins`: `--settings <file>` is merged with (not a full
replacement for) the resolved settings from the sources selected by `--setting-sources`
(per spike (a), keep `user` in the source list so the base `~/.claude/settings.json`
`enabledPlugins`/hooks/env still apply). The profile overlay should therefore only need
to express a *diff* — an `enabledPlugins` map with the profile's unwanted plugins set to
`false` (or, if this repo prefers positive lists, the full set with only the
wanted ones `true`) — rather than duplicating the whole settings file. This diff-only
overlay is exactly what B2 should generate per profile. One caution to carry into B2:
verify at build time whether CC deep- or shallow-merges the `enabledPlugins` object
across `--settings` sources (not tested in this spike) — either way, a plugin disabled
via the overlay's `enabledPlugins: {"name": false}` does not require touching
`known_marketplaces.json` or `installed_plugins.json`, since `enabledPlugins` toggles are
independent of the marketplace/install-record identity those files hold (the files
`refresh-plugins` owns).
**Verdict (b):** `~/.claude/profiles/*.json` (passed via `--settings`) is safe — it lives
entirely outside the three paths `refresh-plugins` reads/writes/rewrites
(`known_marketplaces.json`, `installed_plugins.json`, `plugins/cache/`), and
`enabledPlugins` toggles in an overlay are orthogonal to the marketplace/cache identity
`refresh-plugins` verifies, so cache refreshes and profile overlays can never clobber
each other.

View File

@ -1,6 +1,34 @@
# System-prompt profiles: assessment & recommended approach # System-prompt profiles: assessment & recommended approach
_Last updated: 2026-07-08 · Status: assessment only, no build started_ _Last updated: 2026-07-08 · Status: assessment + next-actions agreed; deferral lifted —
WS2/WS4 evals confirmed complete 2026-07-08 (Gate G0 cleared), execution may begin_
> Task breakdown for subagent implementation:
> [system-prompt-profiles-tasks.md](system-prompt-profiles-tasks.md) (2026-07-08).
## Agreed next actions (2026-07-08, deferred)
Two tracks, both approved by the user:
1. **Top-down diet** (first, biggest win):
- Split cc-os `CLAUDE.md` (~11k tokens) to a ~23k orientation + on-demand status/changelog
doc, using `@`-import / pointer style per
`~/servers/proxmox-hermes/docs/claude-md-maintenance.md` (keep it short; prefer pointers
to copies; never auto-append).
- Then per-profile plugin enablement (~4.8k max, second priority) and a memsearch
injection review.
- **Do not start while the other session's evals are running** — CLAUDE.md is live input
to those sessions.
2. **Bottom-up audit** (build anytime, read-only):
- One-file logging proxy (`ANTHROPIC_BASE_URL` pass-through) that dumps the `system` and
`tools` arrays from a real request — the audit basis for lean profiles. Expect savings
to come from per-profile `--tools` lists (~18k of schemas), not prompt rewriting (~6.3k).
Related future idea (out of scope, captured in vault note
[[claude-md-budget-linter-plugin-concept]]): a deterministic CLAUDE.md budget linter —
hook-driven, JSON per-project config with quantified user-agreed exceptions, silent in range,
maintenance agent only when out of range; possible extension to whole-project weekly hygiene
scans. Overlaps os-doc-hygiene/os-status — resolve at build time.
Companion to the WS4 orchestration-economics work. Goal: reduce per-session context cost and Companion to the WS4 orchestration-economics work. Goal: reduce per-session context cost and
improve focus by controlling what reaches the model's context — the built-in system prompt, improve focus by controlling what reaches the model's context — the built-in system prompt,

View File

@ -0,0 +1,69 @@
# System-prompt profiles: task breakdown for subagent implementation
_Last updated: 2026-07-08 · Source plan: [system-prompt-profiles-assessment.md](system-prompt-profiles-assessment.md)_
Groups are ordered by dependency, not priority. Group A is read-only and can start
immediately, in parallel. Group B is the biggest win but is **gated**. Groups C and D are
sequential follow-ons.
**Slimmed scope (2026-07-08, user-approved):** execute quick/high-value wins only —
B1 → B3 ∥ C3 → B2, then a minimal C (settings-overlay aliases, no auto-detection).
A1/A2 are deferred until a live interactive `/context` check shows tool schemas still
dominate despite ToolSearch deferred loading (one user-assisted check replaces Group A;
A3's headless matrix is already substantially in the assessment doc). D is dropped from
the plan — revisit only if CC economics change; max upside ~6.3k with fork-maintenance
downside.
**Gate G0 — CLEARED (2026-07-08):** user confirmed the os-orchestration (WS4) and os-vault
(WS2) eval loops are complete; no eval runs remain in flight. Group B is unblocked.
---
## Group A — Bottom-up audit (read-only, start anytime, parallelizable)
| ID | Task | Depends on | Suggested agent |
|---|---|---|---|
| A1 | **Logging proxy.** One-file `ANTHROPIC_BASE_URL` pass-through that forwards to `api.anthropic.com` and dumps the `system` and `tools` arrays of each request to a local file. Ruby preferred (Sandi Metz style not required for a one-file tool). No response mutation, streaming must pass through untouched. Deliverable: `bin/` or `tools/` script + 3-line usage note. | — | sonnet |
| A2 | **Live prompt capture + audit.** Run one headless and (user-assisted) one interactive session through the A1 proxy from cc-os and from an empty dir; save the dumped `system`/`tools` arrays; produce an audit doc: token cost per system-prompt section, per tool schema, and a "safely droppable per profile" table. Expect savings in `--tools` lists (~18k), not prompt text (~6.3k). | A1 | sonnet |
| A3 | **Phase 0 measurement.** Script the headless baseline matrix (already partially done in the plan: 4 configs measured) and extend to 34 representative session types (cc-os design, client dev, brainstorm) via `--output-format json` usage fields; record one interactive `/context` check (user-assisted) to validate the headless numbers against a Fable session. Deliverable: a measurements table appended to the assessment doc. | — | haiku (scripted runs) + user for the interactive check |
A1 and A3 run in parallel; A2 follows A1.
## Group B — Top-down diet (**gated on G0**, sequential)
| ID | Task | Depends on | Suggested agent |
|---|---|---|---|
| B1 | **Split cc-os CLAUDE.md.** ~11k tokens → ~23k orientation file + `docs/implementation-status.md` (the append-only changelog moved out, linked not `@`-imported so it loads on demand). Follow `~/servers/proxmox-hermes/docs/claude-md-maintenance.md`: keep it short, pointers over copies, never auto-append. Must preserve: the "keep this file current" contract (redirected to the new doc), plugin naming rule, ADR discipline, OpenSpec workflow. Verify a fresh session still orients correctly. | G0 | sonnet (judgment about what is orientation vs changelog); main-loop review before commit |
| B2 | **Per-profile plugin enablement.** Design + create settings overlay files (one per profile: e.g. `cc-os-design`, `client-dev`, `brainstorm`) listing `enabledPlugins`; a brainstorm profile drops rails-ui-component / api-wrapper / invoice-ninja / creative-team etc. Resolve where overlays live so they don't fight `bin/refresh-plugins` (open question #3). | B1 (so measured savings are attributable) | sonnet |
| B3 | **memsearch injection review.** Measure the SessionStart recall injection size across a week of sessions (~13 KB observed once); review memsearch plugin config options for capping/trimming it; recommend (not apply) a setting. | — (parallel with B1/B2 once G0 clears) | haiku |
## Group C — `cyolo` profile wrapper (Phase 2, after B)
| ID | Task | Depends on | Suggested agent |
|---|---|---|---|
| C1 | **Profile prompt components.** Create `~/.claude/profiles/` with `dev.md`, `ruby.md`, `planning.md`, `brainstorm.md`. Objective profiles state the session endpoint explicitly ("this session ends when the plan is written…"). Small files, ~0.5k tokens each. | B2 (profiles reference the settings overlays) | sonnet |
| C2 | **Wrapper script.** `cyolo` shell wrapper: resolve profile from flag (`--dev`, `--planning`) + project detection (pwd, Gemfile…), compose append-file, launch `claude --append-system-prompt-file <composed> --settings <profile-settings> [--tools …]`. | C1 | sonnet |
| C3 | **Open-question spikes** (can run before/parallel with C1): (a) does `--setting-sources` interact cleanly with symlinked local plugins + absolute-path hooks? (b) confirmed overlay location from B2 survives `bin/refresh-plugins`. Small experiments, written up as findings notes. | — | haiku |
## Group D — Eval-gated replacement experiment (optional, last)
| ID | Task | Depends on | Suggested agent |
|---|---|---|---|
| D1 | **Eval design.** Read `~/Documents/SecondBrain/howto/running-autoresearch-skill-evals.md` FIRST (standing rule). Design the grid: scenario set × {default, dieted stack, profile stack, full-replacement lean prompt} × model tiers; score task success + tokens/turn + wrong-tool proxies. Design only — do not run without user sign-off. | C2 (profile stack must exist to be a grid column) | sonnet |
| D2 | **Run + verdict.** Execute the grid headless-only; promote full replacement only if it wins on *outcome*, not just token count. | D1 | per playbook |
## Out of scope (captured, do not implement)
- CLAUDE.md budget linter plugin — vault note [[claude-md-budget-linter-plugin-concept]];
overlaps os-doc-hygiene/os-status, resolve at build time.
- Per-model profile variants — deferred by user decision; revisit after profiles exist.
## Suggested execution waves
Superseded by the slimmed scope above. Current order:
1. **Wave 1 (parallel):** B1 (main-loop), B3 + C3 (subagents).
2. **Wave 2:** B2 (needs C3's overlay-location answer).
3. **Wave 3 (minimal C):** settings-overlay aliases only; full C1/C2 wrapper deferred
until the friction is felt.
4. **Deferred:** A1/A2 pending the interactive `/context` check; D dropped (pointer kept).