mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-05-13 10:36:38 +02:00
* test(3255): add red/green tests for --json-errors structured error mode
Ten tests covering the --json-errors mode contract:
- Unknown command → sdk_unknown_command
- Dotted unknown command → sdk_unknown_command
- Missing --pick value → usage
- Config key not found → config_key_not_found
- Unknown subcommand → sdk_unknown_command
- GSD_JSON_ERRORS=1 env var activation
- Successful command unaffected
- Stable error shape ({ok, reason, message})
- Single error line per invocation
- Unknown flag → usage
All assertions use JSON.parse on stderr captures, never .includes() on
text (#2974 / CONTRIBUTING.md "Prohibited: Raw Text Matching" rule).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(3255): add typed ERROR_REASON codes and GSD_JSON_ERRORS env var support
- Destructure ERROR_REASON from core in gsd-tools.cjs
- Add GSD_JSON_ERRORS=1 env var as alternative to --json-errors CLI flag
- Pass ERROR_REASON.SDK_UNKNOWN_COMMAND to unknown top-level command default path
- Pass ERROR_REASON.SDK_UNKNOWN_COMMAND to unknown intel subcommand path
- Pass ERROR_REASON.USAGE to --pick missing value error path
- Pass ERROR_REASON.USAGE to --version flag rejection path
All ten tests in feat-3255-json-errors-mode.test.cjs pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(3255): add json-errors taxonomy doc, changeset, and CHANGELOG entry
- docs/json-errors.md: full error code taxonomy, wire format spec, and
test-authoring guidelines for the --json-errors mode
- .changeset/gentle-tigers-roar.md: changeset fragment (pr will be updated
after PR is opened)
- CHANGELOG.md: Unreleased → Added entry for the new structured error mode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: update changeset PR number to 3304
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(gsd-tools): document --json-errors in usage/help text (#3255)
Add [--json-errors] to the TOP_LEVEL_USAGE synopsis line and introduce a
"Global flags:" section describing all four global flags (--raw, --pick,
--cwd, --ws) plus --json-errors with its GSD_JSON_ERRORS=1 env-var
alternative, so operators can discover the flag via `gsd-tools --help`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: drop redundant CHANGELOG.md edit (use .changeset/ fragment per CONTRIBUTING.md)
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
4.1 KiB
4.1 KiB
JSON Error Mode — gsd-tools Structured Errors
Overview
gsd-tools supports a JSON error mode that emits all 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").
Activating
Either flag or env var activates the mode:
# Flag (preferred in test code):
node gsd-tools.cjs --json-errors <command> [args]
# Env var (preferred for shell wrappers and CI):
GSD_JSON_ERRORS=1 node gsd-tools.cjs <command> [args]
Wire format
On any error, exactly one JSON line is written to stderr and the process exits with code 1:
{ "ok": false, "reason": "<error_code>", "message": "<human text>" }
Fields:
| Field | Type | Description |
|---|---|---|
ok |
false |
Always false for error objects. |
reason |
string | Typed reason code from the taxonomy below. |
message |
string | Human-readable description (may change; do not assert on it). |
Error code taxonomy
Codes are frozen constants in get-shit-done/bin/lib/core.cjs under
ERROR_REASON. Tests must assert on reason values (stable), not message
text (unstable).
Dispatch errors (gsd-tools routing layer)
| Code | When emitted |
|---|---|
sdk_unknown_command |
Unknown top-level command (gsd-tools bogus-cmd) |
sdk_unknown_command |
Unknown dotted command (gsd-tools foo.bar where foo is not a known command) |
sdk_unknown_command |
Unknown subcommand within a domain (e.g. gsd-tools intel bogus-sub) |
sdk_missing_arg |
Required argument omitted by an SDK-level guard |
sdk_fail_fast |
SDK fail-fast policy triggered |
Usage / flag errors
| Code | When emitted |
|---|---|
usage |
--pick flag used without a following value |
usage |
Version flag (--version, -v) which gsd-tools never accepts |
usage |
Top-level no-args invocation (usage text) |
Config errors (config-get, config-set, config-ensure-section)
| Code | When emitted |
|---|---|
config_key_not_found |
config-get for a key that is absent from the config file |
config_no_file |
Config operation when .planning/config.json does not exist |
config_parse_failed |
Config file exists but is not valid JSON |
config_invalid_key |
config-set for a key outside the allowed whitelist |
Phase / workflow errors
| Code | When emitted |
|---|---|
phase_not_found |
Phase directory lookup returns no match |
summary_no_planning |
Summary operation when no .planning/ directory exists |
Graphify errors
| Code | When emitted |
|---|---|
graphify_no_graph |
Graphify query or diff when no graph has been built |
graphify_invalid_query |
Graphify query with a malformed query string |
Hook / security errors
| Code | When emitted |
|---|---|
hooks_opt_out |
Hooks are disabled via opt-out config |
security_scan_failed |
Security scan produced a finding that blocks the operation |
Fallback
| Code | When emitted |
|---|---|
unknown |
All other errors without a specific reason code assigned |
Writing tests
Always parse stderr with JSON.parse and assert on typed fields. Never use
.includes(), .match(), or regex on the raw error string.
// CORRECT: parse then assert on typed field
const result = runGsdTools(['--json-errors', 'bogus-command'], tmpDir);
assert.strictEqual(result.success, false);
const err = JSON.parse(result.error);
assert.strictEqual(err.ok, false);
assert.strictEqual(err.reason, 'sdk_unknown_command');
// WRONG: text matching (banned by lint-no-source-grep policy)
// assert.ok(result.error.includes('Unknown command'));
Adding a new error code
- Add the constant to
ERROR_REASONinget-shit-done/bin/lib/core.cjs(snake_case, prefixed by subsystem). - Pass it as the second argument to
error()at the call site. - Add a row to this document.
- Add a test asserting the new
reasoncode viaJSON.parse.