Compare commits

..

1 Commits

View File

@@ -6290,9 +6290,5 @@ Original filing (2026-04-18): the session emitted `SessionStart hook (completed)
348. **Top-level `plugins list --output-format json` returns plugin inventory only as a prose `message` string instead of structured `plugins[]` entries** — dogfooded 2026-04-29 for the 21:00 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `cca6f682`. Running `./rust/target/debug/claw plugins list --output-format json` repeatedly returned valid stdout JSON with `{"action":"list","kind":"plugin","message":"Plugins\n example-bundled v0.1.0 disabled\n sample-hooks v0.1.0 disabled","reload_runtime":false,"target":null}` and no stderr. The actual plugin names, versions, and enabled/disabled states are present only inside the human-formatted `message` table; there is no `plugins[]` array, no per-plugin `name`, `version`, `enabled`, `source`, `load_error`, or lifecycle/action metadata. This is distinct from #325's broad help JSON opacity and the config/MCP/agent items: the affected surface is plugin lifecycle inventory, where automation needs a structured list before enabling, disabling, updating, or uninstalling plugins. **Required fix shape:** (a) add `plugins[]` with stable per-plugin fields such as `name`, `version`, `enabled`, `source`, `configured`, `load_status`, and optional `error`; (b) keep `message` only as a human summary, not the sole inventory payload; (c) expose counts and truncation metadata if the list can be large; (d) add regression coverage proving `plugins list --output-format json` can be parsed without scraping the prose message and that disabled/enabled state survives as booleans/enums. **Why this matters:** plugin lifecycle management is a control-plane path. If the JSON inventory is just a text table, claws must scrape spacing-sensitive prose before deciding whether a plugin is installed, disabled, broken, or safe to mutate. Source: gaebal-gajae dogfood follow-up for the 21:00 nudge on rebuilt `./rust/target/debug/claw` `cca6f682`.
349. **Top-level `plugins show <name> --output-format json` returns success-shaped JSON for an unsupported plugin action instead of a typed unsupported-action error** — dogfooded 2026-04-29 for the 21:30 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `a2a38df9`. After rebuilding and verifying the binary provenance, repeated bounded runs of `./rust/target/debug/claw plugins show does-not-exist --output-format json` returned stdout JSON with `{"action":"show","kind":"plugin","message":"Unknown /plugins action 'show'. Use list, install, enable, disable, uninstall, or update.","reload_runtime":false,"target":"does-not-exist"}` and no stderr. The command therefore reports the requested unsupported action as the top-level `action:"show"` and exits successfully while hiding the failure class inside a human `message`; it does not provide `status:"unsupported_action"`, `code:"plugin_action_unsupported"`, or structured `supported_actions[]`. This is distinct from #348's prose-only plugin inventory schema: #348 covers `plugins list` payload shape, while this pinpoint covers unsupported plugin action classification and recovery metadata. **Required fix shape:** (a) return a typed stdout JSON error or explicit non-ok status for unsupported plugin actions, with `requested_action`, `supported_actions`, and `target` fields; (b) do not label the primary `action` as the unsupported requested verb unless a separate `status`/`code` makes the failure unambiguous; (c) keep the human message optional and avoid making it the only way to detect the unsupported action; (d) add regression coverage proving `plugins show foo --output-format json` is machine-classifiable as unsupported without scraping prose. **Why this matters:** plugin lifecycle automation follows action/status fields. If an unsupported mutation/inspection verb returns success-shaped JSON and only says "Unknown" in prose, claws can treat a failed preflight as a valid plugin show result and continue toward unsafe lifecycle actions. Source: gaebal-gajae dogfood follow-up for the 21:30 nudge on rebuilt `./rust/target/debug/claw` `a2a38df9`; invalid hang PR #2885 was closed after repeated bounded repros returned stdout JSON.
350. **Top-level `plugins enable <missing-plugin> --output-format json` hangs with zero stdout/stderr instead of returning a typed plugin-not-found or unsupported-target response** — dogfooded 2026-04-29 for the 22:00 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `ee44ff98`. After rebuilding and verifying the binary provenance, repeated bounded runs of `timeout 8 ./rust/target/debug/claw plugins enable does-not-exist --output-format json` exited `124` with `stdout=0` and `stderr=0`; a third sample was still stuck until killed. In the same rebuilt binary, `plugins list --output-format json` returned promptly with the known plugin inventory payload, proving the plugin top-level surface is reachable and narrowing the hang to missing-plugin lifecycle mutation. This is distinct from #348's prose-only list inventory and #349's unsupported `plugins show` success-shaped JSON: #350 covers a supported lifecycle verb (`enable`) against an absent target, where the CLI should be able to fail fast before any plugin runtime work. **Required fix shape:** (a) validate the target plugin against the discovered/configured inventory before invoking enable-side effects; (b) return bounded stdout JSON such as `kind:"plugin"`, `action:"enable"`, `status:"not_found"` or `kind:"error"`, `code:"plugin_not_found"`, `plugin`, and optional `available_plugins[]`; (c) add internal timeout/diagnostic metadata for plugin lifecycle operations so registry or hook stalls do not produce silent zero-byte hangs; (d) add regression coverage proving `plugins enable does-not-exist --output-format json` returns a typed JSON outcome within a deterministic budget and does not mutate plugin state. **Why this matters:** enable/disable/update/uninstall are destructive control-plane actions. A missing or stale plugin name must fail safely and machine-readably; otherwise claws cannot preflight plugin lifecycle operations, distinguish typo from loader deadlock, or recover without killing a hung process. Source: gaebal-gajae dogfood follow-up for the 22:00 nudge on rebuilt `./rust/target/debug/claw` `ee44ff98`.
351. **Top-level `plugins disable <missing-plugin> --output-format json` sends the JSON error envelope to stderr only, leaving stdout empty** — dogfooded 2026-04-29 for the 22:30 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `0f9e8915`. After rebuilding and verifying the binary provenance, repeated bounded runs of `timeout 8 ./rust/target/debug/claw plugins disable does-not-exist --output-format json` exited `1` with `stdout=0` and `stderr=113`; stderr contained JSON (`{"error":"plugin `does-not-exist` is not installed or discoverable","hint":null,"kind":"unknown","type":"error"}`), but stdout was empty. In the same rebuilt binary, `plugins list --output-format json` returned stdout JSON promptly with the known plugin inventory payload, proving the plugin command surface is reachable. This is distinct from #350's missing-target `plugins enable` zero-byte timeout: the disable path fails fast, but its JSON-mode error envelope is routed to stderr and uses generic `kind:"unknown"`/`type:"error"` instead of a plugin-specific stdout outcome. **Required fix shape:** (a) define and consistently document whether JSON mode emits machine-readable envelopes on stdout, stderr, or both for nonzero exits; (b) return a plugin-specific typed error with `kind:"plugin"` or `domain:"plugin"`, `action:"disable"`, `status:"not_found"` or `code:"plugin_not_found"`, `plugin`, and optional `available_plugins[]`; (c) keep stdout/stderr placement consistent across plugin lifecycle verbs so callers do not need per-action stream heuristics; (d) add regression coverage proving `plugins disable does-not-exist --output-format json` produces a typed plugin-not-found JSON contract on the documented stream. **Why this matters:** disable is a recovery/control-plane operation. A stale plugin name should be a structured, domain-specific not-found result on a predictable stream; otherwise claws that read stdout JSON for normal responses and stderr for human diagnostics must special-case this lifecycle failure. Source: gaebal-gajae dogfood follow-up for the 22:30 nudge on rebuilt `./rust/target/debug/claw` `0f9e8915`; invalid hang PR #2891 was closed after repeated bounded repros returned exit 1 with JSON on stderr.
352. **Top-level `plugins update <missing-plugin> --output-format json` sends a generic JSON error envelope to stderr only, leaving stdout empty** — dogfooded 2026-04-29 for the 23:00 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `5eb1d7d8`. After rebuilding and verifying the binary provenance, repeated bounded runs of `timeout 8 ./rust/target/debug/claw plugins update does-not-exist --output-format json` exited `1` with `stdout=0` and `stderr=97`; stderr contained JSON (`{"error":"plugin `does-not-exist` is not installed","hint":null,"kind":"unknown","type":"error"}`), but stdout was empty. In the same rebuilt binary, `plugins list --output-format json` returned stdout JSON promptly with the known plugin inventory payload. This is distinct from #350's missing-target `plugins enable` zero-byte timeout and parallel to #351's `plugins disable` stderr-only JSON envelope: update fails fast, but the JSON-mode error lives on stderr only and uses generic `kind:"unknown"`/`type:"error"` instead of a plugin-specific not-found contract. **Required fix shape:** (a) define and consistently document stdout/stderr placement for JSON-mode lifecycle errors; (b) return a plugin-specific typed error with `kind:"plugin"` or `domain:"plugin"`, `action:"update"`, `status:"not_found"` or `code:"plugin_not_found"`, `plugin`, and optional `available_plugins[]`; (c) share missing-target error-envelope behavior across disable/update/uninstall and reconcile it with enable's timeout path; (d) add regression coverage proving `plugins update does-not-exist --output-format json` produces a typed plugin-not-found JSON contract on the documented stream. **Why this matters:** update is a maintenance/control-plane operation often run in automation. A stale plugin name should produce a predictable, domain-specific not-found result, not require callers to special-case stderr-only generic error envelopes after explicitly requesting JSON. Source: gaebal-gajae dogfood follow-up for the 23:00 nudge on rebuilt `./rust/target/debug/claw` `5eb1d7d8`; invalid hang PR #2894 was closed after repeated bounded repros returned exit 1 with JSON on stderr.
353. **Top-level `plugins uninstall <missing-plugin> --output-format json` sends a generic JSON error envelope to stderr only, leaving stdout empty** — dogfooded 2026-04-29 for the 23:30 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `6f92e54d`. After rebuilding and verifying the binary provenance, repeated bounded runs of `timeout 8 ./rust/target/debug/claw plugins uninstall does-not-exist --output-format json` exited `1` with `stdout=0` and `stderr=97`; stderr contained JSON (`{"error":"plugin `does-not-exist` is not installed","hint":null,"kind":"unknown","type":"error"}`), but stdout was empty. In the same rebuilt binary, `plugins list --output-format json` returned stdout JSON promptly with the known plugin inventory payload. This is distinct from #350's missing-target `plugins enable` zero-byte timeout and parallel to #351/#352 for disable/update: uninstall fails fast, but the JSON-mode error lives on stderr only and uses generic `kind:"unknown"`/`type:"error"` instead of a plugin-specific not-found contract. **Required fix shape:** (a) define and consistently document stdout/stderr placement for JSON-mode lifecycle errors; (b) return a plugin-specific typed error with `kind:"plugin"` or `domain:"plugin"`, `action:"uninstall"`, `status:"not_found"` or `code:"plugin_not_found"`, `plugin`, and optional `available_plugins[]`; (c) share missing-target error-envelope behavior across disable/update/uninstall and reconcile it with enable's timeout path; (d) add regression coverage proving `plugins uninstall does-not-exist --output-format json` produces a typed plugin-not-found JSON contract on the documented stream. **Why this matters:** uninstall is the most destructive plugin lifecycle action. A stale plugin name should produce a predictable, domain-specific not-found result before cleanup hooks or loader work, not require callers to special-case stderr-only generic error envelopes after explicitly requesting JSON. Source: gaebal-gajae dogfood follow-up for the 23:30 nudge on rebuilt `./rust/target/debug/claw` `6f92e54d`; invalid hang PR #2897 was closed after repeated bounded repros returned exit 1 with JSON on stderr.
354. **Top-level `memory list` and `memory help` with `--output-format json` hang with zero stdout/stderr instead of returning bounded memory inventory/help or a typed unavailable response** — dogfooded 2026-04-30 for the 00:00 nudge on current `origin/main` / rebuilt `./rust/target/debug/claw` with embedded `git_sha` `19947545`. After rebuilding and verifying the binary provenance, bounded runs of `timeout 8 ./rust/target/debug/claw memory list --output-format json` produced `stdout=0` and `stderr=0`; the first sample exited `124` and the second sample was still stuck until killed. A follow-up sanity check of `timeout 8 ./rust/target/debug/claw memory help --output-format json` also exited `124` with `stdout=0` and `stderr=0`, so the issue is broader than list inventory: even the memory help path can hang silently in JSON mode. This is distinct from prior plugin lifecycle stream/status items: the affected surface is memory command introspection, where claws need safe local help/inventory before reading or mutating memory. **Required fix shape:** (a) make `memory help` and `memory list --output-format json` return bounded local JSON without requiring external/authenticated backing store availability; (b) return stdout JSON with `kind:"memory"`, `action:"help"|"list"`, `status`, usage or `entries[]`, source/provenance, counts, and truncation metadata; (c) if credentials/config/backing store are missing or slow, return a typed JSON unavailable/config/timeout error instead of hanging; (d) add regression coverage proving both `memory help --output-format json` and `memory list --output-format json` return machine-readable outcomes within a deterministic budget. **Why this matters:** memory is a core clawability surface. If even help/list can hang silently with no bytes, agents cannot tell whether memory is empty, unavailable, remote-auth blocked, or deadlocked, and any higher-level recall/debug flow stalls at the first introspection step. Source: gaebal-gajae dogfood follow-up for the 00:00 nudge on rebuilt `./rust/target/debug/claw` `19947545`.
372. **`/status --output-format json` workspace object is missing `git_commit`, `git_remote_url`, and `git_ahead_behind` fields; only `git_branch` and `git_state` (a prose dirty-state string) are provided** — dogfooded 2026-04-30 by Jobdori on `8e24f304`. Running `/status --output-format json` with a git repo active returns workspace keys: `['changed_files','cwd','discovered_config_files','git_branch','git_state','git_state','loaded_config_files','memory_file_count','project_root','session','session_id','session_lifecycle','staged_files','unstaged_files','untracked_files']`. `git_commit` (current HEAD SHA), `git_remote_url`, and `git_ahead_behind` are absent. `git_state` is a prose string `"dirty · 4 files · 4 untracked"` — not structured. An orchestrator cannot determine: (a) which exact commit is checked out without a separate `git rev-parse HEAD` call; (b) whether the local branch is ahead/behind its upstream without a separate `git status -sb` call; (c) the remote repository URL without a separate `git remote get-url origin` call. All three are standard git workspace properties available without network access. **Required fix shape:** (a) add `git_commit: "<sha>"` (full or short); (b) add `git_remote_url: "<url>"` (or `null` if no remote configured); (c) add `git_ahead: <int>` and `git_behind: <int>` (or combined `git_ahead_behind: {ahead, behind}`) relative to the tracking branch; (d) split `git_state` prose into structured fields: `git_is_dirty: bool`, `git_changed_count: int`, `git_untracked_count: int`; (e) add regression coverage confirming `git_commit` is non-null in a git workspace. Source: Jobdori live dogfood, mengmotaHost, `8e24f304`, 2026-04-30.
365. **`/status --output-format json` `allowed_tools.entries: null` is ambiguous: callers cannot distinguish "all tools allowed" from "tool list not loaded / unavailable"** — dogfooded 2026-04-30 by Jobdori on `0f9e8915`. Running `/status --output-format json` returns `{"allowed_tools":{"entries":null,"restricted":false,"source":"default"}}`. The `entries` field is `null` rather than an empty array `[]` or an explicit "all" sentinel. Three possible interpretations are all consistent with `null`: (a) all tools are allowed (unrestricted); (b) the tool list was not loaded or is undefined; (c) the runtime could not enumerate available tools. The companion field `restricted: false` provides some indication but does not resolve whether `null` means "list is irrelevant" or "list is unknown". An orchestrator checking whether a specific tool is available cannot distinguish a null allowlist (all-pass) from a failed enumeration (unknown). **Required fix shape:** (a) when `restricted: false`, either set `entries: null` with a documented semantic of "all tools permitted" in the schema, or return an explicit `entries: []` for "no restrictions" with a separate `allow_all: true` flag; (b) add `has_tool_allowlist: bool` field to disambiguate; (c) if the tool list could not be loaded, emit `entries_load_error: "reason"` and set `entries: null` with `restricted: null` (unknown); (d) add regression coverage confirming the three states (all-allowed, restricted with list, and load-error) are distinguishable from `entries` and companion fields. Source: Jobdori live dogfood, mengmotaHost, `0f9e8915`, 2026-04-30.