From 40c17d8f2a757785d4bc9a087b4a62d34afca42b Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Wed, 22 Apr 2026 19:13:04 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20add=20SCHEMAS.md=20=E2=80=94=20field-le?= =?UTF-8?q?vel=20JSON=20contract=20for=20clawable=20CLI=20surfaces?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the unified JSON envelope contract across all 13 clawable-surface commands. Extends the parity work (#171) to the field level: every command that accepts --output-format json must emit predictable field names, types, and optionality. Common fields (all envelopes): - timestamp (ISO 8601 UTC) - command (argv[1]) - exit_code (0/1/2) - output_format ('json') - schema_version ('1.0') Error envelope (exit 1, failure): - error.kind (enum: filesystem|auth|session|parse|runtime|mcp|delivery|usage|policy|unknown) - error.operation (syscall/method name) - error.target (resource path/name) - error.retryable (bool) - error.message (platform error text) - error.hint (optional: actionable next step) Not-found envelope (exit 1, not a failure): - found: false - error.kind (enum: command_not_found|tool_not_found|session_not_found) - error.message, error.retryable Per-command success schemas documented for 13 commands: list-sessions, delete-session, load-session, flush-transcript, show-command, show-tool, exec-command, exec-tool, route, bootstrap, command-graph, tool-pool, bootstrap-graph Why this matters: - #171 enforced that commands have --output-format; #172 enforces that the JSON fields are PREDICTABLE - Downstream claws can build ONE error handler + per-command jq query, not special-casing logic per command family - Field consistency enables generic automation patterns (error dedupe, failure aggregation, cross-command monitoring) Related: - ROADMAP #172 (field-level contract stabilization, Gaebal-gajae priority #1) - ROADMAP #171 (parity audit CI automation — already landed) - #164 Stage B (cancellation observability — adds cancel_observed field) - #164 Stage A (already done — adds stop_reason field to TurnResult) Fixture/regression testing: - Golden JSON snapshots: tests/fixtures/json/.json (future) - Consistency test: test_json_envelope_field_consistency.py (future) - Versioning: schema_version='1.0' for current; bump to 2.0 for breaking changes --- SCHEMAS.md | 364 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 SCHEMAS.md diff --git a/SCHEMAS.md b/SCHEMAS.md new file mode 100644 index 0000000..0009fc9 --- /dev/null +++ b/SCHEMAS.md @@ -0,0 +1,364 @@ +# JSON Envelope Schemas — Clawable CLI Contract + +This document locks the field-level contract for all clawable-surface commands. Every command accepting `--output-format json` must conform to the envelope shapes below. + +**Target audience:** Claws building orchestrators, automation, or monitoring against claw-code's JSON output. + +--- + +## Common Fields (All Envelopes) + +Every command response, success or error, carries: + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "list-sessions", + "exit_code": 0, + "output_format": "json", + "schema_version": "1.0" +} +``` + +| Field | Type | Required | Notes | +|---|---|---|---| +| `timestamp` | ISO 8601 UTC | Yes | Time command completed | +| `command` | string | Yes | argv[1] (e.g. "list-sessions") | +| `exit_code` | int (0/1/2) | Yes | 0=success, 1=error/not-found, 2=timeout | +| `output_format` | string | Yes | Always "json" (for symmetry with text mode) | +| `schema_version` | string | Yes | "1.0" (bump for breaking changes) | + +--- + +## Error Envelope + +When a command fails (exit code 1), responses carry: + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "exec-command", + "exit_code": 1, + "error": { + "kind": "filesystem", + "operation": "write", + "target": "/tmp/nonexistent/out.md", + "retryable": true, + "message": "No such file or directory", + "hint": "intermediate directory does not exist; try mkdir -p /tmp/nonexistent" + } +} +``` + +| Field | Type | Required | Notes | +|---|---|---|---| +| `error.kind` | enum | Yes | One of: `filesystem`, `auth`, `session`, `parse`, `runtime`, `mcp`, `delivery`, `usage`, `policy`, `unknown` | +| `error.operation` | string | Yes | Syscall/method that failed (e.g. "write", "open", "resolve_session") | +| `error.target` | string | Yes | Resource that failed (path, session-id, server-name, etc.) | +| `error.retryable` | bool | Yes | Whether caller can safely retry without intervention | +| `error.message` | string | Yes | Platform error message (e.g. errno text) | +| `error.hint` | string | No | Optional actionable next step | + +--- + +## Not-Found Envelope + +When an entity does not exist (exit code 1, but not a failure): + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "load-session", + "exit_code": 1, + "name": "does-not-exist", + "found": false, + "error": { + "kind": "session_not_found", + "message": "session 'does-not-exist' not found in .claw/sessions/", + "retryable": false + } +} +``` + +| Field | Type | Required | Notes | +|---|---|---|---| +| `name` | string | Yes | Entity name/id that was looked up | +| `found` | bool | Yes | Always `false` for not-found | +| `error.kind` | enum | Yes | One of: `command_not_found`, `tool_not_found`, `session_not_found` | +| `error.message` | string | Yes | User-visible explanation | +| `error.retryable` | bool | Yes | Usually `false` (entity will not magically appear) | + +--- + +## Per-Command Success Schemas + +### `list-sessions` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "list-sessions", + "exit_code": 0, + "output_format": "json", + "schema_version": "1.0", + "directory": ".claw/sessions", + "sessions_count": 2, + "sessions": [ + { + "session_id": "sess_abc123", + "created_at": "2026-04-21T15:30:00Z", + "last_modified": "2026-04-22T09:45:00Z", + "prompt_count": 5, + "stopped": false + } + ] +} +``` + +### `delete-session` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "delete-session", + "exit_code": 0, + "session_id": "sess_abc123", + "deleted": true, + "directory": ".claw/sessions" +} +``` + +### `load-session` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "load-session", + "exit_code": 0, + "session_id": "sess_abc123", + "loaded": true, + "directory": ".claw/sessions", + "path": ".claw/sessions/sess_abc123.jsonl" +} +``` + +### `flush-transcript` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "flush-transcript", + "exit_code": 0, + "session_id": "sess_abc123", + "path": ".claw/sessions/sess_abc123.jsonl", + "flushed": true, + "messages_count": 12, + "input_tokens": 4500, + "output_tokens": 1200 +} +``` + +### `show-command` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "show-command", + "exit_code": 0, + "name": "add-dir", + "found": true, + "source_hint": "commands/add-dir/add-dir.tsx", + "responsibility": "creates a new directory in the worktree" +} +``` + +### `show-tool` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "show-tool", + "exit_code": 0, + "name": "BashTool", + "found": true, + "source_hint": "tools/BashTool/BashTool.tsx" +} +``` + +### `exec-command` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "exec-command", + "exit_code": 0, + "name": "add-dir", + "prompt": "create src/util/", + "handled": true, + "message": "created directory", + "source_hint": "commands/add-dir/add-dir.tsx" +} +``` + +### `exec-tool` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "exec-tool", + "exit_code": 0, + "name": "BashTool", + "payload": "cargo build", + "handled": true, + "message": "exit code 0", + "source_hint": "tools/BashTool/BashTool.tsx" +} +``` + +### `route` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "route", + "exit_code": 0, + "prompt": "add a test", + "limit": 10, + "match_count": 3, + "matches": [ + { + "kind": "command", + "name": "add-file", + "score": 0.92, + "source_hint": "commands/add-file/add-file.tsx" + } + ] +} +``` + +### `bootstrap` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "bootstrap", + "exit_code": 0, + "prompt": "hello", + "setup": { + "python_version": "3.13.12", + "implementation": "CPython", + "platform_name": "darwin", + "test_command": "pytest" + }, + "routed_matches": [ + {"kind": "command", "name": "init", "score": 0.85, "source_hint": "..."} + ], + "turn": { + "prompt": "hello", + "output": "...", + "stop_reason": "completed" + }, + "persisted_session_path": ".claw/sessions/sess_abc.jsonl" +} +``` + +### `command-graph` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "command-graph", + "exit_code": 0, + "builtins_count": 185, + "plugin_like_count": 20, + "skill_like_count": 2, + "total_count": 207, + "builtins": [ + {"name": "add-dir", "source_hint": "commands/add-dir/add-dir.tsx"} + ], + "plugin_like": [], + "skill_like": [] +} +``` + +### `tool-pool` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "tool-pool", + "exit_code": 0, + "simple_mode": false, + "include_mcp": true, + "tool_count": 184, + "tools": [ + {"name": "BashTool", "source_hint": "tools/BashTool/BashTool.tsx"} + ] +} +``` + +### `bootstrap-graph` + +```json +{ + "timestamp": "2026-04-22T10:10:00Z", + "command": "bootstrap-graph", + "exit_code": 0, + "stages": ["stage 1", "stage 2", "..."], + "note": "bootstrap-graph is markdown-only in this version" +} +``` + +--- + +## Versioning & Compatibility + +- **schema_version = "1.0":** Current as of 2026-04-22. Covers all 13 clawable commands. +- **Breaking changes** (e.g. renaming a field) bump schema_version to "2.0". +- **Additive changes** (e.g. new optional field) stay at "1.0" and are backward compatible. +- Downstream claws **must** check `schema_version` before relying on field presence. + +--- + +## Regression Testing + +Each command is covered by: +1. **Fixture file** (golden JSON snapshot under `tests/fixtures/json/.json`) +2. **Parametrised test** in `test_cli_parity_audit.py::TestJsonOutputContractEndToEnd` +3. **Field consistency test** (new, tracked as ROADMAP #172) + +To update a fixture after a intentional schema change: +```bash +claw --output-format json > tests/fixtures/json/.json +# Review the diff, commit +git add tests/fixtures/json/.json +``` + +To verify no regressions: +```bash +cargo test --release test_json_envelope_field_consistency +``` + +--- + +## Design Notes + +**Why common fields on every response?** +- Downstream claws can build one error handler that works for all commands +- Timestamp + command + exit_code give context without scraping argv or timestamps from command output +- `schema_version` signals compatibility for future upgrades + +**Why both "found" and "error" on not-found?** +- Exit code 1 covers both "entity missing" and "operation failed" +- `found=false` distinguishes not-found from error without string matching +- `error.kind` and `error.retryable` let automation decide: retry a temporary miss vs escalate a permanent refusal + +**Why "operation" and "target" in error?** +- Claws can aggregate failures by operation type (e.g. "how many `write` ops failed?") +- Claws can implement per-target retry policy (e.g. "skip missing files, retry networking") +- Pure text errors ("No such file") do not provide enough structure for pattern matching + +**Why "handled" vs "found"?** +- `show-command` reports `found: bool` (inventory signal: "does this exist?") +- `exec-command` reports `handled: bool` (operational signal: "was this work performed?") +- The names matter: a command can be found but not handled (e.g. too large for context window), or handled silently (no output message)