You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ROADMAP ultraworkers#121: hooks schema incompatible with Claude Code; error message misleading; doctor JSON emits 2 objects on failure breaking single-doc parsing; doctor has duplicate message+report fields
Dogfooded 2026-04-18 on main HEAD b81e642 from /tmp/cdWW.
Four related findings in one:
1. hooks schema incompatible with Claude Code (primary):
claw-code: {'hooks':{'PreToolUse':['cmd1','cmd2']}}
Claude Code: {'hooks':{'PreToolUse':[
{'matcher':'Bash','hooks':[{'type':'command','command':'...'}]}
]}}
Flat string array vs matcher-keyed object array. Incompatible.
User copying .claude.json hooks to .claw.json hits parse-fail.
2. Error message misleading:
'field hooks.PreToolUse must be an array of strings, got an array'
Both input and expected are arrays. Correct diagnosis:
'got an array of objects where array of strings expected'
3. Missing Claude Code hook event types:
claw-code supports: PreToolUse, PostToolUse, PostToolUseFailure
Claude Code supports: above + UserPromptSubmit, Notification,
Stop, SubagentStop, PreCompact, SessionStart
5+ event types missing.
matcher regex not supported.
type: 'command' vs type: 'http' extensibility not supported.
4. doctor NDJSON output on failures:
With failures present, --output-format json emits TWO
concatenated JSON objects on stdout:
Object 1: {kind:'doctor', has_failures:true, ...}
Object 2: {type:'error', error:'doctor found failing checks'}
python json.load() fails: 'Extra data: line 133 column 1'
Flag name 'json' violated — NDJSON is not JSON.
5. doctor message + report byte-duplicated:
.message and .report top-level fields have identical prose
content. Parser ambiguity + byte waste.
Trace:
config.rs:750-771 parse_optional_hooks_config_object:
optional_string_array(hooks, 'PreToolUse', context)
Expects ['cmd1', 'cmd2']. Claude Code gives
[{matcher,hooks:[{type,command}]}]. Schema-incompatible.
config.rs:775-779 validate_optional_hooks_config:
calls same parser. Error bubbles up.
Message comes from optional_string_array path —
technically correct but misleading.
Fix shape (~200 lines + migration docs):
- Dual-schema hooks parser: accept native + Claude Code forms
- Add missing event types to RuntimeHookConfig
- Implement matcher regex
- Fix error message to distinguish array-element types
- Fix doctor: single JSON object regardless of failure state
- De-duplicate message + report (keep report, drop message)
- Regression per schema form + event type + matcher
Joins Claude Code migration parity (ultraworkers#103, ultraworkers#109, ultraworkers#116, ultraworkers#117,
ultraworkers#119, ultraworkers#120) as 7th — most severe parity break since hooks is
load-bearing automation infrastructure.
Joins Truth-audit on misleading error message.
Joins Silent-flag on --output-format json emitting NDJSON.
Cross-cluster with Unplumbed-subsystem (ultraworkers#78, ultraworkers#96, ultraworkers#100, ultraworkers#102,
ultraworkers#103, ultraworkers#107, ultraworkers#109, ultraworkers#111, ultraworkers#113) — hooks subsystem exists but
schema incompatible with reference implementation.
Natural bundles:
Claude Code migration parity septet (grown flagship):
ultraworkers#103 + ultraworkers#109 + ultraworkers#116 + ultraworkers#117 + ultraworkers#119 + ultraworkers#120 + ultraworkers#121
Complete coverage of every migration failure mode.
ultraworkers#107 + ultraworkers#121 — hooks-subsystem pair:
ultraworkers#107 hooks invisible to JSON diagnostics
ultraworkers#121 hooks schema incompatible with migration source
Filed in response to Clawhip pinpoint nudge 1494963222157983774
in #clawcode-building-in-public.
**Blocker.** Policy decisions (strict vs JSON5; alias table meanings; fallback mode when config drop happens) overlap with #86 + #87 + #115 + #116 decisions. Resolving all five together as a "permission-posture-plus-config-parsing audit" would be efficient.
4085
4085
4086
4086
**Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdVV` on main HEAD `7859222` in response to Clawhip pinpoint nudge at `1494955670791913508`. Extends #86 (silent-drop) with novel JSON5-partial-acceptance angle + alias-collapse security inversion. Joins **Permission-audit / tool-allow-list** (#94, #97, #101, #106, #115) as 6th member — this is the CONFIG-PARSE anchor of the permission-posture problem, completing the matrix: #87 absence (no config), #101 env-var fail-OPEN, #115 init-generated dangerous default, **#120** config-drops-to-dangerous-default. Joins **Truth-audit / diagnostic-integrity** on the `loaded_config_files=0` + `permission_mode=danger-full-access` inconsistency. Joins **Reporting-surface / config-hygiene** (#90, #91, #92, #110, #115, #116) on the silent-drop-plus-no-stderr-plus-exit-0 axis. Joins **Claude Code migration parity** (#103, #109, #116, #117, #119) as 6th — claw-code is strict-where-Claude-was-lax (#116) AND lax-where-Claude-was-strict (#120). Natural bundle: **#86 + #120** — config-parse reliability pair: silent-drop general case (#86) + JSON5-partial-acceptance + alias-inversion security flip (#120). Also **permission-drift-at-every-boundary 4-way**: #87 + #101 + #115 + **#120** — absence + env-var + init-generated + config-drop. Complete coverage of how a workspace can end up at `DangerFullAccess`. Also **Jobdori+gaebal-gajae mega-bundle** ("security-critical permission drift audit"): #86 + #87 + #101 + #115 + #116 + **#120** (five-way sweep of every path to wrong permissions). Session tally: ROADMAP #120.
4087
+
4088
+
121. **`hooks` configuration schema is INCOMPATIBLE with Claude Code. claw-code expects `{"hooks": {"PreToolUse": [<command-string>, ...]}}` — a flat array of command strings. Claude Code's schema is `{"hooks": {"PreToolUse": [{"matcher": "<tool-name>", "hooks": [{"type": "command", "command": "..."}]}]}}` — a matcher-keyed array of objects with nested command arrays. A user migrating their Claude Code `.claude.json` hooks block gets parse-fail: `field "hooks.PreToolUse" must be an array of strings, got an array (line 3)`. The error message is ALSO wrong — both schemas use arrays; the correct diagnosis is "array-of-objects where array-of-strings was expected." Separately, `claw --output-format json doctor` when failures present emits TWO concatenated JSON objects on stdout (`{kind:"doctor",...}` then `{type:"error",error:"doctor found failing checks"}`), breaking single-document parsing for any claw that does `json.load(stdout)`. Doctor output also has both `message` and `report` top-level fields containing identical prose — byte-duplicated** — dogfooded 2026-04-18 on main HEAD `b81e642` from `/tmp/cdWW`.
{"error":"runtime config failed to load: /private/tmp/cdWW/.claw/settings.json: field \"hooks.PreToolUse\" must be an array of strings, got an array (line 3)","type":"error"}
4110
+
# Error message: "must be an array of strings, got an array" — both are arrays.
4111
+
# Correct diagnosis: "got an array of objects where an array of strings was expected."
4112
+
4113
+
# claw-code's own expected format (flat string array):
- `rust/crates/runtime/src/config.rs:775-779` — `validate_optional_hooks_config` calls the same parser; the error message "must be an array of strings" comes from `optional_string_array`'s path — but the user's actual input WAS an array (of objects). The message is technically correct but misleading.
- `matcher` regex per hook (e.g. `"Bash|Write|Edit"`) — not supported.
4176
+
- `type: "command"` vs `type: "http"` etc. (Claude Code extensibility) — not supported.
4177
+
- `rust/crates/rusty-claude-cli/src/main.rs` doctor path — builds `DoctorReport` struct, renders BOTH a prose report AND emits it in `message` + `report` JSON fields. When failures present, appends a second `{"type":"error","error":"doctor found failing checks"}` to stdout.
4178
+
4179
+
**Why this is specifically a clawability gap.**
4180
+
1. *Claude Code migration parity hard-block.* Users with existing `.claude.json` hooks cannot copy them over. Error message misleads them about what's wrong. No migration tool or adapter.
4181
+
2. *Feature gap: no matchers, no event types beyond 3.* PreToolUse/PostToolUse/PostToolUseFailure only. Missing Notification, UserPromptSubmit, Stop, SubagentStop, PreCompact, SessionStart — all of which are documented Claude Code capabilities claws rely on.
4182
+
3. *Error message lies about what's wrong.* "Must be an array of strings, got an array" — both are arrays. The correct message would be "expected an array of command strings, got an array of objects (Claude Code hooks format is not supported; see migration docs)."
4183
+
4. *Doctor NDJSON output breaks JSON consumers.* `--output-format json` promises a single JSON document per the flag name. Getting NDJSON (or rather: concatenated JSON objects without line separators) breaks every `json.load(stdout)` style consumer.
4184
+
5. *Byte-duplicated prose in `message` + `report`.* Two top-level fields with identical content. Parser ambiguity (which is the canonical source?). Byte waste.
4185
+
6. *Joins Claude Code migration parity* (#103, #109, #116, #117, #119, #120) as 7th member — hooks is the most load-bearing Claude Code feature that doesn't work. Users who rely on hooks for workflow automation (log-tool-calls.sh, format-on-edit.sh, require-bash-approval.sh) cannot migrate.
4186
+
7. *Joins truth-audit* — the diagnostic surface lies with a misleading error message.
4187
+
8. *Joins silent-flag / documented-but-unenforced* — `--output-format json` says "json" not "ndjson"; violation of the flag's own semantics.
4188
+
4189
+
**Fix shape — extend the hooks schema to accept Claude Code format.**
4190
+
1. *Dual-schema hooks parser.* Accept either form:
4191
+
- claw-code native: `["cmd1", "cmd2"]`
4192
+
- Claude Code: `[{"matcher": "pattern", "hooks": [{"type": "command", "command": "..."}]}]`
4193
+
Translate both to the internal `RuntimeHookConfig` representation. ~80 lines.
4194
+
2. *Add the missing event types.* Extend `RuntimeHookConfig` to include `UserPromptSubmit`, `Notification`, `Stop`, `SubagentStop`, `PreCompact`, `SessionStart`. ~50 lines.
4195
+
3. *Implement matcher regex.* When a Claude Code-format hook includes `"matcher": "Bash|Write"`, apply the regex against the tool name before firing the hook. ~30 lines.
4196
+
4. *Fix the error message.* Change "must be an array of strings" to "expected an array of command strings. Claude Code hooks format (matcher + typed commands) is not yet supported — see ROADMAP #121 for migration path." ~10 lines.
4197
+
5. *Fix doctor NDJSON output.* Emit a single JSON object with `has_failures: true` + `error: "..."` fields rather than concatenating a separate error object. ~15 lines.
4198
+
6. *De-duplicate `message` and `report`.* Pick one (`report` is more descriptive for a doctor JSON surface); drop `message`. ~5 lines.
4199
+
7. *Regression tests.* (a) Claude Code hooks format parses and runs. (b) Native-format hooks still work. (c) Matcher regex matches correct tools. (d) All 8 event types dispatch. (e) Doctor failure emits single JSON object. (f) Doctor JSON has no duplicated fields.
4200
+
4201
+
**Acceptance.** A user's `.claude.json` hooks block works verbatim as `.claw.json` hooks. Error messages correctly distinguish "wrong type for array elements" from "wrong element structure." `claw --output-format json doctor` emits exactly ONE JSON document regardless of failure state. No duplicated fields.
4202
+
4203
+
**Blocker.** Implementation work is sizable (~200 lines + tests + migration docs). Product decision needed: full Claude Code hooks compatibility as a goal, or subset-plus-adapter. The current schema is claw-code-native; Claude Code compat requires either extending or replacing.
4204
+
4205
+
**Source.** Jobdori dogfood 2026-04-18 against `/tmp/cdWW` on main HEAD `b81e642` in response to Clawhip pinpoint nudge at `1494963222157983774`. Joins **Claude Code migration parity** (#103, #109, #116, #117, #119, #120) as 7th member — the most severe parity break since hooks is load-bearing automation infrastructure. Joins **Truth-audit / diagnostic-integrity** on misleading error message axis. Joins **Silent-flag / documented-but-unenforced** on NDJSON-output-violating-json-flag. Cross-cluster with **Unplumbed-subsystem** (#78, #96, #100, #102, #103, #107, #109, #111, #113) — hooks subsystem exists but schema is incompatible with the reference implementation. Natural bundle: **Claude Code migration parity septet (grown)**: #103 + #109 + #116 + #117 + #119 + #120 + **#121**. Complete coverage of every migration failure mode: silent drop (#103) + stderr prose warnings (#109) + hard-fail on unknown keys (#116) + prompt corruption from muscle memory (#117) + slash-verb fallthrough (#119) + JSON5-partial-accept + alias-inversion (#120) + hooks-schema-incompatible (#121). Also **#107 + #121** — hooks-subsystem pair: #107 hooks invisible to JSON diagnostics + #121 hooks schema incompatible with migration source. Also **NDJSON-violates-json-flag 2-way (new)**: #121 + probably more; worth sweep. Session tally: ROADMAP #121.
0 commit comments