mirror of
https://github.com/instructkr/claw-code.git
synced 2026-10-29 00:00:35 +08:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ca2d3c39a |
+28
-27
@@ -6429,39 +6429,40 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
|
||||
|
||||
450. **`prompt` emits `kind:"missing_credentials"` JSON on STDERR (not stdout), leaving stdout at 0 bytes — automation pattern `output=$(claw prompt hello --output-format json)` captures nothing on auth-absent failure; `doctor` correctly surfaces `auth.status:"warn"` with `api_key_present:false` but exposes no `prompt_ready:false` field that automation can check before invoking `prompt`** — dogfooded 2026-05-16 by Jobdori on `a35ee9a0` in response to Clawhip pinpoint nudge at `1505208225321062521`. Exact reproduction (isolated env, no creds, fresh git repo, HEAD `a35ee9a0`): `timeout 5 env -i HOME=$ISOLATED_HOME PATH=$PATH CLAW_CONFIG_HOME=$PROBE/.claw-cfg claw prompt hello --output-format json > stdout.txt 2> stderr.txt` → stdout = **0 bytes**, stderr = 195 bytes containing `{"error":"missing Anthropic credentials…","exit_code":1,"hint":null,"kind":"missing_credentials","type":"error"}`, exit code 1. Confirms Gaebal's `1505208553793781792` pinpoint that `prompt` timeout + zero bytes was the prior state — HEAD `a35ee9a0` now correctly exits 1 with `kind:"missing_credentials"` **but the envelope is still routed to stderr** (issue #447 class, same class as prior entries #422, #435). **Contrast with `doctor`:** `claw doctor --output-format json 2>/dev/null` succeeds to stdout with `checks[auth].status:"warn"`, `api_key_present:false`, `auth_token_present:false` — but the auth check has no `prompt_ready:false` field. Automation that gates on `doctor` before invoking `prompt` must re-derive readiness from `api_key_present && auth_token_present` — there is no single canonical boolean. **Three compound problems:** (a) **stdout-empty on `--output-format json` failure**: same class as #447; `prompt`'s error envelope goes to stderr, not stdout. The canonical automation idiom `if ! result=$(claw prompt "q" --output-format json); then echo "$result" | jq .kind; fi` sees `$result=""` on failure — the jq call gets nothing. All `--output-format json` error paths must route JSON to stdout per #447 contract; (b) **`doctor` missing `prompt_ready` field**: `doctor --output-format json` already knows auth is absent (`api_key_present:false`) but surfaces no derived `prompt_ready:bool` or `prompt_blocked_reason:string` field. Automation must infer readiness from `api_key_present || auth_token_present || legacy_*_present` — a 5-field OR across legacy fields that is fragile as auth mechanisms evolve. A single `prompt_ready:false` (with `prompt_blocked_reason:"auth_missing"`) inside the `auth` check would give downstream a stable contract; (c) **`claw prompt` with no auth does no preflight and fires straight at the API**: the preflight check that `doctor` runs (auth discovery) is not reused by `prompt` to emit a fast typed error before attempting the network call. Both Gaebal's pinpoint (prompt hanging silently on older HEAD) and the current behavior (prompt hitting auth gate after a brief API attempt) stem from the same root: prompt does not short-circuit at the point where `doctor` already knows auth is absent. If `doctor` can emit `kind:"doctor"` with `auth.status:"warn"` in ~20ms without a network call, `prompt` should emit `kind:"missing_credentials"` in the same window and output it to stdout. **Required fix shape:** (a) `prompt --output-format json` must write the `kind:"missing_credentials"` JSON envelope to **stdout**, not stderr — same fix as #447 for all error envelopes; (b) add `prompt_ready:bool` and `prompt_blocked_reason:string|null` to the `auth` check in `doctor --output-format json`; derive it as `api_key_present || auth_token_present || legacy_saved_oauth_present`; (c) `prompt` must run the credential preflight check (same codepath as doctor's auth check) before attempting any API call and emit `{"kind":"missing_credentials","prompt_blocked_reason":"auth_missing"}` on **stdout** with exit 1 if the check fails; (d) `--output-format json` stdout routing fix must cover: `prompt`, `session list` (cross-ref #449), `skills uninstall` (cross-ref #431), `resume` (cross-ref #435), `acp serve` (cross-ref #443) — the full `kind:"missing_credentials"` class; (e) regression test: `claw prompt hello --output-format json` with no creds writes JSON to stdout (0 bytes stderr), exits 1, `kind:"missing_credentials"`, in under 200ms (no network attempt). **Why this matters:** `prompt` is the primary consumer entry point. Auth-absent failure routing to stderr breaks every automation wrapper that captures `$(claw prompt ... --output-format json)`. The `doctor` preflight metadata gap means auth-readiness checks require parsing 5 legacy fields instead of reading one boolean. Cross-references #447 (all JSON error envelopes on stderr), #449 (session list hits auth gate), #431 (skills uninstall hits auth gate), #357 (auth gate on local ops cluster), #422 (exit-code parity). Source: Jobdori live dogfood, `a35ee9a0`, 2026-05-16.
|
||||
|
||||
682. **Unsupported native-agent mutation verbs (`claw agents add/remove/enable`) return generic help JSON with `exit=0` instead of a typed unsupported/not-implemented error, so automation can treat a failed staffing/control-plane mutation as success** — dogfooded 2026-05-24 for the 20:30 Clawhip nudge at message `1508205480818774086`, reproduced on local `./rust/target/debug/claw` `git_sha 003b739d` (origin/main `f8e1bb72`) in a clean isolated env. This was found by carrying forward #681's “help + unexpected + exit 0” stealth-success pattern from `mcp` to sibling local route helpers. Number intentionally follows #681 and avoids Jobdori-announced #680.
|
||||
|
||||
Reproduction:
|
||||
462. **`claw version --output-format json` envelope is missing the `build_date` structured field even though all four other fields the human prose displays ARE structured (`version`, `git_sha`, `target`, `kind`) AND ROADMAP #79's "contrast" claim at line 1380 explicitly cites version as exemplary by listing `{kind, message, version, git_sha, target, build_date}` — the documentation says "structured", the code emits four fields out of five, and the string `"build_date"` literally does not appear anywhere in `rust/crates/rusty-claude-cli/` (zero hits across src/ and tests/). This is both a missing-field bug AND a 40-day-old ROADMAP self-contradiction: the entry filed 2026-04-17 to argue init should match version's structure cited a field that was never there** — dogfooded 2026-05-24 for the 13:00 Clawhip pinpoint nudge at message `1508092230261539039`, reproduced on local `./rust/target/debug/claw` `git_sha 003b739d` (origin/main `f8e1bb72`). Live envelope from clean isolated env (`HOME=/tmp/iso16/home`, fresh `/tmp/iso16/proj`):
|
||||
|
||||
```bash
|
||||
$ env -i HOME=/tmp/iso30/home PATH=/usr/bin:/bin TERM=dumb \
|
||||
claw agents add demo -- /bin/echo hi --output-format json
|
||||
$ env -i HOME=/tmp/iso16/home PATH=/usr/bin:/bin TERM=dumb claw version --output-format json
|
||||
{
|
||||
"action": "help",
|
||||
"kind": "agents",
|
||||
"unexpected": "add demo -- /bin/echo hi",
|
||||
"usage": {
|
||||
"direct_cli": "claw agents [list|help]",
|
||||
"slash_command": "/agents [list|help]",
|
||||
"sources": [".claw/agents", "~/.claw/agents", "$CLAW_CONFIG_HOME/agents"]
|
||||
}
|
||||
"git_sha": "003b739d",
|
||||
"kind": "version",
|
||||
"message": "Claw Code\n Version 0.1.0\n Git SHA 003b739d\n Target x86_64-unknown-linux-gnu\n Build date 2026-05-04",
|
||||
"target": "x86_64-unknown-linux-gnu",
|
||||
"version": "0.1.0"
|
||||
}
|
||||
# exit 0, stderr empty, no agent created/registered
|
||||
```
|
||||
|
||||
Same shape for:
|
||||
Field parity audit:
|
||||
|
||||
```bash
|
||||
claw agents remove demo --output-format json
|
||||
claw agents enable demo --output-format json
|
||||
| Field shown in `message` prose | Structured envelope field | Source constant |
|
||||
|---|---|---|
|
||||
| `Version 0.1.0` | ✅ `version` | `VERSION = env!("CARGO_PKG_VERSION")` |
|
||||
| `Git SHA 003b739d` | ✅ `git_sha` | `GIT_SHA = option_env!("GIT_SHA")` |
|
||||
| `Target x86_64-unknown-linux-gnu` | ✅ `target` | `BUILD_TARGET = option_env!("TARGET")` |
|
||||
| `Build date 2026-05-04` | ❌ **MISSING** | `DEFAULT_DATE = option_env!("BUILD_DATE")` exists |
|
||||
|
||||
**Root cause (traced):** `rust/crates/rusty-claude-cli/src/main.rs:2629-2637`:
|
||||
|
||||
```rust
|
||||
fn version_json_value() -> serde_json::Value {
|
||||
json!({
|
||||
"kind": "version",
|
||||
"message": render_version_report(),
|
||||
"version": VERSION,
|
||||
"git_sha": GIT_SHA,
|
||||
"target": BUILD_TARGET,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
All return `exit=0`, `kind:"agents"`, `action:"help"`, `unexpected:"<verb ...>"` and no stderr. The supported contract in the returned help object is only `list|help`, so these mutation verbs are unsupported by contract, but the command-level outcome is success.
|
||||
|
||||
**Root cause shape:** the agents command parser shares the same “unknown tail becomes help with `unexpected`” pattern as MCP. It preserves the unrecognized verb in a decorative help field but does not set an error status, code, nonzero exit, or structured `unsupported_action` metadata. The result is neither a successful mutation nor a machine-classifiable failure.
|
||||
|
||||
**Why distinct from existing items:** ROADMAP #328 covers agent source-root/provenance mismatch. #329 covers resume-safe slash `/agents` flattening structured inventory to prose. #346 covers natural `agents show <name>` detail inspection collapsing to help-success. This entry covers unsupported *mutation/control-plane verbs* (`add/remove/enable`) returning success, which matters for setup/staffing automation and not just detail lookup. #681 covers the same failure class for `mcp`; #682 documents the native-agent subsystem's separate route and required regression coverage.
|
||||
|
||||
**Why this matters:** (1) **Automation false success.** A bootstrap script can run `claw agents add reviewer ...` and continue because exit code is 0, even though no agent exists. (2) **Staffing state becomes unverifiable.** Native-agent availability controls delegation; a failed add/enable/remove must not look like successful help. (3) **Mutation verbs are natural expectations.** Users will try `agents add/remove/enable` after seeing agent inventory, and the CLI should fail closed if those mutations are intentionally unsupported. (4) **`unexpected` is not an error contract.** Claws should not have to infer failure from an optional field inside an otherwise successful help object. (5) **The bug is reusable.** #681 proved the pattern in MCP; this sibling route shows the parser/helper abstraction likely needs a shared unsupported-subaction contract across local route helpers.
|
||||
|
||||
**Required fix shape:** (a) Unsupported agents sub-actions should return a typed JSON error or explicit non-ok status such as `{type:"error", kind:"unsupported_agents_action", requested_action:"add", supported_actions:["list","help"], hint:"Native-agent mutation commands are not implemented; add agent files under a documented agents root or use ..."}` and exit non-zero. (b) Keep help fallback only for explicit `agents help` / `agents --help`; attempted mutations must not be reported as successful help. (c) If `add/remove/enable` are planned features, return `not_implemented` with nonzero exit and no file writes until the write-target/source-layer semantics exist. (d) Add parser/output tests for `agents add`, `agents remove`, and `agents enable` proving they are distinguishable from successful help and successful list. (e) Consider a shared helper for local route families (`agents`, `mcp`, maybe `skills`/`plugins`) so `unexpected` can never be the sole machine signal for unsupported actions. **Acceptance check:** `claw agents add demo -- /bin/echo hi --output-format json >/tmp/out 2>/tmp/err; test $? -ne 0 && jq -e '.kind == "unsupported_agents_action" and .requested_action == "add"' /tmp/err` should pass; currently exit is 0 and stdout is a help object. Source: gaebal-gajae dogfood for the 2026-05-24 20:30 Clawhip nudge. Coordination note: avoided Jobdori #680/session-sort, F/CLAW_CONFIG_HOME, already-covered MCP items, and prior agent items #328/#329/#346; targeted mutation semantics after route-sibling probe.
|
||||
The function consumes the prose via `render_version_report()` for `message`, then re-extracts three of the four structured values from compile-time constants — but skips `DEFAULT_DATE` (the BUILD_DATE constant defined at `main.rs:159-162`). The fourth structured field is just missing from the json! macro. One-line fix: add `"build_date": DEFAULT_DATE,`. Verification: `grep -rnE '"build_date"|build_date' rust/crates/rusty-claude-cli/{src,tests}/ --include='*.rs'` returns **zero hits** — the field name has never appeared in either source or test code. **ROADMAP self-contradiction:** ROADMAP entry #79 at line 1380 contrasts `init`'s prose-only envelope against `version` as exemplary: `"claw --output-format json version → {kind, message, version, git_sha, target, build_date} — structured."` This claim was filed 2026-04-17 and has been wrong ever since — the actual envelope is `{kind, message, version, git_sha, target}` (5 fields, not 6). The contrast argument for #79 stands because version IS more structured than init, but the specific field list is incorrect. The pre-grep gate caught this near-miss: I almost wrote up "version envelope missing build_date" as a brand-new pinpoint, then grepped ROADMAP for `build_date.*version`, found line 1380, realized it was a documentation-vs-implementation drift instead of a brand-new symptom. **Why distinct from existing items:** ROADMAP #324 covers the broader binary-provenance freshness problem (compare embedded git_sha vs workspace HEAD, emit `stale_binary` boolean) across `version`/`status`/`doctor`. #324 mentions `binary_provenance`/`workspace_head`/`stale_binary` as proposed structured fields but does NOT itemize the existing-field gap on `build_date`. #79 cites version as exemplary structured (the contrast direction) but never re-verified the cited field set. #83 covers BUILD_DATE leaking into the system prompt as "today's date" (the wrong-place-for-build-date problem). **None** document the simple fact that `version`'s ONE legitimate place to display build_date (the `version` JSON envelope) just doesn't expose it as a structured field. **Why this matters:** (1) **Bug reports and CI fingerprinting need machine-readable build_date.** A claw producing a bug report wants `{"version": "0.1.0", "git_sha": "003b739d", "build_date": "2026-05-04"}` as a self-describing provenance triple. Today it must regex `/Build date\s+(\S+)/` against the `message` prose — the same anti-pattern ROADMAP #79 was filed against for `init`. (2) **The fix is one line.** `"build_date": DEFAULT_DATE,` in `version_json_value()`. There is no architectural cost, no breakage risk, no behavior change beyond adding the field. (3) **Self-fulfilling documentation drift:** future ROADMAP entries (#324 already does this) cite the `version` envelope as the reference shape for binary-provenance JSON. Each new entry that propagates the wrong field list makes the drift harder to detect because the documented shape now disagrees with itself across multiple ROADMAP entries. Catching this at #462 prevents further drift accumulation. (4) **Cross-envelope inconsistency:** if `version` had `build_date` as a structured field, `status`/`doctor` could legitimately reference it as a precedent for #324's `binary_provenance` work. Without it, every related entry must re-justify the structured-field principle. (5) **Aligns with #459 (memory_files[] absent), #455 (missing_credentials hint shape), #79 (init artifacts[]), #326 (status panes[])** — the pattern is: a prose surface emits a value derived from an in-process structured source, but the JSON envelope discards the structure. Per-entry one-line fixes accumulate; together they define a "structured-envelope completeness" doctrine. **Required fix shape:** (a) **At `main.rs:2636`**, add `"build_date": DEFAULT_DATE,` to the `version_json_value()` json! macro. (b) **Fix ROADMAP #79 line 1380** in the same commit: keep the contrast point (version is more structured than init) but make the field-list accurate. (c) **Regression coverage:** add an `output_format_contract.rs` assertion that `claw version --output-format json | jq -e '.build_date'` returns a non-null string matching `/^\d{4}-\d{2}-\d{2}$|^unknown$/`. (d) **Optional, related to #324:** while in this file, consider adding `build_date` to `doctor` and `status` envelopes so binary-provenance fields cluster naturally for the #324 work. (e) **Optional, related to #83:** add a `today_date` field that uses `chrono::Utc::today()` so the difference between BUILD_DATE and runtime today is observable from one envelope alone. **Acceptance check (one-liner):** `claw version --output-format json | jq -e '.build_date | type == "string"'` should print `true` (currently returns `null` → exit 1). Source: gaebal-gajae dogfood follow-up for the 2026-05-24 13:00 Clawhip pinpoint nudge at message `1508092230261539039`. Triple-grep gate caught one near-miss on ROADMAP #83 (BUILD_DATE in system prompt — same root coLine truncated
|
||||
Reference in new issue
Block a user