Claude Adapter — Full Reference
P: Everything
src/adapters/claude/does for a consumer project — install, hook rendering, MCP wiring, formula-agent dispatch, observability. R: Touchingsrc/adapters/claude/**, debugging the Claude install, adding/renaming hooks, enabling new SDK features, planning permission or MCP changes for Claude users. S: Working onsrc/core/, other adapters, or pure docs. N: claude-sdk-architecture.md, claude-deepening-checklist.md, claude-migration-2026-05.md
Nav: Adapters Index | Docs Index
SDK floor:
claude-agent-sdk>=0.2.110,<0.3.0(bundles Claude CLI 2.1.191). CLI floor:@anthropic-ai/claude-code>=2.1.119(stable). Renamed 2026-05-04 from "Claude-SDK Dispatcher" to full adapter reference.
1. The mRNA layer — what this adapter is
Claude is the primary adapter coding-os targets. Codex is secondary; it reuses the same kernel but exposes fewer capabilities (Codex has Bash-only PreToolUse). This file documents the Claude-specific translation of the agent-agnostic kernel.
src/core/ ──► src/adapters/claude/ ──► consumer project's .claude/
(DNA) (mRNA — this file) (phenotype)
Three jobs:
- Install — render
.claude/{settings.json,settings.local.json,.mcp.json,agents,skills,commands,rules,hooks}into a consumer project (via install.sh + src/core/scripts/install-adapter.sh). - Dispatch — spawn formula-agent sub-sessions via
claude-agent-sdk(sdk_dispatcher.py). - Declare capabilities — tell the kernel which
{event, matcher}pairs Claude's CLI can actually fire (adapter.yaml).
2. SDK + tooling versions (audit 2026-06-09)
| Component | Floor | Latest | Notes |
|---|---|---|---|
claude-agent-sdk (PyPI) |
>=0.2.95,<0.3.0 |
0.2.95 (2026-06-09) | Pinned in pyproject.toml::dependencies. 0.2.87→0.2.95 = bundled-CLI bumps (2.1.150→2.1.170) + a trio session-store fix; no new Python API. |
mcp (PyPI) |
>=1.27.0 |
1.27.0 (2026-04-02) | Used by central FastMCP server. |
@anthropic-ai/claude-code (npm CLI) |
2.1.119 stable | 2.1.170 (bundled) | The SDK bundles its own CLI; a user-installed CLI is optional. |
@anthropic-ai/claude-agent-sdk (npm) |
n/a (Py only) | reference | Reference only — coding-os uses the Py SDK exclusively. |
Verify:
uv pip show claude-agent-sdk mcp | grep -E "^(Name|Version)"
claude --version # CLI on PATH
3. Install flow
bash src/adapters/claude/install.sh invoked from a consumer project root:
- Calls shared installer (src/core/scripts/install-adapter.sh) which:
- Creates
.claude/{hooks,rules,skills,commands,agents}/symlinking tosrc/core/{hooks,rules,skills,commands,thinking_os/agents}/. - Writes
.claude/cos-env.sh(sourcesCOS_AGENT_DIR,COS_AGENT, etc.).
- Creates
- Renders
.claude/settings.jsonfrom settings.template.json (substituting theHOOKS_DIRplaceholder). - Adds
coding-osentry to.mcp.jsonvia _install_helpers/update_mcp_json.py:- Prefers
cos server-startifcosis onPATH. - Falls back to
uv run --directory <CODING_OS_ROOT>/core/thinking_os python server.py.
- Prefers
- Symlinks role prompts to
.claude/agents/role-*.md(SDK reads from this dir for filesystem-fallbackAgentDefinition). - Copies settings.local.template.json to
.claude/settings.local.json(only if absent — never overwrites user customizations).
4. settings.json (rendered, do not hand-edit)
src/adapters/claude/settings.template.json is derived from
src/core/hooks/registry.yaml via make regen-adapter-templates. Hand-edits
are caught by the golden-parity tests (test_adapter_parity), which fail if the
template drifts from the registry.
Hook events declared (post-2026-05-04 hardening):
| Event | Matchers | Why |
|---|---|---|
PreToolUse |
Bash, Write|Edit, Write|Edit|MultiEdit, Skill |
Block secrets, dangerous commands, missing doc anchor, missing memory check, missing skill invocation. |
PostToolUse |
Bash, Write|Edit, Write|Edit|MultiEdit, Skill, mcp__coding-os__cos_backtrack_log |
Capture observation, regen doc index, route backtrack-log calls. |
PostToolUseFailure |
"" |
(NEW 2026-05-04) keep agent-presence accurate when a tool errors. |
Stop |
"" |
Session summary + memory enrichment. |
SubagentStart |
"" |
(NEW 2026-05-04) agent-presence ping when sub-session spawns. |
SubagentStop |
"" |
(NEW 2026-05-04) agent-presence ping when sub-session exits. |
SessionStart |
startup, compact|resume |
Bootstrap session, decay scan, MCP liveness probe. |
UserPromptSubmit |
"" |
Caveman-mode gating, presence ping. |
Events the Py SDK supports but coding-os does NOT yet wire:
PreCompact, Notification, PermissionRequest. These are TS-only or
filesystem-hook-only as of SDK 0.1.73 — leave them off until a need
emerges.
5. settings.local.json (user-scoped allow-list)
Template at settings.local.template.json.
Copied once during install; never overwritten so user customizations
survive cos sync-all.
Critical entry added 2026-05-04:
{ "permissions": { "allow": [ ..., "mcp__coding-os__*" ] } }
Why required: Claude Agent SDK 0.1.73 evaluates permissions in this
order (digest §B.4): hooks → deny rules → permission_mode → allow rules
→ can_use_tool. acceptEdits mode auto-approves filesystem ops only —
MCP tools are NOT auto-approved. Without the mcp__coding-os__*
wildcard, every cos_* call would either deny silently (in dontAsk)
or prompt the user (in default).
6. .mcp.json wiring
Generated by _install_helpers/update_mcp_json.py
during install. Loaded by Claude Code only when setting_sources includes
"project" (default).
The central server lives at src/core/thinking_os/server.py
and exposes ~60 tools under the cos_* namespace (categories in
AGENTS.md §Four-Layer Retrieval).
Claude addresses these tools as mcp__coding-os__cos_<name> per SDK
docs §D.2.
7. Formula-agent dispatcher
src/adapters/claude/sdk_dispatcher.py — the only place
in coding-os that calls claude_agent_sdk.query().
7.1 Why a dispatcher
formula roles (researcher, analyst, architect, …, refactorer)
need to run as real sub-sessions so the supervisor (cos_supervise)
can collect parallel evidence and merge it into a typed EvidenceBundle.
The dispatcher converts a DispatchRequest into a single-turn SDK
query() call.
7.2 Hardened options (2026-05-04)
ClaudeAgentOptions(
system_prompt={
"type": "preset",
"preset": "claude_code",
"append": <role spec + dispatch context + JSON instruction>,
"exclude_dynamic_sections": True, # cross-cwd cache reuse
},
max_turns=1,
allowed_tools=[..., "mcp__coding-os__*"], # MCP wildcard always added
permission_mode="dontAsk", # headless; no prompts
setting_sources=["project"], # isolate from ~/.claude/
model=request.model,
effort="xhigh" if request.model.startswith(XHIGH_MODEL_PREFIXES) else None,
skills=role_skills, # from agent frontmatter
cwd=request.cwd,
env=_claude_auth_env(request.cwd), # subscription vs api_key — §7.7
)
Why each line:
| Line | Reason |
|---|---|
system_prompt = preset claude_code + append |
Default SDK prompt is minimal since the rename. Without the claude_code preset, formulas lose Claude Code's coding/safety baseline. |
exclude_dynamic_sections=True |
Strips cwd/git/date/OS/memory_paths from the system prompt, emits them as a first-user-message block. Lets the prompt cache survive across consumer-project cwds. |
permission_mode="dontAsk" |
Headless — never prompt the user. Allow-list is the contract; unmatched tools deny silently. |
setting_sources=["project"] |
Sub-sessions must be reproducible across machines. Default loads user+project+local — ~/.claude/ would silently change behavior. |
allowed_tools includes mcp__coding-os__* |
acceptEdits does NOT auto-approve MCP. Always inject the wildcard alongside caller-provided allow-list. |
effort="xhigh" on high-tier models |
Fable 5 / Opus 4.8 / 4.7 get "xhigh" — the best level for coding/agentic and the Claude Code default. Available in the Py SDK since 0.1.74 (was TS-only before); falls back to "high" on models that don't support it. Other models use the SDK default. |
skills=role_skills |
Sub-sessions don't inherit parent skills. Each role's skills: frontmatter declares dependencies (e.g. implementer → ["clean-code"]). |
7.3 Contract
Input — DispatchRequest:
| field | type | description |
|---|---|---|
formula_id |
str | e.g. "implementer". Restricted to [A-Za-z0-9_-]+. |
agent_file |
str | absolute path to role md file. Dispatcher rejects relative paths. |
prompt |
str | composed system+user prompt. |
input_slice |
dict | upstream-only bundle view from build_input_slice(). |
persona_id |
str|None | dispatch persona. |
intensity |
"light"|"standard"|"full" |
filters role step list. |
allowed_tools |
list[str] | caller-provided allow-list — mcp__coding-os__* is always appended. |
timeout_s |
float | hard timeout, default 300s. |
cwd |
str|None | project root for the sub-session. |
model |
str|None | (NEW) model id. None = SDK default. |
Model resolution at request build (_build_dispatch_request) —
precedence, first match wins; the decision source is logged
(dispatch model resolved … via <source> in .mcp.log):
| tier | source | key |
|---|---|---|
| 1 | explicit model argument on the run tools |
verbatim |
| 2 | active preset's roles_adapter_hints[<role>].model_pref (presets/registry.yaml; the session's composed preset is read back from persona_selections) |
complexity lowercased |
| 3 | role frontmatter model_pref (agents/README.md) |
complexity lowercased |
| 4 | cos_route_model empirical recommendation — only when real outcome history exists (data_points > 0), never the cold-start static default |
complexity |
| 5 | None → SDK default |
— |
Aliases pass through verbatim — the adapter, not the kernel, owns
alias→id mapping (Rule 11). The caller passes complexity explicitly
because the Cynefin gate is per-panel filesystem state the long-lived
MCP server cannot attribute to a panel. cos_supervise mirrors tiers
2–3 by attaching model_hints to every dispatch action so the main
agent can pick before calling the run tool.
Output — DispatchResult:
| field | type |
|---|---|
status |
"ok"|"timeout"|"error"|"skipped" |
output_json |
parsed JSON from the formula's json block |
latency_ms |
wall clock |
dispatcher_name |
"claude-sdk" or "default" |
error |
str|None |
raw_transcript |
str|None |
7.4 Failure modes
| Failure | Status | Note |
|---|---|---|
claude_agent_sdk not importable |
error |
Factory falls back to default dispatcher. |
agent_file is relative |
error |
Dispatcher rejects to avoid silent cwd-search ambiguity. |
| Agent file missing | error |
FileNotFoundError. |
Sub-session exceeds timeout_s |
timeout |
Includes partial transcript. |
No json block in transcript |
error |
"no JSON block found in agent output". |
| Tool denied by permission_mode | n/a | Logged in result.permission_denials; transcript continues without that tool. |
7.5 MCP envelope path
After successful dispatch, callers should NOT call cos_supervise_record_output
manually — cos_dispatch_formula_run (in src/core/thinking_os/tools/cognition.py)
persists the bundle and writes the formula_dispatches audit row.
7.6 Budget gates — spent + projected
Two USD ceilings gate every dispatch, both computed over formula_dispatches.cost_usd
and both disabled (allow-everything) when their source is unset:
| Gate | Scope | Cap source |
|---|---|---|
budget.check |
today, UTC | COS_DAILY_BUDGET_USD, else hub-settings.json::budget_cap |
budget.chain_check |
one task_marker |
COS_CHAIN_BUDGET_USD |
Both compare projected = spent + additional_estimate_usd against the cap, not
spend alone. A single check authorizes a single dispatch, so cos_dispatch_formula_run
passes no estimate — spent-only is the correct semantic there. cos_dispatch_parallel_run
authorizes N concurrent sub-agents from one check, so it must pass a forward estimate
or the cap can be overrun by up to N× a dispatch before any cost row lands.
budget.estimate_dispatch_cost(db_path, count) supplies it: the median of the recent
cost-bearing dispatches × count. Median (not mean) keeps one pathological run from
inflating the estimate into spurious blocks, and an empty history returns 0.0 — the
gate then behaves exactly as it did before, honoring the module's fail-open contract.
The estimate is a guard rail on starting work, never a refund: actual cost is still
recorded per dispatch.
7.7 Auth mode — subscription OAuth vs API key (TASK-756)
ClaudeAgentOptions.env always carries a resolved ANTHROPIC_API_KEY
override, computed by _claude_auth_env(cwd) from
<cwd-or-$COS_STATE_DIR>/hub-settings.json::claude_auth
(Hub Settings → Claude Auth panel; _ClaudeAuthIn in
settings.py):
claude_auth.mode |
env forwarded | effect |
|---|---|---|
"subscription" (default) |
ANTHROPIC_API_KEY="" |
explicit clear — the CLI subprocess falls through to its own stored OAuth session, per the documented precedence (subscription is priority 6, below the env var). |
"api_key" with a stored key |
ANTHROPIC_API_KEY=<key> |
beats subscription OAuth in non-interactive/SDK mode (precedence tier 3) — this is how the SDK subprocess is spawned, so the switch is deterministic, not a race with an approval prompt. |
Applies uniformly to every claude_session_options() profile (chat +
chat_resume) and to dispatch() (formula sub-sessions) — one resolver,
read from the adapter so neither cognition.py nor the formula composer
needs to know this setting exists (P8: the adapter owns its own auth
seam). The explicit-clear-on-subscription branch exists because a stray
ANTHROPIC_API_KEY already exported in the Hub server's own shell would
otherwise silently win over a project that has not opted into
API-key billing — merging {} (an omitted override) is not safe here.
hub-settings.json::claude_auth.api_key is masked on every HTTP read
(api_key_set + last-4 api_key_preview only) and the file is
chmod 0600 on every write.
8. Subagent skill inheritance
Agent SDK 0.1.73 docs are explicit: "Subagents do NOT inherit skills
unless listed in AgentDefinition.skills." This adapter implements that
contract by reading skills: from each role's frontmatter and forwarding
to ClaudeAgentOptions.skills.
Current mapping (2026-05-04):
| Role | Skills declared |
|---|---|
implementer |
[clean-code] |
reviewer |
[clean-code] |
debugger |
[search, codebase-explorer] |
security_auditor |
[security-web, clean-code] |
refactorer |
[clean-code, search] |
researcher |
[search, codebase-explorer] |
analyst |
[thinking_os] |
architect |
[thinking_os] |
documenter, deployer, observer |
(none) |
Add a skill to a role: edit src/core/thinking_os/agents/<role>.md frontmatter.
Other adapters that don't understand skills: ignore it (Rule 1 — core
stays agent-agnostic).
9. Permissions — gotchas the SDK docs flag
Evaluation order (digest §B.4):
- Hooks
- Deny rules (
disallowedTools+ settings.jsondeny) — wins even inbypassPermissions. - Permission mode
- Allow rules (
allowedTools+ settings.jsonallow) can_use_toolcallback (skipped indontAsk)
Multi-decision precedence: deny > defer > ask > allow.
| Mode | Behavior | Use for coding-os |
|---|---|---|
default |
Unmatched tools call can_use_tool; no callback = deny |
NOT used (would prompt). |
acceptEdits |
Auto-approves Edit/Write/filesystem-Bash within cwd | NOT used in dispatcher (would still prompt for MCP). |
dontAsk |
Never prompts; only allow-listed tools run | Dispatcher uses this. |
plan |
No tool execution; AskUserQuestion only |
n/a. |
bypassPermissions |
All tools run; deny rules + hooks still apply; allowed_tools does NOT constrain |
NEVER use — security hole. |
auto |
Model classifier per call | Reserve for interactive UI. |
Subagent inheritance warning: parent in bypassPermissions/acceptEdits/auto
inherits to all children — cannot override per-subagent. Dispatcher uses
dontAsk so this is moot for our tree.
9.1 Docs-scoped sessions (the onboard endpoint)
POST /api/cognition/onboard (TASK-246) runs the onboarder role with writes
confined to docs/. Because can_use_tool is skipped under dontAsk
(evaluation order above), path-scoping is enforced with a PreToolUse hook
(evaluated first, mode-independent), not the callback:
permission_mode="dontAsk"+allowed_tools=[mcp__coding-os__*, Write, Edit, MultiEdit, Read, Glob, Grep, TodoWrite, WebFetch, WebSearch]+disallowed_tools=["Bash"].- a
PreToolUseHookMatcher(matcher="Write|Edit|MultiEdit|NotebookEdit")that resolves the target path and returnspermissionDecision: "deny"when it is not under<project>/docs/(the pure check iscognition._onboard_write_allowed, unit-tested as the acceptance contract).
A named endpoint keeps the docs-only permission set auditable in one place rather than scattered allow-rules.
10. Skills — frontmatter contract
Per SDK docs §E.1:
| Field | Limit | Notes |
|---|---|---|
name |
≤64 chars, lowercase + digits + hyphens | Cannot contain "anthropic"/"claude". |
description |
≤1024 chars | Third-person voice. |
name + description + when_to_use |
≤1,536 chars listing budget | Otherwise truncated in skill listing. |
Spot-check by running:
uv run python -c "from pathlib import Path; import yaml; [print(p, len(yaml.safe_load(p.read_text().split('---', 2)[1])['description'])) for p in Path('src/core/skills').rglob('SKILL.md')]"
YAML gotcha: descriptions containing : (colon-space) MUST be quoted.
The SDK loader uses YAML — unquoted colon-space breaks the parser
(found and fixed in src/core/skills/search/SKILL.md 2026-05-04).
11. Plugins
Loadable via plugins=[{type:"local", path}] only — SDK 0.1.73 has no
remote plugin registry. coding-os "templates" model is compatible —
distribute as local plugin paths.
Plugin layout per SDK §E.2:
my-plugin/
├── .claude-plugin/plugin.json # required
├── skills/<name>/SKILL.md
├── commands/*.md # legacy
├── agents/*.md
├── hooks/hooks.json
└── .mcp.json
coding-os does not currently package any plugin manifests; templates ship as scaffold overlays instead.
12. Observability
12.1 Cost / usage
ResultMessage.total_cost_usd is a client-side estimate — do NOT
bill from it. Use Anthropic's Usage and Cost API for billing.
Per-step:
assistant_msg.usage.input_tokens / output_tokens / cache_read_input_tokens / cache_creation_input_tokens- Dedupe by
assistant_msg.message_id(parallel tool calls share IDs).
Per-model breakdown: result.model_usage map.
12.2 OpenTelemetry
| Signal | Enable env | Notes |
|---|---|---|
| Metrics | OTEL_METRICS_EXPORTER=otlp |
Tokens, cost, sessions, LoC. |
| Logs | OTEL_LOGS_EXPORTER=otlp |
Prompts, API requests, tool results. |
| Traces | OTEL_TRACES_EXPORTER=otlp + CLAUDE_CODE_ENHANCED_TELEMETRY_BETA=1 |
Hook spans require ENABLE_BETA_TRACING_DETAILED=1. |
Never use console exporter — it collides with the SDK's stdout
pipe. Override OTEL_SERVICE_NAME (default claude-code) per-adapter
when shipping multiple agents in one collector.
12.3 Sessions
Stored at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl where
<encoded-cwd> replaces non-alphanumerics with -. Cross-host = ship
the .jsonl.
12.4 File checkpointing
NOT enabled by default in dispatcher. To enable:
ClaudeAgentOptions(
enable_file_checkpointing=True,
extra_args={"replay-user-messages": None},
...
)
Caveats:
- Only tracks
Write,Edit,NotebookEdit—Bashmutations bypass. - Same-session only; rewind requires
resume: sessionId+ empty""prompt +rewind_files(uuid)+ break.
13. Verification
# Unit + smoke
uv run --extra claude-sdk --extra rag pytest src/core/thinking_os/tests/test_dispatcher.py -q
# Adapter install + parity
uv run pytest tests/test_adapters.py tests/test_adapter_parity.py tests/test_adapter_registry.py -q
# Real CLI dispatch (requires Claude CLI on PATH + Anthropic API key)
uv run --extra claude-sdk python src/scripts/e2e_dispatch_tool.py
# Hook syntax
make verify-hooks
# SDK version smoke
uv pip show claude-agent-sdk mcp | grep -E "^(Name|Version)"
claude --version
14. Branding
Per Anthropic licensing: cannot ship as "Claude Code" / "Claude Code Agent". OK: "Claude Agent", "Powered by Claude". coding-os internal tooling is fine; user-facing surfaces (UI labels, README, marketing) require review.
15. What this adapter does NOT do (yet)
PermissionRequesthook — SDK supports it, would let coding-os centralize permission UX. Not wired.- Plugin manifests — coding-os ships skills directly under
.claude/skills/, noplugin.json. Reasonable trade-off; revisit when consumer projects need third-party plugin discovery. autopermission mode UI — TS preview reaches further than Py; leave off until coding-os has an interactive permission surface.- OTEL collector defaults — env vars work, but no project-default collector endpoint in install.sh. Future enhancement.
PreCompacthook — useful for memory-state rehydration; SDK supports it but no coding-os hook listens yet.
16. Related
- src/adapters/claude/adapter.yaml — capability declaration
- src/adapters/claude/sdk_dispatcher.py — formula dispatcher
- src/adapters/claude/install.sh — install entry
- src/adapters/claude/settings.template.json — rendered hook config (do not hand-edit)
- src/adapters/claude/settings.local.template.json — user permission seed
- src/core/thinking_os/dispatcher.py — Protocol + factory
- src/core/thinking_os/dispatchers/default.py — non-Claude fallback
- src/core/hooks/registry.yaml — hook SSOT
- docs/engineering/adapter-parity.md — cross-adapter contract
- docs/engineering/mcp-error-envelope.md —
cos_*response shape - docs/playbooks/adapter-authoring.md — generic adapter authoring contract
- docs/playbooks/mcp-tool-authoring.md — MCP tool authoring
17. Changelog
| Date | Change |
|---|---|
| 2026-04-20 | Initial Claude-SDK dispatcher (claude-agent-sdk>=0.1.0). |
| 2026-04-24 | Doc anchor pinned at v0.1.0 dispatcher reference. |
| 2026-05-04 | Q-bundle: SDK floor → >=0.1.73,<0.2.0; dispatcher hardened (preset+exclude_dynamic_sections+setting_sources+dontAsk+Opus 4.7 effort gate+abs-path assertion+role-skills inheritance); mcp__coding-os__* added to settings.local.template.json; SubagentStart/SubagentStop/PostToolUseFailure declared in registry.yaml + adapter.yaml; agent-presence.sh extended; skill descriptions audited (search SKILL.md frontmatter quoted); doc rewritten as full reference (TASK-002). |
| 2026-05-05 | Q.deep P0 wave: output_format JSON Schema enforcement; max_budget_usd ceiling; programmatic PreToolUse+PostToolUseFailure hooks; OTEL env propagation; disallowed_tools deny-list; schema migration v23 (6 cost columns on formula_dispatches); skill paths: globs + pytest gate; ClaudeAgentOptions regression test; .claude/agents/ cleanup (D2); cos sync-all propagation (TASK-003 P0 wave). |
| 2026-05-05 | Q.deep P1 wave: UserMessage.uuid capture for file checkpointing (T9); session_id forwarded to SDK (T7.1); error_max_structured_output_retries surfaced in DispatchResult.error (T1.5); long_context: true on researcher frontmatter (T10.3); enable_file_checkpointing on implementer/refactorer (T9.1); dispatch metrics emitted via agent_metrics (T2.5/T8.4); hub /api/cognition/cost+/dispatchers+/dispatchers/{id}/tools endpoints (T2.4/T19.1/T19.2); cos doctor --otel probe (T8.3); sdk_e2e pytest marker (T12.2); import-failure test (T12.4). |
| 2026-05-05 | Q.deep wave 3: schema migration v27 — formula_dispatches gains sub_session_id / model / checkpoints_jsonb columns + 2 indices for SDK telemetry persistence; _persist_dispatch_output writes 18-column INSERT including the 3 new fields; T1.6 — Pydantic validate runs before INSERT, malformed output skips row; T19.3 — /api/board/list exposes sub_session_counts; T16.2 — branding test (test_branding.py); T17.1 — version 0.2.0 → 0.3.0. |
17a. Architectural Decisions (D1–D6)
Decisions settled 2026-05-05 during TASK-003. See claude-deepening-checklist.md for full rationale.
| ID | Decision | Rationale |
|---|---|---|
| D1 | KEEP query() per formula |
agents={…} + Agent tool forces sub-sessions to inherit parent permission_mode; headless dontAsk contract would break. Cache reuse via exclude_dynamic_sections already achieved. |
| D2 | DELETE .claude/agents/ symlinks |
D1 keeps query() so symlinks are misleading scaffolding. Slash-command path (.claude/commands/role-*.md) retained. Removed in install.sh + scaffold_manifest.json. |
| D3 | KEEP scaffold (no plugin manifest) | Plugins target third-party distribution; coding-os ships its own kernel. Revisit when external consumers want plug-and-play. |
| D4 | ALLOW adapter-private hooks under src/adapters/claude/hooks/ |
Cross-adapter hooks stay in src/core/hooks/; SDK-only matchers (SubagentStart etc.) live adapter-side. hook_renderer.py respects adapter_scope: field. |
| D5 | Leave OTEL collector to operator | coding-os exports the env-var contract; bundling a collector would couple the kernel to a specific backend. cos doctor --otel probes the configured endpoint instead. |
| D6 | Long context opt-in per request | Formula bundles fit in 200k; only big-doc research needs 1M. DispatchRequest.long_context: bool + role frontmatter long_context: true (researcher). |