mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-25 13:44:06 +08:00
docs: add SCHEMAS.md — field-level JSON contract for clawable CLI surfaces
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/<command>.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
This commit is contained in:
parent
b048de8899
commit
40c17d8f2a
364
SCHEMAS.md
Normal file
364
SCHEMAS.md
Normal file
@ -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/<command>.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 <command> --output-format json <args> > tests/fixtures/json/<command>.json
|
||||||
|
# Review the diff, commit
|
||||||
|
git add tests/fixtures/json/<command>.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)
|
||||||
Loading…
x
Reference in New Issue
Block a user