diff --git a/docs/byoh.md b/docs/byoh.md index e762709..d867e9b 100644 --- a/docs/byoh.md +++ b/docs/byoh.md @@ -122,9 +122,9 @@ and fallbacks per enhancement are in ### Named capabilities: real code for free -Everything that is genuinely code — transcript stitching, guard hooks, slug sanitization, shadow -scanning — is a **named capability** a descriptor references. If your harness emits a compatible -stream, you get the full feature from configuration alone: +Everything that is genuinely code — transcript stitching, slug sanitization, shadow scanning — +is a **named capability** a descriptor references. If your harness emits a compatible stream, +you get the full feature from configuration alone: - `transcript.parser = "claude-stream-json"` — Claude Code `-p --output-format stream-json` events. - `transcript.parser = "codex-items"` — Codex `item.started`/`item.completed` JSONL. @@ -149,11 +149,12 @@ An unknown capability name fails the schema gate listing the allowed values. ### The guard restriction User-supplied descriptors may **not** declare `[guard]` or set `run.supports_guard = true`: the -write guard installs native hook config into dispatch environments and stays restricted to -built-in descriptors until the guard engine is opened up (fail-open safety). Unguarded runs fall -back to the `detect-stray-writes` audit. A project-local overlay of a guarded built-in is fine — -the restriction applies to the user file's own content, and the embedded guard merges through -underneath it. +write guard **fails open** — a mistyped guard block would silently disarm it — so guard data +stays restricted to the embedded built-in descriptors. Unguarded runs fall back to the +`detect-stray-writes` audit, and `run --guard` with a harness defined only by user descriptors is +rejected in preflight (the run stops rather than continuing silently unguarded). A project-local +overlay of a guarded built-in is fine — the restriction applies to the user file's own content, +and the embedded guard merges through underneath it. ## The workflow diff --git a/docs/claude-notes.md b/docs/claude-notes.md index e32a126..c8fac0d 100644 --- a/docs/claude-notes.md +++ b/docs/claude-notes.md @@ -16,7 +16,10 @@ descriptor references: | `stream_json.rs` | `-p --output-format stream-json` transcript parsing (`claude-stream-json`) | | `transcript.rs` | JSONL record shapes + shared tool-call extractors | | `plugin_shadow.rs` | plugin-shadow detection + isolation banner (`claude-plugins`) | -| `guard.rs` | write-guard hook install + `hookSpecificOutput` deny verdict (`claude-hooks`) | + +The write guard has no per-harness code: the descriptor's `[guard]` block (hook file, matcher, +hook-entry and `hookSpecificOutput` verdict templates) is rendered by the generic engine in +`src/adapters/guard.rs`; the hidden `guard` subcommand is its frozen hook entry-point alias. ## Dispatch quirks (all forced by the `claude` CLI) diff --git a/docs/codex-notes.md b/docs/codex-notes.md index 809f901..538561d 100644 --- a/docs/codex-notes.md +++ b/docs/codex-notes.md @@ -14,7 +14,11 @@ references: |------|--------------| | `harnesses/codex.toml` | the descriptor — every declarative value + capability references | | `transcript.rs` | `item.completed` event-stream parsing (`codex-items`) | -| `guard.rs` | write-guard hook install + `{"decision": "block"}` verdict (`codex-hooks`) | + +The write guard has no per-harness code: the descriptor's `[guard]` block (hook file, matcher, +hook-entry and `{"decision": "block"}` verdict templates) is rendered by the generic engine in +`src/adapters/guard.rs`; the hidden `guard-codex` subcommand is its frozen hook entry-point +alias. ## Dispatch (`codex exec`) diff --git a/docs/progressive-enhancements.md b/docs/progressive-enhancements.md index 25acf8f..9bf76b6 100644 --- a/docs/progressive-enhancements.md +++ b/docs/progressive-enhancements.md @@ -64,10 +64,11 @@ only). Only genuinely contradictory flag combinations stay errors. `src/adapters/descriptor_adapter.rs` — the one generic `DescriptorAdapter` implementing the trait from a descriptor. - `src/adapters/capabilities.rs` — the **named capabilities**: closed enums a descriptor references - by kebab-case name for everything that is real code (transcript parsers, guard engines, slug - generation, shadow preflight). -- `src/adapters//` — only the code behind those capabilities: transcript parsers, guard - hooks, the plugin-shadow scan, the OpenCode slug sanitizer. + by kebab-case name for everything that is real code (transcript parsers, slug generation, + shadow preflight). The write guard needs no named capability: it is pure `[guard]` data + rendered by the one generic engine in `src/adapters/guard.rs`. +- `src/adapters//` — only the code behind those capabilities: transcript parsers, the + plugin-shadow scan, the OpenCode slug sanitizer. - `run_capabilities()` (descriptor table `[run]`) + `harness_run_preflight()` (`src/cli/run/util.rs`) — the `run` preflight: undeclared enhancements warn naming their fallback and adjust the options (`--guard` forced off, missing `skills_dir` forces @@ -147,18 +148,24 @@ afterwards. arm whose subagent read the live skill source instead of its staged copy, which contaminates the arm; fatal in revision mode, where the `old_skill` arm then sees new-skill content.) -*Descriptor fields:* the `[guard]` table — `engine`, `armed_message` — plus `[tools]` (the -write/patch/shell/read vocabulary) and `run.supports_guard` (validated to stay in lockstep with -the `[guard]` table). -*Capability:* `guard.engine` names the hook installer (`claude-hooks`, `codex-hooks`); each engine -also exposes its hook matcher so validation can prove every hooked tool is declared in `[tools]`. -The guard arbiter and `detect-stray-writes` classify tool names against the cross-harness -vocabulary union (`all_tool_vocabulary`), so wiring a guard or transcript parser without declaring -the harness's tool names is rejected at descriptor load. The hidden `guard` / `guard-codex` -subcommands are the hook entry points — their names are a stable on-disk contract. Shared +*Descriptor fields:* the `[guard]` table — `hooks_file`, `matcher`, `command_template`, +`hook_entry`, `verdict_template`, `armed_message` — plus `[tools]` (the write/patch/shell/read +vocabulary) and `run.supports_guard` (validated to stay in lockstep with the `[guard]` table). +There is no guard code capability: one generic engine (`src/adapters/guard.rs`) renders the +install (hook entry merged into `hooks_file`) and the deny verdict from these templates, whose +authored JSON key order is serialized verbatim — the verdict bytes are the harness's on-disk +contract. Validation proves every hooked `matcher` tool is declared in `[tools]`, that the +templates parse as JSON, and that their `{command}`/`{matcher}`/`{reason}` placeholders sit in +string values. The guard arbiter and `detect-stray-writes` classify tool names against the +cross-harness vocabulary union (`all_tool_vocabulary`), so wiring a guard or transcript parser +without declaring the harness's tool names is rejected at descriptor load. The hidden `guard` / +`guard-codex` subcommands are frozen hook entry-point aliases (a stable on-disk contract); a +future guard-capable built-in uses the generic `guard-hook --harness