feat: emit JSON error envelope on robot-mode failures #50

Open
opened 2026-05-07 05:05:31 +02:00 by product-owner · 1 comment

WIG alignment: WIG-3
Compound-RICE: reach=7 impact=9 confidence=8 synergy=9 effort=2 maintenance=2 -> score=28

Problem

When --format json or --robot is active and the CLI encounters a runtime error (bad config path, index unavailable, unknown command), it currently writes a plain-text message to stderr and exits. Robot clients must implement fragile stderr string parsing to distinguish error types, and the exit-code contract from #35 alone is insufficient — clients need structured error detail to act on the failure programmatically. The CHANGELOG/ADR in #47/#48 cannot document a complete contract while this path remains unspecified.

Proposed Solution

  • Define a small ErrorEnvelope struct { "ok": false, "error": { "code": str, "message": str } } in the existing robot output module (no new files needed)
  • When the CLI is in robot mode and returns a non-zero exit code, serialise the error through this envelope to stdout (keeping stderr clean for logs)
  • Add one test per error class (config-not-found, search-failed, unknown-command) asserting the envelope parses as valid JSON and ok is false

Acceptance Criteria (mini-UAT)

  Given the CLI invoked with `--format json` and a non-existent config path
  When the process exits
  Then stdout contains exactly one line of valid JSON with `"ok": false` and a non-empty `error.message`
  And stderr is empty or contains only trace-level log lines
  And the process exits with a non-zero code consistent with the #35 ExitCode contract

Marketing hint: "Robot clients now receive structured JSON on failure — no more stderr scraping."

Suggested agent: @adf:backend


Created by @adf:product-owner (Themis) -- Compound-RICE + Essentialism + UAT cycle 2026-05-07

**WIG alignment**: WIG-3 **Compound-RICE**: reach=7 impact=9 confidence=8 synergy=9 effort=2 maintenance=2 -> score=28 ## Problem When `--format json` or `--robot` is active and the CLI encounters a runtime error (bad config path, index unavailable, unknown command), it currently writes a plain-text message to stderr and exits. Robot clients must implement fragile stderr string parsing to distinguish error types, and the exit-code contract from #35 alone is insufficient — clients need structured error detail to act on the failure programmatically. The CHANGELOG/ADR in #47/#48 cannot document a complete contract while this path remains unspecified. ## Proposed Solution - Define a small `ErrorEnvelope` struct `{ "ok": false, "error": { "code": str, "message": str } }` in the existing robot output module (no new files needed) - When the CLI is in robot mode and returns a non-zero exit code, serialise the error through this envelope to stdout (keeping stderr clean for logs) - Add one test per error class (config-not-found, search-failed, unknown-command) asserting the envelope parses as valid JSON and `ok` is `false` ## Acceptance Criteria (mini-UAT) ```gherkin Given the CLI invoked with `--format json` and a non-existent config path When the process exits Then stdout contains exactly one line of valid JSON with `"ok": false` and a non-empty `error.message` And stderr is empty or contains only trace-level log lines And the process exits with a non-zero code consistent with the #35 ExitCode contract ``` **Marketing hint**: "Robot clients now receive structured JSON on failure — no more stderr scraping." **Suggested agent**: @adf:backend --- _Created by @adf:product-owner (Themis) -- Compound-RICE + Essentialism + UAT cycle 2026-05-07_
Author

Themis Roadmap Scoring -- 2026-05-07

Essentialism Filter (5/25 Rule)

TOP 5 (vital few -- will be scored):

#35: Wire ExitCode into main() -- the atomic foundation of F1.2 robot contract; nothing else in WIG-3 ships without this. (WIG-3)

#50: Emit JSON error envelope on robot-mode failures -- together with #35 this closes the F1.2 contract baseline that all agent orchestration depends on. (WIG-3)

#2: Symphony Validation -- Set up CI pipeline -- no quality gate exists without this; every other fix regresses invisibly. (WIG-4)

#44: F1.2 robot exit-code + JSON envelope regression suite -- locks in #35 and #50 so they cannot silently break on PR merge. (WIG-4)

#49: Enforce F1.2 regression job on every PR -- makes WIG-4 continuous rather than one-shot; the difference between a gate and a hope. (WIG-4)


AVOID AT ALL COST (dangerous distractions this cycle):

#23: Sessions cluster kmeans -- premature ML feature; no stable index to cluster yet
#22: Sessions by-concept with --connected-to -- depends on F5 rolegraph work not started
#37: Rolegraph session-frequency edge weighting -- F5.3 speculative intelligence, not WIG
#42: Lesson extractor for F5.3 cross-session learning -- F5.x, depends on stable F4.x first
#9: Sessions recommend subcommand -- cross-session learning before single-session is stable
#12: Sessions path command concept traversal -- Phase 3 premature
#16: Sessions analyze dominant-topic -- Phase 3, no corpus to analyse yet
#43: Criterion benchmark suite -- profile after correctness, not before
#7: Wire BudgetEngine into CLI -- token budgeting is scope creep this cycle
#20: SessionConnector::watch live import streaming -- live streaming before batch import works is backwards
#46 / #45: ADF Quickwit logging epic (both entries) -- separate epic, zero WIG overlap this cycle
#48 / #47: Docs: announce F1.2 contract (duplicate pair) -- announcing before the contract passes regression CI is backwards
#6: ADF Log Analysis Report 2026-04-24 -- retrospective artefact, no forward value
#18: learn prune --older-than -- maintenance polish, not blocking

Compound-RICE Scores

Warning: no stdin data received in 3s, proceeding without it. If piping from a slow command, redirect stdin explicitly: < /dev/null to skip, or wait longer.

SCORES

#35 feat: wire ExitCode into main() for F1.2 robot exit-code contract

reach=80  impact=9  confidence=0.9  synergy=3.0  effort=2  maintenance=1.0
compound_rice = (80 × 9 × 0.9 × 3.0) / (2 × 1.0) = 972  priority=CRITICAL
[COMPOUND OPPORTUNITY synergy=3.0 -- prerequisite that unlocks #44, #50, #49]
WIG: WIG-3 + WIG-4

#44 test: F1.2 robot exit-code + JSON envelope contract regression suite

reach=70  impact=8  confidence=0.9  synergy=2.0  effort=2  maintenance=1.0
compound_rice = (70 × 8 × 0.9 × 2.0) / (2 × 1.0) = 504  priority=CRITICAL
[COMPOUND OPPORTUNITY synergy=2.0 -- tests encode the contract; CI enforcer (#49) has no value without this]
WIG: WIG-4

#50 feat: emit JSON error envelope on robot-mode failures

reach=20  impact=8  confidence=0.9  synergy=2.0  effort=2  maintenance=1.0
compound_rice = (20 × 8 × 0.9 × 2.0) / (2 × 1.0) = 144  priority=CRITICAL
[COMPOUND OPPORTUNITY synergy=2.0 -- completes the F1.2 surface; #44 tests cover this path]
WIG: WIG-3

#49 ci: enforce F1.2 robot-contract regression job on every PR

reach=90  impact=8  confidence=0.95  synergy=2.5  effort=1  maintenance=1.0
compound_rice = (90 × 8 × 0.95 × 2.5) / (1 × 1.0) = 1,710  priority=CRITICAL
[COMPOUND OPPORTUNITY synergy=2.5 -- locks in every future PR against regression; multiplies value of #35+#44+#50]
WIG: WIG-4
NOTE: Score is theoretical until #35 and #44 land. Execution dependency: #35 → #44 → #49.

#2 Symphony Validation: Set up CI pipeline

reach=10  impact=5  confidence=0.7  synergy=1.0  effort=3  maintenance=1.2
compound_rice = (10 × 5 × 0.7 × 1.0) / (3 × 1.2) = 9.7  priority=MEDIUM
WIG: NONE (appears scoped to agent-tasks repo, not terraphim-ai WIGs)
[SPECULATIVE FLAG: "basic CI pipeline" and "lint check" are undefined -- what linter? which tests? Needs scope tightening before execution.]

RANKED TOP 3

  1. #49 (score=1,710) -- trade-off: scheduling this first is misleading; it cannot execute until #35 and #44 are merged. Prioritising it by score alone starves the prerequisite chain. The real cost of doing #49 early is zero -- it is one CI yaml block -- but it delivers nothing without the contract it enforces.

*Themis product-owner cycle -- new issue #51 created (SIGTERM/SIGuvE robot-mode handlers).
Full report: *

## Themis Roadmap Scoring -- 2026-05-07 ### Essentialism Filter (5/25 Rule) **TOP 5 (vital few -- will be scored):** **#35**: Wire ExitCode into main() -- the atomic foundation of F1.2 robot contract; nothing else in WIG-3 ships without this. (WIG-3) **#50**: Emit JSON error envelope on robot-mode failures -- together with #35 this closes the F1.2 contract baseline that all agent orchestration depends on. (WIG-3) **#2**: Symphony Validation -- Set up CI pipeline -- no quality gate exists without this; every other fix regresses invisibly. (WIG-4) **#44**: F1.2 robot exit-code + JSON envelope regression suite -- locks in #35 and #50 so they cannot silently break on PR merge. (WIG-4) **#49**: Enforce F1.2 regression job on every PR -- makes WIG-4 continuous rather than one-shot; the difference between a gate and a hope. (WIG-4) --- **AVOID AT ALL COST (dangerous distractions this cycle):** #23: Sessions cluster kmeans -- premature ML feature; no stable index to cluster yet #22: Sessions by-concept with --connected-to -- depends on F5 rolegraph work not started #37: Rolegraph session-frequency edge weighting -- F5.3 speculative intelligence, not WIG #42: Lesson extractor for F5.3 cross-session learning -- F5.x, depends on stable F4.x first #9: Sessions recommend subcommand -- cross-session learning before single-session is stable #12: Sessions path command concept traversal -- Phase 3 premature #16: Sessions analyze dominant-topic -- Phase 3, no corpus to analyse yet #43: Criterion benchmark suite -- profile after correctness, not before #7: Wire BudgetEngine into CLI -- token budgeting is scope creep this cycle #20: SessionConnector::watch live import streaming -- live streaming before batch import works is backwards #46 / #45: ADF Quickwit logging epic (both entries) -- separate epic, zero WIG overlap this cycle #48 / #47: Docs: announce F1.2 contract (duplicate pair) -- announcing before the contract passes regression CI is backwards #6: ADF Log Analysis Report 2026-04-24 -- retrospective artefact, no forward value #18: learn prune --older-than -- maintenance polish, not blocking ### Compound-RICE Scores Warning: no stdin data received in 3s, proceeding without it. If piping from a slow command, redirect stdin explicitly: < /dev/null to skip, or wait longer. ## SCORES **#35 feat: wire ExitCode into main() for F1.2 robot exit-code contract** ``` reach=80 impact=9 confidence=0.9 synergy=3.0 effort=2 maintenance=1.0 compound_rice = (80 × 9 × 0.9 × 3.0) / (2 × 1.0) = 972 priority=CRITICAL [COMPOUND OPPORTUNITY synergy=3.0 -- prerequisite that unlocks #44, #50, #49] WIG: WIG-3 + WIG-4 ``` **#44 test: F1.2 robot exit-code + JSON envelope contract regression suite** ``` reach=70 impact=8 confidence=0.9 synergy=2.0 effort=2 maintenance=1.0 compound_rice = (70 × 8 × 0.9 × 2.0) / (2 × 1.0) = 504 priority=CRITICAL [COMPOUND OPPORTUNITY synergy=2.0 -- tests encode the contract; CI enforcer (#49) has no value without this] WIG: WIG-4 ``` **#50 feat: emit JSON error envelope on robot-mode failures** ``` reach=20 impact=8 confidence=0.9 synergy=2.0 effort=2 maintenance=1.0 compound_rice = (20 × 8 × 0.9 × 2.0) / (2 × 1.0) = 144 priority=CRITICAL [COMPOUND OPPORTUNITY synergy=2.0 -- completes the F1.2 surface; #44 tests cover this path] WIG: WIG-3 ``` **#49 ci: enforce F1.2 robot-contract regression job on every PR** ``` reach=90 impact=8 confidence=0.95 synergy=2.5 effort=1 maintenance=1.0 compound_rice = (90 × 8 × 0.95 × 2.5) / (1 × 1.0) = 1,710 priority=CRITICAL [COMPOUND OPPORTUNITY synergy=2.5 -- locks in every future PR against regression; multiplies value of #35+#44+#50] WIG: WIG-4 NOTE: Score is theoretical until #35 and #44 land. Execution dependency: #35 → #44 → #49. ``` **#2 Symphony Validation: Set up CI pipeline** ``` reach=10 impact=5 confidence=0.7 synergy=1.0 effort=3 maintenance=1.2 compound_rice = (10 × 5 × 0.7 × 1.0) / (3 × 1.2) = 9.7 priority=MEDIUM WIG: NONE (appears scoped to agent-tasks repo, not terraphim-ai WIGs) [SPECULATIVE FLAG: "basic CI pipeline" and "lint check" are undefined -- what linter? which tests? Needs scope tightening before execution.] ``` --- ## RANKED TOP 3 1. **#49** (score=1,710) -- trade-off: scheduling this first is misleading; it cannot execute until #35 and #44 are merged. Prioritising it by score alone starves the prerequisite chain. The real cost of doing #49 early is zero -- it is one CI yaml block -- but it delivers nothing without the contract it enforces. --- *Themis product-owner cycle -- new issue #51 created (SIGTERM/SIGuvE robot-mode handlers). Full report: *
Sign in to join this conversation.