diff --git a/.gitignore b/.gitignore
index 6990f19..e05ee6a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@ tests/unit/*
!tests/unit/test-opencode-adapter.mjs
!tests/unit/test-session-distiller.mjs
!tests/unit/test-control-plane.mjs
+!tests/unit/test-runtime-doctor.mjs
!tests/unit/test-metrics-worker.mjs
!tests/unit/test-cli.mjs
test-results/
diff --git a/AI_INDEX.md b/AI_INDEX.md
index 4c456c1..71a6980 100644
--- a/AI_INDEX.md
+++ b/AI_INDEX.md
@@ -64,7 +64,7 @@
### HTTP server
- Entry: `src/server.mjs`
- Cross-platform path boundary checks: `src/path-security.mjs`
-- Search: `createServer`, `/api/scan`, `/api/move`, `/api/delete`, `/api/save-markdown`, `/api/context-budget`, `/api/security-scan`, `/api/file-content`, `/api/session-preview`, `/api/export`
+- Search: `createServer`, `/api/scan`, `/api/move`, `/api/delete`, `/api/save-markdown`, `/api/context-budget`, `/api/security-scan`, `/api/runtime-doctor`, `/api/file-content`, `/api/session-preview`, `/api/export`
- Tests: `tests/e2e/dashboard.spec.mjs`
- Connects to:
- Scanner — inventory via `scanAll()`
@@ -118,6 +118,16 @@
- Harness Doctor consent UI — `src/ui/app.js`, `src/ui/index.html`
- First-party Cloudflare Worker + D1 — deduplicates `(month, monthly_id)`
+### Live Runtime Map / MCP Runtime Doctor
+- Entry: `src/runtime-doctor.mjs`
+- Search: `collectRuntimeDoctorReport`, `stopRuntimeSession`, `buildSessions`, `attachDirectMcpProcesses`, `findConnectedSessions`, `collectIntentProbeStatus`, `buildRuntimeFindings`, `commandLineHasSecret`
+- Behavior: privacy-bounded Linux snapshot of same-user Claude Code, Codex, OpenCode, and DSH process trees; tree RSS/age/process attribution; configured direct-MCP matching; duplicate direct-instance summary; 1MCP cgroup health and persistent loopback client-socket attribution; IntentProbe install/daemon/hook readiness. Command lines, paths, raw sockets, and credential values are never returned.
+- Stop contract: explicit UI confirmation plus opaque PID/start-time identity; server revalidates same user and harness identity and sends only `SIGTERM`; CCO refuses self-containing trees.
+- Tests: `tests/unit/test-runtime-doctor.mjs`, `tests/e2e/dashboard.spec.mjs`
+- Connects to:
+ - Server — `GET /api/runtime-doctor`, `POST /api/runtime/session/stop` in `src/server.mjs`
+ - Harness Doctor — session cards, direct/shared topology, duplicate MCP recommendations, IntentProbe readiness, resource/package/credential findings, and stop confirmation in `src/ui/app.js`
+
---
## Frontend
@@ -175,6 +185,7 @@
- `test-opencode-adapter.mjs` — OpenCode config, instruction, skill, MCP, and plugin discovery
- `test-dsh-adapter.mjs` — DeepSeek Harness settings, profiles, official skill roots, and capability gates
- `test-control-plane.mjs` — local privacy metrics and monthly signal behavior
+- `test-runtime-doctor.mjs` — privacy-bounded 1MCP process, cgroup, config, and finding collection
- `test-metrics-worker.mjs` — collector validation, strict schema, and D1 writes
### E2E tests
diff --git a/PRIVACY.md b/PRIVACY.md
index 585ccd2..e8d90dd 100644
--- a/PRIVACY.md
+++ b/PRIVACY.md
@@ -31,6 +31,9 @@ You can disable metrics at any time in Harness Doctor. CCO does not use cookies,
## What It Accesses
- Reads selected config and customization files for supported harnesses, including `~/.claude/`, `$CODEX_HOME` (default `~/.codex/`), `~/.config/opencode/`, `$DSH_HOME` (default `~/.dsh/`), `$DSH_AGENTS_HOME` (default `~/.agents/`), and discovered project directories
+- On Linux, Live Runtime Map reads same-user procfs metadata to group supported harness process trees and sum RSS, threads, age, and process counts. It may read same-user file-descriptor links plus `/proc/net/tcp*` to associate a persistent loopback 1MCP socket with a session. It reads configured MCP command markers only for local process matching. Command lines, working directories, raw socket details, and credential values are never returned to the browser.
+- Runtime Doctor can also read a HOME-scoped 1MCP config, cgroup resource counters, its loopback health endpoint, and Claude settings to detect an IntentProbe hook. It checks command-line arguments in memory for credential-shaped values but returns only a count.
+- “Stop session” is the only runtime-map mutation. After an explicit browser confirmation, CCO revalidates the same-user PID, process start identity, and supported harness executable, then sends `SIGTERM` to that one harness root. It never stops a session tree containing the CCO server itself.
- Writes only when you use an editing, move, delete, restore, export, backup, or harness-control action
- Runs an HTTP server bound to `127.0.0.1` (default port 3847) and rejects non-local browser requests
@@ -49,4 +52,4 @@ CCO does not upload the contents of your harness files. Optional monthly metrics
---
-Last updated: 2026-09-11
+Last updated: 2026-10-16
diff --git a/README.md b/README.md
index 269e634..fb74622 100644
--- a/README.md
+++ b/README.md
@@ -112,6 +112,10 @@ Open **Harness Doctor** from the sidebar for a scope-level audit:
- **Explainable hygiene score** — every deduction is tied to a visible finding such as duplicate identities, missing discovery metadata, large context-bearing files, stale sessions, or setting overrides. It is a diagnostic heuristic, not a security certification.
- **Safe auto-repair** — only byte-identical artifacts with the same name, category, and scope are eligible. CCO archives the extra copy, fingerprints it, and provides Undo.
- **Cross-harness migration** — v1 copies portable `SKILL.md` bundles between Claude Code, Codex CLI, OpenCode, and DSH. It previews ready, conflicting, identical, and unsupported items and never overwrites by default.
+- **Live Runtime Map** — on Linux, groups same-user Claude Code, Codex, OpenCode, and DSH process trees into live sessions; shows tree RSS, age, process/thread counts, matched direct MCPs, duplicate per-session MCP instances, and a shared 1MCP node. A loopback TCP snapshot can link persistent 1MCP clients to sessions; short-lived requests are explicitly reported as unobservable rather than guessed.
+- **Runtime safety controls** — high-memory and 24-hour sessions are flagged. “Stop session” is always an explicit confirmation and revalidates the same-user PID, process start identity, and harness executable before sending graceful `SIGTERM`. CCO refuses to stop the process tree that contains itself.
+- **IntentProbe readiness** — detects whether the runtime scanner, warm JSONL daemon, and Claude hook are present. Semantic “what is this session doing?” activity remains unavailable until a future receipt adapter is connected; process names are never presented as intent evidence.
+- **1MCP Doctor** — checks cgroup memory/task pressure, unhealthy upstreams, floating `npx` versions, lazy-loading state, and credential-shaped command-line arguments. Command lines and credential values are never returned to the browser.
- **Private product signals** — optional metrics are off by default. Detailed daily event counts and coarse inventory buckets stay local. If enabled, CCO submits a deduplicated monthly activity signal containing only a rotating anonymous ID, UTC month, CCO version, and harness ID. The identifier changes every month, so it cannot build cross-month user histories.
## Context Budget: See How Many Tokens Claude Code Pre-Loads
@@ -401,6 +405,12 @@ MIT
## Updates
+### Unreleased
+- Added a Linux Live Runtime Map for Claude Code, Codex, OpenCode, and DSH sessions, including tree RSS, age, process/thread counts, direct MCP attribution, duplicate per-session MCP detection, and persistent 1MCP client-socket evidence
+- Added explicit, identity-pinned graceful session stop; CCO revalidates UID, PID start time, and harness identity and refuses to stop its own containing process tree
+- Added IntentProbe runtime readiness plus 1MCP cgroup, package-pinning, lazy-loading, upstream-health, and credential-exposure diagnostics
+- Added privacy documentation and regression coverage; 405 tests pass (188 unit + 217 E2E)
+
### 2026-09-11
- v0.20.0: Added inventory-first OpenCode and DeepSeek Harness support and expanded Codex inventory, including custom agents, nested memories, skill enablement state, project/inline/plugin hooks, plugin-provided skills, and standalone profiles
- Added a source-labelled All Memories scope and local body search across Claude memory files
diff --git a/docs/CONTROL_PLANE_PLAN.md b/docs/CONTROL_PLANE_PLAN.md
index cea3712..ef1f759 100644
--- a/docs/CONTROL_PLANE_PLAN.md
+++ b/docs/CONTROL_PLANE_PLAN.md
@@ -1,5 +1,10 @@
# Harness Doctor control-plane plan
+> **Historical v0.20 slice.** This document records the original Harness Doctor
+> contract. It no longer controls the product backlog. The canonical overall
+> roadmap is [LIVING_PLAN.md](LIVING_PLAN.md), and its release/feature evidence
+> gates are in [QA_PLAN.md](QA_PLAN.md).
+
## Outcome
CCO should answer four questions for any supported coding harness:
diff --git a/docs/LIVING_PLAN.md b/docs/LIVING_PLAN.md
new file mode 100644
index 0000000..4452bcf
--- /dev/null
+++ b/docs/LIVING_PLAN.md
@@ -0,0 +1,479 @@
+# CCO Living Product Plan
+
+Last updated: 2026-10-16 PT
+Owner: Nicole (product decisions) + PM session (requirements and sequencing)
+Status: **Draft for product approval — no feature implementation is authorized by this document**
+
+This is the single source of truth for CCO product direction and delivery order. It replaces the backlog portions of `README.md`, `docs/CONTROL_PLANE_PLAN.md`, and older review notes. Those files remain evidence or public documentation until a later documentation session reconciles them.
+
+## Product outcome
+
+CCO is the neutral local reality and policy layer for a person's existing AI coding environment. It does not require sessions to be launched inside CCO. It should let a user answer, from one understandable interface:
+
+1. What configurations, capabilities, sessions, and MCP processes exist?
+2. Before a session starts, what will be loaded, why, and with what estimated context cost?
+3. During and after a session, what is active context versus cumulative token use, and what evidence supports each number?
+4. What is active, inherited, duplicated, costly, risky, or stale?
+5. Which resources are shared and which must remain isolated?
+6. What safe, reversible action can CCO take now?
+7. What happened after the action, and how can the user undo it?
+
+The primary customer is an individual developer running several coding-agent sessions on one computer. Enterprise policy is a later extension of the same local model, not the starting point.
+
+## Locked product decisions
+
+- **CCO is the product.** `1MCP` is not a separate user-facing area or prerequisite.
+- **CCO is not another agent harness.** Its first-party advantage is observing and explaining independently launched tools across product boundaries, then planning reversible external changes.
+- The user-facing concepts are **MCP Runtime**, **Shared MCP Pool**, **Live Session Map**, **Resource Usage**, **Duplicate Detection**, **Session Isolation**, **Quarantine**, and **Runtime Security**.
+- CCO must remain useful when 1MCP is absent. It discovers direct MCP processes and can offer a safe path to a shared runtime.
+- When 1MCP is present, CCO may import or adopt it through a replaceable backend adapter. Advanced diagnostics may identify the engine; ordinary product copy should not require the user to know what 1MCP is.
+- CCO owns inventory, policy, security boundaries, migration UX, evidence, and rollback. A shared-runtime engine is an implementation detail.
+- CCO does not build a new MCP broker while an adoptable open-source engine can satisfy the approved boundary. Kiro Crew Gateway and 1MCP must be evaluated through the same adapter contract before any CCO-owned broker is proposed.
+- The policy vocabulary is: **Shared**, **Isolated**, **On-demand**, **Review**, **Quarantine**, and **Disabled**.
+- Browser automation and other stateful per-session tools default to **Isolated**. Low-frequency stateless services may be candidates for **Shared**. CCO must show evidence and let the user decide.
+- IntentProbe is a complementary scanner and receipt producer. It is not presented as a universal hard security boundary.
+- A token ring, historical usage chart, or generic session list is not a standalone CCO product area. Official harnesses and mature open-source tools already provide them.
+- A shared daemon, per-app MCP singleton, session resource table, token meter, or context compactor is not a moat by itself. Cline, Roo, Zed, Aider and Kiro Crew contain verified implementations of those components.
+- CCO's context advantage is the explanation-to-action loop: resolve what will load, preserve source and confidence, reconcile runtime evidence, preview a safe change, and roll it back.
+- Active context, cumulative processed input, cache read/write, output, reasoning, quota, credits, estimated cost, and actual billed cost are different measures. CCO never combines them under an ambiguous `tokens` or `cost` label.
+- Per-tool attribution must state its precision: exact native count, payload estimate, request association, derived re-read amplification, missing, or unsupported. Association is never represented as an exact marginal bill.
+- Every mutation is preview-first, narrowly scoped, revalidated immediately before execution, recorded, and reversible wherever technically possible.
+- Product work follows: requirement discussion -> approved Living Plan slice -> separate implementation session -> QA evidence -> integration decision.
+
+## Current product truth
+
+### Public baseline
+
+- npm `latest` is **0.19.3**.
+- The repository contains and tags **v0.20.0**, but npm and GitHub Releases do not expose it as the current public release.
+- The latest v0.20.0 publish run completed its test, audit, pack, and provenance stages, then npm returned `E404` on publish. The package exists at 0.19.3, so the observed failure is a publication-authority/configuration problem, not evidence that the package name is absent.
+- GitHub reports 383 stars and 33 forks at this review. There are zero open issues and five open discussions.
+
+### Shipped capability groups in the v0.20 codebase
+
+1. Cross-harness inventory for Claude Code, Codex CLI, OpenCode, and DeepSeek Harness.
+2. Effective-context and source/scope explanation where an adapter declares its precedence model.
+3. Context-budget estimates, including supported import expansion.
+4. Hygiene score and exact-duplicate, reversible repair.
+5. Unified memory search and source-aware Markdown editing.
+6. Static MCP security scanning, change baselines, and click-through remediation.
+7. MCP enable/disable controls and copy-only skill portability.
+8. Session Distiller, image trimming, export, backup, and undo flows.
+9. Local, opt-in privacy metrics and hardened loopback dashboard access.
+
+### Frozen work in progress
+
+Branch `feat/mcp-runtime-doctor` contains an uncommitted, unpushed Linux runtime-observation prototype. It already demonstrates that CCO can group sessions, attribute direct MCP processes, inspect a shared runtime, show resource pressure, detect selected risks, and stop a revalidated session. It is **not** an approved product implementation yet.
+
+The prototype must be treated as input to F1, not merged as-is. In particular, the current `1MCP Doctor` framing must be replaced by generic CCO runtime language, and the read-only observation contract must be separated from later mutation and policy work.
+
+### Live-machine evidence informing the roadmap
+
+A local snapshot on 2026-10-16 observed 12 Claude/Codex sessions, a shared runtime with 17 configured MCP servers, approximately 1.85 GB of runtime memory, 99 processes, and 352 of 512 cgroup tasks. It also found floating package specifications and credential-shaped command-line arguments without returning their values. This is one machine snapshot, not market prevalence, but it validates the personal-user problem: resource use, duplication, ownership, and isolation are difficult to see today.
+
+Persistent loopback socket observation is incomplete by design; short-lived requests can disappear between snapshots. CCO must report this limitation instead of inferring a complete client map.
+
+### Open-source architecture audit correction
+
+A maintainer primary-source audit of [Cline](https://github.com/cline/cline), [Roo Code](https://github.com/RooCodeInc/Roo-Code), [Zed](https://github.com/zed-industries/zed), [Aider](https://github.com/Aider-AI/aider), and [Kiro Crew](https://github.com/kirodotdev/KiroCrew) changes the competitive baseline:
+
+- Cline has an OS-singleton shared Hub but constructs MCP managers while building each session runtime. CCO must distinguish shared control plane from shared MCP backend rather than treating either label as proof.
+- Roo shares one MCP set only inside one extension host; Zed shares context servers at project scope. Runtime records therefore need an explicit owner scope, not a Boolean `shared` field.
+- Aider already provides a useful preflight token breakdown. F13 must explain cross-harness precedence, load mode, unknowns and scenario differences rather than reproduce `/tokens`.
+- Kiro Crew already provides multi-harness ACP orchestration, shared runtime support, per-session process-tree resource views, and a sophisticated MCP backend pool with host budgets and shareability evidence. CCO must not rebuild this stack by default.
+- Kiro Crew's resource API starts from sessions registered in its own `SessionManager`. CCO's differentiated first slice is a neutral join across arbitrary supported sessions already launched by editors and terminal harnesses: effective config -> live process/MCP topology -> evidence -> reversible plan.
+- Kiro and Devin's named official public repositories do not contain their product cores. Adapters for them must use documented or locally observable contracts and label internal details unknown.
+
+## Delivery map
+
+There is one blocking release gate and **14 feature packets**. The number is deliberately based on independently reviewable user outcomes, not UI screens or code modules. Every row links to its own Living Plan; the master roadmap does not authorize implementation by itself.
+
+| Order | ID | Feature packet | User outcome | Depends on | Decision |
+|---:|---|---|---|---|---|
+| 0 | P0 | [Release recovery and product-truth reset](plans/P0_RELEASE_RECOVERY.md) | Users can install the intended tested baseline—v0.20.0 if recoverable, otherwise a superseding version—and public claims match what is actually available. | None | Do first |
+| 1 | F1 | [External Runtime Reality Map](plans/F1_LIVE_RUNTIME_MAP.md) | Join independently launched supported sessions to process trees, MCP ownership scope, memory, age, duplication, confidence and observation limits without making CCO their launcher. | P0 | Differentiated first slice |
+| 2 | F13 | [Session Admission Preview](plans/F13_SESSION_ADMISSION_PREVIEW.md) | Before launch, see the resolved cross-harness local sources, precedence, load mode, estimated footprint, unknowns, and a scenario difference that can later be checked against F1. | P0, current inventory/effective-context model | Build after F1 schema is stable |
+| 3 | F2 | [Runtime Engine Compatibility](plans/F2_SHARED_RUNTIME_ADAPTER.md) | Test Kiro Crew Gateway and 1MCP against one backend-neutral external-client contract; adopt a working engine and document gaps before proposing any new broker. | F1 | Research/spike before pool work |
+| 4 | F14 | [Action-linked Context Evidence](plans/F14_CONTEXT_TOKEN_LEDGER.md) | Normalize only the context/token evidence needed to explain a cross-harness discrepancy or support an approved CCO action; do not recreate native meters. | F13, F1 | Narrow, after the reality join |
+| 5 | F3 | [Consolidate into Shared Pool](plans/F3_SHARED_POOL_MIGRATION.md) | Preview, migrate, verify, and roll back eligible direct MCP configurations without requiring 1MCP knowledge. | F2, F13 | Build next |
+| 6 | F4 | [Runtime Policy and Isolation](plans/F4_RUNTIME_POLICY.md) | Assign Shared, Isolated, On-demand, Review, Quarantine, or Disabled policy per capability and scope. | F2, F3 | Build next |
+| 7 | F5 | [Lifecycle and Resource Guardrails](plans/F5_RESOURCE_GUARDRAILS.md) | Start, stop, restart, idle-stop, and set resource thresholds with explicit ownership and safe defaults. | F1, F4 | Build next |
+| 8 | F6 | [Runtime Security and Quarantine](plans/F6_RUNTIME_SECURITY.md) | Connect IntentProbe receipts to CCO, explain decisions, quarantine safely, and restore after review. | F4, stable receipt contract | Build after control plane |
+| 9 | F7 | [Hygiene Findings v2](plans/F7_HYGIENE_FINDINGS.md) | Find broken references, unreachable MCPs, stale paths, semantic overlap, floating packages, and secret-exposure risks with evidence. | F1, adapter contracts | Build after runtime core |
+| 10 | F8 | [Context and Runtime Relationship Graph](plans/F8_RELATIONSHIP_GRAPH.md) | Trace session -> admission source -> runtime evidence -> policy -> MCP/tool and show blast radius before a change. | F13, F1, F14, F4, F7 | Build after data model stabilizes |
+| 11 | F9 | [Headless CLI and JSON Contract](plans/F9_HEADLESS_CLI.md) | Run inventory, findings, admission previews, runtime snapshots, and policy checks in scripts or CI with stable schemas and exit codes. | F13, F1, F7 | Build after contracts stabilize |
+| 12 | F10 | [Personal Vault and Reusable Profiles](plans/F10_PERSONAL_VAULT.md) | Park inactive artifacts, reuse reviewed settings fragments, and apply project profiles with preview and rollback. | Reversible transaction model | Conditional, after core runtime |
+| 13 | F11 | [Semantic Portability and Adapter Expansion](plans/F11_PORTABILITY.md) | Add conversions and new harnesses one verified artifact type and harness pair at a time. | Stable adapter and diff contracts | Later |
+| 14 | F12 | [Team Baselines](plans/F12_TEAM_BASELINES.md) | Define, audit, and optionally enforce shared team policy without weakening local privacy or rollback. | F4, F9, F11 | Last / enterprise extension |
+
+## Why this order
+
+```text
+P0 public baseline
+ -> F1 observe independently launched process and MCP reality
+ -> F13 explain the effective configuration that should create that reality
+ -> F2 test/adopt an existing runtime engine
+ -> F14 add only action-linked context/token evidence (also consumes F13)
+ -> F3 move safely
+ -> F4 decide shared vs isolated
+ -> F5 control resources
+ -> F6 quarantine with security evidence
+ -> F7 improve findings
+ -> F8 join admission, runtime, usage and blast radius
+ -> F9 expose stable automation contracts
+ -> F12 support teams
+
+F10 and F11 remain later, parallel candidates after the transaction and adapter
+contracts stop moving.
+```
+
+External runtime observation comes first because it is the part the reviewed harnesses do not provide across one another, and because CCO already has a frozen prototype to mine. Admission explanation then joins CCO's existing static strengths to that runtime schema. Engine evaluation comes before migration so a solo maintainer does not build infrastructure already available under an open-source license. Token/context work remains downstream and action-linked because native products already provide mature meters. Reversible migration comes before automatic optimization. Policy comes before quarantine. Stable data contracts come before CI and team enforcement.
+
+## Feature packet contracts
+
+Each packet below is the boundary for one future implementation session. A session may split the packet further after PM approval, but it may not silently pull work from another packet.
+
+### P0 — Release recovery and product-truth reset
+
+Outcome: v0.20.0 is either published and verified, or deliberately superseded by a new version with a documented reason.
+
+In scope:
+
+- Verify the npm trusted-publisher tuple and package ownership without exposing credentials.
+- Rerun the idempotent release workflow only after the publisher configuration is correct.
+- Verify npm, MCP Registry, GitHub Release, provenance, install smoke, and `latest` dist-tag.
+- Reconcile public roadmap statuses and remove claims that refer only to unmerged runtime work.
+- Decide whether the 34 MB activation-research corpus remains in CCO or is replaced by compact fixtures and links to IntentProbe.
+
+Acceptance:
+
+- A clean machine can install the intended public version and run `--help`, `--version`, and the dashboard.
+- All registries expose the same version.
+- README, release notes, package metadata, and runtime behavior agree.
+
+Non-goal: add runtime features during release recovery.
+
+### F13 — Session Admission Preview
+
+Outcome: before starting a session, a user can see which visible local sources will be admitted, why they apply, how they load, their estimated context footprint, what remains unknowable, and how that manifest relates to F1's observed runtime topology.
+
+In scope:
+
+- Resolve the selected harness, working directory, profile, model, scope and precedence path without launching an agent session.
+- List applicable instruction and memory files, skill metadata/body load mode, plugins, hooks, built-in/user tools, MCP servers/tool schemas, and other adapter-declared startup inputs.
+- Classify each component as always-on, catalog/description-only, on-demand, event-triggered, runtime-only, hidden vendor content, unavailable, or unsupported.
+- Report token method and confidence per component: supported native exact count, model tokenizer estimate, serialized-schema estimate, character heuristic, unknown, or hidden.
+- Compare bounded scenarios such as disable, move scope, on-demand, shared, or isolated, then hand the selected change to CCO's existing preview/rollback path.
+
+Acceptance:
+
+- The same fixture resolves to a deterministic manifest with source, precedence, load mode, method, confidence, and unknowns.
+- CCO never presents visible-local totals as the complete vendor prompt when hidden system content exists.
+- Default preview does not launch an agent, execute a hook, call a tool, or start an MCP process.
+- Optional active MCP schema probing, if approved later, is separately consented, bounded, redacted, and visibly different from static preview.
+
+Non-goal: predict the task's eventual conversation length, exact bill, model quality, or whether the model will invoke a tool.
+
+### F1 — External Runtime Reality Map
+
+Outcome: a user can understand current session and MCP resource use across independently launched supported harnesses without moving those sessions into a CCO-owned runtime or changing anything.
+
+In scope:
+
+- Same-user session/process discovery with platform capability labels and no requirement that CCO launched the process.
+- Session age, RSS, process/thread counts, direct MCPs, shared-runtime nodes, duplicate instances, owner scope, and observation confidence.
+- Join effective static configuration evidence to live process and transport evidence where the adapter can do so safely.
+- Represent `shared within extension host`, `shared within project`, `shared app runtime`, `OS-user pool`, and `remote service` separately.
+- Clear distinction between measured, inferred, unavailable, and unsupported data.
+- Generic CCO terminology; backend engine details live under Advanced diagnostics.
+
+Acceptance:
+
+- Useful with zero, one, and many sessions; with and without a shared runtime.
+- No command line, config content, secret value, raw socket record, or unrelated user process reaches the browser.
+- Short-lived connections and unsupported platforms are shown as unknown, not guessed.
+
+Non-goal: token/context accounting, migration, policy writes, quarantine, or automatic stopping. Token and context semantics belong to F14.
+
+### F14 — Action-linked Context Evidence
+
+Outcome: where a runtime/configuration diagnosis needs it, a user can inspect normalized context evidence without mixing incompatible counters or rebuilding the harness's native usage dashboard.
+
+In scope:
+
+- Add no adapter merely to show a number already clear in the native harness. Claude Code and Codex remain the first candidates only when a concrete F1/F13 discrepancy or action requires their evidence.
+- Separate active context occupancy/limit, request or turn usage, cumulative processed input, fresh input, cache read/write, output, reasoning, compaction, quota, credits, and cost authority.
+- Preserve source adapter/version, timestamp, model, unit, precision, completeness, and missing/interrupted state for every observation.
+- Attribute tool/MCP effects as exact native count, direct serialized payload estimate, request association, derived re-read amplification, or unavailable; never upgrade one class into another.
+- Show session/turn/tool evidence only when it produces an action or diagnosis that the native harness does not already explain; generic history charts remain out of scope.
+- Link recommendations to F13 scenario preview and later CCO policy/migration actions.
+
+Acceptance:
+
+- Ledger totals reconcile with native harness counters for frozen fixtures within an approved tolerance and explain any non-reconciling fields.
+- Active context is never computed by summing cumulative input; reasoning is not double-counted when it is a subset of output.
+- Compaction, cache snapshots, duplicated events, interrupted turns, missing final usage, model changes, and schema drift remain explicit.
+- API-equivalent estimates, subscription quota, credits, and actual billed money are visibly distinct.
+- No OAuth token scraping, TLS interception, prompt/tool-result upload, or unsupported private API is required.
+
+Non-goal: reproduce provider invoices, assign a fabricated exact marginal token bill to each tool call, or compete with native quota/status-line UI.
+
+### F2 — Runtime Engine Compatibility
+
+Outcome: CCO can prove whether an existing shared MCP engine can serve independently launched harnesses through a stable, backend-neutral contract before any migration or new broker work begins.
+
+In scope:
+
+- A versioned adapter contract for health, capabilities, inventory, session/client evidence, lifecycle support, and config import/export.
+- States for absent, available, healthy, degraded, incompatible, and externally managed runtimes.
+- A bounded compatibility experiment for both the existing local 1MCP runtime and Kiro Crew Gateway.
+- A written capability matrix covering external-client attachment, identity, pool key, stateful isolation, lifecycle, resource evidence, failure containment, install footprint, licensing and rollback.
+- A read-only adapter only for an engine that passes the minimum boundary; otherwise an explicit no-fit result and smallest missing contract.
+- One-click setup may be proposed only after an engine passes the compatibility experiment and install source, version pinning, consent, rollback, licensing and platform behavior are specified.
+
+Acceptance:
+
+- CCO continues to work when the adapter or engine is missing or broken.
+- UI and stored policy do not depend on 1MCP-specific names or file layout.
+- A fake adapter can exercise the contract without installing any engine.
+- No CCO-owned broker is proposed without code-level evidence that both evaluated engines fail the required boundary.
+
+### F3 — Consolidate into Shared Pool
+
+Outcome: a user can reduce duplicate eligible MCP processes safely.
+
+In scope:
+
+- Eligibility analysis with reasons: share candidate, must isolate, needs review, unsupported.
+- Preview showing source configs, proposed shared destination, changed loading behavior, and expected resource effect as an estimate.
+- Backup, fingerprint, apply, post-change health verification, transaction record, and rollback.
+- Partial-failure recovery and stale-preview rejection.
+
+Acceptance:
+
+- No silent overwrite, deletion, credential copying to logs, or partial success reported as complete.
+- Rollback restores the exact pre-change configuration when targets have not independently changed.
+- Stateful browser MCPs default to excluded/isolated.
+
+### F4 — Runtime Policy and Isolation
+
+Outcome: a user can explicitly choose which capabilities are shared and which belong to one session.
+
+In scope:
+
+- Policy schema for Shared, Isolated, On-demand, Review, Quarantine, and Disabled.
+- Scope and precedence rules with an effective-policy explanation.
+- Conflict detection between harness config, runtime capability, and CCO policy.
+- Preview of affected sessions before a policy mutation.
+
+Acceptance:
+
+- Every effective state names its source and precedence path.
+- Unknown capability semantics default to Review, not Shared.
+- Policy changes are transactional and auditable.
+
+### F5 — Lifecycle and Resource Guardrails
+
+Outcome: a user can recover resources without killing the wrong process or losing work unexpectedly.
+
+In scope:
+
+- Explicit start, graceful stop, restart, and detach where supported.
+- Optional idle-stop and resource thresholds, off by default.
+- Ownership checks, PID/start-time revalidation, CCO self-protection, graceful timeout, and escalation policy.
+- Per-session and per-runtime memory/task trends from bounded local samples, not unlimited background telemetry.
+
+Acceptance:
+
+- The default remains read-only.
+- Every destructive control names the exact target and consequence before confirmation.
+- PID reuse, stale views, child-process churn, and unsupported runtimes fail closed.
+
+### F6 — Runtime Security and Quarantine
+
+Outcome: suspicious capability or runtime events can be reviewed and contained with explainable evidence.
+
+In scope:
+
+- Versioned IntentProbe receipt ingestion for registration-time and runtime events.
+- Evidence view for subject type, decision, signal versions, redacted static spans, thresholds, and policy action.
+- Manual quarantine first; any automatic action requires a later separately approved policy.
+- Restore/review flow and false-positive reporting without uploading private scan contents by default.
+- Optional Hugging Face model card/artifact mirror as a distribution experiment for discovery and reproducibility; CCO integration must not depend on page traction or an online scan API.
+
+Acceptance:
+
+- Missing, late, incompatible, or malformed receipts do not become fabricated safety verdicts.
+- Raw prompts, arguments, responses, credentials, and private paths remain local and are retention-bounded.
+- Quarantine is reversible and names the exact object and sessions affected.
+
+### F7 — Hygiene Findings v2
+
+Outcome: findings are actionable evidence, not a generic score.
+
+In scope:
+
+- Broken references, unreachable MCPs, stale paths, conflicting or shadowed definitions, floating package versions, command-line secret exposure, and narrowly defined semantic overlap.
+- Severity, evidence, confidence, affected scope, recommended action, and reversibility for each rule.
+- Suppression with reason and expiry.
+
+Acceptance:
+
+- Deterministic rules have fixtures for true and false cases.
+- Heuristic findings are labelled and never presented as proven compromise.
+- A score never hides the underlying counts and evidence.
+
+### F8 — Context and Runtime Relationship Graph
+
+Outcome: a user can see why a capability is present, what admission/runtime/usage evidence exists, and what a proposed change will affect.
+
+In scope:
+
+- A normalized graph model joining config sources, admission-manifest components, inheritance, policies, sessions, usage observations, runtime instances, MCP servers, tools, hooks, and security receipts.
+- Filtered paths and blast-radius preview; the first UI can be a tree/table before a graphical canvas.
+
+Acceptance:
+
+- Every edge has a source, timestamp where relevant, and confidence/precision class.
+- Cycles, missing nodes, and partial runtime observations render without crashing or implying certainty.
+- The graph reuses canonical inventory rather than creating a second scanner.
+
+### F9 — Headless CLI and JSON Contract
+
+Outcome: users can automate the same evidence CCO shows in the dashboard.
+
+In scope:
+
+- Versioned JSON for inventory, findings, runtime snapshots, and policy checks.
+- Stable exit codes, redaction rules, human summaries, schema files, and compatibility policy.
+- CI examples that are read-only by default.
+
+Acceptance:
+
+- JSON stdout contains no progress noise; diagnostics go to stderr.
+- Golden fixtures and backward-compatibility tests cover schema evolution.
+- No mutating CI mode ships without a separate approved contract.
+
+### F10 — Personal Vault and Reusable Profiles
+
+Outcome: a user can keep a reviewed artifact inactive and later reuse it without rebuilding a bundle from memory.
+
+In scope:
+
+- A CCO-owned inert vault, not a fake harness scope.
+- Copy-first artifact storage, settings fragments with semantic preview, and reusable project profiles.
+- Secret references rather than casual duplication of credential values.
+
+Acceptance:
+
+- Harnesses cannot load vault contents accidentally.
+- Applying a profile is idempotent, conflict-aware, transactional, and reversible.
+- This packet proceeds only after fresh user validation; GitHub discussion #13 alone is not sufficient demand.
+
+### F11 — Semantic Portability and Adapter Expansion
+
+Outcome: users can move supported customizations without false claims of equivalence.
+
+In scope:
+
+- One artifact type and harness pair per sub-slice.
+- Field mapping, unsupported-field preservation, semantic diff, capability gates, and copy-first behavior.
+- Candidate harnesses from the public roadmap are Cursor, Windsurf, and Aider; their order requires fresh usage evidence.
+
+Acceptance:
+
+- Round-trip and behavior fixtures exist for every declared mapping.
+- Unsupported semantics remain visible and block one-click conversion when necessary.
+
+### F12 — Team Baselines
+
+Outcome: a team can compare local effective state with an approved baseline while keeping local content private.
+
+In scope:
+
+- Signed/versioned baseline format, audit mode, drift explanation, exceptions, and CI consumption.
+- Enforcement is optional and later than audit.
+
+Acceptance:
+
+- Personal paths, prompts, session content, and credentials are excluded from baseline exchange.
+- Local exceptions and organization policy precedence are explicit.
+- This packet does not redefine CCO around enterprise buyers.
+
+## Items deliberately not promoted to core roadmap
+
+- **Empirica-specific vectors, findings, and calibration UI:** useful inside Empirica, but not a general CCO control-plane concern. Generic runtime/context inputs may use an adapter later.
+- **CLI-Anything dependency:** the underlying request for extensible actions is valid, but no dependency is accepted without a concrete CCO workflow and security contract.
+- **External scanner engine marketplace:** defer until the core finding and receipt schemas are stable.
+- **Automatic quarantine or automatic session killing:** not before manual workflows produce false-positive and recovery evidence.
+- **Generic token meter or cost tracker as a standalone product area:** native harnesses and mature open-source tools already cover rings, quotas and historical charts. F14 exists only to normalize semantics, preserve provenance, connect observations to F13/F4 actions, and keep API-equivalent cost separate from actual subscription spend.
+
+## Session workflow
+
+For each packet:
+
+1. The PM session opens the existing feature-specific Living Plan in `docs/plans/`, resolves its open requirements with Nicole, and records the decisions there.
+2. Nicole explicitly approves the feature plan.
+3. A fresh implementation session receives only that approved scope, relevant files, non-goals, and acceptance criteria.
+4. The implementation session updates the plan with decisions and produces a bounded diff; it does not merge, publish, or widen scope by itself.
+5. A QA session follows `docs/QA_PLAN.md`, records evidence, and reports pass/fail/unknown for every acceptance criterion.
+6. Integration happens only after failures and unknowns are resolved or explicitly accepted by Nicole.
+
+Every feature-session handoff must contain:
+
+- packet ID and approved objective;
+- user story and non-goals;
+- exact starting branch/commit and dirty-worktree warning;
+- interfaces allowed to change;
+- privacy, security, reversibility, and platform constraints;
+- acceptance criteria and required QA rows;
+- stop conditions and unresolved product decisions;
+- required documentation and evidence artifacts.
+
+## Decision gates still requiring Nicole
+
+These are intentionally deferred until their packet reaches planning:
+
+- Whether CCO installs a shared-runtime engine or only guides/imports an existing installation.
+- Default threshold values and whether idle-stop ever becomes opt-out.
+- Retention duration for runtime samples and IntentProbe receipts.
+- F14 retention duration for normalized usage observations and whether text-free aggregates survive source-log deletion.
+- Whether F13 ever performs explicit active MCP schema probing or remains static/cache-only by default.
+- Whether the Personal Vault has enough real demand for F10.
+- Which harness follows the current four adapters.
+- Which organization owns any Hugging Face model/dataset pages and what artifact subset is mirrored.
+
+## Evidence ledger
+
+| Evidence | Retrieved | What it supports | Limits / adverse point |
+|---|---|---|---|
+| `README.md`, `AI_INDEX.md`, `docs/CONTROL_PLANE_PLAN.md`, `docs/REPO_REVIEW_2026-09-11.md`, `docs/releases/v0.20.0.md` at local `39cf32f` plus frozen WIP | 2026-10-16 | Shipped claims, old roadmap, architecture, release intent, runtime prototype boundary | README contains unmerged runtime claims and stale roadmap statuses |
+| GitHub issues 2, 3, 4, 6, 11, 12, 17, 27, 32, 33, 35, 37 and associated comments | 2026-10-16 | All issue demand and defect history reviewed | All are closed; no open issue is current backlog evidence |
+| GitHub discussions 7, 8, 9, 10, 13 and comments | 2026-10-16 | Runtime context idea, Empirica scope decision, Personal Vault/template proposal | Discussions are old and are not proof of broad demand |
+| GitHub repository/actions/releases API | 2026-10-16 | 383 stars, 33 forks, zero open issues, five discussions, v0.20 workflow state | Counts are time-sensitive |
+| npm registry for `@mcpware/cross-code-organizer` | 2026-10-16 | `latest` remains 0.19.3 | Does not reveal npm account configuration |
+| Publish run `38975225907` | 2026-10-16 | v0.20 tests/pack/provenance passed; npm publish returned E404 permission/not-found response | Exact npm-side misconfiguration still needs authenticated account inspection |
+| Local runtime snapshot from `feat/mcp-runtime-doctor` | 2026-10-16 | Personal-machine resource and topology problem exists | One machine; socket sampling is incomplete |
+| `notable-repos/1mcp-app-agent` README/source, Apache-2.0 | 2026-10-16 | First backend supports aggregation, templates, lazy/async loading, lifecycle | CCO must not inherit 1MCP as its product vocabulary or hard dependency |
+| IntentProbe `README.md`, `ROADMAP.md`, `docs/V0_2_DEMO_PACK.md` on local `1716099` worktree | 2026-10-16 | Warm JSONL receipt capability and honest research-preview limits | Worktree is ahead/dirty; integration must target a versioned stable contract |
+| Public primary sources linked from the feature packets, including Cline, Roo Code, Zed, Aider, Kiro Crew, Claude Code, Cursor and Kiro documentation | 2026-10-16 | Official harness matrix, open-source prior art, threat model, accounting ontology, and F13/F14 sequencing | Product landscape changes quickly; adapter claims require revalidation during implementation |
+| Installed Codex `0.163.0` generated app-server schemas and Claude Code `2.1.261` local transcript key-shape inspection | 2026-10-16 | Supported local usage primitives, tool event fields, and absence of a stable exact per-tool token field in reviewed interfaces | Local schema/log formats can drift; internal-only Codex notifications are not an implementation contract |
+
+## Change policy for this plan
+
+- Update the `Last updated`, status, packet state, decision log, and evidence ledger whenever a product decision changes.
+- Never mark a feature shipped from a code diff alone; require QA evidence and public-version verification where applicable.
+- New ideas enter as evidence or an option first. They become ordered packets only after the user problem, boundary, dependency, and acceptance criteria are defined.
+- If README roadmap and this document disagree, this document governs planning; README governs only public claims about a published release.
+
+## Decision log
+
+| Date | Decision | Reason |
+|---|---|---|
+| 2026-10-16 | Make CCO the user-facing product and treat 1MCP as a replaceable adapter. | Users understand the problem CCO solves; they should not need to know or install a separately branded runtime first. |
+| 2026-10-16 | Prioritize individual local runtime control before enterprise baselines. | It matches the intended customer and can be built/tested by a solo developer in bounded slices. |
+| 2026-10-16 | Freeze the current runtime prototype pending feature-plan approval. | It proves feasibility but mixes observation, control, backend branding, and future security work. |
+| 2026-10-16 | Put release recovery before new features. | npm users still receive 0.19.3 while the repository claims v0.20 capability. |
+| 2026-10-16 | Separate the roadmap into 14 implementation packets. | Each packet has one reviewable user outcome and can be handed to a fresh implementation session. |
+| 2026-10-16 | Add F13 before runtime control and keep it separate from F14. | Pre-launch resolution is a bounded static user outcome; live/retrospective accounting has different sources, privacy risks, failure modes, and QA. |
+| 2026-10-16 | Reject a generic token meter or session list as CCO's differentiator. | Claude, Codex, Cursor, Copilot, Kiro, Qwen, Cline, Roo, Zed, Junie, Factory and open-source dashboards already commoditize those surfaces. |
diff --git a/docs/QA_PLAN.md b/docs/QA_PLAN.md
new file mode 100644
index 0000000..ce2c951
--- /dev/null
+++ b/docs/QA_PLAN.md
@@ -0,0 +1,239 @@
+# CCO Product QA Plan
+
+Last updated: 2026-10-16 PT
+Owner: Nicole (risk acceptance) + feature QA session (evidence)
+Status: **Draft paired with `docs/LIVING_PLAN.md`**
+
+This plan defines the minimum evidence required before a CCO feature can be integrated and the additional evidence required before it can be described as shipped. It does not authorize implementation, merging, publishing, or destructive live-machine tests.
+
+## Release rule
+
+A feature is ready for integration only when all three are true:
+
+1. Its approved requirements and non-goals are traceable to tests or inspected evidence.
+2. Every required QA row is PASS, or Nicole explicitly accepts a documented residual risk.
+3. Package, privacy, security, platform, and documentation checks appropriate to the change pass.
+
+A feature is shipped only after the final release checklist also passes, including installation and smoke testing of the exact public artifact after publication.
+
+Passing unit tests alone is not release evidence. A green test suite on an unpublished tag is not a shipped release.
+
+## Required evidence bundle per feature
+
+Each QA session produces one dated Markdown report under `docs/qa/` containing:
+
+- feature packet ID, approved plan link, branch, commit, and dirty-worktree state;
+- environment matrix and exact commands;
+- acceptance-criterion table with `PASS`, `FAIL`, or `UNVERIFIED`;
+- test counts and failures, including intentionally skipped cases and reasons;
+- screenshots only for visual behavior that DOM assertions cannot establish;
+- privacy/data-flow review and secret-leak checks;
+- mutation, rollback, stale-state, and partial-failure evidence where applicable;
+- resource measurements for runtime/background work;
+- package contents, install smoke, and documentation-claim comparison;
+- unresolved risks and the smallest next action.
+
+## Core QA matrix
+
+Every feature selects all applicable rows; `N/A` requires a reason.
+
+| Area | Required checks |
+|---|---|
+| Requirements | Every acceptance criterion maps to code/test/evidence; no unapproved adjacent feature entered the diff. |
+| Regression | Targeted unit tests, affected E2E flows, then full unit/E2E suites before integration. |
+| Privacy | Enumerate every value crossing process/API/UI/storage boundaries; verify redaction, retention, opt-in, and no content upload. |
+| Local security | Loopback/host/origin checks, path allowlists, symlink boundaries, body limits, command construction, dependency audit, and no secret-bearing logs. |
+| Reversibility | Preview fingerprint, stale-preview rejection, exact backup, apply result, undo, edited-target refusal, and partial-failure recovery. |
+| Process safety | Same-user ownership, PID start identity, executable/harness identity, CCO self-protection, PID reuse, disappearing process, and graceful timeout. |
+| Resource use | Idle and loaded CPU/RSS, sampling interval, number of child processes, cgroup/task pressure where available, bounded storage, and shutdown cleanup. |
+| Platform | Linux native, macOS, Windows native, and WSL according to declared support; unsupported behavior is explicit and safe. |
+| UX/accessibility | Keyboard path, focus, labels, empty/loading/error/partial states, narrow viewport, high-data volume, confirmation copy, and no backend jargon in normal mode. |
+| Concurrency | Multiple browser tabs, overlapping scans, config changed between preview/apply, simultaneous sessions, and server restart. |
+| Compatibility | No shared runtime, supported runtime, incompatible version, unhealthy runtime, mixed direct/shared MCPs, and older stored transactions/policies. |
+| Accounting | Keep active context, cumulative processed input, cache, output, reasoning, quota, credits and cost authority separate; preserve source, unit, precision, timestamp and completeness. |
+| Packaging | `npm pack --dry-run`, allowlisted files, clean tarball install, Node support range, CLI flags, dashboard start, and no research/cache/secret artifacts. |
+| Documentation | README, privacy notice, schemas, help text, release notes, and UI agree with observed behavior; no WIP claim appears as shipped. |
+| Supply chain | `npm audit --omit=dev`, pinned release actions, provenance, registry version agreement, and post-publish install verification. |
+
+## Standard test environments
+
+Use fixtures or disposable test homes for mutations. Do not modify Nicole's active harness configuration merely to obtain QA evidence.
+
+Minimum scenarios:
+
+- zero, one, five, and high-count concurrent sessions;
+- two or more independently launched harness families that CCO did not start;
+- zero MCPs, direct-only MCPs, shared-only MCPs, and mixed topology;
+- sharing scopes represented separately: session, extension host, project, app runtime, OS-user pool, and remote service;
+- duplicate stateless MCPs and distinct stateful browser MCPs;
+- no runtime engine, healthy engine, unhealthy engine, incompatible engine, and engine disappearing during refresh;
+- no IntentProbe, installed but stopped, warm scanner ready, malformed/late receipt, and version mismatch;
+- clean paths, spaces/unicode, symlinks, Windows drive paths, UNC paths, stale paths, and permission errors;
+- empty, normal, and high-volume inventories;
+- admission fixtures with nested/cyclic/missing imports, shadowed scopes, always-on/catalog/on-demand/event-triggered inputs, hidden vendor content, and multiple count methods;
+- usage fixtures with cache snapshots, duplicate events, retries, compaction, model changes, interrupted turns, missing final usage, and schema drift;
+- clean shutdown and forced interruption during each transaction phase.
+
+Real-machine observation may supplement fixtures for resource accuracy. Destructive controls on live sessions require a separate, explicit test target and confirmation; they are never inferred from approval of this plan.
+
+## Packet-specific QA gates
+
+### P0 — Release recovery
+
+- Confirm the exact npm trusted-publisher package, organization, repository, workflow filename, and environment tuple through authenticated npm UI/API evidence.
+- Verify the release job uses OIDC and does not fall back to a long-lived token.
+- Re-run only the intended version; verify idempotency if npm or MCP Registry already contains it.
+- Verify npm version/dist-tag, MCP Registry version, GitHub Release, provenance, checksums, and clean install output.
+- Compare the installed tarball behavior with release notes and README.
+
+### F1 — External Runtime Reality Map
+
+- Synthetic `/proc`/process fixtures cover parent churn, PID reuse, zombie/disappearing processes, unrelated same-name commands, and multiple users.
+- Compare reported RSS/process/thread counts with an independent OS command on a bounded fixture.
+- Verify measured/declared/inferred/unknown labels, owner scope, and short-lived socket limitations.
+- Cover Cline's shared Hub plus session-built MCP managers, Roo's extension-host singleton, Zed's project store and a Kiro Crew multiplexed runtime; no fixture may reduce these to the same Boolean `shared` state.
+- Verify a shared PID/tree is measured once in host totals. Any per-session split is labelled attribution and does not multiply process/MCP counts by tenant count.
+- Verify the map remains useful when every session was launched outside CCO and no runtime engine adapter is installed.
+- Browser receives no command lines, environment variables, raw sockets, private paths, or secret values.
+- Refresh, error, empty, high-count, and unsupported-platform UI states pass DOM and accessibility checks.
+
+### F13 — Session Admission Preview
+
+- Golden fixtures cover each supported harness/cwd/profile/model tuple, source scope, precedence rule, effective/shadowed item, and load mode.
+- Resolve nested imports, cycles, duplicates, missing targets, symlinks, unicode/spaces, Windows/UNC paths, permission failures and path escape without duplicate counting.
+- Verify every number exposes its tokenizer or heuristic, model assumption, confidence and freshness; incompatible methods are not silently summed.
+- Verify visible subtotal, hidden vendor content, unavailable runtime additions and unsupported adapter fields remain distinct.
+- Default preview starts no agent, hook or MCP child process, performs no network request, and writes no harness configuration.
+- Scenario-diff arithmetic matches component changes and produces no real mutation; any later action handoff keeps preview/fingerprint/rollback boundaries.
+- Browser/API/storage payload review proves that secret values, environment values and private file bodies are excluded.
+
+### F14 — Action-linked Context Evidence
+
+- Frozen fixtures for the minimum harness pair required by the approved F1/F13 case reconcile active context, per-request/turn usage and cumulative usage independently against native source records.
+- Test input, net-new input, cache read, cache write, output, reasoning and total under both subset and additive provider semantics; reject double counting.
+- Test duplicate snapshots, partial streams, retries, compaction, retained/dropped context, model switches, interrupted turns, missing final events, rotated logs and unsupported schema versions.
+- Every observation includes harness/adapter version, session/turn/request identity where available, model, timestamp, source, unit, precision and completeness.
+- Per-tool/MCP cases distinguish native exact, payload estimate, request association, derived amplification and unavailable; copy and UI never call association an exact marginal bill.
+- API-list-price estimate, provider estimate, subscription quota, internal credits and actual billed money remain separate labels and fields.
+- Verify no OAuth credential reuse, private API dependency, TLS interception, or raw prompt/source/command/tool payload persistence.
+- High-session and long-log fixtures meet pre-approved scan latency, CPU, RSS and storage budgets; incremental refresh does not rescan unmodified history without reason.
+- The QA report names the F1/F13 finding or approved action that requires each metric family and demonstrates why native UI alone is insufficient; an unlinked dashboard or history chart fails scope review.
+
+### F2 — Runtime Engine Compatibility
+
+- Contract tests run against fake absent, available, healthy, degraded, incompatible, externally managed, and crashing adapters.
+- Run the same external-attachment, identity, isolation, lifecycle, failure and rollback cases against pinned Kiro Crew Gateway and local 1MCP; record PASS, FAIL or UNVERIFIED per case with source/runtime evidence.
+- Kiro Crew- or 1MCP-specific fields do not leak into generic stored policy or ordinary UI.
+- Adapter timeout/crash cannot block the main inventory or leave orphan processes.
+- Version/capability negotiation has explicit compatibility fixtures.
+- Absence or installation failure of either engine leaves F1 inventory operational.
+- A CCO-owned broker implementation in the diff is an automatic scope failure.
+
+### F3 — Consolidate into Shared Pool
+
+- Golden previews cover eligible, isolate, review, unsupported, conflicting, and already-shared cases.
+- Apply and rollback run against disposable real config trees for every supported harness/path type.
+- Inject failure before backup, after backup, midway through writes, during health verification, and during rollback.
+- Verify no credential value appears in logs, UI, transaction metadata, or snapshots.
+- Prove stateful browser MCPs are excluded by default.
+
+### F4 — Runtime Policy and Isolation
+
+- Truth-table tests cover every policy state, scope, precedence collision, default, and unsupported capability.
+- Effective-policy explanation identifies the winning source.
+- Stale policy edits and concurrent writers return conflicts instead of overwriting.
+- Policy migration/versioning and unknown future enum values fail safely.
+
+### F5 — Lifecycle and Resource Guardrails
+
+- Test exact-target confirmation, same-user checks, PID/start-time revalidation, self-tree refusal, graceful stop, timeout, and disappearing target.
+- Idle detection uses controlled clocks and activity fixtures; defaults remain off.
+- Threshold crossing, hysteresis, cooldown, restart loop prevention, and storage bounds are tested.
+- Measure monitor overhead at idle and with high session count.
+
+### F6 — Runtime Security and Quarantine
+
+- Validate IntentProbe receipt schema/version, redaction, ordering, deduplication, retention, replay, malformed input, and late delivery.
+- Verify missing receipts display `unknown`, never `allow`.
+- Quarantine preview/apply/restore covers active use, concurrent policy changes, partial failure, and target edits.
+- Run known clean, warn, and block fixtures; report false-positive/false-negative evidence without overstating security guarantees.
+- If Hugging Face artifacts are published, verify model card, license, checksums, artifact/version mapping, download path, and offline fallback.
+
+### F7 — Hygiene Findings v2
+
+- Each deterministic rule has positive, negative, boundary, and platform fixtures.
+- Heuristic rules include confidence labels and a reviewed false-positive set.
+- Suppression reason, expiry, scope, and reappearance after expiry are tested.
+- Score arithmetic exactly reconciles with visible findings.
+
+### F8 — Relationship Graph
+
+- Graph fixtures cover cycles, missing nodes, duplicate identities, partial observations, stale edges, and high node counts.
+- Every edge resolves to evidence and confidence; blast-radius results match a direct traversal oracle.
+- Keyboard and non-visual alternatives expose the same information as any graphical view.
+
+### F9 — Headless CLI and JSON
+
+- JSON Schema validation, golden output, stdout/stderr separation, stable exit codes, redaction, and deterministic ordering.
+- Compatibility tests read at least one prior schema version.
+- CI fixtures cover clean, warning, failure, unsupported, and internal-error outcomes.
+- Headless mode performs no mutation unless a future separately approved contract adds it.
+
+### F10 — Personal Vault and Reusable Profiles
+
+- Prove vault paths are outside every harness discovery root and remain inert.
+- Copy/apply/remove/rollback tests cover conflicts, missing references, secret references, idempotency, and edited targets.
+- Settings-fragment merges use semantic fixtures for arrays, nested objects, deletion, ordering, and unsupported keys.
+
+### F11 — Semantic Portability and Adapter Expansion
+
+- Each harness-pair/artifact-type slice has field mapping, unsupported-field, semantic-diff, round-trip, and behavior fixtures.
+- Inventory-only adapters cannot expose unsupported mutation controls.
+- Source remains unchanged unless the approved operation explicitly says otherwise.
+
+### F12 — Team Baselines
+
+- Baseline schema/version/signature verification, drift, exception, expiry, precedence, and offline behavior.
+- Inspect exchanged payloads to prove exclusion of prompts, sessions, paths, credentials, and private content.
+- Audit mode and enforcement mode remain distinguishable in API, UI, and exit codes.
+
+## Visual QA rule
+
+Use DOM assertions and accessibility inspection for behavior. Capture screenshots only for layout, hierarchy, overflow, color, responsive behavior, or visual ambiguity. A visual pass must inspect at least desktop and narrow widths, long names, large counts, error states, and mixed runtime topology.
+
+## Performance budgets
+
+Each runtime feature plan must set numeric budgets before implementation. At minimum record:
+
+- cold and warm snapshot latency;
+- idle and active CPU;
+- CCO RSS excluding managed MCP/runtime processes;
+- additional child-process count;
+- bytes retained per hour/day;
+- maximum scan/refresh concurrency;
+- shutdown time and orphan-process count.
+
+The PM session chooses acceptable thresholds after the F1 baseline measurement. QA must not invent passing thresholds after seeing results.
+
+## QA stop conditions
+
+Stop integration and return to the feature plan when:
+
+- observed behavior requires a new product decision;
+- a mutation cannot be made stale-safe or recoverable;
+- secrets or private content cross an undocumented boundary;
+- backend-specific behavior leaks into the generic product contract;
+- unsupported platforms appear to work but are untested;
+- the feature materially increases idle resource use without an approved budget;
+- documentation needs caveats that contradict the promised user outcome.
+
+## Final release checklist
+
+- Approved feature plan and completed QA report exist.
+- Full unit and E2E suites pass from a clean checkout.
+- Dependency and code-scanning results are reviewed.
+- Packed artifact contents and clean-install smoke pass.
+- Version metadata, release notes, README, translations selected for the release, privacy notice, schemas, and UI copy agree.
+- Published npm, MCP Registry, GitHub Release, provenance, and checksums are verified.
+- The installed public artifact is tested, not merely the repository checkout.
+- Only then may the feature be marked shipped in `docs/LIVING_PLAN.md` and public roadmap copy.
diff --git a/docs/plans/F10_PERSONAL_VAULT.md b/docs/plans/F10_PERSONAL_VAULT.md
new file mode 100644
index 0000000..f4927a2
--- /dev/null
+++ b/docs/plans/F10_PERSONAL_VAULT.md
@@ -0,0 +1,54 @@
+# F10 — Personal Vault and Reusable Profiles — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — discovery required**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f10--personal-vault-and-reusable-profiles`](../QA_PLAN.md#f10--personal-vault-and-reusable-profiles)
+
+## User problem
+
+Users may want to keep a reviewed skill, command, MCP definition, or settings fragment inactive, then reuse it later without rebuilding it from memory or making it globally active.
+
+## Outcome
+
+Create a CCO-owned inert vault with copy-first storage and previewable, reversible project profiles, if fresh discovery confirms demand.
+
+## In scope candidate
+
+- Vault outside every harness discovery root.
+- Copy-first artifact storage.
+- Settings fragments with semantic diff and reversible apply/remove.
+- Project profiles referencing reviewed vault items.
+- Secret references instead of casual credential duplication.
+
+## Non-goals
+
+- The vault is not a fake harness scope.
+- It is not a backup replacement or sync service.
+- Templates do not silently overwrite or claim cross-harness equivalence.
+
+## Existing demand evidence
+
+GitHub discussion #13 proposed a Holding Area, settings fragments, and project templates. The prior response correctly noted that disabled MCP/plugin states already solve part of the problem. One old discussion is insufficient to start implementation.
+
+## Open decisions for Nicole
+
+- Whether this pain is important enough after runtime work.
+- Which artifact types v1 supports.
+- Local-only versus optional user-managed sync.
+- Secret reference format and supported credential stores.
+
+## Discovery gate
+
+Before approval, collect at least three concrete workflows that existing disable, backup, and copy-only migration cannot handle. Rewrite the scope around those workflows and remove unused artifact types.
+
+## Acceptance criteria candidate
+
+- Vault contents are provably inert to every supported harness.
+- Profile apply is idempotent, conflict-aware, transactional, and reversible.
+- Missing references and edited targets fail safely.
+- Secrets never enter templates, logs, or browser payloads accidentally.
+
+## Implementation-session boundary
+
+No implementation session may start while this plan remains `discovery required`.
diff --git a/docs/plans/F11_PORTABILITY.md b/docs/plans/F11_PORTABILITY.md
new file mode 100644
index 0000000..15a325a
--- /dev/null
+++ b/docs/plans/F11_PORTABILITY.md
@@ -0,0 +1,54 @@
+# F11 — Semantic Portability and Adapter Expansion — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — discovery required**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f11--semantic-portability-and-adapter-expansion`](../QA_PLAN.md#f11--semantic-portability-and-adapter-expansion)
+
+## User problem
+
+Users repeat customizations across coding harnesses, but formats and runtime semantics differ. Blind file copying can silently change behavior.
+
+## Outcome
+
+Add one verified artifact type and harness pair per sub-plan, with semantic diff, unsupported-field preservation, and copy-first behavior.
+
+## In scope candidate
+
+- Field mapping and capability matrix for one artifact/harness pair.
+- Semantic preview and explicit unsupported fields.
+- Copy-first conversion, rollback where a target is created, and round-trip fixtures.
+- Inventory-only adapter support before mutation support.
+
+## Non-goals
+
+- No universal converter.
+- No claim that similarly named features behave identically.
+- No simultaneous addition of Cursor, Windsurf, and Aider.
+
+## Locked decisions
+
+- Each harness pair/artifact type receives its own approved sub-plan.
+- Unsupported semantics block one-click conversion when behavior could change materially.
+- Source artifacts remain unchanged by default.
+
+## Open decisions for Nicole
+
+- Next harness based on current usage evidence, not the old README list alone.
+- First artifact type after portable skills.
+- Required round-trip fidelity and what losses may be accepted.
+
+## Discovery gate
+
+Collect current official format/precedence documentation and user demand for candidate harnesses. Select exactly one harness and one artifact type before changing this status to Approved.
+
+## Acceptance criteria candidate
+
+- Mapping and unsupported-field behavior are documented and fixture-tested.
+- Semantic diff predicts the actual target behavior within declared limits.
+- Round trips preserve all supported information.
+- Unsupported mutations remain disabled in API and UI.
+
+## Implementation-session boundary
+
+No session receives “add more harnesses” as an open-ended task. The handoff must name one exact pair and artifact type.
diff --git a/docs/plans/F12_TEAM_BASELINES.md b/docs/plans/F12_TEAM_BASELINES.md
new file mode 100644
index 0000000..32cdb1e
--- /dev/null
+++ b/docs/plans/F12_TEAM_BASELINES.md
@@ -0,0 +1,57 @@
+# F12 — Team Baselines — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — later enterprise extension**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f12--team-baselines`](../QA_PLAN.md#f12--team-baselines)
+
+## User problem
+
+Teams may eventually need to compare each developer's effective local setup with an approved policy without collecting private session or configuration content.
+
+## Outcome
+
+Define a privacy-preserving, versioned baseline format with audit, drift explanation, exceptions, and optional later enforcement.
+
+## In scope candidate
+
+- Signed/versioned baseline format.
+- Local audit and drift explanation.
+- Scoped exceptions with owner, reason, and expiry.
+- Headless consumption through F9.
+- Separate audit and enforcement modes.
+
+## Non-goals
+
+- No enterprise-first redesign of CCO.
+- No upload of prompts, sessions, paths, credentials, or private file content.
+- No enforcement in the first slice.
+
+## Dependencies
+
+- F4 policy semantics must be stable.
+- F9 schemas and exit codes must be stable.
+- F11 must establish how supported artifacts vary across harnesses.
+- Individual-user runtime work must already deliver value independently.
+
+## Open decisions for Nicole
+
+- Whether real team demand exists.
+- Baseline distribution and signature ownership.
+- Organization versus local exception precedence.
+- Whether enforcement belongs in CCO or an external policy system.
+
+## Discovery gate
+
+Obtain concrete team workflows and privacy requirements before approval. Audit-only is the default proposal.
+
+## Acceptance criteria candidate
+
+- Baseline exchange excludes all prohibited private fields.
+- Signature/version, drift, exception, expiry, precedence, and offline behavior are tested.
+- UI and CLI clearly distinguish audit from enforcement.
+- Local CCO remains functional without a team baseline or network service.
+
+## Implementation-session boundary
+
+No implementation begins until individual-user runtime, policy, and CLI contracts are stable and Nicole explicitly promotes this plan.
diff --git a/docs/plans/F13_SESSION_ADMISSION_PREVIEW.md b/docs/plans/F13_SESSION_ADMISSION_PREVIEW.md
new file mode 100644
index 0000000..f576ad9
--- /dev/null
+++ b/docs/plans/F13_SESSION_ADMISSION_PREVIEW.md
@@ -0,0 +1,97 @@
+# F13 — Session Admission Preview — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f13--session-admission-preview`](../QA_PLAN.md#f13--session-admission-preview)
+
+## User problem
+
+Before opening a coding-agent session, a user cannot reliably see which instructions, memories, skills, plugins, hooks, tools and MCP schemas will apply for the chosen harness and working directory. File size, token cost, precedence and load timing are spread across different scopes and harness rules. The user discovers the overhead only after the session has started.
+
+## Outcome
+
+Given one selected harness, working directory, profile and model, produce a deterministic pre-launch manifest of visible local inputs: why each item applies, when it loads, its estimated footprint and method, which parts are unknown, how it differs from another bounded scenario, and which later F1 runtime observations could confirm or contradict it.
+
+## In scope
+
+- Claude Code, Codex CLI, OpenCode and DeepSeek Harness only where the current adapter can declare verified resolution semantics.
+- Resolve user, project, local and managed sources with the adapter's explicit precedence model.
+- Inventory instruction and memory files, imported content, skill descriptions and bodies, plugins, hooks, built-in/user tool catalogs, MCP server/tool schemas and adapter-declared startup metadata.
+- Load-mode vocabulary: **Always-on**, **Catalog only**, **On-demand**, **Event-triggered**, **Runtime-only**, **Hidden vendor content**, **Unavailable**, and **Unsupported**.
+- Count-method vocabulary: **Native exact**, **Model tokenizer estimate**, **Serialized-schema estimate**, **Character heuristic**, **Unknown**, and **Hidden**.
+- Per-component source, scope, precedence path, effective/shadowed state, load mode, token estimate, method, confidence and warnings.
+- Bounded scenario comparison for disabling, moving scope, converting to on-demand, sharing, isolating or removing duplicate visible inputs.
+- Handoff to an existing or later approved CCO preview-first mutation. F13 itself stays read-only.
+- Exportable, versioned manifest fixture for QA and later F14 reconciliation.
+
+## Default safety contract
+
+- Preview does not launch a coding-agent session.
+- Preview does not execute hooks, prompts, skills or tools.
+- Preview does not start or connect to an MCP process by default.
+- Secret values, environment values, file bodies, tool arguments and private paths do not enter telemetry or browser payloads beyond the minimum redacted display contract.
+- Hidden system prompts and provider-side transformations remain `Hidden` or `Unknown`; CCO never invents their size.
+- An optional active MCP schema probe is a separate unresolved product decision. If later approved, it must be explicit, bounded, time-limited, redacted and visibly separated from static/cache evidence.
+
+## Non-goals
+
+- No prediction of final conversation length, output tokens, task quality, latency or actual bill.
+- No claim that a configured skill/tool will be selected by the model.
+- No generic prompt linter or prompt-writing coach.
+- No universal exact count when the complete provider request is unavailable.
+- No reproduction of Aider-style file/token itemization unless CCO also adds verified precedence, load-mode, cross-harness comparison or an action handoff.
+- No automatic optimization or configuration write.
+
+## Locked decisions
+
+- This is a pre-launch explanation, not another context ring.
+- The selected harness/cwd/profile/model tuple is part of the manifest identity.
+- Estimate and unknown are allowed to coexist; visible subtotal is never labelled complete when hidden content exists.
+- Counts from different tokenizers or heuristics are not silently summed without a displayed method and compatibility rule.
+- Scenario savings are estimates until F14 can compare them with runtime evidence.
+- F13 ships after the F1 observation schema stabilizes and before F14. It reuses CCO's inventory/effective-context strengths and emits expectations that later runtime evidence can reconcile.
+
+## Open decisions for Nicole
+
+- Whether v1 supports only one selected tuple at a time or includes side-by-side harness comparison.
+- Whether the main screen shows a compact manifest with Advanced detail, or opens directly into the existing context-budget view.
+- Whether active MCP schema probing belongs in F13 v1, a later slice, or never.
+- Which tokenizer packages may be shipped locally and the acceptable package-size/security trade-off.
+- Whether a scenario comparison may deep-link into an already shipped mutation, or remains copy-only until its target feature is approved.
+
+## Delivery slices
+
+1. Freeze the admission-manifest schema, load-mode vocabulary, count-method vocabulary and privacy boundary.
+2. Implement one complete Claude Code fixture and one Codex fixture without active processes.
+3. Add source/precedence explanation and explicit hidden/unknown rows.
+4. Add one scenario diff that changes only a disposable fixture and reports estimated savings.
+5. Extend only to adapters whose resolution rules can be verified.
+6. Complete F13 QA and package-content review.
+
+## Acceptance criteria
+
+- Identical inputs produce a stable manifest with deterministic ordering and versioned adapter identity.
+- Nested includes, cycles, missing files, symlinks, shadowed sources and unsupported fields are represented without duplicate counting or path escape.
+- Every numeric component identifies tokenizer/method, model assumption, confidence and freshness.
+- Always-on, description/catalog-only, on-demand and event-triggered content remain separate.
+- Hidden vendor content and unresolved runtime additions remain visible unknowns.
+- Default preview starts zero agent/MCP/hook child processes and performs no network request.
+- Scenario deltas reconcile arithmetically with their component changes and do not mutate the real config.
+- A dated QA report passes every F13 gate.
+
+## Implementation-session boundary
+
+The F13 session may extend the canonical inventory and context-resolution adapters only as required to emit the admission manifest. It must not parse live session usage, build historical charts, start MCP servers by default, change config, or implement F14.
+
+## Research basis
+
+A maintainer primary-source review covered [Aider commands](https://aider.chat/docs/usage/commands.html), [Claude Code status lines](https://code.claude.com/docs/en/statusline), [Cursor context](https://cursor.com/docs/agent/prompting), and the [Zed Agent Panel](https://zed.dev/docs/ai/agent-panel). Aider already has a useful `/tokens` breakdown; the remaining opportunity is verified cross-scope resolution, load timing, cross-harness comparison and reconciliation with external runtime evidence.
+
+## Decision log
+
+| Date | Decision | Reason |
+|---|---|---|
+| 2026-10-16 | Split pre-launch admission from runtime accounting. | They use different sources, have different unknowns, and can be implemented and QA'd independently. |
+| 2026-10-16 | Put F13 after the differentiated F1 schema. | The code audit showed generic preflight token itemization is prior art; CCO's value is joining the expected manifest to independently observed runtime reality. |
+| 2026-10-16 | Default to static/cache-only inspection. | A preview must not spend resources or trigger executable MCP/hook behavior merely to estimate a future session. |
diff --git a/docs/plans/F14_CONTEXT_TOKEN_LEDGER.md b/docs/plans/F14_CONTEXT_TOKEN_LEDGER.md
new file mode 100644
index 0000000..333b159
--- /dev/null
+++ b/docs/plans/F14_CONTEXT_TOKEN_LEDGER.md
@@ -0,0 +1,105 @@
+# F14 — Action-linked Context Evidence — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f14--action-linked-context-evidence`](../QA_PLAN.md#f14--action-linked-context-evidence)
+
+## User problem
+
+Harnesses expose token numbers with different meanings. A current context snapshot, cumulative processed input, cached input, output, quota percentage, credits and API-equivalent dollars are often shown beside each other and mistaken for the same measure. Tool and MCP activity is visible, but an exact per-tool token bill usually is not. Existing dashboards can therefore produce confident but incompatible totals.
+
+## Outcome
+
+Provide the smallest local, provenance-preserving context evidence needed to explain a cross-harness discrepancy or support an approved CCO action. Reconcile against native harness evidence and state exactly how strong any tool/MCP attribution is; do not recreate native usage dashboards.
+
+## In scope
+
+- Claude Code and Codex are first candidates, not automatic deliverables. Each adapter starts only from a named F1/F13 diagnosis or action that native UI cannot explain.
+- Supported local interfaces: documented statusline/telemetry/app-server events and durable transcript records. Private provider APIs, OAuth-token reuse and TLS interception are excluded.
+- Observation identity: harness, adapter version, session/thread, request/turn where available, model, timestamp, source record and completeness state.
+- Metric vocabulary:
+ - active context occupancy and context limit;
+ - request/turn input and cumulative processed input;
+ - fresh/net-new input where natively exposed;
+ - cache read and cache creation/write;
+ - output and reasoning semantics;
+ - compaction and retained/dropped evidence where exposed;
+ - quota, credits, API-list-price equivalent, provider estimate and actual billed cost as separate authorities.
+- Attribution vocabulary:
+ - **Native exact** — the source reports tokens for that exact component;
+ - **Payload estimate** — CCO counts serialized arguments, result or schema using a named method;
+ - **Request-associated** — a request is tagged as using a tool/MCP result;
+ - **Derived amplification** — estimated later re-reading under an explicit formula;
+ - **Unavailable** or **Unsupported**.
+- Turn and session reconciliation, including duplicated snapshots, partial streams, interrupted turns, compaction and model changes.
+- Minimal historical persistence required for reconciliation and trend/action evidence, subject to an approved retention decision.
+- Recommendations only when they connect to F13 scenario preview or an approved F3/F4/F5 action.
+- Default presentation is attached evidence on the relevant F1/F13 node or finding, not a standalone analytics home.
+
+## Non-goals
+
+- No provider invoice reconstruction or subscription-value claim.
+- No fabricated exact marginal token count per tool call.
+- No replacement for native quota/statusline UI.
+- No storage or upload of raw prompts, source code, command output, tool arguments or tool results for analytics.
+- No generic observability backend, replay viewer or chart suite.
+- No historical token dashboard whose only value is reproducing a native Cline, Zed, Aider, Kiro Crew or vendor view.
+- No use of internal-only protocol fields as the sole production contract.
+
+## Locked decisions
+
+- `Active context` and `Processed input` are separate top-level concepts.
+- Cache read/write remain separate from fresh input and are never hidden inside one ambiguous total.
+- Reasoning tokens follow the source's subset/additive semantics; the adapter declares the rule.
+- A number without source, unit, time, precision and completeness is invalid ledger data.
+- Tool activity, payload size and token attribution are separate facts.
+- Missing final usage after an interruption remains missing; CCO does not extrapolate a false exact total.
+- API-equivalent dollars are estimates and never labelled as the amount paid under a subscription.
+
+## Open decisions for Nicole
+
+- Retention: current session only, bounded rolling window, or text-free durable aggregates.
+- Main UI: session ledger table, relationship-graph drilldown, or a compact summary attached to F1 cards.
+- Whether CCO may configure official local OTel exporters for the user, or only read an existing endpoint/file.
+- Acceptable reconciliation tolerance for estimated versus native counters.
+- Whether derived re-read amplification belongs in v1 or waits until exact active-context reconciliation is stable.
+- Whether first release is read-only or can hand one recommendation to F13 preview.
+
+## Delivery slices
+
+1. Name one approved F1/F13 diagnosis or action that requires context evidence.
+2. Freeze only the schema and metric semantics required for that case, retaining the full provenance/precision vocabulary.
+3. Implement frozen native fixtures for the minimum harness pair involved.
+4. Reconcile against native commands/events on disposable sessions.
+5. Attach the evidence to the originating finding and prove that removing it would make the decision materially weaker.
+6. Add no history chart or second metric family without another approved use case.
+7. Complete F14 QA, privacy review and performance measurement.
+
+## Acceptance criteria
+
+- For every supported fixture, active context, per-turn/request use and cumulative processed use are independently reproducible.
+- Native totals reconcile within the approved tolerance; differences name the exact field, source semantics and missing records.
+- Duplicate snapshot records, compaction, cache changes, model switches, retries, interrupted turns and missing final events do not inflate exact totals.
+- Reasoning is not double-counted; cache read/write is not mislabeled as fresh input.
+- Each tool/MCP row shows attribution class and method; no request association is described as marginal tokens.
+- Source schema drift fails visibly and preserves the last valid evidence without silently reinterpreting fields.
+- Raw prompts, source, command output, arguments, results, credentials and OAuth tokens are absent from stored analytics and browser payloads.
+- High-count session fixtures stay inside pre-approved CPU, RSS, latency and storage budgets.
+- A dated QA report passes every F14 gate.
+
+## Implementation-session boundary
+
+The F14 session builds normalized read-only evidence for one approved action-linked case. It must not broaden into a standalone token product, process control, replay, invoice estimation, all-harness support, config mutation, or a general telemetry platform.
+
+## Research basis
+
+A primary-source review of [Cline task management](https://github.com/cline/cline/blob/main/docs/core-workflows/task-management.mdx), the [Zed Agent Panel](https://zed.dev/docs/ai/agent-panel), [Aider commands](https://aider.chat/docs/usage/commands.html), and [Kiro context management](https://kiro.dev/docs/cli/chat/context/) confirms mature native accounting in Cline and Zed, Aider preflight counts and Kiro Crew runtime metrics; only action-linked normalization is justified.
+
+## Decision log
+
+| Date | Decision | Reason |
+|---|---|---|
+| 2026-10-16 | Use a ledger ontology rather than one token total. | Harness counters describe different denominators and time windows. |
+| 2026-10-16 | Keep Claude Code and Codex as first candidates, not automatic scope. | An adapter starts only when a named F1/F13 case requires evidence that native UI cannot supply. |
+| 2026-10-16 | Reject exact per-tool billing as a v1 claim. | Reviewed public interfaces expose activity, payloads and request association, but not a universal exact marginal token field. |
diff --git a/docs/plans/F1_LIVE_RUNTIME_MAP.md b/docs/plans/F1_LIVE_RUNTIME_MAP.md
new file mode 100644
index 0000000..261d646
--- /dev/null
+++ b/docs/plans/F1_LIVE_RUNTIME_MAP.md
@@ -0,0 +1,88 @@
+# F1 — External Runtime Reality Map — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f1--external-runtime-reality-map`](../QA_PLAN.md#f1--external-runtime-reality-map)
+
+## User problem
+
+A user running several independently launched coding-agent sessions cannot see which processes belong to each session, how much memory they consume, which effective configuration produced them, which MCPs are duplicated, or the scope within which an MCP is actually shared.
+
+## Outcome
+
+Provide a neutral, read-only, privacy-bounded join from CCO's effective configuration inventory to arbitrary supported live sessions and their runtime topology. Clearly separate measured facts, declarations, bounded inference, unknowns, and unsupported observations.
+
+## In scope
+
+- Detect same-user Claude Code, Codex, OpenCode, and DSH sessions where the platform supports it.
+- Do not require sessions to be launched, imported or owned by CCO.
+- Show session age, process/thread count, tree RSS, direct MCP matches, shared-runtime nodes, duplicate instances, owner scope, and observation confidence.
+- Model sharing scope explicitly: session, extension host, project, app runtime, OS-user pool, or remote service.
+- Join a live node to an effective static source only when the adapter can preserve the evidence path; otherwise report the relationship as unknown.
+- Explain when short-lived connections or platform limitations prevent attribution.
+- Put backend identity and low-level diagnostics under Advanced.
+
+## Non-goals
+
+- No configuration migration.
+- No policy writes, quarantine, automatic stopping, or background optimization.
+- No token/context accounting or historical cost charts; those belong to F14.
+- No claim that process names reveal semantic user intent.
+- No attempt to become a replacement agent workspace, ACP host, or native session dashboard.
+
+## Existing prototype input
+
+The frozen `feat/mcp-runtime-doctor` work may be mined for collectors, privacy boundaries, fixtures, and UI lessons. Its `1MCP Doctor` product framing and mixed read/write scope are not approved.
+
+## Locked decisions
+
+- The normal UI says Runtime Reality Map, MCP Runtime, and Shared MCP Pool.
+- CCO must work without 1MCP or IntentProbe.
+- `shared` is not a Boolean conclusion. Every shared claim carries an owner scope and evidence source.
+- A common Cline Hub PID, Roo extension host, Zed project store, or Kiro Crew runtime does not by itself prove that its MCP backend processes are shared.
+- Command lines, environment variables, raw sockets, private paths, and secret values never reach the browser.
+- Refresh is user-visible and bounded; continuous monitoring is not assumed.
+
+## Open decisions for Nicole
+
+- Default refresh model: manual, foreground polling, or an explicit monitoring toggle.
+- Which summary numbers belong on the main screen versus Advanced.
+- Whether session stop is removed from F1 entirely or retained disabled behind a future F5 marker.
+
+## Delivery slices
+
+1. Freeze the runtime observation schema and privacy contract.
+2. Implement collectors behind platform capability flags.
+3. Build empty, partial, error, and high-count UI states.
+4. Add observation-confidence and methodology explanations.
+5. Complete F1 QA and package verification.
+
+## Acceptance criteria
+
+- Useful with zero, one, and many sessions, and with no shared runtime.
+- At least one fixture covers sessions launched outside CCO by two different harness families.
+- Fixtures distinguish Cline's shared Hub from session-built MCP managers, Roo's extension-host singleton, Zed's project store and a Kiro Crew multiplexed runtime.
+- Shared runtime totals are measured once; per-session attribution is labelled and never multiplied into a false host total.
+- Independent OS checks agree with bounded fixture counts and RSS methodology.
+- Unobserved short-lived traffic remains Unknown rather than guessed.
+- Unsupported platforms fail safely and honestly.
+- Runtime observation has measured latency and idle/active resource cost.
+- A dated QA report passes every F1 gate.
+
+## Implementation-session boundary
+
+The F1 session may refactor the frozen prototype into a generic read-only vertical slice. It must not implement migration, policy, lifecycle controls, or IntentProbe receipt ingestion.
+
+## Decision log
+
+| Date | Decision | Reason |
+|---|---|---|
+| 2026-10-16 | Separate observation from control. | Runtime visibility can ship safely before mutation semantics are settled. |
+| 2026-10-16 | Move token/context semantics to F14. | Process topology and token accounting use different evidence and failure modes; combining them would make F1 too broad. |
+| 2026-10-16 | Make external ownership the differentiator. | Kiro Crew already has a strong resource view for sessions registered in its own manager; CCO must explain the independently launched environment instead of becoming another harness. |
+| 2026-10-16 | Replace Boolean sharing with owner scope. | Cline, Roo, Zed and Kiro Crew share at different process/project/app boundaries; one `shared` flag would be materially misleading. |
+
+## Research basis
+
+A maintainer primary-source audit reviewed [Cline](https://github.com/cline/cline), [Roo Code](https://github.com/RooCodeInc/Roo-Code), [Zed](https://github.com/zed-industries/zed), and [Kiro Crew](https://github.com/kirodotdev/KiroCrew). It found mature native dashboards and shared runtimes, but no reviewed product joined arbitrary independently launched harnesses to cross-product effective configuration and live MCP ownership. Implementation must revalidate the pinned upstream revisions recorded in the QA evidence bundle.
diff --git a/docs/plans/F2_SHARED_RUNTIME_ADAPTER.md b/docs/plans/F2_SHARED_RUNTIME_ADAPTER.md
new file mode 100644
index 0000000..7ecd28e
--- /dev/null
+++ b/docs/plans/F2_SHARED_RUNTIME_ADAPTER.md
@@ -0,0 +1,72 @@
+# F2 — Runtime Engine Compatibility — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f2--runtime-engine-compatibility`](../QA_PLAN.md#f2--runtime-engine-compatibility)
+
+## User problem
+
+CCO needs a shared MCP engine only if an existing implementation can safely serve independently launched harnesses. Kiro Crew Gateway already implements a sophisticated pool, while 1MCP exists locally; building another broker before testing both would be unjustified solo-founder scope.
+
+## Outcome
+
+Define a backend-neutral external-client contract, test Kiro Crew Gateway and 1MCP against it, and implement a read-only adapter only for an engine that passes. Produce a code-evidenced no-fit result before any CCO-owned broker can be proposed.
+
+## In scope
+
+- Versioned adapter contract for discovery, health, capability negotiation, inventory, client evidence, lifecycle support, and config import/export.
+- Runtime states: absent, available, healthy, degraded, incompatible, and externally managed.
+- A fake adapter for contract tests.
+- Bounded compatibility probes for Kiro Crew Gateway and the existing local 1MCP runtime.
+- Capability matrix: external attachment, authenticated session identity, pool-key semantics, stateful isolation, browser-per-session handling, lifecycle ownership, resource evidence, host budgets, failure containment, configuration handoff, rollback, install footprint, licensing and update burden.
+- A read-only real adapter only after one engine satisfies the minimum contract.
+- Timeouts, failure isolation, and explicit unsupported operations.
+
+## Non-goals
+
+- No user config migration; that belongs to F3.
+- No policy engine; that belongs to F4.
+- No automatic installation until its requirements are approved as a separate F2 slice.
+- No CCO-owned MCP broker, proxy or daemon implementation.
+
+## Locked decisions
+
+- Stored CCO policy and ordinary UI cannot depend on 1MCP names or file layout.
+- Stored CCO policy and ordinary UI cannot depend on Kiro Crew names or file layout either.
+- CCO inventory remains usable when every runtime adapter fails.
+- Adopt before build: an existing engine is preferred unless verified boundary evidence rules it out.
+- One-click setup, if approved, must pin source/version and provide cleanup/rollback.
+
+## Open decisions for Nicole
+
+- Should v1 only adopt an existing runtime, or may CCO install one?
+- Which platforms must the first adapter support?
+- Is runtime ownership external, CCO-managed, or selectable per installation?
+
+## Delivery slices
+
+1. Freeze the external-client contract and fake-adapter fixtures.
+2. Inspect and probe Kiro Crew Gateway at a pinned revision without adopting its workspace runtime.
+3. Inspect and probe the existing local 1MCP runtime against the same cases.
+4. Publish a side-by-side pass/fail/unknown matrix and smallest missing boundary for each engine.
+5. Implement read-only discovery/health for the selected engine, if any.
+6. Optional setup only after a separate approved slice.
+
+## Acceptance criteria
+
+- Fake adapters cover every state and capability combination.
+- Either engine's absence, crash, timeout, license incompatibility or version mismatch does not block CCO.
+- Generic schemas and normal UI contain no backend-specific dependency.
+- Adapter child processes terminate cleanly and do not leak secrets.
+- F2 QA passes with neither engine and with the selected engine installed.
+- The final report records why the rejected engine failed; `not tested` is not a rejection.
+- A new CCO broker remains unauthorized unless both engines have a verified no-fit result.
+
+## Implementation-session boundary
+
+The F2 session owns the contract, compatibility experiment and at most one read-only selected adapter. It may not alter harness configuration, add shared-pool migration or implement a new broker.
+
+## Research basis
+
+A maintainer primary-source audit reviewed [Kiro Crew](https://github.com/kirodotdev/KiroCrew) and [1MCP](https://github.com/1mcp-app/agent). Kiro Crew's Apache-2.0 source already contains pool identity, shareability evidence, private fallback, host budgets and lifecycle handling; F2 must revalidate a pinned revision and test reuse/integration before replacing it.
diff --git a/docs/plans/F3_SHARED_POOL_MIGRATION.md b/docs/plans/F3_SHARED_POOL_MIGRATION.md
new file mode 100644
index 0000000..5f97ace
--- /dev/null
+++ b/docs/plans/F3_SHARED_POOL_MIGRATION.md
@@ -0,0 +1,65 @@
+# F3 — Consolidate into Shared Pool — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f3--consolidate-into-shared-pool`](../QA_PLAN.md#f3--consolidate-into-shared-pool)
+
+## User problem
+
+The same stateless MCP may be started independently by several sessions, wasting memory and obscuring ownership. Moving config manually risks broken paths, lost credentials, or an unrecoverable partial migration.
+
+## Outcome
+
+Let a user preview, apply, verify, and roll back consolidation of eligible MCPs into a shared pool.
+
+## In scope
+
+- Eligibility results: share candidate, must isolate, needs review, unsupported, and already shared.
+- Preview of exact sources, destination, loading change, conflicts, and estimated resource effect.
+- Fingerprinted backup, transaction manifest, stale-preview check, apply, health verification, and rollback.
+- Partial-failure recovery across supported harness/config types.
+
+## Non-goals
+
+- No automatic migration.
+- No assumption that every MCP is shareable.
+- No policy editor beyond the decision required for this transaction.
+- No migration until F2 selects an existing engine or a later approved plan supplies code-level evidence for a different implementation.
+
+## Locked decisions
+
+- Browser/stateful automation defaults to Must Isolate.
+- Unknown semantics default to Needs Review.
+- Credentials may be moved only through approved config semantics and never copied into logs or browser payloads.
+- Source config is not deleted until the shared target verifies healthy.
+
+## Open decisions for Nicole
+
+- Which MCP categories are preclassified in v1, if any?
+- Must v1 support multiple harness configs in one atomic transaction?
+- What post-migration observation period is required before success?
+
+## Delivery slices
+
+1. Eligibility and preview only, reusing the selected engine's real pool/shareability contract rather than inventing a parallel promise.
+2. Transaction/backup format and dry-run oracle.
+3. One harness plus one runtime apply/verify/rollback path.
+4. Failure injection and recovery.
+5. Additional harnesses only as separately reviewed sub-slices.
+
+## Acceptance criteria
+
+- Every eligibility outcome has a reason and evidence.
+- No silent overwrite, destructive partial success, or secret-bearing record.
+- Rollback restores exact bytes unless an independently edited target requires refusal.
+- Health failure preserves or restores the last working state.
+- Stateful browser MCP fixtures never enter the shared pool by default.
+
+## Implementation-session boundary
+
+The F3 session owns migration transactions only. It consumes the F2 adapter contract and may not redesign it casually or add lifecycle automation.
+
+## Research constraint
+
+Kiro Crew's source demonstrates that identical MCP handshakes do not prove process-global state is safe to share. `Unknown` and `Needs Review` remain first-class outcomes; CCO must not upgrade a successful probe into proof of safe co-tenancy.
diff --git a/docs/plans/F4_RUNTIME_POLICY.md b/docs/plans/F4_RUNTIME_POLICY.md
new file mode 100644
index 0000000..a6cfa81
--- /dev/null
+++ b/docs/plans/F4_RUNTIME_POLICY.md
@@ -0,0 +1,58 @@
+# F4 — Runtime Policy and Isolation — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f4--runtime-policy-and-isolation`](../QA_PLAN.md#f4--runtime-policy-and-isolation)
+
+## User problem
+
+A user needs an explicit, persistent answer to whether each capability is shared, session-specific, available on demand, under review, quarantined, or disabled.
+
+## Outcome
+
+Create an explainable policy model whose effective state can be previewed and safely applied across supported scopes.
+
+## In scope
+
+- Versioned states: Shared, Isolated, On-demand, Review, Quarantine, and Disabled.
+- Scope, inheritance, precedence, effective-policy explanation, and conflicts.
+- Capability support checks against harness and runtime adapters.
+- Transactional policy changes with affected-session preview and audit record.
+
+## Non-goals
+
+- No automatic optimization or quarantine.
+- No organization/team enforcement; that belongs to F12.
+- No invented equivalence where a harness cannot express a requested state.
+
+## Locked decisions
+
+- Unknown capability semantics default to Review.
+- Effective state always identifies its winning source.
+- A requested but unsupported state remains visible as a conflict; CCO does not pretend it was applied.
+
+## Open decisions for Nicole
+
+- Scope hierarchy for user, harness, project, session, and capability policies.
+- Default policy for newly discovered MCPs.
+- Whether policy is stored centrally by CCO, projected into harness files, or both.
+
+## Delivery slices
+
+1. Policy schema, precedence truth table, and migration rules.
+2. Read-only effective-policy rendering.
+3. Preview and transactional mutation for one supported path.
+4. Conflict handling and additional adapters.
+
+## Acceptance criteria
+
+- Truth-table tests cover all states, scopes, defaults, and collisions.
+- Concurrent/stale writes fail with a conflict.
+- Unknown future states and incompatible stored versions fail safely.
+- Policy changes are reversible where they mutate harness/runtime config.
+- UI never shows an unsupported policy as effective.
+
+## Implementation-session boundary
+
+The F4 session owns policy schema, evaluation, and explicit policy mutation. Resource automation and security receipt decisions remain F5 and F6.
diff --git a/docs/plans/F5_RESOURCE_GUARDRAILS.md b/docs/plans/F5_RESOURCE_GUARDRAILS.md
new file mode 100644
index 0000000..4daa165
--- /dev/null
+++ b/docs/plans/F5_RESOURCE_GUARDRAILS.md
@@ -0,0 +1,58 @@
+# F5 — Lifecycle and Resource Guardrails — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f5--lifecycle-and-resource-guardrails`](../QA_PLAN.md#f5--lifecycle-and-resource-guardrails)
+
+## User problem
+
+Forgotten or runaway sessions consume memory and process limits, but killing the wrong process can lose work or stop unrelated applications.
+
+## Outcome
+
+Provide explicit lifecycle controls and optional resource guardrails with exact ownership, stale-state protection, and safe defaults.
+
+## In scope
+
+- Start, graceful stop, restart, and detach where the relevant adapter supports them.
+- Same-user, PID/start-time, executable/harness, and CCO self-tree validation.
+- Optional idle-stop and memory/task thresholds, off by default.
+- Bounded local sampling for trends, hysteresis, cooldown, and restart-loop prevention.
+
+## Non-goals
+
+- No default automatic killing.
+- No semantic claim about what a session is doing from process metadata.
+- No OS-wide process manager.
+
+## Locked decisions
+
+- Read-only remains the default mode.
+- Every destructive action names the exact target and consequence before confirmation.
+- Unsupported or stale targets fail closed.
+
+## Open decisions for Nicole
+
+- Whether v1 includes only explicit stop/restart or also opt-in idle-stop.
+- Graceful timeout and whether escalation beyond `SIGTERM` is ever allowed.
+- Numeric performance, retention, and threshold defaults after F1 baselines exist.
+
+## Delivery slices
+
+1. Explicit lifecycle controls with identity revalidation.
+2. Resource sampling and budget measurement.
+3. Threshold notifications without automation.
+4. Optional automatic action only after separate approval and recovery evidence.
+
+## Acceptance criteria
+
+- PID reuse, disappearing targets, child churn, wrong user, and self-tree attempts are refused.
+- Graceful stop and restart results are verified and recorded.
+- Monitoring overhead stays within pre-approved numeric budgets.
+- Automated controls, if later approved, include hysteresis, cooldown, and recovery.
+- Live destructive QA uses only an explicitly approved disposable target.
+
+## Implementation-session boundary
+
+The F5 session may add only the approved lifecycle slice. It cannot turn notification thresholds into automatic termination without a Living Plan decision update.
diff --git a/docs/plans/F6_RUNTIME_SECURITY.md b/docs/plans/F6_RUNTIME_SECURITY.md
new file mode 100644
index 0000000..af432ce
--- /dev/null
+++ b/docs/plans/F6_RUNTIME_SECURITY.md
@@ -0,0 +1,62 @@
+# F6 — Runtime Security and Quarantine — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f6--runtime-security-and-quarantine`](../QA_PLAN.md#f6--runtime-security-and-quarantine)
+
+## User problem
+
+Static installation-time scans cannot explain or contain suspicious runtime tool definitions, calls, or responses. Scanner evidence also becomes useless if CCO cannot connect it to the affected capability and provide a safe action.
+
+## Outcome
+
+Ingest versioned IntentProbe receipts, explain them in CCO, and support manual, reversible quarantine and restore.
+
+## In scope
+
+- Versioned receipt contract for registration-time and runtime subjects.
+- Redaction, ordering, deduplication, replay, retention, compatibility, and missing-receipt states.
+- Evidence view: subject type, decision, signal/scanner versions, thresholds, redacted spans, and resulting policy action.
+- Manual quarantine, affected-session preview, restore, and false-positive workflow.
+- Optional Hugging Face model card/artifact mirror for discovery and reproducibility.
+
+## Non-goals
+
+- No universal security guarantee.
+- No automatic quarantine in the first slice.
+- No cloud scanning requirement or upload of private prompts, arguments, responses, credentials, or paths.
+
+## Locked decisions
+
+- Missing, malformed, incompatible, or late receipts result in Unknown, not Allow.
+- IntentProbe remains a complementary research-preview signal.
+- Quarantine uses the F4 policy model and is reversible.
+- CCO works offline after required local artifacts are installed.
+
+## Open decisions for Nicole
+
+- Which runtime subject types ship first: definitions, pre-call arguments, or post-call responses?
+- Receipt retention duration and whether full redacted evidence or summaries are stored.
+- Whether Hugging Face hosts probe-only artifacts, model cards, datasets, demos, or a subset.
+- What evidence would be required before any automatic quarantine policy is considered.
+
+## Delivery slices
+
+1. Freeze and version the receipt schema in IntentProbe and CCO fixtures.
+2. Read-only ingestion and evidence UI.
+3. Manual quarantine preview/apply/restore.
+4. Feedback/export path with privacy review.
+5. Optional Hugging Face distribution experiment.
+
+## Acceptance criteria
+
+- Known allow/warn/block fixtures and malformed/late/version-mismatch cases behave as specified.
+- No private raw event content crosses an undocumented boundary.
+- Quarantine and restore survive stale state and partial failure.
+- The exact object and affected sessions are shown before action.
+- Public copy preserves IntentProbe's documented limitations.
+
+## Implementation-session boundary
+
+The F6 session consumes stable F4 policy and IntentProbe receipt contracts. If either contract is not stable, implementation stops at fixtures and interface review.
diff --git a/docs/plans/F7_HYGIENE_FINDINGS.md b/docs/plans/F7_HYGIENE_FINDINGS.md
new file mode 100644
index 0000000..7d5d31b
--- /dev/null
+++ b/docs/plans/F7_HYGIENE_FINDINGS.md
@@ -0,0 +1,59 @@
+# F7 — Hygiene Findings v2 — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f7--hygiene-findings-v2`](../QA_PLAN.md#f7--hygiene-findings-v2)
+
+## User problem
+
+The current score covers a narrow set of static conditions. Users need evidence for broken, unreachable, stale, conflicting, risky, or wasteful configuration and runtime state.
+
+## Outcome
+
+Expand CCO from a score into an evidence-first finding system with clear confidence, action, suppression, and reversibility.
+
+## In scope
+
+- Broken references, unreachable MCPs, stale paths, shadowed/conflicting definitions, floating package versions, command-line secret exposure, and narrowly specified semantic overlap.
+- Finding schema: rule/version, severity, confidence, evidence, affected scope, recommended action, and reversibility.
+- Suppression with reason, scope, and expiry.
+- Score reconciliation with visible findings.
+
+## Non-goals
+
+- No unsupported claim that a finding proves malware or compromise.
+- No opaque AI-generated cleanup action.
+- No automatic fixes except separately approved deterministic/reversible rules.
+
+## Locked decisions
+
+- Deterministic and heuristic findings are visibly different.
+- Every score deduction is traceable to findings.
+- Sensitive evidence is redacted before reaching UI or logs.
+
+## Open decisions for Nicole
+
+- Initial v2 rule set and severity weights.
+- Whether semantic overlap ships locally in v2 or remains research until a false-positive benchmark exists.
+- Suppression defaults and expiry UX.
+
+## Delivery slices
+
+1. Versioned finding schema and score reconciliation.
+2. Deterministic static rules.
+3. Runtime-backed rules using F1 evidence.
+4. Suppression/expiry.
+5. Heuristic semantic overlap only after evaluation approval.
+
+## Acceptance criteria
+
+- Every deterministic rule has positive, negative, boundary, and platform fixtures.
+- Heuristic rules include a reviewed false-positive set and confidence label.
+- Visible counts exactly reproduce the score.
+- Suppressions are auditable and reappear after expiry.
+- Recommended actions state whether they are previewable and reversible.
+
+## Implementation-session boundary
+
+Each F7 implementation session takes a bounded rule group. It may not add a scanner model or mutation flow merely because the finding schema can represent one.
diff --git a/docs/plans/F8_RELATIONSHIP_GRAPH.md b/docs/plans/F8_RELATIONSHIP_GRAPH.md
new file mode 100644
index 0000000..609b34f
--- /dev/null
+++ b/docs/plans/F8_RELATIONSHIP_GRAPH.md
@@ -0,0 +1,59 @@
+# F8 — Context and Runtime Relationship Graph — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f8--relationship-graph`](../QA_PLAN.md#f8--relationship-graph)
+
+## User problem
+
+Inventory lists do not show why a capability reaches a session, how pre-launch admission relates to observed runtime/usage evidence, or how many sessions and policies a proposed change will affect.
+
+## Outcome
+
+Provide a source-backed path from session to harness, admission component, config source, usage observation, effective policy, runtime instance, MCP/tool, and security evidence, including pre-change blast radius.
+
+## In scope
+
+- Normalized node/edge model over existing canonical inventory, F13 admission manifests, F14 ledger observations, and runtime data.
+- Evidence, timestamp where relevant, and confidence/precision for every edge.
+- Path filtering, partial-state handling, and change-impact preview.
+- Accessible tree/table representation before or alongside a graphical canvas.
+
+## Non-goals
+
+- No second scanner or duplicate inventory database.
+- No graph visualization before the relationship model is correct.
+- No inferred edge shown as a measured fact.
+
+## Locked decisions
+
+- Canonical inventory remains the source of nodes.
+- Graphical presentation is optional; accessible equivalent information is mandatory.
+- Missing and stale edges remain visible as incomplete evidence.
+
+## Open decisions for Nicole
+
+- First user journey: explain current load, diagnose duplication, or preview a policy change.
+- Whether v1 ships as an expandable path/table or full canvas.
+- Maximum default graph size and filtering strategy.
+
+## Delivery slices
+
+1. Graph schema and traversal oracle.
+2. One end-to-end path for a single session/MCP.
+3. Blast-radius preview.
+4. High-volume filtering and accessible UI.
+5. Optional canvas visualization.
+
+## Acceptance criteria
+
+- Every edge resolves to evidence and a confidence class.
+- Cycles, missing nodes, duplicates, partial runtime observations, and stale edges are safe.
+- Blast-radius results match independent traversal fixtures.
+- Keyboard and non-visual users can obtain the same facts.
+- High-volume fixtures stay inside an approved performance budget.
+
+## Implementation-session boundary
+
+The F8 session builds relationships from F13/F1/F14/F4/F7 contracts. It cannot change those semantics solely to simplify visualization.
diff --git a/docs/plans/F9_HEADLESS_CLI.md b/docs/plans/F9_HEADLESS_CLI.md
new file mode 100644
index 0000000..9722222
--- /dev/null
+++ b/docs/plans/F9_HEADLESS_CLI.md
@@ -0,0 +1,60 @@
+# F9 — Headless CLI and JSON Contract — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#f9--headless-cli-and-json`](../QA_PLAN.md#f9--headless-cli-and-json)
+
+## User problem
+
+CCO evidence is available only through the interactive dashboard, so users cannot script repeatable audits or use stable results in CI.
+
+## Outcome
+
+Expose read-only inventory, admission previews, findings, runtime snapshots, and policy checks through versioned JSON schemas and predictable exit codes.
+
+## In scope
+
+- Headless commands for approved read-only domains.
+- F13 admission-manifest output only after its schema is stable; the CLI reuses that contract and does not perform a second resolution pass with different semantics.
+- Versioned JSON Schema, deterministic ordering, redaction, stdout/stderr separation, and compatibility policy.
+- Human summaries and CI examples.
+- Explicit clean, warning, policy-failure, unsupported, and internal-error outcomes.
+
+## Non-goals
+
+- No headless mutations.
+- No remote SaaS requirement.
+- No promise of backward compatibility before the schema policy is approved.
+
+## Locked decisions
+
+- JSON stdout contains data only; progress and diagnostics use stderr.
+- CI is read-only by default.
+- Private paths/content follow the same or stricter redaction contract as the dashboard.
+
+## Open decisions for Nicole
+
+- Command shape and naming.
+- First stable schema version and compatibility duration.
+- Which findings should cause a non-zero policy exit versus a warning result.
+
+## Delivery slices
+
+1. Inventory JSON and schema.
+2. Admission manifest after F13 schema stability.
+3. Findings/policy result and exit-code contract.
+4. Runtime snapshot after F1 schema stability.
+5. CI examples and compatibility fixtures.
+
+## Acceptance criteria
+
+- All output validates against the declared schema.
+- Golden fixtures prove deterministic order and redaction.
+- At least one prior schema version remains readable after a version change.
+- Exit codes distinguish policy result from internal failure.
+- Headless execution performs no mutations.
+
+## Implementation-session boundary
+
+The F9 session exposes existing stable contracts. It must not invent missing domain semantics inside CLI serialization.
diff --git a/docs/plans/P0_RELEASE_RECOVERY.md b/docs/plans/P0_RELEASE_RECOVERY.md
new file mode 100644
index 0000000..9b7e77b
--- /dev/null
+++ b/docs/plans/P0_RELEASE_RECOVERY.md
@@ -0,0 +1,68 @@
+# P0 — Release Recovery and Product Truth — Living Plan
+
+Last updated: 2026-10-16 PT
+Status: **Draft — requirements not approved**
+Parent: [`../LIVING_PLAN.md`](../LIVING_PLAN.md)
+QA gate: [`../QA_PLAN.md#p0--release-recovery`](../QA_PLAN.md#p0--release-recovery)
+
+## User problem
+
+The repository and tag contain v0.20.0, while npm `latest` and GitHub Releases still expose v0.19.3. Users cannot reliably obtain the version described by current project documentation.
+
+## Outcome
+
+Publish one coherent, installable baseline whose npm package, MCP Registry entry, GitHub Release, provenance, documentation, and observed behavior all agree.
+
+## In scope
+
+- Inspect the authenticated npm trusted-publisher tuple and package ownership.
+- Decide whether to recover v0.20.0 or cut a superseding version if registry rules make the existing tag unsuitable.
+- Run the existing idempotent pipeline after configuration is corrected.
+- Verify all public artifacts and a clean install.
+- Reconcile roadmap statuses and remove public claims that exist only in the frozen runtime branch.
+- Decide whether CCO keeps the activation-research corpus or retains only compact fixtures and links to IntentProbe.
+
+## Non-goals
+
+- No runtime feature implementation.
+- No dependency upgrade unless it is required to make the approved release safe and reproducible.
+- No long-lived npm token fallback.
+
+## Known evidence
+
+- Latest npm version: 0.19.3.
+- Tag v0.20.0 exists.
+- Publish run `38975225907` passed verification, tests, audit, pack, and provenance, then npm returned `E404` during publish.
+- GitHub Release creation occurs only after npm and MCP Registry verification, so no v0.20.0 release was created.
+
+## Open decisions for Nicole
+
+- Recover version 0.20.0, or supersede it if npm requires a new version.
+- Keep or separate the CCO research corpus.
+
+## Delivery steps
+
+1. Record the exact intended release version and immutable commit.
+2. Verify publisher configuration without exposing credentials.
+3. Run release QA from a clean checkout.
+4. Publish once through the reviewed workflow.
+5. Verify every registry and install the public artifact.
+6. Reconcile public documentation and mark this plan Shipped.
+
+## Acceptance criteria
+
+- The intended version is returned by npm `latest` and installs on a clean machine.
+- npm, MCP Registry, GitHub Release, provenance, package metadata, and release notes use the same version.
+- `--help`, `--version`, and dashboard startup work from the published tarball.
+- No unmerged runtime feature is described as shipped.
+- A dated QA report records exact commands, artifact identifiers, and results.
+
+## Implementation-session boundary
+
+The release session may change release configuration and truthful release documentation only. Any product-code fix discovered during release QA stops this plan and returns the defect to PM triage.
+
+## Decision log
+
+| Date | Decision | Reason |
+|---|---|---|
+| 2026-10-16 | Release recovery precedes F1. | A stable public baseline is required before stacking another unreleased feature. |
diff --git a/docs/plans/README.md b/docs/plans/README.md
new file mode 100644
index 0000000..9c89d80
--- /dev/null
+++ b/docs/plans/README.md
@@ -0,0 +1,49 @@
+# CCO Feature Living Plans
+
+Last updated: 2026-10-16 PT
+Status: planning index
+
+The master sequence lives in [`../LIVING_PLAN.md`](../LIVING_PLAN.md). Each release gate or feature has its own Living Plan below. A feature-specific plan, not the master roadmap alone, is the implementation contract for a future session.
+
+| ID | Living Plan | Current status |
+|---|---|---|
+| P0 | [Release Recovery and Product Truth](P0_RELEASE_RECOVERY.md) | Draft — requirements not approved |
+| F1 | [External Runtime Reality Map](F1_LIVE_RUNTIME_MAP.md) | Draft — requirements not approved |
+| F13 | [Session Admission Preview](F13_SESSION_ADMISSION_PREVIEW.md) | Draft — requirements not approved |
+| F2 | [Runtime Engine Compatibility](F2_SHARED_RUNTIME_ADAPTER.md) | Draft — requirements not approved |
+| F14 | [Action-linked Context Evidence](F14_CONTEXT_TOKEN_LEDGER.md) | Draft — requirements not approved |
+| F3 | [Consolidate into Shared Pool](F3_SHARED_POOL_MIGRATION.md) | Draft — requirements not approved |
+| F4 | [Runtime Policy and Isolation](F4_RUNTIME_POLICY.md) | Draft — requirements not approved |
+| F5 | [Lifecycle and Resource Guardrails](F5_RESOURCE_GUARDRAILS.md) | Draft — requirements not approved |
+| F6 | [Runtime Security and Quarantine](F6_RUNTIME_SECURITY.md) | Draft — requirements not approved |
+| F7 | [Hygiene Findings v2](F7_HYGIENE_FINDINGS.md) | Draft — requirements not approved |
+| F8 | [Context and Runtime Relationship Graph](F8_RELATIONSHIP_GRAPH.md) | Draft — requirements not approved |
+| F9 | [Headless CLI and JSON Contract](F9_HEADLESS_CLI.md) | Draft — requirements not approved |
+| F10 | [Personal Vault and Reusable Profiles](F10_PERSONAL_VAULT.md) | Draft — discovery required |
+| F11 | [Semantic Portability and Adapter Expansion](F11_PORTABILITY.md) | Draft — discovery required |
+| F12 | [Team Baselines](F12_TEAM_BASELINES.md) | Draft — later enterprise extension |
+
+## State model
+
+Every plan uses one of these states:
+
+1. `Draft — requirements not approved`
+2. `Approved for implementation`
+3. `In implementation`
+4. `Implementation complete — QA pending`
+5. `QA failed` or `QA passed — integration pending`
+6. `Integrated — release pending`
+7. `Shipped` or `Deferred`
+
+Only Nicole can move a plan from Draft to Approved. An implementation session may update progress, discoveries, and evidence, but may not silently change the approved user outcome or add another feature packet.
+
+## Required update points
+
+Update the relevant Living Plan when:
+
+- a requirement, non-goal, interface, dependency, or product decision changes;
+- implementation discovers an assumption that affects user behavior;
+- QA produces a failure, limitation, or residual risk;
+- integration or release changes the actual public state.
+
+Every implementation and QA session must begin by reading the master roadmap, the exact feature Living Plan, and the relevant section of [`../QA_PLAN.md`](../QA_PLAN.md).
diff --git a/src/runtime-doctor.mjs b/src/runtime-doctor.mjs
new file mode 100644
index 0000000..7d61af7
--- /dev/null
+++ b/src/runtime-doctor.mjs
@@ -0,0 +1,924 @@
+/**
+ * Read-only health report for a local shared MCP runtime.
+ *
+ * The first supported runtime is 1MCP on Linux. The collector reads only a
+ * same-user config under HOME, procfs metadata, cgroup counters, and a
+ * loopback-only health endpoint. Command lines are inspected in memory for
+ * credential-shaped arguments but are never returned to the caller.
+ */
+
+import http from "node:http";
+import { access, readFile, readdir, readlink, realpath } from "node:fs/promises";
+import { basename, isAbsolute, join, relative, resolve } from "node:path";
+import { homedir } from "node:os";
+import { constants as fsConstants } from "node:fs";
+import { createHash } from "node:crypto";
+
+const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
+const SECRET_NAME = /(?:api[_-]?key|access[_-]?token|auth[_-]?token|token|secret|password|credential)/i;
+const SECRET_OPTION_NAME = /^(?:-{1,2})?(?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth[_-]?token|token|secret|password|passwd|credential)s?$/i;
+const NON_SECRET_OPTION = /(?:[_-](?:file|path|limit|count|max|min|budget|ttl|length|size))s?$|^(?:-{1,2})?(?:max|min)[_-]/i;
+const BOOLEAN_TEXT = /^(?:true|false|yes|no|on|off|0|1)$/i;
+const GENERIC_RUNNERS = new Set(["node", "npx", "npm", "python", "python3", "uv", "uvx", "bun", "deno"]);
+const HARNESS_EXECUTABLES = new Map([
+ ["claude", { id: "claude", label: "Claude Code" }],
+ ["codex", { id: "codex", label: "Codex" }],
+ ["opencode", { id: "opencode", label: "OpenCode" }],
+ ["dsh", { id: "dsh", label: "DeepSeek Harness" }],
+]);
+const ISOLATE_MCP = /(?:agent[-_ ]?chrome|browser|chrome|chromium|playwright|puppeteer|filesystem|file[-_ ]?system|shell|terminal|sqlite|database|git(?:hub)?[-_ ]?(?:write|admin))/i;
+const REVIEW_MCP = /(?:gmail|email|mail|calendar|drive|notion|slack|teams|discord|telegram|credential|oauth)/i;
+
+function finiteNumber(value) {
+ if (value === null || value === undefined || value === "") return null;
+ const number = Number(value);
+ return Number.isFinite(number) && number >= 0 ? number : null;
+}
+
+function safeRatio(value, limit) {
+ if (!Number.isFinite(value) || !Number.isFinite(limit) || limit <= 0) return null;
+ return Math.min(value / limit, 10);
+}
+
+function isWithinHome(path, home) {
+ if (!path || !home || !isAbsolute(path)) return false;
+ const rel = relative(resolve(home), resolve(path));
+ return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel));
+}
+
+async function isWithinHomeResolved(path, home, fsApi) {
+ if (!isWithinHome(path, home)) return false;
+ if (typeof fsApi.realpath !== "function") return true;
+ try {
+ const [resolvedPath, resolvedHome] = await Promise.all([fsApi.realpath(path), fsApi.realpath(home)]);
+ return isWithinHome(resolvedPath, resolvedHome);
+ } catch {
+ return false;
+ }
+}
+
+function optionValue(tokens, name, fallback = null) {
+ const equalsPrefix = `${name}=`;
+ for (let index = 0; index < tokens.length; index++) {
+ const token = String(tokens[index]);
+ if (token.startsWith(equalsPrefix)) return token.slice(equalsPrefix.length);
+ if (token === name && index + 1 < tokens.length) return String(tokens[index + 1]);
+ }
+ return fallback;
+}
+
+function hasFlag(tokens, name) {
+ return tokens.some(token => token === name || String(token).startsWith(`${name}=`));
+}
+
+export function parseOneMcpFlags(tokens = []) {
+ const port = finiteNumber(optionValue(tokens, "--port", 3050));
+ const host = optionValue(tokens, "--host", "127.0.0.1");
+ return {
+ configPath: optionValue(tokens, "--config"),
+ host,
+ port: port !== null && port <= 65535 ? port : 3050,
+ transport: optionValue(tokens, "--transport", "http"),
+ asyncLoading: hasFlag(tokens, "--enable-async-loading"),
+ lazyLoading: hasFlag(tokens, "--enable-lazy-loading"),
+ lazyMode: optionValue(tokens, "--lazy-mode", "full"),
+ configReload: hasFlag(tokens, "--enable-config-reload"),
+ sessionPersistence: hasFlag(tokens, "--enable-session-persistence"),
+ };
+}
+
+export function commandLineHasSecret(tokens = []) {
+ const isSecretOption = name => SECRET_OPTION_NAME.test(name) && !NON_SECRET_OPTION.test(name);
+ for (let index = 0; index < tokens.length; index++) {
+ const token = String(tokens[index]);
+ const equalsIndex = token.indexOf("=");
+ if (equalsIndex > 0 && isSecretOption(token.slice(0, equalsIndex))) return true;
+ if (/[?&](?:api[_-]?key|token|secret|password|auth)=/i.test(token)) return true;
+ if (token.startsWith("-") && isSecretOption(token)) {
+ const next = String(tokens[index + 1] || "");
+ if (next && !next.startsWith("-") && !BOOLEAN_TEXT.test(next)) return true;
+ }
+ }
+ return false;
+}
+
+function npxPackageSpec(config) {
+ const command = basename(String(config?.command || "")).toLowerCase();
+ const args = Array.isArray(config?.args) ? config.args.map(String) : [];
+ let candidates = args;
+ if (command === "npm" && args[0] === "exec") candidates = args.slice(1);
+ else if (command !== "npx") return null;
+ return candidates.find(arg => !arg.startsWith("-") && arg !== "exec") || null;
+}
+
+function hasExactPackageVersion(spec) {
+ if (!spec) return false;
+ const version = spec.startsWith("@")
+ ? spec.match(/^@[^/]+\/[^@]+@(.+)$/)?.[1]
+ : spec.match(/^[^@]+@(.+)$/)?.[1];
+ return Boolean(version && /^v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version));
+}
+
+export function analyzeOneMcpConfig(config = {}) {
+ const entries = Object.entries(config.mcpServers || config.servers || {});
+ let enabledCount = 0;
+ let floatingNpxCount = 0;
+ let secretEnvKeyCount = 0;
+
+ for (const [, server] of entries) {
+ if (server?.disabled === true) continue;
+ enabledCount++;
+ const spec = npxPackageSpec(server);
+ if (spec && !hasExactPackageVersion(spec)) floatingNpxCount++;
+ for (const key of Object.keys(server?.env || {})) {
+ if (SECRET_NAME.test(key)) secretEnvKeyCount++;
+ }
+ }
+
+ return {
+ configuredCount: entries.length,
+ enabledCount,
+ disabledCount: entries.length - enabledCount,
+ floatingNpxCount,
+ secretEnvKeyCount,
+ };
+}
+
+export function buildRuntimeFindings(report) {
+ const findings = [];
+ const resources = report.resources || {};
+ const inventory = report.inventory || {};
+ const features = report.features || {};
+
+ if (report.runtime?.unhealthyServers > 0) {
+ findings.push({
+ code: "unhealthy-upstreams", severity: "high",
+ title: `${report.runtime.unhealthyServers} upstream server${report.runtime.unhealthyServers === 1 ? " is" : "s are"} unhealthy`,
+ detail: "The shared runtime is reachable, but at least one configured MCP backend failed its health check.",
+ });
+ }
+ if (report.exposure?.commandLineSecretProcesses > 0) {
+ findings.push({
+ code: "command-line-secret", severity: "high",
+ title: "Credential material is visible in a process command line",
+ detail: `${report.exposure.commandLineSecretProcesses} runtime process instance${report.exposure.commandLineSecretProcesses === 1 ? " has" : "s have"} a credential-shaped command-line argument. One credential may be repeated across parent and child processes. Other same-user processes and diagnostic tools can read it. Move the value to a protected environment or secret store and rotate the exposed credential.`,
+ });
+ }
+ if (inventory.floatingNpxCount > 0) {
+ findings.push({
+ code: "floating-npx", severity: "medium",
+ title: `${inventory.floatingNpxCount} npx server${inventory.floatingNpxCount === 1 ? " uses" : "s use"} a floating package version`,
+ detail: "Pin exact package versions so a restart cannot silently execute a different upstream release.",
+ });
+ }
+ if (resources.memoryMaxRatio !== null && resources.memoryMaxRatio >= 0.8) {
+ findings.push({
+ code: "memory-max", severity: "high", title: "Runtime memory is close to its hard limit",
+ detail: `${Math.round(resources.memoryMaxRatio * 100)}% of the configured hard memory limit is in use. New MCP work may be killed by the service manager.`,
+ });
+ } else if (resources.memoryHighRatio !== null && resources.memoryHighRatio >= 0.8) {
+ findings.push({
+ code: "memory-high", severity: "medium", title: "Runtime memory is in the pressure zone",
+ detail: `${Math.round(resources.memoryHighRatio * 100)}% of the configured soft memory limit is in use.`,
+ });
+ }
+ if (resources.taskRatio !== null && resources.taskRatio >= 0.65) {
+ findings.push({
+ code: "task-pressure", severity: resources.taskRatio >= 0.85 ? "high" : "medium",
+ title: "Runtime task count is approaching its limit",
+ detail: `${Math.round(resources.taskRatio * 100)}% of the configured task limit is in use across the shared process group.`,
+ });
+ }
+ if (!features.lazyLoading && inventory.enabledCount >= 10) {
+ findings.push({
+ code: "lazy-loading-off", severity: "info", title: "Lazy tool loading is off",
+ detail: `${inventory.enabledCount} upstream servers load into the shared runtime. 1MCP lazy loading can reduce the tool schema surface exposed to each agent session.`,
+ });
+ }
+ return findings;
+}
+
+async function readText(path, fsApi) {
+ try { return await fsApi.readFile(path, "utf8"); } catch { return null; }
+}
+
+function parseCmdline(content) {
+ if (content === null || content === undefined) return [];
+ return String(content).split("\0").filter(Boolean);
+}
+
+function parseCgroupPath(content) {
+ for (const line of String(content || "").split(/\r?\n/)) {
+ const match = line.match(/^\d+::(.+)$/);
+ if (match) return match[1];
+ }
+ return null;
+}
+
+function parseProcessUid(content) {
+ const match = String(content || "").match(/^Uid:\s+(\d+)/m);
+ return match ? Number(match[1]) : null;
+}
+
+function statusField(content, name) {
+ return String(content || "").match(new RegExp(`^${name}:\\s+([^\\n]+)`, "m"))?.[1]?.trim() || null;
+}
+
+function parseProcessStatus(content) {
+ const memoryKb = finiteNumber(statusField(content, "VmRSS")?.match(/^(\d+)/)?.[1]);
+ const ppid = finiteNumber(statusField(content, "PPid"));
+ const threads = finiteNumber(statusField(content, "Threads"));
+ return {
+ name: statusField(content, "Name") || "process",
+ state: statusField(content, "State")?.slice(0, 1) || "?",
+ uid: parseProcessUid(content),
+ ppid: ppid === null ? 0 : ppid,
+ memoryBytes: memoryKb === null ? 0 : memoryKb * 1024,
+ threads: threads === null ? 1 : threads,
+ };
+}
+
+function parseProcessStartTicks(content) {
+ const text = String(content || "");
+ const close = text.lastIndexOf(")");
+ if (close < 0) return null;
+ const fields = text.slice(close + 1).trim().split(/\s+/);
+ return finiteNumber(fields[19]);
+}
+
+function harnessForProcess(processInfo) {
+ const candidates = [processInfo.name, basename(String(processInfo.tokens?.[0] || ""))];
+ for (const candidate of candidates) {
+ const normalized = String(candidate).toLowerCase().replace(/\.exe$/, "");
+ if (HARNESS_EXECUTABLES.has(normalized)) return HARNESS_EXECUTABLES.get(normalized);
+ }
+ return null;
+}
+
+function isHarnessMcpServer(processInfo) {
+ const args = (processInfo.tokens || []).slice(1).map(value => String(value).toLowerCase());
+ if (args.includes("mcp-server")) return true;
+ const mcpIndex = args.indexOf("mcp");
+ return mcpIndex >= 0 && args[mcpIndex + 1] === "serve";
+}
+
+async function collectProcessSnapshot(procRoot, currentUid, fsApi, clockTicks = 100) {
+ const pids = await listPids(procRoot, fsApi);
+ const uptimeSeconds = finiteNumber((await readText(join(procRoot, "uptime"), fsApi))?.trim().split(/\s+/)[0]);
+ const processes = new Map();
+
+ for (let offset = 0; offset < pids.length; offset += 64) {
+ await Promise.all(pids.slice(offset, offset + 64).map(async pidText => {
+ const [statusText, cmdlineText, cgroupText, statText] = await Promise.all([
+ readText(join(procRoot, pidText, "status"), fsApi),
+ readText(join(procRoot, pidText, "cmdline"), fsApi),
+ readText(join(procRoot, pidText, "cgroup"), fsApi),
+ readText(join(procRoot, pidText, "stat"), fsApi),
+ ]);
+ const status = parseProcessStatus(statusText);
+ if (currentUid !== null && status.uid !== currentUid) return;
+ const startTicks = parseProcessStartTicks(statText);
+ const ageSeconds = uptimeSeconds !== null && startTicks !== null && clockTicks > 0
+ ? Math.max(0, Math.floor(uptimeSeconds - startTicks / clockTicks))
+ : null;
+ const pid = Number(pidText);
+ processes.set(pid, {
+ pid,
+ ...status,
+ tokens: parseCmdline(cmdlineText),
+ cgroupPath: parseCgroupPath(cgroupText),
+ startTicks,
+ ageSeconds,
+ });
+ }));
+ }
+
+ return processes;
+}
+
+function sessionIdFor(processInfo, harness) {
+ return `${harness.id}:${processInfo.pid}:${processInfo.startTicks ?? "unknown"}`;
+}
+
+function nearestSession(processInfo, sessionByPid, processes) {
+ let cursor = processInfo;
+ const seen = new Set();
+ while (cursor && !seen.has(cursor.pid)) {
+ seen.add(cursor.pid);
+ if (sessionByPid.has(cursor.pid)) return sessionByPid.get(cursor.pid);
+ cursor = processes.get(cursor.ppid);
+ }
+ return null;
+}
+
+function safeProcessState(state) {
+ if (state === "R") return "running";
+ if (state === "Z") return "zombie";
+ if (state === "T" || state === "t") return "stopped";
+ if (state === "D") return "waiting";
+ return "sleeping";
+}
+
+function buildSessions(processes, currentPid = process.pid) {
+ const roots = [...processes.values()]
+ .map(processInfo => ({ processInfo, harness: harnessForProcess(processInfo) }))
+ .filter(entry => entry.harness && entry.processInfo.state !== "Z" && !isHarnessMcpServer(entry.processInfo))
+ .sort((a, b) => a.processInfo.pid - b.processInfo.pid);
+ const sessionByPid = new Map();
+ const harnessCounts = new Map();
+ const sessions = roots.map(({ processInfo, harness }) => {
+ const harnessIndex = (harnessCounts.get(harness.id) || 0) + 1;
+ harnessCounts.set(harness.id, harnessIndex);
+ const session = {
+ id: sessionIdFor(processInfo, harness),
+ harness: harness.id,
+ label: `${harness.label} #${harnessIndex}`,
+ pid: processInfo.pid,
+ state: safeProcessState(processInfo.state),
+ ageSeconds: processInfo.ageSeconds,
+ rootMemoryBytes: processInfo.memoryBytes,
+ memoryBytes: 0,
+ processCount: 0,
+ threadCount: 0,
+ controllable: true,
+ mcpServers: [],
+ };
+ sessionByPid.set(processInfo.pid, session);
+ return session;
+ });
+
+ const assignment = new Map();
+ for (const processInfo of processes.values()) {
+ const session = nearestSession(processInfo, sessionByPid, processes);
+ if (!session) continue;
+ assignment.set(processInfo.pid, session.id);
+ session.memoryBytes += processInfo.memoryBytes;
+ session.processCount++;
+ session.threadCount += processInfo.threads;
+ }
+
+ // A harness can be nested inside another harness (for example,
+ // Claude Code -> Codex -> CCO). Protect every session root on the CCO
+ // process's ancestor chain so stopping an outer session cannot terminate
+ // the server that is handling the request.
+ let cursor = processes.get(currentPid);
+ const seen = new Set();
+ while (cursor && !seen.has(cursor.pid)) {
+ seen.add(cursor.pid);
+ const session = sessionByPid.get(cursor.pid);
+ if (session) session.controllable = false;
+ cursor = processes.get(cursor.ppid);
+ }
+ for (const session of sessions) {
+ session.attention = [];
+ if (session.memoryBytes >= 768 * 1024 * 1024) session.attention.push("high-memory");
+ if (session.ageSeconds !== null && session.ageSeconds >= 24 * 60 * 60) session.attention.push("long-lived");
+ }
+ return { sessions, assignment };
+}
+
+function normalizedPackageMarker(config = {}) {
+ const command = basename(String(config.command || "").trim().split(/\s+/)[0] || "").toLowerCase();
+ const args = Array.isArray(config.args)
+ ? config.args.map(value => String(value))
+ : typeof config.command === "string"
+ ? config.command.trim().split(/\s+/).slice(1)
+ : [];
+ let meaningful;
+ if (command === "uv") {
+ const runIndex = args.indexOf("run");
+ meaningful = runIndex >= 0 ? args.slice(runIndex + 1).find(value => !value.startsWith("-")) : null;
+ } else if (command === "npm") {
+ const execIndex = args.indexOf("exec");
+ meaningful = args.slice(execIndex >= 0 ? execIndex + 1 : 0).find(value => !value.startsWith("-"));
+ } else {
+ meaningful = args.find(value => !value.startsWith("-") && value !== "exec");
+ }
+ const marker = meaningful
+ ? basename(meaningful.replace(/@(?:latest|next|canary|\d[^/]*)$/, ""))
+ : command;
+ const meaningfulText = String(meaningful || "");
+ const fullPath = meaningfulText.includes("/") || meaningfulText.includes("\\")
+ ? meaningfulText.replaceAll("\\", "/")
+ : null;
+ return { command, marker: String(marker || "").toLowerCase(), fullPath };
+}
+
+function recommendationForMcp(name, config = {}) {
+ const { marker } = normalizedPackageMarker(config);
+ const subject = `${name} ${marker}`;
+ if (ISOLATE_MCP.test(subject)) {
+ return {
+ policy: "isolate",
+ reason: "Browser, filesystem, shell, or mutable local state can collide across agent sessions.",
+ };
+ }
+ if (REVIEW_MCP.test(subject)) {
+ return {
+ policy: "review",
+ reason: "Sharing can save memory, but every session would use the same account, credentials, and server-side state.",
+ };
+ }
+ return {
+ policy: "shared-candidate",
+ reason: "No session-bound browser or local-state marker was detected; verify the server's own concurrency contract before sharing.",
+ };
+}
+
+function mcpIdentity(name, config = {}) {
+ const safeConfig = {
+ command: config.command || null,
+ args: Array.isArray(config.args) ? config.args : null,
+ url: config.url || null,
+ type: config.type || null,
+ };
+ const digest = createHash("sha256").update(JSON.stringify(safeConfig)).digest("hex").slice(0, 12);
+ return digest;
+}
+
+function buildMcpMatchers(mcpItems = []) {
+ const seen = new Set();
+ const matchers = [];
+ for (const item of mcpItems) {
+ if (item?.category && item.category !== "mcp") continue;
+ const config = item?.mcpConfig || item?.config || {};
+ if (!config.command) continue;
+ const { command, marker, fullPath } = normalizedPackageMarker(config);
+ const identity = mcpIdentity(item.name, config);
+ if (seen.has(identity)) continue;
+ seen.add(identity);
+ matchers.push({
+ identity,
+ name: String(item.name || marker || command || "MCP server"),
+ command,
+ marker,
+ fullPath,
+ recommendation: recommendationForMcp(item.name, config),
+ });
+ }
+ return matchers;
+}
+
+function processMatchesMcp(processInfo, matcher) {
+ const rawTokens = processInfo.tokens.map(String);
+ if (matcher.fullPath) {
+ return rawTokens.some(token => token.replaceAll("\\", "/") === matcher.fullPath);
+ }
+ const tokens = rawTokens.map(token => token.toLowerCase());
+ const tokenNames = tokens.map(token => basename(token));
+ const packageMarkers = tokenNames.map(token => token.replace(/@(?:latest|next|canary|\d[^/]*)$/, ""));
+ if (matcher.marker && matcher.marker.length >= 3 && (tokens.includes(matcher.marker) || tokenNames.includes(matcher.marker) || packageMarkers.includes(matcher.marker))) {
+ return true;
+ }
+ return matcher.command && !GENERIC_RUNNERS.has(matcher.command) && tokenNames[0] === matcher.command;
+}
+
+function descendantsOf(rootPid, processes, allowedSessionId, assignment) {
+ const result = new Set([rootPid]);
+ let changed = true;
+ while (changed) {
+ changed = false;
+ for (const processInfo of processes.values()) {
+ if (result.has(processInfo.pid) || !result.has(processInfo.ppid)) continue;
+ if (assignment.get(processInfo.pid) !== allowedSessionId) continue;
+ result.add(processInfo.pid);
+ changed = true;
+ }
+ }
+ return result;
+}
+
+function attachDirectMcpProcesses(sessions, assignment, processes, matchers) {
+ const sessionById = new Map(sessions.map(session => [session.id, session]));
+ const claimedPids = new Set();
+ for (const matcher of matchers) {
+ const matches = [...processes.values()].filter(processInfo => {
+ const sessionId = assignment.get(processInfo.pid);
+ return sessionId && !claimedPids.has(processInfo.pid) && processMatchesMcp(processInfo, matcher);
+ });
+ const matchedPids = new Set(matches.map(processInfo => processInfo.pid));
+ const roots = matches.filter(processInfo => !matchedPids.has(processInfo.ppid));
+ for (const root of roots) {
+ const sessionId = assignment.get(root.pid);
+ const session = sessionById.get(sessionId);
+ if (!session) continue;
+ let entry = session.mcpServers.find(server => server.identity === matcher.identity);
+ if (!entry) {
+ entry = {
+ identity: matcher.identity,
+ name: matcher.name,
+ mode: "direct",
+ instanceCount: 0,
+ processCount: 0,
+ memoryBytes: 0,
+ ...matcher.recommendation,
+ };
+ session.mcpServers.push(entry);
+ }
+ const owned = descendantsOf(root.pid, processes, sessionId, assignment);
+ for (const pid of owned) claimedPids.add(pid);
+ entry.instanceCount++;
+ entry.processCount += owned.size;
+ for (const pid of owned) entry.memoryBytes += processes.get(pid)?.memoryBytes || 0;
+ }
+ }
+}
+
+function buildDuplicateMcpSummary(sessions) {
+ const groups = new Map();
+ for (const session of sessions) {
+ for (const server of session.mcpServers) {
+ if (!groups.has(server.identity)) groups.set(server.identity, { ...server, sessions: [], memoryBytes: 0, instanceCount: 0 });
+ const group = groups.get(server.identity);
+ group.sessions.push({ id: session.id, label: session.label });
+ group.memoryBytes += server.memoryBytes;
+ group.instanceCount += server.instanceCount;
+ }
+ }
+ return [...groups.values()]
+ .filter(group => group.sessions.length > 1 || group.instanceCount > 1)
+ .map(({ identity, name, policy, reason, sessions: groupSessions, memoryBytes, instanceCount }) => ({
+ identity, name, policy, reason, sessions: groupSessions, memoryBytes, instanceCount,
+ }))
+ .sort((a, b) => b.memoryBytes - a.memoryBytes || a.name.localeCompare(b.name));
+}
+
+function parseTcpClientInodes(content, targetPort) {
+ const inodes = new Set();
+ for (const line of String(content || "").split(/\r?\n/).slice(1)) {
+ const fields = line.trim().split(/\s+/);
+ if (fields.length < 10 || fields[3] !== "01") continue;
+ const remotePort = Number.parseInt(fields[2]?.split(":").at(-1) || "", 16);
+ if (remotePort !== targetPort) continue;
+ const inode = fields[9];
+ if (/^\d+$/.test(inode)) inodes.add(inode);
+ }
+ return inodes;
+}
+
+async function findConnectedSessions(port, procRoot, processes, assignment, fsApi) {
+ if (!Number.isInteger(port) || typeof fsApi.readlink !== "function") {
+ return { sessionIds: [], evidence: "unavailable" };
+ }
+ const [tcp, tcp6] = await Promise.all([
+ readText(join(procRoot, "net", "tcp"), fsApi),
+ readText(join(procRoot, "net", "tcp6"), fsApi),
+ ]);
+ const inodes = new Set([
+ ...parseTcpClientInodes(tcp, port),
+ ...parseTcpClientInodes(tcp6, port),
+ ]);
+ if (!inodes.size) return { sessionIds: [], evidence: "tcp-snapshot" };
+
+ const sessionIds = new Set();
+ await Promise.all([...assignment.keys()].map(async pid => {
+ let entries;
+ try { entries = await fsApi.readdir(join(procRoot, String(pid), "fd")); } catch { return; }
+ for (const entry of entries) {
+ let target;
+ try { target = await fsApi.readlink(join(procRoot, String(pid), "fd", String(entry))); } catch { continue; }
+ const inode = String(target).match(/^socket:\[(\d+)\]$/)?.[1];
+ if (inode && inodes.has(inode)) sessionIds.add(assignment.get(pid));
+ }
+ }));
+ return { sessionIds: [...sessionIds].filter(Boolean), evidence: "tcp-snapshot" };
+}
+
+function objectContainsIntentProbeHook(value, depth = 0) {
+ if (depth > 8 || value === null || value === undefined) return false;
+ if (typeof value === "string") return /intentprobe(?:-hook)?\s+runtime\s+(?:scan|serve-jsonl)/i.test(value);
+ if (Array.isArray(value)) return value.some(item => objectContainsIntentProbeHook(item, depth + 1));
+ if (typeof value === "object") return Object.values(value).some(item => objectContainsIntentProbeHook(item, depth + 1));
+ return false;
+}
+
+async function executableExists(name, runtimeEnv, fsApi) {
+ if (typeof fsApi.access !== "function") return false;
+ const directories = String(runtimeEnv.PATH || "").split(":").filter(Boolean);
+ for (const directory of directories) {
+ try {
+ await fsApi.access(join(directory, name), fsConstants.X_OK);
+ return true;
+ } catch {}
+ }
+ return false;
+}
+
+async function collectIntentProbeStatus(home, runtimeEnv, processes, fsApi) {
+ const daemonRunning = [...processes.values()].some(processInfo =>
+ processInfo.tokens.some(token => /(?:^|\/)intentprobe(?:-hook)?$/.test(String(token))) &&
+ processInfo.tokens.includes("serve-jsonl")
+ );
+ const installed = daemonRunning || await executableExists("intentprobe", runtimeEnv, fsApi) || await executableExists("intentprobe-hook", runtimeEnv, fsApi);
+ let hookConfigured = false;
+ for (const path of [join(home, ".claude", "settings.json"), join(home, ".claude", "settings.local.json")]) {
+ const text = await readText(path, fsApi);
+ if (!text) continue;
+ try {
+ if (objectContainsIntentProbeHook(JSON.parse(text))) {
+ hookConfigured = true;
+ break;
+ }
+ } catch {}
+ }
+ return {
+ installed,
+ daemonRunning,
+ hookConfigured,
+ eventBoundaryScanning: installed && hookConfigured,
+ receiptIngestion: "not-connected",
+ supportedEvents: ["tool_definition", "before_tool_call", "after_tool_call"],
+ };
+}
+
+function sharedServerInventory(config = {}) {
+ return Object.entries(config.mcpServers || config.servers || {})
+ .filter(([, server]) => server?.disabled !== true)
+ .map(([name, server]) => ({
+ identity: mcpIdentity(name, server),
+ name,
+ mode: "shared",
+ ...recommendationForMcp(name, server),
+ }));
+}
+
+function isOneMcpServe(tokens) {
+ return tokens.includes("serve") && tokens.some(token => basename(String(token)) === "1mcp");
+}
+
+async function listPids(procRoot, fsApi) {
+ try {
+ return (await fsApi.readdir(procRoot, { withFileTypes: true }))
+ .filter(entry => entry.isDirectory() && /^\d+$/.test(entry.name))
+ .map(entry => entry.name);
+ } catch {
+ return [];
+ }
+}
+
+function findOneMcpProcess(processes, home) {
+ for (const processInfo of processes.values()) {
+ if (!isOneMcpServe(processInfo.tokens)) continue;
+ const flags = parseOneMcpFlags(processInfo.tokens);
+ if (flags.configPath && !isWithinHome(flags.configPath, home)) continue;
+ return { pid: processInfo.pid, flags, cgroupPath: processInfo.cgroupPath };
+ }
+ return null;
+}
+
+async function readCounter(path, fsApi) {
+ const value = (await readText(path, fsApi))?.trim();
+ if (!value || value === "max") return null;
+ return finiteNumber(value);
+}
+
+export async function collectProcessGroup(runtime, processes, cgroupRoot, fsApi) {
+ const tree = new Set([runtime.pid]);
+ let changed = true;
+ while (changed) {
+ changed = false;
+ for (const processInfo of processes.values()) {
+ if (tree.has(processInfo.pid) || !tree.has(processInfo.ppid)) continue;
+ tree.add(processInfo.pid);
+ changed = true;
+ }
+ }
+ let processCount = 0;
+ let commandLineSecretProcesses = 0;
+ for (const processInfo of processes.values()) {
+ if (!tree.has(processInfo.pid)) continue;
+ processCount++;
+ if (commandLineHasSecret(processInfo.tokens)) commandLineSecretProcesses++;
+ }
+
+ const sharedCgroup = !runtime.cgroupPath || [...processes.values()].some(processInfo =>
+ processInfo.cgroupPath === runtime.cgroupPath && !tree.has(processInfo.pid)
+ );
+ if (sharedCgroup) return {
+ processCount, taskCount: null, taskMax: null, memoryBytes: null,
+ memoryHighBytes: null, memoryMaxBytes: null, commandLineSecretProcesses,
+ };
+
+ const counterRoot = resolve(cgroupRoot, runtime.cgroupPath.replace(/^\/+/, ""));
+ if (!isWithinHome(counterRoot, cgroupRoot)) return {
+ processCount, taskCount: null, taskMax: null, memoryBytes: null,
+ memoryHighBytes: null, memoryMaxBytes: null, commandLineSecretProcesses,
+ };
+ return {
+ processCount,
+ taskCount: await readCounter(join(counterRoot, "pids.current"), fsApi),
+ taskMax: await readCounter(join(counterRoot, "pids.max"), fsApi),
+ memoryBytes: await readCounter(join(counterRoot, "memory.current"), fsApi),
+ memoryHighBytes: await readCounter(join(counterRoot, "memory.high"), fsApi),
+ memoryMaxBytes: await readCounter(join(counterRoot, "memory.max"), fsApi),
+ commandLineSecretProcesses,
+ };
+}
+
+export async function fetchLoopbackHealth(host, port, timeoutMs = 900) {
+ if (!LOOPBACK_HOSTS.has(host) || !Number.isInteger(port)) return null;
+ return new Promise(resolveHealth => {
+ let settled = false;
+ const finish = value => {
+ if (settled) return;
+ settled = true;
+ resolveHealth(value);
+ };
+ const request = http.get({ hostname: host, port, path: "/health", timeout: timeoutMs }, response => {
+ let body = "";
+ let bodyBytes = 0;
+ response.setEncoding("utf8");
+ response.on("data", chunk => {
+ bodyBytes += Buffer.byteLength(chunk, "utf8");
+ if (bodyBytes > 65536) {
+ finish(null);
+ request.destroy();
+ return;
+ }
+ body += chunk;
+ });
+ response.on("end", () => {
+ if (response.statusCode !== 200) return finish(null);
+ try { finish(JSON.parse(body)); } catch { finish(null); }
+ });
+ response.on("aborted", () => finish(null));
+ response.on("error", () => finish(null));
+ response.on("close", () => finish(null));
+ });
+ request.on("timeout", () => { request.destroy(); finish(null); });
+ request.on("error", () => finish(null));
+ });
+}
+
+/**
+ * Collect a privacy-bounded report. Dependency injection keeps procfs and the
+ * loopback request deterministic in tests.
+ */
+export async function collectRuntimeDoctorReport(options = {}) {
+ const runtimeHome = options.home || homedir();
+ const runtimeEnv = options.env || process.env;
+ const runtimePlatform = options.platform || process.platform;
+ const fsApi = options.fs || { access, readFile, readdir, readlink, realpath };
+ const procRoot = options.procRoot || "/proc";
+ const cgroupRoot = options.cgroupRoot || "/sys/fs/cgroup";
+ const healthFetcher = options.healthFetcher || fetchLoopbackHealth;
+ const currentUid = options.uid ?? (typeof process.getuid === "function" ? process.getuid() : null);
+
+ if (runtimeEnv.CCO_RUNTIME_DOCTOR_DISABLED === "1" || runtimePlatform !== "linux") {
+ return {
+ ok: true,
+ detected: false,
+ supported: runtimePlatform === "linux",
+ findings: [],
+ topology: { sessions: [], duplicateDirectMcps: [], sharedRuntimes: [] },
+ };
+ }
+
+ const processes = await collectProcessSnapshot(procRoot, currentUid, fsApi, options.clockTicks || 100);
+ const { sessions, assignment } = buildSessions(processes, options.currentPid ?? process.pid);
+ const matchers = buildMcpMatchers(options.mcpItems || []);
+ attachDirectMcpProcesses(sessions, assignment, processes, matchers);
+ const intentProbe = await collectIntentProbeStatus(runtimeHome, runtimeEnv, processes, fsApi);
+ const baseReport = {
+ ok: true,
+ detected: false,
+ supported: true,
+ methodology: {
+ version: 2,
+ readOnly: true,
+ secretValuesReturned: false,
+ memory: "sum of same-user VmRSS assigned to the nearest harness process",
+ directMcpAttribution: "configured command markers matched inside each session process tree",
+ sharedClientAttribution: "live loopback TCP socket snapshot; short-lived requests may be missed",
+ semanticActivity: "requires IntentProbe event receipts; process names are not treated as intent",
+ },
+ topology: {
+ sessions,
+ duplicateDirectMcps: buildDuplicateMcpSummary(sessions),
+ sharedRuntimes: [],
+ },
+ intentProbe,
+ findings: [],
+ };
+
+ const runtime = findOneMcpProcess(processes, runtimeHome);
+ if (!runtime) return baseReport;
+
+ const defaultConfigPath = join(runtimeEnv.XDG_CONFIG_HOME || join(runtimeHome, ".config"), "1mcp", "mcp.json");
+ const configPath = runtime.flags.configPath || runtimeEnv.ONE_MCP_CONFIG || defaultConfigPath;
+ if (!await isWithinHomeResolved(configPath, runtimeHome, fsApi)) {
+ return baseReport;
+ }
+
+ let config = {};
+ const configText = await readText(configPath, fsApi);
+ if (configText) {
+ try { config = JSON.parse(configText); } catch { config = {}; }
+ }
+ const inventory = analyzeOneMcpConfig(config);
+ const group = await collectProcessGroup(runtime, processes, cgroupRoot, fsApi);
+ const health = runtime.flags.transport === "http"
+ ? await healthFetcher(runtime.flags.host, runtime.flags.port)
+ : null;
+ const connectionEvidence = runtime.flags.transport === "http" && LOOPBACK_HOSTS.has(runtime.flags.host)
+ ? await findConnectedSessions(runtime.flags.port, procRoot, processes, assignment, fsApi)
+ : { sessionIds: [], evidence: "unavailable" };
+ const sessionLabels = new Map(sessions.map(session => [session.id, session.label]));
+
+ const resources = {
+ memoryBytes: group.memoryBytes,
+ memoryHighBytes: group.memoryHighBytes,
+ memoryMaxBytes: group.memoryMaxBytes,
+ memoryHighRatio: safeRatio(group.memoryBytes, group.memoryHighBytes),
+ memoryMaxRatio: safeRatio(group.memoryBytes, group.memoryMaxBytes),
+ processCount: group.processCount,
+ taskCount: group.taskCount,
+ taskMax: group.taskMax,
+ taskRatio: safeRatio(group.taskCount, group.taskMax),
+ };
+ const report = {
+ ...baseReport,
+ detected: true,
+ runtime: {
+ name: "1MCP",
+ status: typeof health?.status === "string" ? health.status : "running",
+ version: typeof health?.version === "string" ? health.version : null,
+ transport: runtime.flags.transport,
+ host: LOOPBACK_HOSTS.has(runtime.flags.host) ? runtime.flags.host : "non-loopback",
+ port: runtime.flags.port,
+ uptimeSeconds: finiteNumber(health?.system?.uptime),
+ healthyServers: finiteNumber(health?.servers?.healthy),
+ unhealthyServers: finiteNumber(health?.servers?.unhealthy) || 0,
+ },
+ inventory,
+ resources,
+ features: {
+ asyncLoading: runtime.flags.asyncLoading,
+ lazyLoading: runtime.flags.lazyLoading,
+ lazyMode: runtime.flags.lazyMode,
+ configReload: runtime.flags.configReload,
+ sessionPersistence: runtime.flags.sessionPersistence,
+ },
+ exposure: { commandLineSecretProcesses: group.commandLineSecretProcesses },
+ };
+ report.topology.sharedRuntimes.push({
+ id: "1mcp",
+ name: "1MCP",
+ serverCount: inventory.enabledCount,
+ memoryBytes: resources.memoryBytes,
+ processCount: resources.processCount,
+ connectedSessions: connectionEvidence.sessionIds.map(id => ({ id, label: sessionLabels.get(id) || "Agent session" })),
+ connectionEvidence: connectionEvidence.evidence,
+ servers: sharedServerInventory(config),
+ });
+ report.findings = buildRuntimeFindings(report);
+ return report;
+}
+
+/**
+ * Gracefully ask one still-identical harness root to stop. The opaque session
+ * id includes procfs start ticks, preventing a recycled PID from being killed.
+ */
+export async function stopRuntimeSession(sessionId, options = {}) {
+ const runtimePlatform = options.platform || process.platform;
+ if (runtimePlatform !== "linux") return { ok: false, error: "Session stop is currently supported on Linux only" };
+ const runtimeEnv = options.env || process.env;
+ if (runtimeEnv.CCO_RUNTIME_DOCTOR_DISABLED === "1") return { ok: false, error: "Runtime Doctor is disabled" };
+ const fsApi = options.fs || { access, readFile, readdir, readlink, realpath };
+ const procRoot = options.procRoot || "/proc";
+ const currentUid = options.uid ?? (typeof process.getuid === "function" ? process.getuid() : null);
+ const processes = await collectProcessSnapshot(procRoot, currentUid, fsApi, options.clockTicks || 100);
+ const { sessions } = buildSessions(processes, options.currentPid ?? process.pid);
+ const session = sessions.find(candidate => candidate.id === sessionId);
+ if (!session) return { ok: false, error: "That session ended or changed identity; refresh the runtime map" };
+ if (!session.controllable) return { ok: false, error: "CCO is running inside that session and will not stop its own process tree" };
+ if (!Number.isInteger(session.pid) || session.pid <= 1) return { ok: false, error: "Invalid session process" };
+ const [freshStatusText, freshCmdlineText, freshStatText] = await Promise.all([
+ readText(join(procRoot, String(session.pid), "status"), fsApi),
+ readText(join(procRoot, String(session.pid), "cmdline"), fsApi),
+ readText(join(procRoot, String(session.pid), "stat"), fsApi),
+ ]);
+ const freshStatus = parseProcessStatus(freshStatusText);
+ const freshProcess = {
+ ...freshStatus,
+ pid: session.pid,
+ tokens: parseCmdline(freshCmdlineText),
+ startTicks: parseProcessStartTicks(freshStatText),
+ };
+ const freshHarness = harnessForProcess(freshProcess);
+ if (freshStatus.uid !== currentUid || !freshHarness || sessionIdFor(freshProcess, freshHarness) !== session.id) {
+ return { ok: false, error: "That session ended or changed identity; refresh the runtime map" };
+ }
+ const sendSignal = options.sendSignal || ((pid, signal) => process.kill(pid, signal));
+ try {
+ sendSignal(session.pid, "SIGTERM");
+ } catch (error) {
+ return { ok: false, error: error?.code === "ESRCH" ? "That session already ended" : "The session could not be stopped" };
+ }
+ return {
+ ok: true,
+ sessionId: session.id,
+ signal: "SIGTERM",
+ message: `Asked ${session.label} to stop gracefully`,
+ };
+}
diff --git a/src/server.mjs b/src/server.mjs
index 5c0b537..146e72b 100644
--- a/src/server.mjs
+++ b/src/server.mjs
@@ -40,6 +40,7 @@ import {
} from "./privacy-metrics.mjs";
import { isNewerVersion } from "./version.mjs";
import { isPathWithin, normalizedPath } from "./path-security.mjs";
+import { collectRuntimeDoctorReport, stopRuntimeSession } from "./runtime-doctor.mjs";
// ── Update check ─────────────────────────────────────────────────────
async function checkForUpdate() {
@@ -208,6 +209,20 @@ function invalidateCachedData(harnessId = getDefaultAdapterId()) {
cachedDataByHarness.delete(harnessId);
}
+async function collectRuntimeMcpItems() {
+ const summaries = await listAdapters();
+ const inventories = await Promise.all(summaries.map(async ({ id }) => {
+ try {
+ const data = getCachedData(id) || await refreshScanCache(id);
+ return (data?.items || []).filter(item => item.category === "mcp");
+ } catch {
+ // One unavailable harness must not hide runtime evidence from the rest.
+ return [];
+ }
+ }));
+ return inventories.flat();
+}
+
async function getHarnessOperations(harnessId = getDefaultAdapterId()) {
const adapter = await getAdapter(harnessId);
return adapter.operations || { moveItem, deleteItem, getValidDestinations };
@@ -502,6 +517,24 @@ async function handleRequest(req, res) {
return json(res, { ok: true, harness: cachedData.harness, ...report });
}
+ // GET /api/runtime-doctor — read-only shared MCP runtime health
+ if (path === "/api/runtime-doctor" && req.method === "GET") {
+ if (!cachedData) await freshScan();
+ return json(res, await collectRuntimeDoctorReport({
+ mcpItems: await collectRuntimeMcpItems(),
+ }));
+ }
+
+ // POST /api/runtime/session/stop — explicit, identity-pinned graceful stop
+ if (path === "/api/runtime/session/stop" && req.method === "POST") {
+ const { sessionId, confirmation } = await readBody(req);
+ if (!sessionId || confirmation !== sessionId) {
+ return json(res, { ok: false, error: "A fresh session identity confirmation is required" }, 400);
+ }
+ const result = await stopRuntimeSession(sessionId);
+ return json(res, result, result.ok ? 200 : 409);
+ }
+
// POST /api/control-plane/repair — archive one verified exact duplicate
if (path === "/api/control-plane/repair" && req.method === "POST") {
const { scopeId, actionId } = await readBody(req);
diff --git a/src/ui/app.js b/src/ui/app.js
index cf8be95..148fe04 100644
--- a/src/ui/app.js
+++ b/src/ui/app.js
@@ -4951,7 +4951,16 @@ function doctorFormatBytes(bytes) {
const value = Number(bytes || 0);
if (value < 1024) return `${value} B`;
if (value < 1024 * 1024) return `${(value / 1024).toFixed(1)} KiB`;
- return `${(value / (1024 * 1024)).toFixed(1)} MiB`;
+ if (value < 1024 * 1024 * 1024) return `${(value / (1024 * 1024)).toFixed(1)} MiB`;
+ return `${(value / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
+}
+
+function doctorFormatAge(seconds) {
+ if (seconds === null || seconds === undefined) return "age unavailable";
+ if (seconds < 60) return `${seconds}s`;
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ${Math.floor((seconds % 3600) / 60)}m`;
+ return `${Math.floor(seconds / 86400)}d ${Math.floor((seconds % 86400) / 3600)}h`;
}
function closeHarnessDoctor() {
@@ -5055,6 +5064,130 @@ function renderDoctorFindings(report) {
`).join("");
}
+function renderRuntimeDoctor(report) {
+ const el = document.getElementById("doctorRuntime");
+ const topology = report?.topology || {};
+ const sessions = [...(topology.sessions || [])].sort((a, b) => Number(b.memoryBytes || 0) - Number(a.memoryBytes || 0));
+ const duplicates = topology.duplicateDirectMcps || [];
+ const sharedRuntimes = topology.sharedRuntimes || [];
+ const intentProbe = report?.intentProbe || {};
+ const runtime = report.runtime || {};
+ const inventory = report.inventory || {};
+ const resources = report.resources || {};
+ const features = report.features || {};
+ const maxSessionMemory = Math.max(1, ...sessions.map(session => Number(session.memoryBytes || 0)));
+ const featureFlags = [
+ ["Async loading", features.asyncLoading],
+ ["Lazy loading", features.lazyLoading],
+ ["Config reload", features.configReload],
+ ["Session persistence", features.sessionPersistence],
+ ];
+ const findings = report.findings || [];
+ const sessionCards = sessions.length ? sessions.map(session => `
+
${esc(finding.detail)}
+Agent-session memory, direct versus shared MCP topology, 1MCP pressure, and IntentProbe readiness. Secret values never leave this machine or appear in the report.
+