Elnor Repo Reader

DOC11_R15_OPENCLAW_RELEASE_ALIGNMENT_AMENDMENT_PROPOSAL_R3.md

Current Specs/DOC11/DOC11_R15_OPENCLAW_RELEASE_ALIGNMENT_AMENDMENT_PROPOSAL_R3.md

Generated 2026-06-09T01:23:58.539Z from commit dbaa25962edc11ab30e8d4ca1715f9ae5bf77331. Worktree: clean.

Open text page · Open raw txt · Open path URL

# DOC11 R15 Amendment Proposal R3 — OpenClaw Release Alignment, Unified Native Capability Controls, Active-Run Interaction, Browser/Plugin/Voice/Compaction/Diagnostics Expansion

**Version:** Proposal R3 — supersedes `DOC11_R15_OPENCLAW_RELEASE_ALIGNMENT_AMENDMENT_PROPOSAL_R2.md` in full.  
**Date:** 2026-05-19  
**Base document:** `DOC11_OPENCLAW_GATEWAY_MODEL_CONTROLS_AND_EC_INTEGRATION_R14.md`  
**Target next DOC11 version:** R15  
**Status:** Proposal / coding handoff. This is **not** a DOC11 redline.  
**Release ceiling:** include stable OpenClaw releases after the R2 proposal through `2026.5.18`; track `2026.5.19-beta.1` as watch-only unless promoted before redline drafting.

---

## 0. Purpose and governing interpretation

This R3 proposal is the complete amendment proposal to use before drafting the actual DOC11 R14 → R15 delta redline. It preserves the R2 structure and adds the OpenClaw release wave after `2026.4.26`, especially:

- externalized plugin lifecycle, install/update/repair, ClawHub artifact metadata, runtime dependency state, and plugin-owned Gateway method registries;
- typed OpenClaw tool-plugin authoring/readback (`defineToolPlugin`, `plugins build`, `validate`, `init`);
- active-run interaction controls: `/steer`, `/btw`, `/side`, follow-up, collect, and interrupt queue modes;
- side-result memory policy correction: side results are not native OpenClaw future context by default but remain eligible for ELNOR memory capture under ordinary EC/DOC72/DOC24 policy;
- branch metadata readback and trajectory parent-chain diagnostics without falsely claiming OpenClaw provides a complete branch-chat UI;
- dual browser architecture: Q Browser internal intake versus OpenClaw/Chrome/CDP/browser-extension automation and observation;
- browser dialog/action/URL allowlist/readback, modal dialog handling, evaluate timeout, CDP port/profile state, and browser artifact gating;
- provider-native and plugin-backed search policy, SSRF/private-network/proxy controls, and DOC72 ingest gates;
- Codex app-server/code-mode/native-tool/sandbox-network controls and legacy `codex-cli/*` migration truth;
- native voice/Talk/TTS/STT/realtime controls, including Android Gateway relay voice, without promoting Google Meet into an active ELNOR feature track;
- compaction owner/trigger/context-window/source-ref readback and pre-compaction ELNOR capture protection;
- diagnostics/export/restart/config reloadability/provider quota/session tool progress/readiness traces;
- paired-node file-transfer controls;
- sender-scoped tool authority and future networking/user-profile compatibility;
- model/provider auth, quota, source plan, fallback, catalog repair, and per-agent bootstrap profile controls;
- streaming/progress/final-delivery normalization and no-ghost transcript discipline.

### 0.1 Release status rule

For this R3 proposal:

```text
include_now: OpenClaw stable releases through 2026.5.18
watch_only: 2026.5.19-beta.1 and raw-main-only changes not present in stable release pages
```

The DOC11 R15 redline SHALL NOT canonize beta-only details unless the user explicitly elects to track beta releases or the beta is promoted before redline drafting.

### 0.2 What R3 changes relative to R2

R2 already covered: `commands.list`, plugin inventory, provider/model/auth surfaces, channel setup, memory controls, compaction basics, voice basics, browser basics, diagnostics basics, Codex Computer Use, and subagent readback.

R3 expands the amendment to include the stable May release wave:

1. external plugin lifecycle is now treated as a first-order runtime truth surface;
2. plugin-owned Gateway methods and typed tool-plugin builder state are modeled;
3. active-run steering and side-question semantics are modeled explicitly;
4. branch-related readback is separated from actual branch UX;
5. browser dialog/allowlist/action blocking state is added;
6. Codex app-server/code-mode replaces stale `codex-cli` assumptions;
7. Android/realtime voice and TTS directive behavior become settings/readback items;
8. compaction gains owner/trigger/context-window/checkpoint fields;
9. diagnostics gain restart trace, startup trace, config reloadability, session.tool progress, share-safe diagnostics, and trajectory parent-chain status;
10. paired-node file transfer is added as a high-authority feature family;
11. sender-scoped tool authority is added for future firm/networked access compatibility.

---

## 1. Ownership boundaries R15 must preserve

### 1.1 DOC11 owns OpenClaw runtime truth, not all OpenClaw-facing UX

DOC11 owns:

- OpenClaw Gateway route/RPC truth;
- native command catalog and active-run command behavior;
- OpenClaw feature inventory and effective state;
- plugin registry/readback, install/update/repair adapter routes, setup/auth state, and runtime dependency truth;
- model/provider/auth/quota/catalog/fallback/readiness truth;
- OpenClaw browser/Chrome/CDP/browser-extension runtime truth;
- OpenClaw search provider/native-search policy readback;
- OpenClaw memory feature status and controls;
- OpenClaw compaction status and controls;
- OpenClaw native subagent settings/status;
- Codex runtime/code-mode/computer-use status and controls;
- OpenClaw voice/Talk/TTS/STT/realtime capability state;
- paired-node file-transfer plugin state;
- diagnostics/export/trajectory/startup/restart/config reloadability truth;
- ACP runtime/profile/session truth;
- channel setup/runtime/account truth.

DOC11 does **not** own:

- Q Plugin System lifecycle;
- Q Browser internals;
- DOC3 demonstration interpretation or learned procedure storage;
- DOC20 page layouts, Skills & Connectors UX, or Q tab/page state;
- DOC24 capability routing and packet assembly;
- DOC72 graph promotion, memory node governance, or procedure payload shape;
- EC/PropA policy computation;
- future firm networking/user-profile permissions.

### 1.2 Required cross-doc split

| Concern | Owner | DOC11 responsibility |
|---|---|---|
| OpenClaw plugins / ClawHub / native skills | DOC20/DOC3 visible UX; DOC4 native architecture | Runtime truth, adapter routes, health, install/update/readback, method/catalog facts |
| Q plugins | Q Plugin System / DOC20 / DOC3 / DOC24 / DOC72 | Reference only; do not install/manage as OpenClaw plugins |
| Q Browser internal intake | DOC20/DOC72/EC/PropA | Do not replace; only report OpenClaw external browser lane |
| Chrome extension observation | DOC3 + DOC11 bridge | Activation/deactivation/status/error bridge |
| Memory governance | DOC24/DOC72/DOC1/EC/PropA | OpenClaw memory feature controls/status only |
| Subagent specialist semantics | DOC24/DOC10/DOC72/DOC25 | OpenClaw `sessions_spawn` truth/settings/status only |
| Firm user/network access | future networking spec + EC/PropA/DOC72 | Include principal/policy refs, not permission semantics |

---

## 2. Common R15 primitives

R15 should place these near existing DOC11 shared schema primitives and reuse them across all new routes/read models.

```ts
import { z } from "zod";

export const OpenClawReleaseAdoptionStateSchema = z.enum([
  "stable_include_now",
  "beta_watch_only",
  "raw_main_watch_only",
  "deferred_inventory_only",
  "not_adopted",
]);

export const OpenClawOwnerSurfaceSchema = z.enum([
  "openclaw_gateway_rpc",
  "openclaw_cli_adapter",
  "openclaw_plugin_manifest",
  "openclaw_plugin_runtime",
  "openclaw_control_ui",
  "elnor_doc11_adapter",
  "elnor_ec_policy",
  "elnor_doc20_surface",
  "elnor_doc3_learning",
  "elnor_doc24_routing",
  "elnor_doc72_knowledge",
  "external_provider",
  "future_networking_spec",
  "deferred_inventory_only",
]);

export const MutabilitySchema = z.enum([
  "read_write_native_rpc",
  "read_write_operator_adapter",
  "read_only",
  "write_desired_only_effective_async",
  "disabled_not_installed",
  "deferred_no_control",
]);

export const EffectiveStateSchema = z.enum([
  "enabled",
  "disabled",
  "partially_enabled",
  "unavailable",
  "not_installed",
  "setup_required",
  "auth_required",
  "degraded",
  "blocked_by_policy",
  "blocked_by_version",
  "blocked_by_missing_plugin",
  "blocked_by_runtime_dependency",
  "unknown",
]);

export const DataSurfaceClassificationSchema = z.enum([
  "none",
  "metadata_only",
  "runtime_state",
  "prompt_or_context",
  "browser_content",
  "memory_or_knowledge",
  "diagnostic_bundle",
  "trajectory_or_trace",
  "audio_media",
  "provider_auth",
  "external_account",
  "filesystem_read_write",
  "high_authority_desktop_control",
]);

export const PolicyDecisionRefSchema = z.object({
  policy_generation_id: z.string().max(160),
  decision_id: z.string().max(160),
  decision_kind: z.enum([
    "collection_gate",
    "visibility_gate",
    "injection_gate",
    "sharing_gate",
    "export_gate",
    "tool_authority_gate",
    "browser_observation_gate",
    "diagnostics_export_gate",
    "file_transfer_gate",
    "native_search_gate",
  ]),
  result: z.enum(["allow", "warn", "block", "defer", "not_evaluated"]),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});

export const RuntimePolicyContextRefSchema = z.object({
  principal_id: z.string().max(160).optional(),
  tenant_id: z.string().max(160).optional(),
  runtime_user_context_ref: z.string().max(160).optional(),
  source_policy_refs: z.array(z.string().max(160)).default([]),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  exposure_context_ref: z.string().max(160).optional(),
  future_networking_profile_ref: z.string().max(160).optional(),
  schema_version: z.literal(1),
});

export const DesiredEffectiveBooleanSchema = z.object({
  desired: z.boolean().optional(),
  effective: z.boolean(),
  divergence_reason_codes: z.array(z.string().max(120)).default([]),
  last_refreshed_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const RuntimeSourceRefSchema = z.object({
  source_kind: z.enum([
    "gateway_rpc",
    "cli_json",
    "plugin_manifest",
    "plugin_registry",
    "provider_catalog",
    "config_snapshot",
    "doctor_probe",
    "runtime_probe",
    "ec_policy",
    "release_changelog",
  ]),
  source_ref: z.string().max(300),
  sampled_at: z.string().datetime().optional(),
  schema_version: z.literal(1),
});
```

**Normative rule:** every new read model added by R15 MUST include source references and MUST distinguish `desired`, `effective`, and `divergence_reason_codes` wherever a setting can differ from live runtime truth.

---

## 3. Unified OpenClaw feature inventory and settings truth

### 3.1 Purpose

DOC11 R15 needs one operator-facing read model for all OpenClaw-native capabilities. This is the runtime truth that DOC20 Skills & Connectors, Settings > OpenClaw Features, and Q control surfaces consume.

### 3.2 Schemas

```ts
export const OpenClawFeatureKindSchema = z.enum([
  "runtime_command_catalog",
  "plugin_registry",
  "plugin_install_update",
  "typed_tool_plugin_builder",
  "skill_catalog",
  "clawhub_catalog",
  "model_catalog",
  "provider_auth",
  "provider_native_search",
  "provider_request_overrides",
  "channel_catalog",
  "browser_control",
  "chrome_extension_observation",
  "codex_runtime",
  "codex_computer_use",
  "codex_code_mode",
  "subagents",
  "active_run_interaction",
  "branch_metadata",
  "acp_runtime",
  "compaction",
  "active_memory",
  "memory_wiki",
  "dreaming_rem",
  "qmd_refresh",
  "voice_talk",
  "tts",
  "stt",
  "realtime_voice",
  "diagnostics",
  "trajectory_export",
  "startup_diagnostics_timeline",
  "config_reloadability",
  "paired_node_file_transfer",
  "sandbox_docker_gpu",
  "mobile_node_presence",
  "google_meet_deferred",
]);

export const OpenClawFeatureControlEntrySchema = z.object({
  feature_id: z.string().max(160),
  feature_kind: OpenClawFeatureKindSchema,
  display_name: z.string().max(160),
  description: z.string().max(800).optional(),
  adoption_state: OpenClawReleaseAdoptionStateSchema.default("stable_include_now"),
  owner_surface: OpenClawOwnerSurfaceSchema,
  installed: z.boolean().default(false),
  available: z.boolean().default(false),
  mutability: MutabilitySchema,
  desired_enabled: z.boolean().optional(),
  effective_state: EffectiveStateSchema,
  health: z.enum(["healthy", "degraded", "failing", "unknown", "not_applicable"]).default("unknown"),
  setup_required: z.boolean().default(false),
  auth_required: z.boolean().default(false),
  update_available: z.boolean().default(false),
  restart_required: z.boolean().default(false),
  data_surface_classification: DataSurfaceClassificationSchema,
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  divergence_reason_codes: z.array(z.string().max(120)).default([]),
  last_checked_at: z.string().datetime(),
  schema_version: z.literal(1),
});

export const OpenClawFeaturesDashboardReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  gateway_version: z.string().max(80).optional(),
  openclaw_release_ref: z.string().max(80).optional(),
  stable_release_ceiling: z.literal("2026.5.18"),
  beta_watch_release_ref: z.string().max(80).optional(),
  features: z.array(OpenClawFeatureControlEntrySchema).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});

export const OpenClawFeatureMutationRequestSchema = z.object({
  feature_id: z.string().max(160),
  desired_enabled: z.boolean().optional(),
  config_patch: z.record(z.string(), z.unknown()).optional(),
  expected_revision: z.string().max(120).optional(),
  idempotency_key: z.string().max(160),
  reason: z.string().max(500).optional(),
  schema_version: z.literal(1),
});

export const OpenClawFeatureMutationResultSchema = z.object({
  feature_id: z.string().max(160),
  mutation_state: z.enum(["applied", "queued", "blocked", "degraded_noop", "unsupported"]),
  desired_state_after: z.unknown().optional(),
  effective_state_after: EffectiveStateSchema.optional(),
  restart_required: z.boolean().default(false),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  refreshed_read_model_ids: z.array(z.string().max(160)).default([]),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

### 3.3 Required routes

| Route ID | Method | Path | Request | Success schema | Notes |
|---|---:|---|---|---|---|
| `openclaw_features_get` | GET | `/api/openclaw/features` | none | `OpenClawFeaturesDashboardReadModelSchema` | Primary settings read model. |
| `openclaw_feature_mutate` | PATCH | `/api/openclaw/features/:featureId` | `OpenClawFeatureMutationRequestSchema` | `OpenClawFeatureMutationResultSchema` | Adapter-backed or native depending on feature. |
| `openclaw_features_effective_refresh` | POST | `/api/openclaw/features/refresh` | `{ reason?: string }` | `OpenClawFeaturesDashboardReadModelSchema` | Re-probes runtime state. |

### 3.4 Required event

```ts
export const OpenClawFeatureStateChangedEventSchema = z.object({
  event_name: z.literal("openclaw.feature_state_changed"),
  event_id: z.string().max(160),
  feature_ids: z.array(z.string().max(160)).default([]),
  changed_fields: z.array(z.string().max(120)).default([]),
  refreshed_read_model_ids: z.array(z.string().max(160)).default([]),
  reason_codes: z.array(z.string().max(120)).default([]),
  occurred_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

---

## 4. Runtime command catalog and active-run command availability

### 4.1 Required behavior

DOC11 must keep `commands.list`/runtime command discovery as a canonical read model and expand it to support active-run controls, typed plugin commands, plugin-owned Gateway method entries, diagnostics commands, and CLI adapter routes.

### 4.2 Schemas

```ts
export const OpenClawCommandSourceSchema = z.enum([
  "native_gateway",
  "native_cli",
  "plugin_command",
  "plugin_gateway_method",
  "skill_command",
  "channel_command",
  "provider_command",
  "diagnostic_command",
  "operator_adapter",
]);

export const OpenClawCommandAvailabilitySchema = z.enum([
  "available",
  "requires_setup",
  "requires_auth",
  "requires_plugin",
  "requires_runtime_dependency",
  "blocked_by_policy",
  "degraded",
  "unsupported",
]);

export const OpenClawCommandCatalogEntrySchema = z.object({
  command_id: z.string().max(160),
  command_name: z.string().max(120),
  display_name: z.string().max(160),
  description: z.string().max(800).optional(),
  source: OpenClawCommandSourceSchema,
  owner_plugin_id: z.string().max(160).optional(),
  owner_provider_id: z.string().max(160).optional(),
  gateway_method_name: z.string().max(160).optional(),
  method_scope_metadata: z.array(z.string().max(160)).default([]),
  availability: OpenClawCommandAvailabilitySchema,
  supports_native_rpc: z.boolean().default(false),
  supports_cli_adapter: z.boolean().default(false),
  input_schema_ref: z.string().max(200).optional(),
  output_schema_ref: z.string().max(200).optional(),
  authority_class: z.enum([
    "read_only",
    "runtime_mutation",
    "external_side_effect",
    "desktop_control",
    "filesystem_read_write",
    "credential_or_auth",
    "diagnostics_export",
  ]).default("read_only"),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawCommandCatalogReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  command_count: z.number().int().nonnegative(),
  entries: z.array(OpenClawCommandCatalogEntrySchema).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

### 4.3 Routes

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_commands_catalog_get` | GET | `/api/openclaw/commands/catalog` | `OpenClawCommandCatalogReadModelSchema` |
| `openclaw_commands_refresh` | POST | `/api/openclaw/commands/refresh` | `OpenClawCommandCatalogReadModelSchema` |

Native mapping: use OpenClaw Gateway `commands.list` where available. CLI fallback is degraded and must be shown in `runtime_sources`.

---

## 5. Plugin registry, externalized plugin lifecycle, ClawHub, skills, and Q-plugin boundary

### 5.1 Required boundary language

DOC11 must distinguish:

| Object | Owner | DOC11 role |
|---|---|---|
| OpenClaw native plugin | OpenClaw / DOC4 | Inventory, setup/auth/runtime truth, install/update/enable/disable/repair adapter routes. |
| OpenClaw official externalized plugin | OpenClaw / ClawHub/npm | Installed/missing/update/repair/runtime dependency truth. |
| OpenClaw skill / ClawHub skill | DOC20/DOC3 visible UX, OpenClaw skill ecosystem | Catalog/install/update/readback and runtime visibility. |
| Typed OpenClaw tool plugin | OpenClaw SDK | Build/validate/init status and accepted manifest/tool declarations. |
| Q Dashboard plugin | Q Plugin System / DOC20/DOC3/DOC24/DOC72 | Not installed or managed by DOC11; DOC11 may expose OpenClaw tools it uses. |

### 5.2 Plugin schemas

```ts
export const OpenClawPluginKindSchema = z.enum([
  "native_plugin",
  "official_externalized_plugin",
  "bundle_plugin",
  "skill",
  "skill_bundle",
  "provider_plugin",
  "channel_plugin",
  "speech_plugin",
  "browser_or_web_plugin",
  "diagnostics_plugin",
  "memory_plugin",
  "compaction_plugin",
  "typed_tool_plugin",
]);

export const OpenClawPluginSourceKindSchema = z.enum([
  "bundled",
  "clawhub",
  "npm",
  "git",
  "local_path",
  "workspace",
  "agent_specific",
  "marketplace",
  "unknown",
]);

export const OpenClawPluginInstallStateSchema = z.enum([
  "not_installed",
  "installed",
  "enabled",
  "disabled",
  "missing_payload",
  "stale_payload",
  "repair_available",
  "update_available",
  "invalid_manifest",
  "blocked_by_trust",
  "runtime_deps_missing",
  "managed_peer_deps_missing",
  "setup_required",
  "auth_required",
]);

export const OpenClawPluginCapabilityKindSchema = z.enum([
  "tool",
  "skill",
  "channel",
  "provider",
  "model_catalog",
  "speech_tts",
  "speech_stt",
  "realtime_voice",
  "media_understanding",
  "image_generation",
  "video_generation",
  "music_generation",
  "web_fetch",
  "web_search",
  "browser_control",
  "diagnostics",
  "setup_provider",
  "hook",
  "cron_observer",
  "memory",
  "compaction",
  "gateway_method",
  "file_transfer",
]);

export const OpenClawPluginGatewayMethodRefSchema = z.object({
  method_name: z.string().max(160),
  owner_plugin_id: z.string().max(160),
  scope_metadata: z.array(z.string().max(160)).default([]),
  input_schema_ref: z.string().max(200).optional(),
  output_schema_ref: z.string().max(200).optional(),
  advertised: z.boolean().default(true),
  collision_state: z.enum(["none", "hidden_core_collision", "blocked_collision"]).default("none"),
  schema_version: z.literal(1),
});

export const OpenClawPluginCapabilityRefSchema = z.object({
  capability_id: z.string().max(160),
  capability_kind: OpenClawPluginCapabilityKindSchema,
  display_name: z.string().max(160).optional(),
  command_ids: z.array(z.string().max(160)).default([]),
  tool_ids: z.array(z.string().max(160)).default([]),
  gateway_methods: z.array(OpenClawPluginGatewayMethodRefSchema).default([]),
  route_refs: z.array(z.string().max(200)).default([]),
  schema_version: z.literal(1),
});

export const OpenClawPluginCatalogEntrySchema = z.object({
  plugin_id: z.string().max(160),
  package_name: z.string().max(240).optional(),
  display_name: z.string().max(160),
  plugin_kind: OpenClawPluginKindSchema,
  source_kind: OpenClawPluginSourceKindSchema,
  source_ref: z.string().max(300).optional(),
  package_manager: z.enum(["npm", "pnpm", "clawhub", "git", "local", "unknown"]).default("unknown"),
  package_version: z.string().max(80).optional(),
  git_ref: z.string().max(160).optional(),
  clawhub_artifact_digest: z.string().max(200).optional(),
  manifest_hash: z.string().max(200).optional(),
  install_state: OpenClawPluginInstallStateSchema,
  runtime_dependency_state: z.enum(["not_applicable", "satisfied", "missing", "stale", "repair_available", "unknown"]).default("unknown"),
  managed_peer_dependency_state: z.enum(["not_applicable", "satisfied", "missing", "stale", "pruned", "unknown"]).default("unknown"),
  install_repair_state: z.enum(["none", "repair_available", "repair_running", "repair_failed", "repair_succeeded"]).default("none"),
  setup_required: z.boolean().default(false),
  auth_required: z.boolean().default(false),
  update_available: z.boolean().default(false),
  restart_required: z.boolean().default(false),
  trusted: z.boolean().default(false),
  capabilities: z.array(OpenClawPluginCapabilityRefSchema).default([]),
  doctor_repair_command_ref: z.string().max(240).optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawPluginCatalogReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  registry_revision: z.string().max(160).optional(),
  entries: z.array(OpenClawPluginCatalogEntrySchema).default([]),
  missing_configured_plugin_ids: z.array(z.string().max(160)).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

### 5.3 Typed tool-plugin builder readback

```ts
export const OpenClawTypedToolPluginBuilderStateSchema = z.object({
  workspace_ref: z.string().max(300).optional(),
  plugin_id: z.string().max(160).optional(),
  source_kind: OpenClawPluginSourceKindSchema.default("workspace"),
  builder_available: z.boolean(),
  last_init_state: z.enum(["never", "succeeded", "failed", "unknown"]).default("never"),
  last_build_state: z.enum(["never", "succeeded", "failed", "unknown"]).default("never"),
  last_validate_state: z.enum(["never", "succeeded", "failed", "unknown"]).default("never"),
  generated_manifest_metadata_ref: z.string().max(240).optional(),
  declared_tool_ids: z.array(z.string().max(160)).default([]),
  context_factory_declared: z.boolean().default(false),
  accepted_by_runtime: z.boolean().default(false),
  blocking_errors: z.array(z.string().max(300)).default([]),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(1),
});
```

### 5.4 Plugin routes

| Route ID | Method | Path | Request | Success schema | Notes |
|---|---:|---|---|---|---|
| `openclaw_plugins_catalog_get` | GET | `/api/openclaw/plugins/catalog` | none | `OpenClawPluginCatalogReadModelSchema` | Cold registry + live status. |
| `openclaw_plugins_refresh` | POST | `/api/openclaw/plugins/refresh` | `{ reason?: string }` | `OpenClawPluginCatalogReadModelSchema` | Rebuild/probe read model. |
| `openclaw_plugin_install` | POST | `/api/openclaw/plugins/install` | `OpenClawPluginMutationRequestSchema` | `OpenClawPluginMutationResultSchema` | Adapter/native depending on backing. |
| `openclaw_plugin_update` | POST | `/api/openclaw/plugins/:pluginId/update` | `OpenClawPluginMutationRequestSchema` | `OpenClawPluginMutationResultSchema` | Include artifact digest/git/npm metadata. |
| `openclaw_plugin_enable` | POST | `/api/openclaw/plugins/:pluginId/enable` | `OpenClawPluginMutationRequestSchema` | `OpenClawPluginMutationResultSchema` | Must refresh effective state. |
| `openclaw_plugin_disable` | POST | `/api/openclaw/plugins/:pluginId/disable` | `OpenClawPluginMutationRequestSchema` | `OpenClawPluginMutationResultSchema` | Must not imply uninstall. |
| `openclaw_plugin_uninstall` | POST | `/api/openclaw/plugins/:pluginId/uninstall` | `OpenClawPluginMutationRequestSchema` | `OpenClawPluginMutationResultSchema` | Must report peer-dependency cleanup. |
| `openclaw_plugin_repair` | POST | `/api/openclaw/plugins/:pluginId/repair` | `OpenClawPluginMutationRequestSchema` | `OpenClawPluginMutationResultSchema` | Doctor/update repair adapter. |
| `openclaw_plugin_gateway_methods_get` | GET | `/api/openclaw/plugins/gateway-methods` | none | `{ methods: OpenClawPluginGatewayMethodRefSchema[] }` | Descriptor-backed method registry. |
| `openclaw_typed_tool_plugin_builder_status` | GET | `/api/openclaw/plugins/tool-builder/status` | none | `OpenClawTypedToolPluginBuilderStateSchema` | Readback only unless builder adapter exists. |

```ts
export const OpenClawPluginMutationRequestSchema = z.object({
  plugin_id: z.string().max(160).optional(),
  package_spec: z.string().max(300).optional(),
  source_kind: OpenClawPluginSourceKindSchema.optional(),
  expected_registry_revision: z.string().max(160).optional(),
  idempotency_key: z.string().max(160),
  allow_beta: z.boolean().default(false),
  reason: z.string().max(500).optional(),
  schema_version: z.literal(1),
});

export const OpenClawPluginMutationResultSchema = z.object({
  plugin_id: z.string().max(160),
  mutation_state: z.enum(["applied", "queued", "blocked", "failed", "unsupported"]),
  install_state_after: OpenClawPluginInstallStateSchema.optional(),
  runtime_dependency_state_after: z.string().max(120).optional(),
  restart_required: z.boolean().default(false),
  refreshed_read_model_ids: z.array(z.string().max(160)).default([]),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

---

## 6. Active-run interaction controls: steer, side, follow-up, collect, interrupt

### 6.1 Required behavior

DOC11 R15 must distinguish the following. These are not interchangeable:

| Mode | User intent | OpenClaw behavior | Transcript/future-context effect |
|---|---|---|---|
| `steer_active_run` | Guide the current run while it is working | `/steer`; drain at supported model/runtime boundary | Affects active run; may not create a new turn |
| `side_question` | Ask an aside while preserving main path | `/btw` / `/side`; emits side result | Not native future OpenClaw context by default |
| `queue_followup` | Add a normal later turn | follow-up queue | Durable once processed as normal turn |
| `collect_followups` | Batch compatible follow-ups | collect mode | Durable once processed |
| `interrupt_active_run` | Stop/replace current run | interrupt mode | Aborts/restarts as supported |

### 6.2 Schemas

```ts
export const ActiveRunInteractionModeSchema = z.enum([
  "steer_active_run",
  "side_question",
  "queue_followup",
  "collect_followups",
  "interrupt_active_run",
]);

export const ActiveRunInteractionRequestSchema = z.object({
  session_key: z.string().max(200),
  mode: ActiveRunInteractionModeSchema,
  message: z.string().max(8000),
  target: z.enum(["current_session", "subagent", "acp_session"]).default("current_session"),
  target_id: z.string().max(200).optional(),
  memory_handling_hint: z.enum([
    "standard_policy",
    "do_not_collect",
    "pin_to_context",
    "save_as_note",
    "promote_candidate",
  ]).default("standard_policy"),
  idempotency_key: z.string().max(160),
  schema_version: z.literal(1),
});

export const ActiveRunInteractionResultSchema = z.object({
  interaction_id: z.string().max(160),
  mode: ActiveRunInteractionModeSchema,
  accepted: z.boolean(),
  effective_behavior: z.enum([
    "steered_active_run",
    "queued_followup",
    "collected_followup",
    "interrupted_and_restarted",
    "side_result_emitted",
    "normal_prompt_started",
    "blocked",
    "unsupported",
  ]),
  openclaw_event_name: z.enum([
    "chat",
    "chat.side_result",
    "session.steer",
    "session.interrupt",
    "queue.followup",
    "queue.collect",
  ]).optional(),
  affects_future_openclaw_session_context: z.boolean(),
  eligible_for_elnor_memory_pipeline: z.boolean(),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

### 6.3 Side-result memory policy correction

R15 must not say side results are categorically excluded from memory. Correct rule:

> `/btw` and `/side` results are excluded from OpenClaw native future session context and transcript history by default, but ELNOR may capture, save, pin, classify, summarize, or promote them under the same EC/DOC72/DOC24 memory-policy rules that govern ordinary chat-derived content.

```ts
export const OpenClawSideResultMemoryPolicySchema = z.object({
  side_result_id: z.string().max(160),
  source_command: z.enum(["btw", "side"]),
  openclaw_persistence: z.literal("ephemeral_not_in_chat_history"),
  affects_future_openclaw_session_context: z.boolean().default(false),
  elnor_capture_policy: z.enum([
    "do_not_collect",
    "collect_metadata_only",
    "eligible_for_standard_chat_capture",
    "eligible_only_if_pinned_or_saved",
  ]).default("eligible_for_standard_chat_capture"),
  user_action: z.enum([
    "none",
    "pin_to_context",
    "save_as_note",
    "save_as_artifact",
    "promote_to_memory_candidate",
    "discard",
  ]).default("none"),
  doc72_ingest_state: z.enum([
    "not_evaluated",
    "blocked_by_policy",
    "metadata_recorded",
    "candidate_created",
    "promoted",
    "discarded",
  ]).default("not_evaluated"),
  doc24_future_injection_eligible: z.boolean().default(false),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  source_refs: z.array(z.string().max(200)).default([]),
  schema_version: z.literal(1),
});
```

### 6.4 Routes and events

| Route ID | Method | Path | Request | Success schema |
|---|---:|---|---|---|
| `openclaw_active_run_interaction_post` | POST | `/api/openclaw/sessions/:sessionKey/active-run/interactions` | `ActiveRunInteractionRequestSchema` | `ActiveRunInteractionResultSchema` |
| `openclaw_side_result_policy_get` | GET | `/api/openclaw/sessions/:sessionKey/side-results/:sideResultId/policy` | none | `OpenClawSideResultMemoryPolicySchema` |
| `openclaw_side_result_policy_mutate` | PATCH | `/api/openclaw/sessions/:sessionKey/side-results/:sideResultId/policy` | `{ user_action, memory_handling_hint }` | `OpenClawSideResultMemoryPolicySchema` |

```ts
export const ActiveRunInteractionEventSchema = z.object({
  event_name: z.enum([
    "openclaw.active_run_interaction.accepted",
    "openclaw.active_run_interaction.blocked",
    "openclaw.side_result.emitted",
  ]),
  event_id: z.string().max(160),
  session_key: z.string().max(200),
  interaction_id: z.string().max(160),
  mode: ActiveRunInteractionModeSchema,
  effective_behavior: ActiveRunInteractionResultSchema.shape.effective_behavior,
  occurred_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

---

## 7. Branch metadata and trajectory parent-chain diagnostics

### 7.1 Required behavior

DOC11 R15 should expose branch-adjacent runtime truth without pretending OpenClaw has a full Q branch-chat UX. Q/DOC20/DOC10/DOC24 may build branch UI on top.

```ts
export const OpenClawBranchMetadataSchema = z.object({
  session_key: z.string().max(200),
  parent_session_key: z.string().max(200).optional(),
  branch_parent_message_id: z.string().max(200).optional(),
  branch_summary_anchor_ref: z.string().max(240).optional(),
  trajectory_parent_chain_status: z.enum([
    "complete",
    "incomplete",
    "cyclic",
    "malformed",
    "unknown",
  ]).default("unknown"),
  branch_role: z.enum([
    "mainline",
    "forked_transcript",
    "subagent_child",
    "side_thread",
    "unknown",
  ]).default("unknown"),
  eligible_for_q_branch_ui: z.boolean().default(false),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(1),
});
```

### 7.2 Routes

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_session_branch_metadata_get` | GET | `/api/openclaw/sessions/:sessionKey/branch-metadata` | `OpenClawBranchMetadataSchema` |
| `openclaw_session_branch_metadata_refresh` | POST | `/api/openclaw/sessions/:sessionKey/branch-metadata/refresh` | `OpenClawBranchMetadataSchema` |

### 7.3 Normative branch memory rule

Branches are not automatically merged into main-thread conversational truth, but selected, saved, pinned, summarized, or promoted branch results are eligible for normal EC/DOC72/DOC24 memory and injection pipelines.

---

## 8. Dual browser model: Q Browser vs OpenClaw/Chrome/CDP/browser extension

### 8.1 Required ownership rule

DOC11 R15 must distinguish:

```text
Q Browser internal = ELNOR knowledge-intake browser; DOC20/DOC72/EC/PropA owned.
OpenClaw browser/Chrome/CDP/browser extension = external automation/observation runtime; DOC11 reports/controls OpenClaw truth.
```

OpenClaw-controlled Chrome sessions SHALL NOT automatically feed DOC72. They may feed DOC72 only by explicit domain/session/task allow, policy decision, or user action.

### 8.2 Browser schemas

```ts
export const BrowserSurfaceKindSchema = z.enum([
  "q_browser_internal",
  "openclaw_managed_chrome",
  "openclaw_existing_chrome_session",
  "openclaw_remote_cdp_profile",
  "chrome_extension_observation",
  "mobile_node_browser_capability",
]);

export const BrowserArtifactKindSchema = z.enum([
  "safe_tab_url",
  "aria_role_snapshot",
  "screenshot",
  "full_page_pdf",
  "download",
  "trace",
  "console_log",
  "page_error",
  "network_log",
  "storage_snapshot",
  "browser_doctor_report",
]);

export const BrowserDialogRefSchema = z.object({
  dialog_id: z.string().max(160),
  dialog_type: z.enum(["alert", "confirm", "prompt", "beforeunload", "unknown"]),
  message_preview: z.string().max(500).optional(),
  opened_at: z.string().datetime().optional(),
  handled_at: z.string().datetime().optional(),
  handled_by_command_ref: z.string().max(200).optional(),
  schema_version: z.literal(1),
});

export const BrowserDialogStateSchema = z.object({
  blocked_by_dialog: z.boolean().default(false),
  pending_dialogs: z.array(BrowserDialogRefSchema).default([]),
  recently_handled_dialogs: z.array(BrowserDialogRefSchema).default([]),
  schema_version: z.literal(1),
});

export const BrowserUrlAllowlistPolicySchema = z.object({
  policy_id: z.string().max(160),
  applies_to_actions: z.array(z.enum([
    "act_evaluate",
    "act_batch",
    "highlight",
    "debug_console",
    "page_errors",
    "network_requests",
    "response_body",
    "screenshot",
    "snapshot",
    "storage",
    "trace",
  ])).default([]),
  allowed_url_patterns: z.array(z.string().max(500)).default([]),
  blocked_url_patterns: z.array(z.string().max(500)).default([]),
  action_block_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});

export const OpenClawBrowserSurfaceEntrySchema = z.object({
  browser_surface_id: z.string().max(160),
  surface_kind: BrowserSurfaceKindSchema,
  owner_surface: OpenClawOwnerSurfaceSchema,
  profile_id: z.string().max(160).optional(),
  cdp_url: z.string().max(500).optional(),
  cdp_port: z.number().int().positive().optional(),
  profile_cdp_resolution: z.enum(["from_url", "from_profile_port", "missing", "unknown"]).default("unknown"),
  observable: z.boolean().default(false),
  controllable: z.boolean().default(false),
  learnable: z.boolean().default(false),
  doc72_intake_allowed: z.boolean().default(false),
  q_browser_internal_equivalent: z.boolean().default(false),
  auth_session_state: z.enum(["none", "signed_in", "signed_out", "unknown"]).default("unknown"),
  health_state: z.enum(["healthy", "degraded", "offline", "unknown"]).default("unknown"),
  dialog_state: BrowserDialogStateSchema.optional(),
  evaluate_timeout_ms: z.number().int().positive().optional(),
  url_allowlist_policy: BrowserUrlAllowlistPolicySchema.optional(),
  artifact_outputs: z.array(BrowserArtifactKindSchema).default([]),
  policy_context: RuntimePolicyContextRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawBrowserControlReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  surfaces: z.array(OpenClawBrowserSurfaceEntrySchema).default([]),
  default_external_browser_intake_policy: z.enum([
    "blocked_by_default",
    "domain_allowlist_required",
    "session_allow_required",
    "task_allow_required",
  ]).default("blocked_by_default"),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

### 8.3 Browser routes

| Route ID | Method | Path | Request | Success schema |
|---|---:|---|---|---|
| `openclaw_browser_control_get` | GET | `/api/openclaw/browser/control` | none | `OpenClawBrowserControlReadModelSchema` |
| `openclaw_browser_surface_refresh` | POST | `/api/openclaw/browser/surfaces/:surfaceId/refresh` | `{ reason?: string }` | `OpenClawBrowserControlReadModelSchema` |
| `openclaw_browser_dialog_answer` | POST | `/api/openclaw/browser/dialogs/:dialogId/answer` | `{ answer?: string, accept?: boolean, idempotency_key }` | `BrowserDialogStateSchema` |
| `openclaw_browser_observation_activate` | POST | `/api/openclaw/browser/observation/activate` | `{ learn_session_id, browser_surface_id, idempotency_key }` | `OpenClawBrowserObservationActivationResultSchema` |
| `openclaw_browser_observation_deactivate` | POST | `/api/openclaw/browser/observation/deactivate` | `{ learn_session_id, browser_surface_id, idempotency_key }` | `OpenClawBrowserObservationActivationResultSchema` |

```ts
export const OpenClawBrowserObservationActivationResultSchema = z.object({
  learn_session_id: z.string().max(160),
  browser_surface_id: z.string().max(160),
  activation_state: z.enum(["activated", "deactivated", "blocked", "failed", "unsupported"]),
  eligibility_block_reason_codes: z.array(z.string().max(120)).default([]),
  observation_adapter_source: z.enum(["browser_extension", "browser_cdp", "browser_dom", "none"]),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  schema_version: z.literal(1),
});
```

### 8.4 DOC20/DOC72/DOC24 cross-doc obligations

- DOC20 registers OpenClaw browser artifacts if stored or surfaced in Q.
- DOC72 receives OpenClaw browser-derived observations only when EC/PropA source policy allows them.
- DOC24 packet manifests must show if OpenClaw browser/search artifacts contributed to injection.
- External Chrome observation must be source-labeled separately from Q Browser visits to avoid dedupe errors.

---

## 9. Search provider and provider-native web search controls

### 9.1 Required behavior

OpenClaw native search should not be blocked by ELNOR, but ELNOR must expose and govern it. R15 must distinguish managed search providers, plugin-backed providers, provider-native search, private-network modes, proxy modes, SSRF guard state, credential source, and DOC72 ingest eligibility.

```ts
export const SearchProviderKindSchema = z.enum([
  "managed_plugin_provider",
  "provider_native_search",
  "browser_search",
  "web_fetch",
  "firecrawl",
  "searxng",
  "brave",
  "exa",
  "duckduckgo",
  "gemini",
  "grok_xai",
  "minimax",
  "perplexity",
  "tavily",
  "unknown",
]);

export const SearchPrivateNetworkModeSchema = z.enum([
  "blocked",
  "allow_self_hosted_only",
  "allow_with_policy",
]);

export const OpenClawSearchProviderPolicyEntrySchema = z.object({
  provider_id: z.string().max(160),
  provider_kind: SearchProviderKindSchema,
  owner_plugin_id: z.string().max(160).optional(),
  installed: z.boolean(),
  enabled_desired: z.boolean().optional(),
  enabled_effective: z.boolean(),
  provider_native_allowed: z.boolean().default(false),
  managed_provider_pinned: z.boolean().default(false),
  private_network_mode: SearchPrivateNetworkModeSchema.default("blocked"),
  ssrf_guard_mode: z.enum(["strict", "self_hosted_exception", "disabled_not_allowed"]).default("strict"),
  proxy_base_url_ref: z.string().max(240).optional(),
  credential_source: z.enum(["none", "env", "secret_ref", "oauth", "plugin_config", "unknown"]).default("unknown"),
  result_provenance_visible: z.boolean().default(false),
  doc72_ingest_allowed: z.boolean().default(false),
  diagnostics_enabled: z.boolean().default(false),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(1),
});

export const OpenClawSearchPolicyReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  default_provider_id: z.string().max(160).optional(),
  entries: z.array(OpenClawSearchProviderPolicyEntrySchema).default([]),
  reduced_observability_modes_enabled: z.boolean().default(false),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

### 9.2 Routes

| Route ID | Method | Path | Request | Success schema |
|---|---:|---|---|---|
| `openclaw_search_policy_get` | GET | `/api/openclaw/search/policy` | none | `OpenClawSearchPolicyReadModelSchema` |
| `openclaw_search_policy_update` | PATCH | `/api/openclaw/search/policy/:providerId` | `OpenClawSearchProviderPolicyMutationSchema` | `OpenClawSearchPolicyReadModelSchema` |

```ts
export const OpenClawSearchProviderPolicyMutationSchema = z.object({
  enabled_desired: z.boolean().optional(),
  provider_native_allowed: z.boolean().optional(),
  managed_provider_pinned: z.boolean().optional(),
  private_network_mode: SearchPrivateNetworkModeSchema.optional(),
  result_provenance_visible: z.boolean().optional(),
  doc72_ingest_allowed: z.boolean().optional(),
  idempotency_key: z.string().max(160),
  schema_version: z.literal(1),
});
```

Normative rule: provider-native search may be allowed, but DOC11 must expose whether provenance and route-trace visibility are reduced.

---

## 10. Codex runtime, code mode, and Codex Computer Use

### 10.1 Required behavior

DOC11 R15 must treat Codex as a distinct high-authority runtime lane, not just another model string. R15 must remove stale `codex-cli/*` assumptions and expose migration/repair status.

```ts
export const CodexRuntimeModeSchema = z.enum([
  "disabled",
  "app_server",
  "acp",
  "unavailable",
]);

export const CodexNativeToolPolicySchema = z.enum([
  "disabled",
  "agent_scoped",
  "coding_agents_only",
  "allowed_with_approval",
]);

export const CodexSandboxNetworkPolicySchema = z.enum([
  "no_network",
  "sandbox_policy",
  "explicit_allow",
  "blocked_by_policy",
]);

export const OpenClawCodexRuntimeReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  codex_runtime_mode: CodexRuntimeModeSchema,
  code_mode_enabled_desired: z.boolean().optional(),
  code_mode_enabled_effective: z.boolean(),
  codex_native_tool_policy: CodexNativeToolPolicySchema,
  codex_sandbox_network_policy: CodexSandboxNetworkPolicySchema,
  legacy_codex_cli_migrated: z.boolean().default(false),
  stale_model_refs_suppressed: z.boolean().default(false),
  effective_auth_profile_ref: z.string().max(200).optional(),
  agent_scope_refs: z.array(z.string().max(160)).default([]),
  tool_surface_summary: z.array(z.object({
    tool_id: z.string().max(160),
    authority_class: z.enum(["read_only", "filesystem", "exec", "patch", "desktop_control", "network"]),
    enabled_effective: z.boolean(),
    approval_required: z.boolean().default(false),
  })).default([]),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(1),
});

export const CodexComputerUseControlSchema = z.object({
  installed: z.boolean().default(false),
  setup_required: z.boolean().default(false),
  setup_status: z.enum(["not_installed", "ready", "setup_required", "install_failed", "blocked_by_policy", "unknown"]),
  auto_install_allowed: z.boolean().default(false),
  enabled_for_agent_ids: z.array(z.string().max(160)).default([]),
  default_for_non_coding_agents: z.literal(false),
  fail_closed_before_codex_turn: z.boolean().default(true),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  schema_version: z.literal(1),
});
```

### 10.2 Routes

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_codex_runtime_get` | GET | `/api/openclaw/codex/runtime` | `OpenClawCodexRuntimeReadModelSchema` |
| `openclaw_codex_runtime_update` | PATCH | `/api/openclaw/codex/runtime` | `OpenClawCodexRuntimeReadModelSchema` |
| `openclaw_codex_computer_use_get` | GET | `/api/openclaw/codex/computer-use` | `CodexComputerUseControlSchema` |
| `openclaw_codex_computer_use_update` | PATCH | `/api/openclaw/codex/computer-use` | `CodexComputerUseControlSchema` |

Normative rule: Codex code/computer-use capabilities default to coding-agent lanes and must not become hidden general automation.

---

## 11. Voice, Talk, TTS, STT, realtime voice, and Google Meet deferral

### 11.1 Required behavior

R15 must support native OpenClaw voice/Talk/TTS/STT/realtime controls without making Google Meet an active ELNOR feature track.

```ts
export const VoiceTransportKindSchema = z.enum([
  "none",
  "browser_webrtc",
  "android_gateway_relay",
  "voice_call",
  "channel_voice_note",
]);

export const PersistedAudioPolicySchema = z.enum([
  "do_not_store",
  "store_audio_ref_only",
  "store_transcript_only",
  "store_audio_and_transcript",
]);

export const AutoTtsPolicySchema = z.enum([
  "off",
  "manual",
  "chat_scoped",
  "agent_default",
  "channel_default",
]);

export const OpenClawVoiceControlReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  talk_mode_enabled: DesiredEffectiveBooleanSchema,
  voice_transport: VoiceTransportKindSchema,
  mobile_voice_enabled: DesiredEffectiveBooleanSchema.optional(),
  stt_provider_id: z.string().max(160).optional(),
  tts_provider_id: z.string().max(160).optional(),
  realtime_voice_provider_id: z.string().max(160).optional(),
  voice_persona_ref: z.string().max(160).optional(),
  auto_tts_policy: AutoTtsPolicySchema,
  persisted_audio_policy: PersistedAudioPolicySchema,
  tool_result_voice_bridge_enabled: z.boolean().default(false),
  transcript_policy: z.enum(["do_not_collect", "standard_policy", "collect_metadata_only", "always_save_transcript"]).default("standard_policy"),
  doc72_voice_transcript_ingest_policy: z.enum(["blocked", "standard_policy", "only_if_saved", "always_candidate"]).default("standard_policy"),
  google_meet_adoption_state: z.enum(["inventory_visible_if_installed", "deferred_disabled_by_default"]).default("deferred_disabled_by_default"),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(1),
});
```

### 11.2 Routes

| Route ID | Method | Path | Request | Success schema |
|---|---:|---|---|---|
| `openclaw_voice_controls_get` | GET | `/api/openclaw/voice/controls` | none | `OpenClawVoiceControlReadModelSchema` |
| `openclaw_voice_controls_update` | PATCH | `/api/openclaw/voice/controls` | `OpenClawVoiceControlMutationSchema` | `OpenClawVoiceControlReadModelSchema` |

```ts
export const OpenClawVoiceControlMutationSchema = z.object({
  talk_mode_enabled: z.boolean().optional(),
  voice_transport: VoiceTransportKindSchema.optional(),
  stt_provider_id: z.string().max(160).optional(),
  tts_provider_id: z.string().max(160).optional(),
  realtime_voice_provider_id: z.string().max(160).optional(),
  auto_tts_policy: AutoTtsPolicySchema.optional(),
  persisted_audio_policy: PersistedAudioPolicySchema.optional(),
  tool_result_voice_bridge_enabled: z.boolean().optional(),
  transcript_policy: z.enum(["do_not_collect", "standard_policy", "collect_metadata_only", "always_save_transcript"]).optional(),
  idempotency_key: z.string().max(160),
  schema_version: z.literal(1),
});
```

---

## 12. Compaction controls and evidence-preservation guardrails

### 12.1 Required behavior

DOC11 must distinguish OpenClaw runtime/session compaction from DOC24 packet compaction and DOC72 graph/evidence summarization.

```ts
export const CompactionOwnerSchema = z.enum([
  "openclaw_runtime",
  "codex_runtime",
  "context_engine",
  "tokenjuice",
  "doc24_packet",
  "unknown",
]);

export const CompactionTriggerKindSchema = z.enum([
  "manual",
  "preflight_context_pressure",
  "transcript_size",
  "provider_context_limit",
  "runtime_recovery",
  "branch_navigation",
  "unknown",
]);

export const OpenClawCompactionControlSchema = z.object({
  session_key: z.string().max(200).optional(),
  enabled: DesiredEffectiveBooleanSchema,
  max_active_transcript_bytes: z.number().int().positive().optional(),
  compaction_owner: CompactionOwnerSchema,
  trigger_kind: CompactionTriggerKindSchema.optional(),
  native_context_window_ref: z.string().max(160).optional(),
  summary_reserve_tokens: z.number().int().nonnegative().optional(),
  summary_reserve_clamped: z.boolean().default(false),
  post_compaction_loop_guard_state: z.enum(["not_applicable", "armed", "triggered", "cleared", "unknown"]).default("unknown"),
  compaction_checkpoint_ref: z.string().max(240).optional(),
  branch_summary_anchor_present: z.boolean().default(false),
  pi_openclaw_conflict_state: z.enum(["none", "prevented", "detected", "unknown"]).default("unknown"),
  last_compaction_reason: z.string().max(500).optional(),
  compacted_source_refs: z.array(z.string().max(240)).default([]),
  restore_available: z.boolean().default(false),
  pre_compaction_capture_required: z.boolean().default(true),
  elnor_capture_pending: z.boolean().default(false),
  doc24_manifest_compaction_state: z.enum(["not_applicable", "raw_history", "openclaw_compacted", "doc24_rendered", "mixed", "unknown"]).default("unknown"),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawCompactionReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  global_control: OpenClawCompactionControlSchema,
  session_overrides: z.array(OpenClawCompactionControlSchema).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

### 12.2 Routes

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_compaction_get` | GET | `/api/openclaw/compaction` | `OpenClawCompactionReadModelSchema` |
| `openclaw_compaction_update` | PATCH | `/api/openclaw/compaction` | `OpenClawCompactionReadModelSchema` |
| `openclaw_session_compaction_get` | GET | `/api/openclaw/sessions/:sessionKey/compaction` | `OpenClawCompactionControlSchema` |
| `openclaw_session_compaction_request` | POST | `/api/openclaw/sessions/:sessionKey/compaction/request` | `OpenClawCompactionControlSchema` |
| `openclaw_session_compaction_restore` | POST | `/api/openclaw/sessions/:sessionKey/compaction/restore` | `OpenClawCompactionControlSchema` |

### 12.3 Hard rule

OpenClaw may compact runtime history, but it must not erase evidence needed for DOC24 injection manifests, DOC72 provenance, or EC policy receipts before ELNOR captures required refs.

---

## 13. OpenClaw memory controls and ELNOR memory precedence

```ts
export const OpenClawMemoryFeatureIdSchema = z.enum([
  "active_memory",
  "memory_wiki",
  "dreaming",
  "rem_backfill",
  "qmd_startup_refresh",
  "provider_memory_recall",
  "memory_lancedb",
  "memory_search_embeddings",
]);

export const OpenClawMemoryControlModeSchema = z.enum([
  "disabled",
  "observed_only",
  "local_runtime_only",
  "eligible_for_injection",
]);

export const OpenClawMemoryFeatureControlSchema = z.object({
  feature_id: OpenClawMemoryFeatureIdSchema,
  installed: z.boolean(),
  enabled_desired: z.boolean().optional(),
  enabled_effective: z.boolean(),
  mode: OpenClawMemoryControlModeSchema,
  per_conversation_filter_state: z.enum(["none", "allowlist", "denylist", "mixed", "unknown"]).default("unknown"),
  selected_memory_slot_ref: z.string().max(160).optional(),
  embedding_provider_ref: z.string().max(160).optional(),
  elnor_memory_precedence: z.enum(["elnor_wins", "openclaw_wins", "dedupe_required", "not_applicable"]).default("dedupe_required"),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  divergence_reason_codes: z.array(z.string().max(120)).default([]),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawMemoryControlsReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  features: z.array(OpenClawMemoryFeatureControlSchema).default([]),
  default_doc24_bridge_mode: z.enum(["disabled", "observed_only", "dedupe_before_injection", "allow_openclaw_injection"]).default("dedupe_before_injection"),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

Routes:

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_memory_controls_get` | GET | `/api/openclaw/memory/controls` | `OpenClawMemoryControlsReadModelSchema` |
| `openclaw_memory_feature_update` | PATCH | `/api/openclaw/memory/features/:featureId` | `OpenClawMemoryControlsReadModelSchema` |

Normative rule: DOC11 exposes controls and effective state; DOC24/DOC72/DOC1 own memory promotion, dedupe, and injection.

---

## 14. Diagnostics, trajectory export, startup/restart traces, config reloadability, and session tool progress

### 14.1 Schemas

```ts
export const ConfigFieldReloadabilitySchema = z.enum([
  "hot_reloadable",
  "restart_required",
  "no_op",
  "unknown",
]);

export const ConfigReloadabilityEntrySchema = z.object({
  field_path: z.string().max(240),
  reloadability: ConfigFieldReloadabilitySchema,
  current_value_redacted: z.boolean().default(true),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});

export const DiagnosticsExportPolicySchema = z.object({
  diagnostics_enabled: DesiredEffectiveBooleanSchema,
  startup_trace_enabled: DesiredEffectiveBooleanSchema.optional(),
  restart_trace_enabled: DesiredEffectiveBooleanSchema.optional(),
  trajectory_capture_enabled: DesiredEffectiveBooleanSchema.optional(),
  trajectory_export_allowed: z.boolean().default(false),
  owner_only_visibility: z.boolean().default(true),
  share_safe_diagnostics_available: z.boolean().default(false),
  redaction_status: z.enum(["not_applicable", "redacted", "partially_redacted", "unredacted_blocked", "unknown"]).default("unknown"),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  schema_version: z.literal(1),
});

export const OpenClawDiagnosticsReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  startup_trace_ref: z.string().max(240).optional(),
  restart_trace_ref: z.string().max(240).optional(),
  resource_count_costs_visible: z.boolean().default(false),
  provider_quota_usage_visible: z.boolean().default(false),
  session_tool_event_stream_enabled: z.boolean().default(false),
  config_reloadability: z.array(ConfigReloadabilityEntrySchema).default([]),
  trajectory_parent_chain_status: z.enum(["not_applicable", "complete", "incomplete", "cyclic", "malformed", "unknown"]).default("unknown"),
  export_policy: DiagnosticsExportPolicySchema,
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});
```

### 14.2 Routes

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_diagnostics_get` | GET | `/api/openclaw/diagnostics` | `OpenClawDiagnosticsReadModelSchema` |
| `openclaw_diagnostics_export` | POST | `/api/openclaw/diagnostics/export` | `DiagnosticsExportReceiptSchema` |
| `openclaw_trajectory_export` | POST | `/api/openclaw/trajectory/export` | `DiagnosticsExportReceiptSchema` |
| `openclaw_config_reloadability_get` | GET | `/api/openclaw/config/reloadability` | `{ entries: ConfigReloadabilityEntrySchema[] }` |

```ts
export const DiagnosticsExportReceiptSchema = z.object({
  export_id: z.string().max(160),
  export_kind: z.enum(["diagnostics_bundle", "trajectory_export", "startup_trace", "restart_trace"]),
  export_state: z.enum(["created", "blocked", "failed", "queued"]),
  artifact_ref: z.string().max(240).optional(),
  share_safe: z.boolean().default(false),
  redaction_status: DiagnosticsExportPolicySchema.shape.redaction_status,
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

---

## 15. Model/provider/auth/quota/source-plan/fallback controls

```ts
export const ModelCatalogSourceSchema = z.enum([
  "user_config",
  "plugin_manifest",
  "provider_dynamic",
  "runtime_fallback",
  "legacy_repair",
  "suppressed_stale_ref",
]);

export const ProviderQuotaPressureStateSchema = z.enum([
  "none",
  "low",
  "moderate",
  "high",
  "exhausted",
  "unknown",
]);

export const OpenClawModelCatalogEntrySchema = z.object({
  model_ref: z.string().max(200),
  provider_id: z.string().max(160),
  display_name: z.string().max(160).optional(),
  source: ModelCatalogSourceSchema,
  source_priority_rank: z.number().int().nonnegative().optional(),
  effective_auth_profile_ref: z.string().max(200).optional(),
  auth_profiles: z.array(z.string().max(200)).default([]),
  quota_usage: z.object({
    visible: z.boolean().default(false),
    quota_pressure_state: ProviderQuotaPressureStateSchema.default("unknown"),
    usage_summary_redacted: z.boolean().default(true),
  }).optional(),
  fallback_chain: z.array(z.string().max(200)).default([]),
  fallback_attempted: z.boolean().default(false),
  fallback_selected: z.string().max(200).optional(),
  bootstrap_profile_override: z.object({
    context_injection_mode: z.string().max(120).optional(),
    bootstrap_max_chars: z.number().int().nonnegative().optional(),
    bootstrap_total_max_chars: z.number().int().nonnegative().optional(),
  }).optional(),
  field_reloadability: z.array(ConfigReloadabilityEntrySchema).default([]),
  model_ref_repair_state: z.enum(["none", "repaired", "repair_available", "suppressed", "blocked", "unknown"]).default("none"),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawModelProviderReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  entries: z.array(OpenClawModelCatalogEntrySchema).default([]),
  provider_auth_overview_available: z.boolean().default(false),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

Routes:

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_models_catalog_get` | GET | `/api/openclaw/models/catalog` | `OpenClawModelProviderReadModelSchema` |
| `openclaw_models_auth_status_get` | GET | `/api/openclaw/models/auth-status` | `OpenClawModelProviderReadModelSchema` |
| `openclaw_models_add` | POST | `/api/openclaw/models` | `OpenClawModelProviderReadModelSchema` |
| `openclaw_model_override_reset` | POST | `/api/openclaw/models/override-reset` | `OpenClawModelProviderReadModelSchema` |

---

## 16. Subagents, spawned sessions, parent review, and completion handoff

R15 should expose OpenClaw subagent truth but must not make DOC11 the owner of MemoryAgent, DocumentIntelligenceAgent, or specialist semantics.

```ts
export const OpenClawSubagentContextModeSchema = z.enum([
  "isolated",
  "fork",
  "sub_agent_context_pack",
]);

export const OpenClawSubagentRunStateSchema = z.enum([
  "accepted",
  "running",
  "completed",
  "failed",
  "timed_out",
  "archived",
  "killed",
  "unknown",
]);

export const OpenClawSubagentSessionEntrySchema = z.object({
  subagent_id: z.string().max(160),
  parent_session_key: z.string().max(200),
  child_session_key: z.string().max(200),
  agent_id: z.string().max(160).optional(),
  mode: z.enum(["run", "session"]),
  context_mode: OpenClawSubagentContextModeSchema,
  state: OpenClawSubagentRunStateSchema,
  spawned_subagent_route_metadata: z.record(z.string(), z.unknown()).default({}),
  parent_review_required: z.boolean().default(true),
  completion_handoff_state: z.enum(["none", "ready_for_parent_review", "delivered", "failed", "unknown"]).default("unknown"),
  model_effective: z.string().max(200).optional(),
  thinking_effective: z.string().max(120).optional(),
  tool_policy_by_depth: z.record(z.string(), z.unknown()).default({}),
  thread_bound: z.boolean().default(false),
  archive_after_minutes: z.number().int().nonnegative().optional(),
  run_timeout_seconds: z.number().int().nonnegative().optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(2),
});

export const OpenClawSubagentReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  max_spawn_depth_effective: z.number().int().positive().optional(),
  max_children_per_agent_effective: z.number().int().positive().optional(),
  max_concurrent_effective: z.number().int().positive().optional(),
  active_entries: z.array(OpenClawSubagentSessionEntrySchema).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(2),
});
```

Routes:

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_subagents_get` | GET | `/api/openclaw/subagents` | `OpenClawSubagentReadModelSchema` |
| `openclaw_subagent_spawn` | POST | `/api/openclaw/subagents/spawn` | `OpenClawSubagentSessionEntrySchema` |
| `openclaw_subagent_kill` | POST | `/api/openclaw/subagents/:subagentId/kill` | `OpenClawSubagentSessionEntrySchema` |
| `openclaw_subagent_completion_handoff` | PATCH | `/api/openclaw/subagents/:subagentId/handoff` | `OpenClawSubagentSessionEntrySchema` |

---

## 17. Paired-node file-transfer plugin controls

```ts
export const PairedNodeFileTransferPolicySchema = z.object({
  paired_node_id: z.string().max(160),
  file_transfer_enabled: DesiredEffectiveBooleanSchema,
  approved_read_paths: z.array(z.string().max(500)).default([]),
  approved_write_paths: z.array(z.string().max(500)).default([]),
  follow_symlinks: z.boolean().default(false),
  byte_limit: z.number().int().positive().default(16 * 1024 * 1024),
  operator_approval_required: z.boolean().default(true),
  last_transfer_receipt_ref: z.string().max(240).optional(),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});

export const PairedNodeFileTransferReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  plugin_installed: z.boolean(),
  policies: z.array(PairedNodeFileTransferPolicySchema).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

Routes:

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_file_transfer_get` | GET | `/api/openclaw/file-transfer` | `PairedNodeFileTransferReadModelSchema` |
| `openclaw_file_transfer_policy_update` | PATCH | `/api/openclaw/file-transfer/nodes/:nodeId/policy` | `PairedNodeFileTransferPolicySchema` |

Default: disabled or approval-required until path policies are explicit.

---

## 18. Sender-scoped tool authority

R15 should add sender-scoped authority readback to prepare for firm/networked access and to govern high-authority OpenClaw tools.

```ts
export const RuntimeRequesterScopeSchema = z.enum([
  "owner",
  "agent",
  "channel_sender",
  "group",
  "external_user",
  "unknown",
]);

export const OpenClawToolAuthorityEntrySchema = z.object({
  requester_scope: RuntimeRequesterScopeSchema,
  sender_key: z.string().max(200).optional(),
  principal_id: z.string().max(160).optional(),
  tenant_id: z.string().max(160).optional(),
  tool_surface: z.enum([
    "core",
    "bundled_plugin",
    "external_plugin",
    "provider_native",
    "channel_tool",
    "codex_native",
    "browser",
    "file_transfer",
  ]),
  tool_id: z.string().max(160),
  authority_class: z.enum([
    "read_only",
    "message_send",
    "browser_control",
    "filesystem_read",
    "filesystem_write",
    "exec",
    "desktop_control",
    "diagnostics_export",
    "credential_or_auth",
  ]),
  allowed_effective: z.boolean(),
  approval_required: z.boolean().default(false),
  blocked_reason_codes: z.array(z.string().max(120)).default([]),
  policy_decision_ref: PolicyDecisionRefSchema.optional(),
  runtime_sources: z.array(RuntimeSourceRefSchema).default([]),
  schema_version: z.literal(1),
});

export const OpenClawToolAuthorityReadModelSchema = z.object({
  generated_at: z.string().datetime(),
  entries: z.array(OpenClawToolAuthorityEntrySchema).default([]),
  degraded_reason_codes: z.array(z.string().max(120)).default([]),
  schema_version: z.literal(1),
});
```

Routes:

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_tool_authority_get` | GET | `/api/openclaw/tool-authority` | `OpenClawToolAuthorityReadModelSchema` |
| `openclaw_tool_authority_refresh` | POST | `/api/openclaw/tool-authority/refresh` | `OpenClawToolAuthorityReadModelSchema` |

---

## 19. Streaming, progress, preview, side-result, and final delivery event normalization

R15 must prevent non-final progress from becoming false durable transcript truth.

```ts
export const OpenClawPresentationEventKindSchema = z.enum([
  "session.tool",
  "chat.side_result",
  "chat.progress_preview",
  "chat.final_delivery",
  "message.presentation",
  "assistant_media_attachment",
  "delivery_receipt",
  "delivery_failed",
]);

export const OpenClawPresentationEventReadModelSchema = z.object({
  event_id: z.string().max(160),
  event_kind: OpenClawPresentationEventKindSchema,
  session_key: z.string().max(200),
  channel_ref: z.string().max(160).optional(),
  durable_transcript_message: z.boolean().default(false),
  visible_to_user: z.boolean().default(true),
  final_delivery: z.boolean().default(false),
  side_result: z.boolean().default(false),
  progress_only: z.boolean().default(false),
  media_artifact_ref: z.string().max(240).optional(),
  delivery_receipt_ref: z.string().max(240).optional(),
  created_at: z.string().datetime(),
  schema_version: z.literal(1),
});
```

Normative rule: progress and side-result events may render in UI, but they are not durable assistant transcript messages unless OpenClaw reports final/persistent delivery.

---

## 20. ACP fallback backends

```ts
export const AcpFallbackBackendSchema = z.object({
  backend_id: z.string().max(160),
  backend_kind: z.enum(["acp", "codex", "openclaw_runtime", "external", "unknown"]),
  enabled: z.boolean().default(true),
  priority: z.number().int().nonnegative(),
  setup_required: z.boolean().default(false),
  auth_required: z.boolean().default(false),
  health: z.enum(["healthy", "degraded", "failing", "unknown"]).default("unknown"),
  schema_version: z.literal(1),
});

export const AcpRuntimeFallbackPolicySchema = z.object({
  fallback_backends: z.array(AcpFallbackBackendSchema).default([]),
  fallback_policy: z.enum(["disabled", "try_before_output", "try_after_primary_failure", "manual_only"]).default("try_before_output"),
  last_fallback_attempted: z.boolean().default(false),
  last_fallback_selected_backend: z.string().max(160).optional(),
  last_fallback_visible_reason: z.string().max(500).optional(),
  schema_version: z.literal(1),
});
```

Routes:

| Route ID | Method | Path | Success schema |
|---|---:|---|---|
| `openclaw_acp_fallbacks_get` | GET | `/api/openclaw/acp/fallbacks` | `AcpRuntimeFallbackPolicySchema` |
| `openclaw_acp_fallbacks_update` | PATCH | `/api/openclaw/acp/fallbacks` | `AcpRuntimeFallbackPolicySchema` |

---

## 21. Settings page implementation map

DOC11 should provide runtime truth for these settings groupings. DOC20/DOC3 own the visible page layout, but DOC11 must supply complete read models and mutation receipts.

```text
Settings > OpenClaw Features
  - Feature dashboard
  - Plugin registry and repair state
  - Commands and native capability catalog
  - Diagnostics / startup / restart / trajectory
  - Model/provider auth/quota/fallback/source plan
  - Search providers and native search policy
  - Browser/Chrome/CDP/extension controls
  - Voice/Talk/TTS/STT/realtime controls
  - Memory features and bridge modes
  - Compaction controls
  - Codex/code/computer-use
  - Subagents and active-run interaction
  - File-transfer and high-authority tools

Skills & Connectors > OpenClaw Tools
  - Uses DOC11 plugin/skill/catalog truth
  - Renders install/update/disable/uninstall/repair actions via DOC11 adapter routes
  - Keeps Q plugins and OpenClaw plugins visibly typed

Memory & Privacy
  - Uses EC/PropA/DOC24/DOC72 policy state
  - Reads DOC11 OpenClaw feature surface classifications and policy refs
```

No visible setting may claim success unless its mutation emits an event and refreshes the effective-state read model.

---

## 22. Route and event closure checklist

The R15 redline must add every route in this proposal to DOC11's canonical route appendix/control matrices. It must add every event below to the event appendix or the relevant event registry:

```text
openclaw.feature_state_changed
openclaw.plugin_state_changed
openclaw.command_catalog_refreshed
openclaw.active_run_interaction.accepted
openclaw.active_run_interaction.blocked
openclaw.side_result.emitted
openclaw.browser_surface_state_changed
openclaw.browser_observation_state_changed
openclaw.search_policy_changed
openclaw.codex_runtime_state_changed
openclaw.voice_control_changed
openclaw.compaction_state_changed
openclaw.memory_feature_state_changed
openclaw.diagnostics_export_created
openclaw.model_provider_state_changed
openclaw.subagent_state_changed
openclaw.file_transfer_policy_changed
openclaw.tool_authority_changed
openclaw.presentation_event_observed
openclaw.acp_fallback_policy_changed
```

---

## 23. Acceptance tests and anti-ghost checks

The R15 redline should add acceptance tests covering at least:

1. **No phantom OpenClaw feature controls.** Every visible control maps to native RPC, CLI adapter, durable desired write + effective readback, read-only row, or explicit no-op/deferred row.
2. **Plugin registry truth.** Missing externalized official plugins show missing/repairable state; configured channels do not appear healthy if plugin payload is absent.
3. **Plugin-owned Gateway methods.** Advertised plugin RPC methods carry owner and scope metadata; collisions are not rendered as executable user controls.
4. **Typed tool-plugin builder.** Builder state reports init/build/validate/manifest/tool declaration truth without pretending DOC11 owns authoring UX.
5. **Active-run interaction.** `/steer`, `/btw`, `/side`, follow-up, collect, and interrupt produce distinct result states.
6. **Side-result memory policy.** Side results are not native OpenClaw future context by default but are eligible for ELNOR capture under standard policy.
7. **Branch metadata.** Branch/parent-chain readback exists without a fake branch-chat control.
8. **Browser dual-lane.** Q Browser and OpenClaw browser entries are distinguishable; external Chrome does not feed DOC72 without policy allow.
9. **Browser dialogs and URL allowlist.** `blockedByDialog` and URL policy blocks render as blocked/degraded states, not failed invisible actions.
10. **Search provenance.** Provider-native search either exposes provenance/trace visibility or displays reduced-observability warning.
11. **Codex migration.** Stale `codex-cli/*` refs are not canonical; Codex app-server/runtime truth is reported.
12. **Codex authority.** Code mode/computer use is agent-scoped and high-authority.
13. **Voice/Talk.** TTS/STT/realtime settings show effective provider and persisted-audio policy; Google Meet remains deferred inventory-only.
14. **Compaction.** Runtime compaction reports owner/trigger/source refs and does not proceed through ELNOR capture-pending guard without a receipt.
15. **OpenClaw memory.** OpenClaw memory features can be disabled/observed/local/injection-eligible independently; DOC24/DOC72 precedence is not overwritten.
16. **Diagnostics/export.** Share-safe diagnostics export is owner-only by default and redaction status is visible.
17. **Model/provider settings.** Auth profile, quota pressure, fallback chain, source plan, and reloadability are visible.
18. **Subagents.** Spawned sessions report parent/child keys, context mode, parent-review status, and completion handoff.
19. **File transfer.** Paired-node file-transfer defaults to approval/path-policy gated.
20. **Sender authority.** Channel/external senders do not inherit owner-level high-authority tools.
21. **Streaming/progress.** Progress previews and side results do not become durable assistant transcript messages unless final delivery is reported.
22. **ACP fallback.** Fallback backend attempts are visible before output is emitted.
23. **Appendix closure.** All body/prose route mentions appear in Appendix E; all events appear in Appendix C; all control rows appear in Appendix U.

---

## 24. Implementation order for R15 redline

The actual DOC11 R15 delta should apply in this order:

1. Add common primitives.
2. Add unified OpenClaw feature dashboard.
3. Expand plugin/skill/ClawHub/tool-plugin schemas and routes.
4. Expand command catalog.
5. Add active-run interaction and side-result memory policy.
6. Add branch metadata readback.
7. Add browser dual-lane control surface.
8. Add search policy.
9. Add Codex runtime/code/computer-use controls.
10. Add voice/Talk/TTS/STT controls.
11. Add compaction controls.
12. Add OpenClaw memory controls.
13. Add diagnostics/export/reloadability controls.
14. Add model/provider/auth/quota/fallback/source plan controls.
15. Add subagent readback.
16. Add paired-node file-transfer controls.
17. Add sender-scoped tool authority.
18. Normalize streaming/progress/final-delivery events.
19. Add ACP fallback backends.
20. Update canonical route table, control matrix, event appendix, settings appendix, and acceptance tests.

---

## 25. Known deferrals / watch-only rows

Do not canonize these as required R15 behavior unless promoted before redline drafting:

- `2026.5.19-beta.1` beta-only release details;
- Google Meet participant workflows;
- full Q branch-chat UX beyond DOC11 readback hooks;
- QA-Lab release-gate internals except as acceptance-test inspiration;
- individual new skills such as meme-maker, node inspector, fused diagram, Python debugging, and throwaway spike workflows as specific first-class DOC11 surfaces.

---

## 26. Bottom-line amendment posture

DOC11 R15 should become the authoritative OpenClaw native capability control plane for ELNOR: what exists, what is installed, what is active, what is healthy, what is mutable, what is blocked, what emits data, and what policy gate applies. It should not replace DOC20/DOC3 UX, DOC24 routing, DOC72 memory/graph ownership, EC/PropA policy computation, or the future networking/user-profile spec.