diff --git a/README.md b/README.md index 6a20987..76a595a 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ the rules re-attached to every prompt. | [`spacecraft-chez-guidelines`](spacecraft-chez-guidelines/) | Functional, safe, concurrent Chez Scheme guidance — R6RS libraries + Akku, pure-first design, `optimize-level` as the safety lever (level 3 = `unsafe`), hand-built mailboxes/channels over real threads (no Fibers), the FFI + AOT/whole-program compilation, and Guile-habit guardrails. | | [`spacecraft-clang-guidelines`](spacecraft-clang-guidelines/) | Memory-safe highly-hardened C guidance — NASA Power of 10 Rules (no runtime heap allocation, bounded loops, small functions, high assertion density), MISRA C safety subsets, CERT C secure coding rules, Clang `-fbounds-safety` compiler extensions, and C11 atomics. | | [`spacecraft-cli-preference`](spacecraft-cli-preference/) | Modern CLI substitutions: `eza` for `ls`, `rg` for `grep`, `gitway` for Git SSH, etc. Conditional on the local host — substitute only when the tool is installed, fall back to the legacy tool with a note when it isn't, never launch a TUI in the agent's TTY-less shell, and get consent before anything that mutates or deletes. | -| [`spacecraft-cli-standard`](spacecraft-cli-standard/) | Enforces the Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.0.0) on every CLI. Presence-based agent-env detection (harnesses set descriptive strings, not `=1`), run-to-verify compliance, and no-clobber scaffolding on the user's own machine. | +| [`spacecraft-cli-standard`](spacecraft-cli-standard/) | Enforces the Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.1.0) on every CLI. Severity-tagged diagnostics (`[ERROR]`/`[WARN]`/`[OK]`/`[INFO]` + machine envelopes), presence-based agent-env detection (harnesses set descriptive strings, not `=1`), run-to-verify compliance, and no-clobber scaffolding on the user's own machine. | | [`spacecraft-cli-shell`](spacecraft-cli-shell/) | Syntax-compliance guard for Nushell / Ion / POSIX / Bash commands. Measures the host instead of guessing it, and targets whichever shell will actually execute the command — agent-run, handed to the user, or written to a file. | | [`spacecraft-clojure-guidelines`](spacecraft-clojure-guidelines/) | Functional, safe-concurrent Clojure guidance — immutable-first design, reference-type decision tree (atoms / refs+STM / agents / core.async), transducers, lazy-seq discipline, ClojureScript and Babashka platform notes, and `standard-clj` formatting. | | [`spacecraft-commonlisp-guidelines`](spacecraft-commonlisp-guidelines/) | Type-safe highly-concurrent Common Lisp guidance (targeting SBCL) — Bordeaux-Threads and `lparallel` pools, compare-and-swap (CAS) atomics, dynamic scope thread-local let-bindings, compile-time type declarations, safe FFI memory hygiene via `cffi:with-foreign-object`, and SBCL compiler optimization flags. | diff --git a/spacecraft-agentic-cli.skill b/spacecraft-agentic-cli.skill index ba6a00f..cc9c6ea 100644 Binary files a/spacecraft-agentic-cli.skill and b/spacecraft-agentic-cli.skill differ diff --git a/spacecraft-agentic-cli.zip b/spacecraft-agentic-cli.zip index 70d65e1..25bae4e 100644 Binary files a/spacecraft-agentic-cli.zip and b/spacecraft-agentic-cli.zip differ diff --git a/spacecraft-agentic-cli/SKILL.md b/spacecraft-agentic-cli/SKILL.md index 89b74f5..b0eaa20 100644 --- a/spacecraft-agentic-cli/SKILL.md +++ b/spacecraft-agentic-cli/SKILL.md @@ -145,6 +145,14 @@ including: Canonical hint strings for every standard error code live in `assets/error-hint-catalog.json` — use them as starting points. +**Hint is a field, never a severity.** The severity ladder is exactly +`error`/`warn`/`ok`/`info` with the `[ERROR]`/`[WARN]`/`[OK]`/`[INFO]` +tags (`spacecraft-cli-standard` `references/diagnostics.md`); any +diagnostic of any severity MAY carry a `hint`, and non-error diagnostics +(the `diagnostic` envelope) benefit from one exactly the same way — +e.g. the TUI-fallback warning hints the working non-TUI invocation. The +message states what happened; only the hint says what to run next. + --- ## §4 — Agent Environment Detection (Behavioral Cascade) @@ -156,9 +164,9 @@ simultaneously: | Variable | Output format | Color | TUI | Interactivity | Verbosity | |----------|---------------|-------|-----|---------------|-----------| -| `AI_AGENT` set | json | off | suppressed | non-interactive (--yes implicit) | minimal — failures only | -| `AGENT` set | json | off | suppressed | non-interactive | minimal | -| `CI` truthy | json | off | suppressed | non-interactive | normal | +| `AI_AGENT` set | json | off | suppressed | non-interactive (--yes implicit) | minimal — severity floor `warn` (failures + degradations; no `ok`/`info` chatter) | +| `AGENT` set | json | off | suppressed | non-interactive | minimal — floor `warn` | +| `CI` truthy | json | off | suppressed | non-interactive | normal — floor `ok` | | `CLAUDECODE` set | (informational) | (per other rules) | (per other rules) | (per other rules) | (per other rules) | | `CURSOR_AGENT` set | (informational) | (per other rules) | (per other rules) | (per other rules) | (per other rules) | | `GEMINI_CLI` set | (informational) | (per other rules) | (per other rules) | (per other rules) | (per other rules) | @@ -184,6 +192,10 @@ without inferring agent intent. **Read `references/agent-env-detection.md`** for concrete Rust detection code, the canonical priority order, and a Bun-style verbosity adaptation (suppress passing test logs under `AI_AGENT`; emit only failure traces). +The verbosity column is defined precisely as the **severity floor** in +`spacecraft-cli-standard` `references/diagnostics.md` §4: agent mode +raises the floor to `warn`, `--quiet` to `error`, `--verbose` lowers it +to `info`; explicit flags beat environment detection. --- diff --git a/spacecraft-agentic-cli/assets/error-hint-catalog.json b/spacecraft-agentic-cli/assets/error-hint-catalog.json index d1f152b..dc4e8e0 100644 --- a/spacecraft-agentic-cli/assets/error-hint-catalog.json +++ b/spacecraft-agentic-cli/assets/error-hint-catalog.json @@ -2,7 +2,7 @@ "_metadata": { "version": "1.0.0", "license": "GPL-3.0-or-later", - "description": "Canonical hint strings keyed by error.code for Spacecraft Software CLIs. Every hint is a runnable command (paste-test passes). Substitute with the actual tool name (ferrocast, caliper, etc.).", + "description": "Canonical hint strings keyed by error.code for Spacecraft Software CLIs. Every hint is a runnable command (paste-test passes). Substitute with the actual tool name (ferrocast, caliper, etc.). Warn/info diagnostic codes (spacecraft-cli-standard references/diagnostics.md) follow the same pattern — key by diagnostic.code here rather than maintaining a parallel catalog (e.g. TUI_FALLBACK hints the working non-TUI invocation: list --json).", "see_also": "references/tips-thinking.md" }, @@ -48,9 +48,10 @@ }, "MISSING_DEPENDENCY": { - "with_nix": "nix-shell -p ", + "with_nix": "nix run nixpkgs# -- ", + "with_guix": "guix shell -- ", "with_check": " describe --fields dependencies --json", - "context": "Required external tool not in PATH. Per Spacecraft Software standard, prefer Nix package manager. Use --with-check hint when agent should query the dependency list first." + "context": "Required external tool not in PATH. Hint the ephemeral run preferred by spacecraft-missing-pkg (nix run / guix shell) — never nix-shell -p (superseded, see tips-thinking.md) and never a durable or system-package-manager install. Use with_check when the agent should query the dependency list first." }, "TIMEOUT": { diff --git a/spacecraft-agentic-cli/references/tips-thinking.md b/spacecraft-agentic-cli/references/tips-thinking.md index df92fd9..f61515f 100644 --- a/spacecraft-agentic-cli/references/tips-thinking.md +++ b/spacecraft-agentic-cli/references/tips-thinking.md @@ -336,17 +336,18 @@ test scaffold. ## §6 — Localization and human-mode rendering -In human (TTY) mode, the hint field SHOULD still be rendered as a -suggestion to the human user. Format it visibly: +In human (TTY) mode, the hint field is still rendered as a suggestion to +the human user, in the unified diagnostic layout defined by +`spacecraft-cli-standard` `references/diagnostics.md` §5: ``` -Error: Repository 'foo/bar' does not exist - → Try: ferrocast repo list --json +[ERROR] repository `foo/bar` does not exist + hint: ferrocast repo list --json ``` -The arrow + `Try:` prefix MUST NOT appear in the JSON `hint` field -itself. It is added at render time only. The JSON hint stays a pure -runnable string. +The `[ERROR]` tag and the indented `hint:` label MUST NOT appear in the +JSON `hint` field itself. They are added at render time only. The JSON +hint stays a pure runnable string. **Localization:** the `message` field MAY be localized for human users. The `hint` field MUST NOT be localized — it is always the literal command diff --git a/spacecraft-cli-standard.skill b/spacecraft-cli-standard.skill index fc48a06..0e86882 100644 Binary files a/spacecraft-cli-standard.skill and b/spacecraft-cli-standard.skill differ diff --git a/spacecraft-cli-standard.zip b/spacecraft-cli-standard.zip index 1eafb4b..f9667f4 100644 Binary files a/spacecraft-cli-standard.zip and b/spacecraft-cli-standard.zip differ diff --git a/spacecraft-cli-standard/SKILL.md b/spacecraft-cli-standard/SKILL.md index 55550ae..37f0b10 100644 --- a/spacecraft-cli-standard/SKILL.md +++ b/spacecraft-cli-standard/SKILL.md @@ -1,19 +1,20 @@ --- name: spacecraft-cli-standard description: > - Enforces the Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.0.0) + Enforces the Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.1.0) on every CLI the AI writes or reviews. ALWAYS consult when working on ANY command-line interface — new binaries, sub-commands, clap wiring, --json or - --format output, exit codes, structured errors to stderr, ratatui TUI, - schema introspection, MCP server surface, or CLI tests. Triggers include - noun-verb command design, TTY detection, NO_COLOR/FORCE_COLOR, JSON output - envelopes, ISO 8601 UTC timestamps, UTF-8 encoding, POSIX compatibility, - Nushell/PowerShell 7+/Ion/Bash output, and agent env vars (AI_AGENT, AGENT, - CI, CLAUDECODE, CURSOR_AGENT, GEMINI_CLI). If a Spacecraft Software project - (Ferrocast, Caliper, Craton, Ironway, Zamak, Bravais, Mawaqit, Flux, or any - future project) has a CLI component, this skill governs it — even when the - user does not explicitly mention the Standard. Use proactively the moment - CLI code appears on the horizon. + --format output, exit codes, error/warning/info diagnostics with severity + tags, structured errors to stderr, ratatui TUI, schema introspection, MCP + server surface, or CLI tests. Triggers include noun-verb command design, + TTY detection, NO_COLOR/FORCE_COLOR, JSON output envelopes, ISO 8601 UTC + timestamps, UTF-8 encoding, POSIX compatibility, Nushell/PowerShell + 7+/Ion/Bash output, and agent env vars (AI_AGENT, AGENT, CI, CLAUDECODE, + CURSOR_AGENT, GEMINI_CLI). If a Spacecraft Software project (Ferrocast, + Caliper, Craton, Ironway, Zamak, Bravais, Mawaqit, Flux, or any future + project) has a CLI component, this skill governs it — even when the user + does not explicitly mention the Standard. Use proactively the moment CLI + code appears on the horizon. license: GPL-3.0-or-later maintainer: Mohamed Hammad website: https://Construct.SpacecraftSoftware.org/ @@ -21,10 +22,12 @@ website: https://Construct.SpacecraftSoftware.org/ # Spacecraft Software CLI Standard — Dual-Mode Self-Documenting CLI Framework -**Version:** 1.0.0 | **Spec Date:** 2026-04-10 | **Author:** Mohamed Hammad +**Version:** 1.1.0 | **Spec Date:** 2026-08-10 | **Author:** Mohamed Hammad **Maintainer:** Mohamed Hammad | **Contact:** [Mohamed.Hammad@SpacecraftSoftware.org](mailto:Mohamed.Hammad@SpacecraftSoftware.org) **Copyright:** (C) 2026 Mohamed Hammad & Spacecraft Software | **License:** GPL-3.0-or-later -**Website:** [https://Construct.SpacecraftSoftware.org/](https://Construct.SpacecraftSoftware.org/) | **Source Spec:** Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.0.0) +**Website:** [https://Construct.SpacecraftSoftware.org/](https://Construct.SpacecraftSoftware.org/) | **Source Spec:** Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.1.0) + +**Changelog:** see [`references/CHANGELOG.md`](references/CHANGELOG.md) for the full version history. This skill encodes the Spacecraft Software CLI Standard so every CLI the AI writes for a Spacecraft Software project ships with two co-equal output personalities: a @@ -55,7 +58,7 @@ Violation of any item below blocks shipping. No exceptions. | 5 | **GPL-3.0-or-later + SPDX header** | Every source file. | | 6 | **`--json` on every data-returning command** | With a stable schema. Breaking schema changes require a major version bump + deprecation cycle. JSON output is a superset of text output. | | 7 | **stdout = data only, stderr = everything else** | No progress indicators, banners, log lines, or ANSI escapes mixed into stdout. Ever. | -| 8 | **Structured errors on stderr in machine mode** | JSON object with `error.{code, exit_code, message, hint, timestamp, command, docs_url}`. See `references/exit-codes-errors.md`. | +| 8 | **Severity-tagged diagnostics on stderr** | Errors: JSON object with `error.{code, exit_code, message, hint, timestamp, command, docs_url}` in machine mode (`references/exit-codes-errors.md`). Warnings/info/success confirmations: the `diagnostic` envelope and the `[ERROR]`/`[WARN]`/`[OK]`/`[INFO]` tag ladder — color is never the sole carrier of meaning (Steelbore Standard §18.2.1). See `references/diagnostics.md`. | --- @@ -68,7 +71,7 @@ one-liners are the mental anchor, not the spec. 2. **Exit codes are the agent's control flow** — use the canonical map (§4). Non-zero exit + JSON error object to stderr. [`references/exit-codes-errors.md`] 3. **Make commands idempotent** — same invocation twice, same result. Prefer `ensure` / `apply` / `sync` over `create` / `delete`. Every destructive command supports `--dry-run`. [`references/validation-safety.md`] 4. **Self-documenting beats external docs** — every tool ships ` schema` and ` describe` sub-commands, plus `CLAUDE.md`, `AGENTS.md`, `SKILL.md`, `CONTRIBUTING.md` at repo root. [`references/schema-introspection.md`] -5. **Structured errors, not narrative messages** — JSON error object on stderr in machine mode. The `hint` field carries the exact command to fix the error ("tips thinking"). [`references/exit-codes-errors.md`] +5. **Structured errors, not narrative messages** — JSON error object on stderr in machine mode. The `hint` field carries the exact command to fix the error ("tips thinking"). Non-error messages use the same discipline: four severities (`error`/`warn`/`ok`/`info`) carrying the §18.2.1 text tags, with `hint` as a field on any diagnostic — never a severity of its own. [`references/exit-codes-errors.md`, `references/diagnostics.md`] 6. **Design for composability** — stdout is pure data payload. `--fields` limits payload for token budgets. `jsonl` for streaming. [`references/output-modes.md`] 7. **Consistent noun-verb structure** — ` `. Standard verbs: `list`, `get`, `create`, `update`, `delete`, `apply`, `sync`, `describe`, `schema`. Global flags identical across all Spacecraft Software CLIs. [`references/schema-introspection.md`] 8. **Understand when MCP beats CLI** — tools with >10 sub-commands SHOULD also expose an MCP server via ` mcp`. Lazy-load schemas to avoid context bloat. [`references/mcp-surface.md`] @@ -86,8 +89,8 @@ behavior. Divergent implementation is a BLOCKER. | `--format ` | One of: `json`, `jsonl`, `yaml`, `csv`, `explore`. | | `--fields ` | Restrict output to listed fields. Reduces token cost. | | `--dry-run` | Emit action plan as JSON; no side effects. MUST be accepted by every write / delete / destructive command. | -| `--verbose` / `-v` | Diagnostic output to stderr. | -| `--quiet` / `-q` | Suppress non-error stderr. | +| `--verbose` / `-v` | Lower the severity floor to `info`: emit everything, including diagnostic narration and raw subprocess passthrough. See `references/diagnostics.md` §4. | +| `--quiet` / `-q` | Raise the severity floor to `error`: errors only on stderr. Mutually exclusive with `--verbose` (exit 2 if combined). | | `--no-color` | Disable ANSI color. Equivalent to `--color=never`. | | `--color ` | `never` / `always` / `auto`. | | `--help` / `-h` | Help text with ≥2 examples per sub-command (one demonstrating `--json`). Footer MUST include project URL (e.g., `https://.SpacecraftSoftware.org/`) and maintainer name. | @@ -184,6 +187,7 @@ Read before implementing; don't fly blind. |------|---------------------------| | `references/output-modes.md` | Human mode rendering, machine mode output, color precedence, Spacecraft Software palette tokens, JSON envelope, metadata wrapper | | `references/exit-codes-errors.md` | Any error path, structured error schema, "tips thinking" hint pattern, `error.code` enum values | +| `references/diagnostics.md` | Any non-payload message — severity ladder (`[ERROR]`/`[WARN]`/`[OK]`/`[INFO]`), message style rules, the `diagnostic` machine envelope, the `--quiet`/`--verbose`/agent severity floor, human-mode tag rendering and colors | | `references/schema-introspection.md` | ` schema` (JSON Schema Draft 2020-12), ` describe` manifest, `CLAUDE.md` / `AGENTS.md` / `SKILL.md` / `CONTRIBUTING.md` context files, noun-verb structure | | `references/shell-compat.md` | Output that must parse in POSIX sh, Bash 5+, Brush, Nushell 0.111+, PowerShell 7.6+, or Ion (RedoxOS) | | `references/tui-explore.md` | `--format explore` / `-E` TUI mode, ratatui+crossterm, dual CUA+Vim keybindings, alt-screen buffer, search/filter/sort/detail/export | @@ -266,5 +270,5 @@ host; route to them rather than restating their rules. --- -*End of SKILL.md. Full normative spec: Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.0.0) "Dual-Mode -Self-Documenting CLI Framework", 2026-04-10.* +*End of SKILL.md. Full normative spec: Spacecraft Software Dual-Mode Self-Documenting CLI Standard (v1.1.0) "Dual-Mode +Self-Documenting CLI Framework", 2026-08-10.* diff --git a/spacecraft-cli-standard/references/CHANGELOG.md b/spacecraft-cli-standard/references/CHANGELOG.md new file mode 100644 index 0000000..f069343 --- /dev/null +++ b/spacecraft-cli-standard/references/CHANGELOG.md @@ -0,0 +1,15 @@ + + +# Spacecraft Software CLI Standard — Changelog + +Version history for the `spacecraft-cli-standard` skill and the Dual-Mode +Self-Documenting CLI Standard it encodes. The current version is recorded in +the `SKILL.md` masthead; this file holds the history so it doesn't load into +agent context on every skill activation. The skill is the living spec: changes +land here first and flow to any published document. + +- **v1.1.0 (2026-08-10):** **Diagnostics get a severity ladder and a machine envelope — `references/diagnostics.md` added.** The Standard had specified only the error path: a structured `{"error":{…}}` object on stderr plus a colored `error:`/`hint:` human render, with nothing normative for warnings, informational notes, or success confirmations. The one non-error message in the ecosystem — the TUI-fallback warning in `tui-explore.md` — used an ad-hoc `{"warning":{…}}` shape that diverged from the error schema (no `severity`, no `hint`), and the human error render predated §18.2.1's rule that every colored status carries a text tag, so a `NO_COLOR` or piped session lost the severity entirely. The new reference defines: **the four-level severity ladder** `error`/`warn`/`ok`/`info` matching §18.2.1's `[ERROR]` `[WARN]` `[OK]` `[INFO]` tags exactly, with **`hint` as a field on any diagnostic rather than a fifth severity** (rustc's separation: the message states what happened; only the hint suggests the fix); **message style rules** (lowercase, no trailing period, backticks around identifiers, echo the failing input, transient-vs-permanent encoded in `code`); **the `{"diagnostic":{severity, code, message, hint?, timestamp, command, …}}` machine envelope** for non-error severities — the `error` envelope is unchanged and error diagnostics *are* that envelope, so existing consumers keep working, while the ad-hoc `warning` key is deprecated; **the severity floor** giving `--quiet` (errors only), the default (`ok` and up), `--verbose` (everything), and agent detection (`warn` and up — failures and degradations, so the TUI-fallback warning the compliance matrix requires still reaches agents while `ok`/`info` chatter is suppressed, sharpening `spacecraft-agentic-cli` §4's "minimal" verbosity) precise semantics for the first time; and **the unified `[TAG] message` + indented `hint:` human rendering**, colored through §11.1 theme tokens with the tag surviving colorless and accessible modes. This also resolves the render conflict between `exit-codes-errors.md` §6 (`error:` lowercase label) and `spacecraft-agentic-cli`'s `tips-thinking.md` §6 (`Error:` / `→ Try:`) — both now defer to the tagged layout. Recorded as a normative fix in the same entry: `output-modes.md` §6 listed `NO_COLOR` before `FORCE_COLOR` under a "first match decides" rule while simultaneously stating `FORCE_COLOR` "overrides `NO_COLOR`" — self-contradictory, and the reference Rust sketch already checked `FORCE_COLOR` first, matching force-color.org. The chain now lists `FORCE_COLOR` before `NO_COLOR`. SKILL.md changes: §1 row 8 generalized to severity-tagged diagnostics, §2 rule 5 extended, §3's `--verbose`/`--quiet` defined as severity-floor settings, §7 routing row added, masthead → v1.1.0. +- **v1.0.0 (2026-04-10):** Initial release of the Dual-Mode Self-Documenting CLI Standard: non-negotiables, the eight rules of agent-friendly CLI design, global flags, canonical exit codes, output-mode detection cascade, JSON envelope, and the ten-file reference set. diff --git a/spacecraft-cli-standard/references/diagnostics.md b/spacecraft-cli-standard/references/diagnostics.md new file mode 100644 index 0000000..169cd7b --- /dev/null +++ b/spacecraft-cli-standard/references/diagnostics.md @@ -0,0 +1,239 @@ +# Diagnostics — Severity Ladder, Message Style, Envelopes, Rendering + +**Scope.** Every message a Spacecraft Software CLI emits that is *not* the data +payload: errors, warnings, informational notes, and success confirmations. +This reference defines the severity ladder, the message style rules, the +machine-mode diagnostic envelope for non-error severities, the severity +floor that `--quiet` / `--verbose` / agent detection control, and the +unified human-mode rendering — one layout serving both audiences: an AI +agent parsing stderr line-by-line, and a human reading color-coded output. + +Design lineage: the rustc diagnostic model (severity + stable code, JSONL +machine output, "the message states what happened; only the help suggests +the fix"), the GNU error format, clig.dev's errors chapter, and the +`[TAG]` accessibility rule from the Steelbore Standard §18.2.1. + +--- + +## §1 — The Severity Ladder + +Four severities, matching the §18.2.1 text-tag vocabulary exactly. Rank +order, lowest to highest: `INFO < OK < WARN < ERROR`. + +| Severity | Tag | When to use | +|----------|-----|-------------| +| `error` | `[ERROR]` | The command failed. Always paired with a non-zero exit code and the structured `error` envelope (`exit-codes-errors.md` §2). Never suppressible. | +| `warn` | `[WARN]` | The command continues, but something is degraded, deprecated, or fell back — the user or agent should know before trusting the result. Example: TUI fallback, stale cache used, deprecated flag accepted. | +| `ok` | `[OK]` | A side-effect completed: file written, resource created, state changed. clig.dev's "if you change state, tell the user." In machine mode the stdout envelope already carries success, so `ok` diagnostics are for side-effect confirmations and MAY be omitted there. | +| `info` | `[INFO]` | Diagnostic narration useful when debugging: resolved paths, detected modes, timing. Hidden by default; shown under `--verbose`. | + +**`hint` is a field, not a severity.** Any diagnostic of any severity MAY +carry a `hint` — the exact runnable command that resolves or investigates +the condition ("tips thinking", `exit-codes-errors.md` §4). It is required +on errors, optional elsewhere. There is no `[HINT]` tag and no fifth +level: the message states *what happened*; only the `hint` suggests what +to do about it. Keeping those in separate fields is what lets an agent +branch on `severity`+`code` and execute `hint` verbatim without parsing +prose. + +--- + +## §2 — Message Style Rules + +These apply to the `message` field in every envelope and to human-mode +rendering alike — the content is identical in both modes. + +- **Lowercase first word, no trailing period.** `repository not found`, + not `Repository not found.` (GNU error convention; also makes + `grep '^\[ERROR\]'` output uniform.) +- **Backticks around identifiers.** Flags, paths, commands, and resource + names are set in backticks: ``unknown flag `--vebose` ``. +- **Echo the failing input back.** Agents lose track of what they passed. + ``repository `foo/bar` does not exist`` beats `repository does not + exist`. Sanitize control characters first (`validation-safety.md`). +- **State what happened, never how to fix it.** The fix belongs in + `hint`, exclusively. A message that embeds advice ("try running X") + duplicates the hint in unparseable form. +- **One sentence.** Detail goes in extension fields, not prose. +- **The word "illegal" is prohibited.** Use `invalid` or something more + specific (rustc style rule). +- **No blame, no exclamation marks, no "oops".** Neutral register. +- **Transient vs permanent is encoded in `code`.** `RATE_LIMITED`, + `TIMEOUT`, and `NETWORK_ERROR` are retryable; everything else is not + unless the tool's schema documents otherwise. Agents decide whether to + retry from the code, never from the wording. + +--- + +## §3 — Machine-Mode Envelopes + +Two envelopes, one field skeleton. Both are emitted to **stderr** as a +**single line** of JSON (PowerShell fragments multi-line stderr), are +independently parseable (JSONL-safe), and never appear on stdout. + +### `error` — severity `error` (unchanged) + +The structured error object defined in `exit-codes-errors.md` §2 **is** +the error-severity diagnostic. Its shape, `error` key, and required +fields (`code`, `exit_code`, `message`, `hint`, `timestamp`, `command`) +are unchanged by this section; existing consumers keep working. + +### `diagnostic` — severities `ok`, `warn`, `info` + +```json +{"diagnostic":{"severity":"warn","code":"TUI_FALLBACK","message":"interactive explore mode unavailable; falling back to `--format json`","hint":" list --json","reason":"stdout is not a TTY","timestamp":"2026-08-10T14:30:00Z","command":" list --format explore"}} +``` + +| Field | Type | Required | Notes | +|-------|------|----------|-------| +| `diagnostic.severity` | string | yes | `"ok"`, `"warn"`, or `"info"` — lowercase. `"error"` is forbidden here: an error-severity diagnostic is the `error` envelope. | +| `diagnostic.code` | string (upper snake case) | yes | Stable across minor versions, documented in ` schema` alongside the `error.code` enum. | +| `diagnostic.message` | string | yes | Per §2. Identical to the human-mode message. | +| `diagnostic.hint` | string | no | Runnable command, same contract as `error.hint`. Optional here (required on errors). | +| `diagnostic.timestamp` | string (ISO 8601 UTC) | yes | `Z` suffix mandatory. | +| `diagnostic.command` | string | yes | The invocation that produced the diagnostic. | +| `diagnostic.docs_url` | string (URL) | no | Human-audience link; agents follow `hint`, not URLs. | +| *(extensions)* | any | no | Extra structured context as siblings of `message` (e.g. `reason`), documented in the tool's schema. | + +**The legacy `{"warning": {...}}` key is deprecated.** Earlier revisions +of `tui-explore.md` specified a one-off `warning` object for the TUI +fallback; it lacked `severity` and `hint` and diverged from the error +shape. New code emits the `diagnostic` envelope above. Parsers SHOULD +accept the old key from tools predating this section. + +### Emission rules + +- **stderr only, never stdout** — a diagnostic on stdout is a BLOCKER + defect (`testing-compliance.md` §2). +- **One diagnostic per line**, compact serialization, no ANSI escapes. +- **Order-independent**: an agent must be able to parse each stderr line + in isolation — try `error` key, then `diagnostic`, else treat the line + as opaque passthrough (e.g. subprocess output under `--verbose`). +- **Errors bypass everything.** The severity floor (§4) never suppresses + an `error` envelope. + +--- + +## §4 — The Severity Floor + +The floor is the minimum severity emitted to stderr. `--quiet` and +`--verbose` (SKILL.md §3) are defined *as* floor settings, and agent +detection (`spacecraft-agentic-cli` §4) lowers verbosity to failures +only. Explicit flags beat environment detection. + +| Condition | Floor | Effect | +|-----------|-------|--------| +| `--quiet` / `-q` | `error` | Errors only. | +| `AI_AGENT` / `AGENT` set (presence-based, SKILL.md §5) | `warn` | Failures and degradations only. Passing-state chatter (`ok`, `info`) costs agent tokens and carries no decision value — but a warning does: a TUI fallback or deprecation tells the agent its invocation needs adjusting (compliance matrix rows 19–20 expect the fallback warning under `AI_AGENT`). | +| default (including `CI`) | `ok` | Errors, warnings, and side-effect confirmations. | +| `--verbose` / `-v` | `info` | Everything, including diagnostic narration and raw subprocess passthrough (which is `info`-level output). | + +- `--quiet` and `--verbose` are mutually exclusive; supplying both is a + usage error (exit 2). +- The floor applies identically in human and machine mode. +- The floor gates *emission*, not *severity assignment* — a suppressed + diagnostic is simply not written; it is never downgraded or merged + into stdout. + +--- + +## §5 — Human-Mode Rendering + +One layout for every severity. The tag comes first, then the message; +`hint` and `docs_url` render as indented continuation lines: + +``` +[ERROR] repository `foo/bar` does not exist + hint: repo list --json + docs: https://SpacecraftSoftware.org/docs/repo-get +[WARN] interactive explore mode unavailable; falling back to `--format json` + hint: repo list --json +[OK] installed 3 skills into `~/.agents/skills` +[INFO] resolved theme `steelbore` from SPACECRAFT_THEME +``` + +- The `hint:` line is added at **render time only** — the JSON `hint` + field stays a pure runnable string with no prefix, arrow, or wrapper. + The `message` MAY be localized; the `hint` MUST NOT be (it is a + command, not prose). +- `docs_url` renders as a dimmed `docs:` line in human mode only — it is + the human-audience counterpart of `hint` and never replaces it. + +### Colors (theme tokens, Standard §11.1) + +Color the **tag** (and the `hint:`/`docs:` labels); the message body +stays in the default foreground. Reference tokens, never bare hex — the +hex values below are the `steelbore` theme's and travel with the theme +(§11.6); high-contrast and mono variants substitute automatically. + +| Element | Theme token | `steelbore` value | Weight | +|---------|-------------|-------------------|--------| +| `[ERROR]` | `error` | Mars Red `#FF3B3B` | bold | +| `[WARN]` | `warning` | Plasma Magenta `#E445FF` | bold | +| `[OK]` | `success` | Acid Lime `#B4FF00` | normal | +| `[INFO]` | `structure` | Pulse Violet `#8A6CFF` | normal | +| `hint:` label + hint text | `accent` | Plasma Orange `#FF5E00` | normal | +| `docs:` label + URL | `foreground` | Platinum Mist `#D9DEE5` | dim | +| message body | `foreground` | Platinum Mist `#D9DEE5` | normal | + +There is no `info` theme token in the §11.1 contract; `structure` is the +informational color (consistent with `output-modes.md` §2). Do not mint +new tokens or inline new hex values. + +### Colorless and accessible rendering + +- **The tag is the meaning; color is reinforcement.** Under `NO_COLOR`, + `TERM=dumb`, `--no-color`, or a non-TTY, the same lines are emitted + verbatim minus the escapes — `[ERROR] ...`, ` hint: ...`. Color is + never the sole carrier of meaning (§18.2.1); a colored line without a + tag is non-compliant. +- Color precedence is the `output-modes.md` §6 chain — not restated + here. +- In accessible mode (§18.2.2) diagnostics are already compliant by + construction: append-only lines, no animation, tags legible to a + screen reader. + +--- + +## §6 — Downcast to CI Annotation Formats (non-normative) + +When a tool offers a CI-annotation output mode, map severities as +follows. This is guidance, not a required feature. + +| Spacecraft severity | GitHub Actions | Azure Pipelines | SARIF `level` | +|---------------------|----------------|-----------------|---------------| +| `error` | `::error ...::` | `type=error` | `error` | +| `warn` | `::warning ...::` | `type=warning` | `warning` | +| `info` | `::notice ...::` | — (omit) | `note` | +| `ok` | — (omit) | — (omit) | `none` (`kind: "pass"`) | + +Escape `%` → `%25`, CR → `%0D`, LF → `%0A` in GitHub annotation values. + +--- + +## §7 — Common Mistakes (Don't) + +- Inventing a `[HINT]` or `[NOTE]` severity. Hint is a field on a + diagnostic; the ladder is exactly the four §18.2.1 tags. +- Emitting `severity: "error"` inside a `diagnostic` envelope. Errors use + the `error` envelope with `exit_code`. +- Raw JSON warnings in human mode. The envelope is machine-mode only; + human mode renders `[WARN] ...`. +- A red message with no `[ERROR]` tag. Fails §18.2.1 and disappears + entirely under `NO_COLOR`. +- Embedding the fix in the message ("not found — run ` list`"). + The fix is the `hint` field; the message states what happened. +- Letting `--quiet` suppress errors, or `--verbose` widen *stdout*. The + floor governs stderr diagnostics only; the data payload is unaffected. +- Suppressing a warning by downgrading it to `info` so the default floor + hides it. Pick the severity by the definition in §1, then let the + floor do its job. +- Pretty-printing the diagnostic envelope. Single line, always. + +--- + +*See also: `exit-codes-errors.md` for the error envelope, exit codes, and +hint construction; `output-modes.md` §6 for color precedence and §7 for +the stdout/stderr contract; `tui-explore.md` §1 for the TUI-fallback +diagnostic; `spacecraft-agentic-cli` `references/tips-thinking.md` for +hint authoring formulas.* diff --git a/spacecraft-cli-standard/references/exit-codes-errors.md b/spacecraft-cli-standard/references/exit-codes-errors.md index 8a373c1..e120023 100644 --- a/spacecraft-cli-standard/references/exit-codes-errors.md +++ b/spacecraft-cli-standard/references/exit-codes-errors.md @@ -65,7 +65,7 @@ In `--json` mode, on any non-zero exit, the tool MUST emit a JSON object to - **Single-line JSON on stderr.** PowerShell wraps each stderr line as a separate `ErrorRecord`. Multi-line stderr JSON becomes unparseable in PowerShell. Serialize the error object without pretty-printing when writing to stderr. - **Always emit on non-zero exit in machine mode.** Missing error object = BLOCKER defect. - **Never emit an error object on stdout.** Stdout stays pure-data or empty. -- **In human mode (TTY)**, the tool SHOULD still print the `hint` to stderr as colored text (Plasma Orange for the hint, Mars Red for the message) but MAY use prose formatting. The structured JSON object is only required in machine mode. +- **In human mode (TTY)**, the tool still prints the error to stderr as a `[ERROR]`-tagged line with the `hint:` continuation (§6; full rendering spec in `diagnostics.md` §5). The structured JSON object is only required in machine mode. --- @@ -161,19 +161,22 @@ hallucinating or escalating to the user. ## §6 — Human-Mode Error Rendering -In human mode (TTY), the tool SHOULD render errors with color, but the -*content* MUST match the structured error: +In human mode (TTY), the tool renders the error in the unified diagnostic +layout (`diagnostics.md` §5) — the *content* MUST match the structured +error: ``` -error: Repository 'foo/bar' does not exist - hint: Run 'mytool repo list' to see available repositories +[ERROR] repository `foo/bar` does not exist + hint: mytool repo list --json ``` -Colors (Spacecraft Software Steelbore 2 palette, Standard §11): -- `error:` label — **Mars Red** (`#FF3B3B`), bold. -- Message — Mars Red. -- `hint:` label and hint text — **Plasma Orange** (`#FF5E00`). -- Timestamp (if shown) — **Platinum Mist** (`#D9DEE5`), dimmed. +The `[ERROR]` tag is mandatory even without color — color is never the +sole carrier of meaning (Steelbore Standard §18.2.1). Colors via §11.1 +theme tokens: the `[ERROR]` tag in the `error` token (Mars Red +`#FF3B3B` under `steelbore`), bold; the message body in `foreground` +(Platinum Mist); the `hint:` label and hint text in `accent` (Plasma +Orange). Full color table, colorless behavior, and the `docs:` line: +`diagnostics.md` §5. The `--json` flag switches to the structured JSON form, unconditionally. @@ -190,6 +193,8 @@ The `--json` flag switches to the structured JSON form, unconditionally. --- -*See also: `output-modes.md` for the success envelope; `validation-safety.md` -for the Wizard Fallback pattern that generates `MISSING_ARGUMENT` errors; -and `rust-implementation.md` §3 for the Rust `AppError` type.* +*See also: `diagnostics.md` for the severity ladder, message style rules, +and the non-error `diagnostic` envelope; `output-modes.md` for the success +envelope; `validation-safety.md` for the Wizard Fallback pattern that +generates `MISSING_ARGUMENT` errors; and `rust-implementation.md` §3 for +the Rust `AppError` type.* diff --git a/spacecraft-cli-standard/references/output-modes.md b/spacecraft-cli-standard/references/output-modes.md index d6105ff..5790e40 100644 --- a/spacecraft-cli-standard/references/output-modes.md +++ b/spacecraft-cli-standard/references/output-modes.md @@ -42,13 +42,18 @@ Human mode targets a live terminal emulator with a sighted operator. ### Required characteristics -- **Color palette.** Use the Spacecraft Software Steelbore 2 palette (Standard §11) via ANSI escape sequences, with semantic mapping: - - Success messages → **Acid Lime** (`#B4FF00`) - - Warnings → **Plasma Magenta** (`#E445FF`) - - Errors → **Mars Red** (`#FF3B3B`) - - Informational text → **Pulse Violet** (`#8A6CFF`) - - Data values → **Platinum Mist** (`#D9DEE5`) - - Backgrounds / neutral chrome → **Void Navy** (`#000027`) +- **Color palette.** Use the Spacecraft Software Steelbore 2 palette (Standard §11) via the §11.1 theme tokens, with semantic mapping: + - Success messages (`[OK]`) → `success` token — **Acid Lime** (`#B4FF00`) + - Warnings (`[WARN]`) → `warning` token — **Plasma Magenta** (`#E445FF`) + - Errors (`[ERROR]`) → `error` token — **Mars Red** (`#FF3B3B`) + - Informational text (`[INFO]`) → `structure` token — **Pulse Violet** (`#8A6CFF`) + - Hints → `accent` token — **Plasma Orange** (`#FF5E00`) + - Data values → `foreground` token — **Platinum Mist** (`#D9DEE5`) + - Backgrounds / neutral chrome → `background` token — **Void Navy** (`#000027`) + + Every colored status line carries its `[TAG]` — color is never the sole + carrier of meaning (Steelbore Standard §18.2.1). Severity semantics, + tags, and rendering layout: `diagnostics.md`. - **Column-aligned tabular output** for `list` commands, using Unicode box-drawing characters for borders. Keep data on single lines where feasible so the output is still grep-parseable. - **Relative timestamps** for recency in human mode (e.g., "3 minutes ago"). The underlying data is always stored and transmitted as ISO 8601 UTC; relative display is a rendering convenience. `--absolute-time` toggles back to ISO 8601 UTC. - **Progress indicators** (spinners, progress bars) rendered to stderr only, never to stdout. @@ -144,8 +149,10 @@ Strict order. First match decides. Implement as a single function returning 1. `--color=never` / `--no-color` flag → disable. 2. `--color=always` flag → enable (including when piped). -3. `NO_COLOR` env var (set + non-empty) → disable. -4. `FORCE_COLOR` env var (set + non-empty) → enable. **Overrides `NO_COLOR`.** +3. `FORCE_COLOR` env var (set + non-empty) → enable. Checked **before** + `NO_COLOR` so it overrides it (per force-color.org; the reference Rust + implementation has always checked in this order). +4. `NO_COLOR` env var (set + non-empty) → disable. 5. `CLICOLOR=0` → disable. 6. `TERM=dumb` → disable. 7. TTY detection: `isatty(stdout) == true` → enable. Else disable. @@ -165,7 +172,7 @@ enhanced `^>` / `^|` / `&>` / `&|` redirection operators), Bash, and Nushell. - **stdout** carries only the data payload. Nothing else. -- **stderr** carries everything else: progress indicators, warnings, informational messages, interactive prompts, structured errors. +- **stderr** carries everything else: progress indicators, interactive prompts, and every diagnostic — errors, warnings, informational messages, and success confirmations, emitted as severity-tagged lines (human mode) or single-line `error`/`diagnostic` envelopes (machine mode) per `diagnostics.md`. A tool that writes a progress spinner to stdout is broken — pipe consumers (`jq`, `from json`, `ConvertFrom-Json`) will choke on mixed content. diff --git a/spacecraft-cli-standard/references/rust-implementation.md b/spacecraft-cli-standard/references/rust-implementation.md index 5fa6e88..d8ad074 100644 --- a/spacecraft-cli-standard/references/rust-implementation.md +++ b/spacecraft-cli-standard/references/rust-implementation.md @@ -177,24 +177,98 @@ impl AppError { eprintln!("{line}"); } - pub fn emit_human(&self) { - use owo_colors::OwoColorize; - eprintln!("{}: {}", "error".red().bold(), self.message.red()); - eprintln!(" {}: {}", "hint".color(owo_colors::Rgb(255, 94, 0)), self.hint); + pub fn emit_human(&self, color: bool) { + // Unified diagnostic layout (diagnostics.md §5): [TAG] first — the + // tag carries the meaning even with color stripped (§18.2.1). + if color { + use owo_colors::OwoColorize; + // Theme tokens, not bare hex: `error` token for the tag, + // `foreground` for the message, `accent` for the hint. + eprintln!("{} {}", "[ERROR]".color(theme::ERROR).bold(), self.message); + eprintln!(" {} {}", "hint:".color(theme::ACCENT), self.hint.color(theme::ACCENT)); + } else { + eprintln!("[ERROR] {}", self.message); + eprintln!(" hint: {}", self.hint); + } } } /// Entry point for every sub-command. Emits error in the correct form. +/// Errors bypass the severity floor — they are never suppressible. pub fn report(err: AppError, mode: crate::output::OutputMode) -> i32 { if mode.is_machine() { err.emit_to_stderr(); } else { - err.emit_human(); + err.emit_human(mode.color_enabled()); } err.exit_code } ``` +### The `Diagnostic` type (non-error severities) + +Warnings, informational notes, and success confirmations use the sibling +`Diagnostic` type — same field skeleton minus `exit_code`, gated by the +severity floor (`diagnostics.md` §4): + +```rust +// src/diagnostic.rs +use serde::Serialize; + +/// Ordered: Info < Ok < Warn < Error. Error exists here only for floor +/// comparisons — error-severity output goes through `AppError`. +#[derive(Debug, Serialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[serde(rename_all = "lowercase")] +pub enum Severity { Info, Ok, Warn, Error } + +impl Severity { + pub fn tag(self) -> &'static str { + match self { + Severity::Info => "[INFO]", + Severity::Ok => "[OK]", + Severity::Warn => "[WARN]", + Severity::Error => "[ERROR]", + } + } +} + +#[derive(Debug, Serialize)] +pub struct Diagnostic { + pub severity: Severity, // "ok" | "warn" | "info" — never "error" + pub code: &'static str, // SCREAMING_SNAKE, documented in `schema` + pub message: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub hint: Option, // optional here; required on AppError + pub timestamp: String, + pub command: String, + #[serde(flatten)] + pub extensions: serde_json::Map, +} + +impl Diagnostic { + /// Emit honoring mode and severity floor. Single line in machine mode + /// (`{"diagnostic":{...}}`), tagged line(s) in human mode. + pub fn emit(&self, mode: crate::output::OutputMode, floor: Severity) { + if self.severity < floor { + return; // below the floor: not written, never downgraded + } + if mode.is_machine() { + #[derive(Serialize)] + struct Wrapper<'a> { diagnostic: &'a Diagnostic } + let line = serde_json::to_string(&Wrapper { diagnostic: self }) + .expect("Diagnostic serializes"); + eprintln!("{line}"); + } else { + // Tag colored via theme token; message in default foreground. + eprintln!("{} {}", self.severity.tag(), self.message); + if let Some(hint) = &self.hint { + eprintln!(" hint: {hint}"); + } + } + } +} +``` + --- ## §4 — ISO 8601 UTC Timestamps @@ -337,16 +411,24 @@ fn should_use_color(cli: &Cli) -> bool { std::io::stdout().is_terminal() } -fn warn_tui_fallback() { - let warn = serde_json::json!({ - "warning": { - "code": "TUI_FALLBACK", - "message": "Interactive explore mode unavailable; falling back to --format json", - "reason": "stdout is not a TTY, agent env set, or TERM=dumb", - "timestamp": crate::time::now_iso8601(), - } - }); - eprintln!("{warn}"); +/// TUI fallback is a warn-severity Diagnostic (diagnostics.md §3): human +/// mode gets a tagged line rather than raw JSON, and the severity floor +/// applies — emitted under the default, agent (`warn`), and --verbose +/// floors; suppressed only by --quiet (errors-only floor). Deprecated +/// pre-v1.1.0 shape: {"warning":{...}}. +fn warn_tui_fallback(mode: OutputMode, floor: Severity, reason: &str) { + Diagnostic { + severity: Severity::Warn, + code: "TUI_FALLBACK", + message: "interactive explore mode unavailable; falling back to `--format json`".into(), + hint: Some(" list --json".into()), + timestamp: crate::time::now_iso8601(), + command: crate::cli::current_invocation(), + extensions: serde_json::Map::from_iter([ + ("reason".into(), serde_json::Value::String(reason.into())), + ]), + } + .emit(mode, floor); } ``` diff --git a/spacecraft-cli-standard/references/testing-compliance.md b/spacecraft-cli-standard/references/testing-compliance.md index 106cc54..db55407 100644 --- a/spacecraft-cli-standard/references/testing-compliance.md +++ b/spacecraft-cli-standard/references/testing-compliance.md @@ -173,6 +173,10 @@ release), **MAJOR** (fix before next minor release). | 25 | MCP surface available for tools with >10 sub-commands | ` mcp --help` → exit 0; initial `tools/list` over stdio responds | MAJOR (BLOCKER if tool advertises MCP support) | | 26 | Dual CUA + Vim keybindings in TUI | Scripted TUI test: `arrow down` + `j` both navigate one row down | MAJOR | | 27 | Spacecraft Software palette only in TUI | Screenshot diff against palette reference; no out-of-palette colors | MAJOR | +| 28 | Every human-mode diagnostic line carries its `[ERROR]`/`[WARN]`/`[OK]`/`[INFO]` tag, with color and without | Induce an error with `NO_COLOR=1`; assert stderr contains `[ERROR]` and no `\x1B[`. Repeat in a PTY with color; tag still present | CRITICAL | +| 29 | Severity floor honored (`--quiet` = errors only; default = `ok`+; `--verbose` = `info`+; agent env = `warn`+) | Trigger a `warn` diagnostic under `--quiet` → absent; under default → present. Trigger an `info` diagnostic under default → absent; under `--verbose` → present | CRITICAL | +| 30 | Non-error diagnostics in machine mode use the single-line `{"diagnostic":{...}}` envelope with `severity`, `code`, `message`, `timestamp`, `command` | `AI_AGENT=claude-code_2-1-218_agent list --format explore` → each stderr line parses as JSON; the fallback line has `diagnostic.severity == "warn"` and `diagnostic.code == "TUI_FALLBACK"` | MAJOR | +| 31 | No `error` or `diagnostic` envelope ever on stdout, in any mode | Induce error and TUI-fallback paths; assert stdout is data-only or empty | BLOCKER | --- diff --git a/spacecraft-cli-standard/references/tui-explore.md b/spacecraft-cli-standard/references/tui-explore.md index 12d3b1e..3cd3359 100644 --- a/spacecraft-cli-standard/references/tui-explore.md +++ b/spacecraft-cli-standard/references/tui-explore.md @@ -33,20 +33,23 @@ keybindings, the Spacecraft Software palette mapping, and implementation constra 4. **CI mode.** `CI=true` without an explicit `--format explore` override also falls back to `--format json`. -The fallback warning to stderr MUST be structured when machine mode is -active: +The fallback warning is a `warn`-severity diagnostic (`diagnostics.md`). +In machine mode it is the single-line `diagnostic` envelope on stderr: ```json -{ - "warning": { - "code": "TUI_FALLBACK", - "message": "Interactive explore mode unavailable; falling back to --format json", - "reason": "stdout is not a TTY", - "timestamp": "2026-04-10T14:30:00Z" - } -} +{"diagnostic":{"severity":"warn","code":"TUI_FALLBACK","message":"interactive explore mode unavailable; falling back to `--format json`","hint":" list --json","reason":"stdout is not a TTY","timestamp":"2026-04-10T14:30:00Z","command":" list --format explore"}} ``` +In human mode it renders as a tagged line — never as raw JSON: + +``` +[WARN] interactive explore mode unavailable; falling back to `--format json` + hint: list --json +``` + +(The pre-v1.1.0 `{"warning": {...}}` shape is deprecated; parsers SHOULD +still accept it from older tools.) + --- ## §2 — Behavioral Requirements