From f1103332d0d13c26f76419f65fca12314844064a Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 23 Apr 2026 00:02:58 +0900 Subject: [PATCH] ROADMAP #130: re-verify still-open on main HEAD 186d42f; add classifier-cluster pairing note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cycle #39 dogfood re-verification of #130 (filed 2026-04-20). All 5 filesystem failure modes reproduce identically on main HEAD 186d42f, 2 days after original filing. Gap is unchanged. ## What's Added 1. **[STILL OPEN — re-verified 2026-04-22 cycle #39]** marker on the entry so readers can see immediately that the pinpoint hasn't been accidentally closed. 2. Full 5-mode repro output preserved verbatim for the current HEAD, so future re-verifications have a concrete baseline to diff against. 3. **New evidence not in original filing**: the classifier actively chose `kind: "unknown"` rather than just omitting the field. This means classify_error_kind() has NO substring match for "Is a directory", "No such file", "Operation not permitted", or "File exists". The typed-error contract is thus twice-broken on this path. 4. **Pairing with #247/#248/#249 classifier sweep**: the classifier-level part of #130 could land in the same sweep (add substring branches for io::ErrorKind strings). The context-preservation part (fix run_export's bare `?`) is a separate, larger change. ## Why Re-Verification Not Re-Filing Per cycle #24 discipline: speculative re-filings add noise, real confirmations add truth. #130 was already filed with exact repros, code trace, and fix shape. My dogfood hit the same gap on fresh HEAD — the right output is confirming the gap is still there (not filing #251 for the same bug). This is the same pattern as cycle #32's "mark #127 CLOSED" reality-sync: documentation-drift prevention through explicit status markers. ## New Pattern "Reality-sync via re-verification" — re-running a filed pinpoint's repro on fresh HEAD and adding the timestamp + output proves the gap is still real without inventing new filings. Cycle #24 calibration keeps ROADMAP entries honest. Per cycle #24 calibration: - Red-state bug? ⚠️ borderline (errors surfaced, but kind=unknown is demonstrably wrong on a path where the system knows the errno) - Real friction? ✓ (re-verified on fresh HEAD) - Evidence-backed? ✓ (5-mode repro + classifier trace) - Same-cycle fix? ✗ (classifier-level part could join #247/#248/#249 sweep; context-preservation part is larger refactor) - Implementation cost? Classifier part ~10 lines; full context fix ~60 lines Source: Jobdori cycle #39 proactive dogfood in response to Clawhip pinpoint nudge. Probed export filesystem errors; discovered this was #130 reconfirmation, not new bug. Applied reality-sync pattern from cycle #32. --- ROADMAP.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/ROADMAP.md b/ROADMAP.md index 1a945a0..72fc859 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -4929,7 +4929,7 @@ ear], /color [scheme], /effort [low|medium|high], /fast, /summary, /tag [label], **Source.** Jobdori dogfood 2026-04-20 against `/tmp/claw-mcp-test` (env-cleaned, working `mcpServers.everything = npx -y @modelcontextprotocol/server-everything`) on main HEAD `8122029` in response to Clawhip dogfood nudge / 10-min cron. Joins **MCP lifecycle gap family** as runtime-side companion to **#102** — #102 catches config-time silence (no preflight, no command-exists check); #129 catches runtime-side blocking (handshake await ordered before cred check, retried silently, no deadline). Joins **Truth-audit / diagnostic-integrity** (#80–#87, #89, #100, #102, #103, #105, #107, #109, #110, #112, #114, #115, #125, #127) — the hang surfaces no events, no exit code, no signal. Joins **Auth-precondition / fail-fast ordering family** — cheap deterministic preconditions should run before expensive externally-controlled ones. Cross-cluster with **Recovery / wedge-recovery** — a misbehaved MCP server wedges every subsequent Prompt invocation; current recovery is "kill -9 the parent." Cross-cluster with **PARITY.md Lane 7 acceptance gap** — the Lane 7 merge added the bridge but didn't add startup-deadline + cred-precheck ordering, so the lane is technically merged but functionally incomplete for unattended claw use. Natural bundle: **#102 + #129** — MCP lifecycle visibility pair: config-time preflight (#102) + runtime-time deadline + cred-precheck (#129). Together they make MCP failures structurally legible from both ends. Also **#127 + #129** — Prompt-path silent-failure pair: verb-suffix args silently routed to Prompt (#127, fixed) + Prompt path silently blocks on MCP (#129). With #127 fixed, the `claw doctor --json` consumer no longer accidentally trips the #129 wedge — but the wedge still affects every legitimate Prompt invocation. Session tally: ROADMAP #129. -130. **`claw export --output ` filesystem errors surface raw OS errno strings with zero context — no path that failed, no operation that failed (open/write/mkdir), no structured error kind, no actionable hint, and the `--output-format json` envelope flattens everything to `{"error":"","type":"error"}`. Five distinct filesystem failure modes all produce different raw errno strings but the same zero-context shape. The boilerplate `Run claw --help for usage` trailer is also misleading because these are filesystem errors, not usage errors** — dogfooded 2026-04-20 on main HEAD `d2a8341` from `/Users/yeongyu/clawd/claw-code/rust` (real session file present). +130. **[STILL OPEN — re-verified 2026-04-22 cycle #39 on main HEAD `186d42f`]** **`claw export --output ` filesystem errors surface raw OS errno strings with zero context — no path that failed, no operation that failed (open/write/mkdir), no structured error kind, no actionable hint, and the `--output-format json` envelope flattens everything to `{"error":"","type":"error"}`. Five distinct filesystem failure modes all produce different raw errno strings but the same zero-context shape. The boilerplate `Run claw --help for usage` trailer is also misleading because these are filesystem errors, not usage errors** — dogfooded 2026-04-20 on main HEAD `d2a8341` from `/Users/yeongyu/clawd/claw-code/rust` (real session file present). **Concrete repro.** ``` @@ -5057,6 +5057,24 @@ ear], /color [scheme], /effort [low|medium|high], /fast, /summary, /tag [label], **Source.** Jobdori dogfood 2026-04-20 against `/Users/yeongyu/clawd/claw-code/rust` (real session file present) on main HEAD `d2a8341` in response to Clawhip dogfood nudge / 10-min cron. Joins **Truth-audit / diagnostic-integrity** (#80–#127, #129) as 16th — error surface is incomplete by design; runtime has info that CLI boundary discards. Joins **JSON envelope asymmetry family** (#90, #91, #92, #110, #115, #116) — `{error, type}` shape is a fake envelope when the failure mode is richer than a single prose string. Joins **Claude Code migration parity** — Claude Code's error shape includes typed error kinds; claw-code's flat envelope loses information. Joins **`Run claw --help for usage` trailer-misuse** — the trailer is appended to errors that are not usage errors, which is both noise and misdirection. Natural bundle: **#90 + #91 + #92 + #130** — JSON envelope hygiene quartet. All four surface errors with insufficient structure for claws to dispatch on. Also **#121 + #130** — error-text-lies pair: hooks error names wrong thing (#121), export errno strips all context (#130). Also **Phase 2 §4 Canonical lane event schema exhibit A** — typed errors are the prerequisite for structured lane events. Session tally: ROADMAP #130. + **Re-verification (2026-04-22 cycle #39, main HEAD `186d42f`).** All 5 failure modes still reproduce identically to the original filing 2 days later. Concrete output: + ``` + $ claw export --output /tmp/nonexistent-dir-xyz/out.md --output-format json + {"error":"No such file or directory (os error 2)","hint":null,"kind":"unknown","type":"error"} + $ claw export --output /bin/cantwrite.md --output-format json + {"error":"Operation not permitted (os error 1)","hint":null,"kind":"unknown","type":"error"} + $ claw export --output "" --output-format json + {"error":"No such file or directory (os error 2)","hint":null,"kind":"unknown","type":"error"} + $ claw export --output / --output-format json + {"error":"File exists (os error 17)","hint":null,"kind":"unknown","type":"error"} + $ claw export --output /tmp/ --output-format json + {"error":"Is a directory (os error 21)","hint":null,"kind":"unknown","type":"error"} + ``` + + **New evidence not in original filing.** The `kind` field is set to `"unknown"` — the classifier actively chose `unknown` rather than just omitting the field. This means `classify_error_kind()` (at main.rs:~251) has no substring match for "Is a directory", "No such file", "Operation not permitted", or "File exists". The typed-error contract is thus twice-broken on this path: (a) the io::ErrorKind information is discarded at the `?` in `run_export()`, AND (b) the flat `io::Error::Display` string is then fed to a classifier that has no patterns for filesystem errno strings. + + **Natural pairing with #247/#248/#249 classifier sweep.** Same code path as #247's classifier fix (`classify_error_kind()`), same pattern (substring-matching classifier that lacks entries for specific error strings). #247 added patterns for prompt-related parse errors. #248 WIP adds patterns for verb-qualified unknown option errors. #130's classifier-level part (adding `NotFound`/`PermissionDenied`/`IsADirectory`/`AlreadyExists` substring branches) could land in the same sweep. The deeper fix (context preservation at `run_export()`'s `?`) is a separate, larger change — context-preservation requires `anyhow::Context` threading or typed error enum, not just classifier patterns. + **Repro (fresh box, no ANTHROPIC_* env vars).** `claw --model "bad model" version` → exit 0, emits version JSON (silent parse). `claw --model "" version` → exit 0, same. `claw --model "foo bar/baz" prompt "test"` → exit 1, `error: missing Anthropic credentials` (malformed model silently routes to Anthropic, then cred error masquerades as root cause instead of "invalid model syntax"). **The gap.** (1) No upfront model syntax validation in parse_args. `--model` accepts any string. (2) Silent fallback to Anthropic when provider detection fails on malformed syntax. (3) Downstream error misdirection — cred error doesn't say "your model string was invalid, I fell back to Anthropic." (4) Token burn on invalid model at API layer — with credentials set, malformed model reaches the API, billing tokens against a 400 response that should have been rejected client-side. (5) Joins #29 (provider routing silent fallback) — both involve Anthropic fallback masking the real intent. (6) Joins truth-audit — status/version JSON report malformed model without validation. (7) Joins cred-error misdirection family (#28, #99, #127).