fix(#2979): document ExitError plain-text carve-out in json-errors.md (#3093)

* fix(#2979): document ExitError plain-text carve-out in json-errors.md

The JSON-errors doc claimed every error emits a structured JSON envelope,
but usage errors (ExitError) intentionally emit plain text with their own
exit code (src/cli-exit.cts:36-39 catches ExitError before the envelope
branch). Anyone following the doc's 'always parse stderr as JSON' guidance
against a usage error got a parse failure.

Amended the Wire format + Overview + Writing tests sections to scope the
structured envelope to non-ExitError failures, stated the carve-out with a
pointer to cli-exit.cts, and scoped the JSON-parse instruction to the
envelope branch. Added a characterization test pinning both paths together
(ExitError -> plain text + own code; non-ExitError -> JSON envelope) so the
code cannot drift toward the doc's prior overstated claim. No runtime
change — the test passes before and after the doc edit.

Re-scoped per maintainer triage: the smart-entry --json part is already
satisfied (shipped payload exposes the command token); only the doc
correction + characterization test remain.

* chore(#2979): backfill changeset PR number 3093

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-08-05 18:57:10 -04:00
committed by GitHub
parent 2979f2a994
commit 955655407c
3 changed files with 64 additions and 6 deletions

View File

@@ -2,11 +2,12 @@
## Overview
`gsd-tools` supports a **JSON error mode** that emits all errors as structured
`gsd-tools` supports a **JSON error mode** that emits most errors as structured
JSON objects on stderr instead of free-form text. This is the recommended
surface for tests and tooling that need to assert on error types without
grepping raw text (see `CONTRIBUTING.md` — "Prohibited: Raw Text Matching on
Test Outputs").
Test Outputs"). Usage errors are an intentional exception — see the
`ExitError` carve-out below.
## Activating
@@ -37,6 +38,20 @@ Fields:
| `reason` | string | Typed reason code from the taxonomy below. |
| `message` | string | Human-readable description (may change; do not assert on it). |
### `ExitError` carve-out (plain text, not JSON)
Usage errors and explicit exit-code signals take a **different path**: they
throw `ExitError` (`src/cli-exit.cts`), which `runMain` catches *before* the
JSON-envelope branch. An `ExitError` writes its `message` as **plain text**
to stderr (not a JSON object) and exits with the error's own `code` (which
may differ from 1). This is intentional — usage messages are operator-facing
prose, not structured failures.
If you are testing a usage/flag error, do **not** parse stderr as JSON;
assert on the exit code and (if needed) the plain-text message. The
"parse stderr as JSON" guidance below applies only to the structured-envelope
branch (non-`ExitError` failures).
## Error code taxonomy
Codes are frozen constants in `gsd-core/bin/lib/core.cjs` under
@@ -99,8 +114,9 @@ text (unstable).
## Writing tests
Always parse stderr with `JSON.parse` and assert on typed fields. Never use
`.includes()`, `.match()`, or regex on the raw error string.
For **non-usage** errors (the structured-envelope branch), parse stderr with
`JSON.parse` and assert on typed fields. Never use `.includes()`, `.match()`,
or regex on the raw error string.
```js
// CORRECT: parse then assert on typed field