mirror of
https://github.com/ultraworkers/claw-code.git
synced 2026-04-24 13:08:11 +08:00
roadmap: #164 filed — JSON envelope schema-vs-binary divergence
Binary emits different envelope shape than SCHEMAS.md documents: - Missing: timestamp, command, exit_code, output_format, schema_version - Wrong placement: kind is top-level, not nested under error - Extra: type:error field not in schema - Wrong type: error is string, not object with operation/target/retryable Additional issue: 'kind' field is semantically overloaded (verb-id in success envelopes, error-kind in error envelopes) — violates typed contract. Filed as 7th member of typed-error family (joins #102, #121, #127, #129, #130, #245). Recommended fix: Option A — update binary to match schema (principled design).
This commit is contained in:
parent
69a15bd707
commit
5b9097a7ac
104
ROADMAP.md
104
ROADMAP.md
@ -8949,3 +8949,107 @@ Given the complexity, **better path forward:**
|
||||
|
||||
---
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Pinpoint #164. JSON envelope schema-vs-binary divergence: SCHEMAS.md specifies a different envelope shape than the binary actually emits
|
||||
|
||||
**Status: 📋 FILED (cycle #76, 2026-04-23 04:38 Seoul).**
|
||||
|
||||
**Surface.** The JSON envelope documented in SCHEMAS.md does NOT match what the binary actually emits.
|
||||
|
||||
**Example — SCHEMAS.md says:**
|
||||
```json
|
||||
{
|
||||
"timestamp": "2026-04-22T10:10:00Z",
|
||||
"command": "doctor",
|
||||
"exit_code": 1,
|
||||
"output_format": "json",
|
||||
"schema_version": "1.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Binary actually emits:**
|
||||
```json
|
||||
{
|
||||
"error": "unrecognized argument `foo` for subcommand `doctor`",
|
||||
"hint": "Run `claw --help` for usage.",
|
||||
"kind": "cli_parse",
|
||||
"type": "error"
|
||||
}
|
||||
```
|
||||
|
||||
**Divergences:**
|
||||
|
||||
1. **Missing required fields from schema:** `timestamp`, `command`, `exit_code`, `output_format`, `schema_version`
|
||||
2. **Wrong field placement:** Schema says `error.kind` (nested object), binary emits `kind` at top level
|
||||
3. **Extra undocumented field:** `type: "error"` is not in the schema
|
||||
4. **Wrong field type:** Schema says `error` should be an **object** with `operation/target/retryable/message/hint` nested. Binary emits `error` as a **string** (just the message)
|
||||
|
||||
**Additional issue (identified in cycle #76):**
|
||||
|
||||
The top-level `kind` field is **semantically overloaded** across success/error:
|
||||
- **Success envelopes:** `kind` = verb identity (`"kind": "doctor"`, `"kind": "status"`, etc.)
|
||||
- **Error envelopes:** `kind` = error classification (`"kind": "cli_parse"`, `"kind": "no_managed_sessions"`, etc.)
|
||||
|
||||
A consumer cannot dispatch on `kind` alone; they must first check if `type == "error"` exists.
|
||||
|
||||
**Impact.** High for downstream claws:
|
||||
- Python/Node/Rust consumers writing typed deserializers will FAIL against the binary
|
||||
- Orchestrators can't reliably dispatch on envelope shape (schema lies about nested vs. flat)
|
||||
- Documentation is actively misleading — users implement against schema, get runtime errors
|
||||
- Breaks the "typed-error contract" family (§4.44 in ROADMAP) that's supposed to unlock programmatic error handling
|
||||
|
||||
**Root cause hypotheses:**
|
||||
|
||||
1. SCHEMAS.md was written aspirationally (as target design), not documented from actual binary behavior
|
||||
2. Binary was implemented before schema was locked, and they drifted
|
||||
3. Schema was updated post-hoc without binary changes
|
||||
|
||||
**Fix shape — Two options:**
|
||||
|
||||
**Option A: Update binary to match schema (breaking change for existing consumers)**
|
||||
- Add `timestamp`, `command`, `exit_code`, `output_format`, `schema_version` to all envelopes
|
||||
- Nest `error` fields under an `error` object
|
||||
- Remove the `type: "error"` field
|
||||
- Migrate `kind` semantics: top-level `kind` becomes verb identity; errors go under `error.kind`
|
||||
- Requires schema_version bump to "2.0"
|
||||
|
||||
**Option B: Update schema to match binary (documentation-only change)**
|
||||
- Document actual flat envelope: `error`, `hint`, `kind`, `type` at top level
|
||||
- Document semantic overloading of `kind` (verb-id vs. error-kind)
|
||||
- Remove references to `error.operation`, `error.target`, `error.retryable` from SCHEMAS.md
|
||||
|
||||
**Recommendation:** **Option A** (binary to match schema), because:
|
||||
- Schema design is more principled (nested error object is cleaner)
|
||||
- `kind` overloading is bad typed-contract design
|
||||
- `timestamp/command/exit_code` are genuinely useful for orchestrators
|
||||
- Current state is fragile — changes are high-cost now but higher-cost later
|
||||
|
||||
**Acceptance criteria:**
|
||||
|
||||
1. Every command with `--output-format json` emits the envelope shape documented in SCHEMAS.md
|
||||
2. `kind` has one meaning (verb-id in success, or removed in favor of nested error.kind)
|
||||
3. All envelope fields present and correctly typed
|
||||
4. `cargo test` passes with new envelope contract
|
||||
5. Document `schema_version` bump in SCHEMAS.md changelog
|
||||
|
||||
**Dogfood session.** Cycle #76 probe on `/tmp/jobdori-161/rust/target/debug/claw` (latest main). Discovered via systematic JSON output testing across doctor, status, version, sandbox, export, resume verbs.
|
||||
|
||||
**Related:**
|
||||
- §4.44 Typed-error contract (this is an implementation gap in that contract)
|
||||
- Joins #102 + #121 + #127 + #129 + #130 + #245 cluster as 7th member of typed-error family
|
||||
- Violates documented schema at `SCHEMAS.md` lines 24-32 (common fields) and lines 45-65 (error envelope)
|
||||
|
||||
**Classification:** Typed-error family member (joins #102 + #121 + #127 + #129 + #130 + #245). Highest impact of the family because it affects EVERY command, not just a subset.
|
||||
|
||||
---
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user