Summary
On v1.0.0-beta.63 (e531946c8c), a refusing tracedecay tool <name> --json prints one of three different shapes depending on the tool and on where the refusal is produced. The #2651 breaking-change note says tracedecay tool --json output carries the typed problem "only at structuredContent.problem; the top-level problem member is removed". That holds for search, but not for callers or for a daemon-connect refusal. The MCP wire itself is consistent: tools/call tracedecay_callers answers isError: true with structuredContent.problem only.
Repro (installed beta.63, run from /fast/projects/tracedecay)
# 1. search: MCP tool-result shape (matches #2651)
c=$(tracedecay tool search --query formatBytes --limit 2 --format json | jq -r .next_cursor)
tracedecay tool search --query formatSignedBytes --limit 2 --cursor "$c" --json
# exit 1; stdout keys: isError=true, structuredContent.problem.code="cursor.parameter_changed", no top-level "problem"
# 2. callers: application envelope with a top-level problem
tracedecay tool callers --node-id no-such-node-beta63 --json
# exit 1; stdout:
# {"contract":{"schema_id":"schema.application.primitive.code-callers.result","schema_revision":1},
# "request_id":"request.cli.582034c1...","problem":{"revision":1,"kind":"not_found_or_not_authorized",
# "code":"not_found_or_not_authorized",...,"retry":"never",...}}
# no isError, no structuredContent
# 3. daemon connect refusal: a third, untyped shape
TRACEDECAY_DAEMON_SOCKET=/tmp/does-not-exist.sock tracedecay tool status --json
# exit 69; stdout:
# {"problem":{"detail":"TraceDecay daemon socket '/tmp/does-not-exist.sock' named by TRACEDECAY_DAEMON_SOCKET is not available. ...",
# "reason_code":"daemon_connect_down","retryable":true,"tool":"tracedecay_status"}}
For comparison, the same callers refusal over MCP (tracedecay serve --path /fast/projects/tracedecay, tools/call tracedecay_callers {"node_id":"no-such-node-beta63"}) returns result keys _meta, content, isError, structuredContent, with isError: true and structuredContent = {problem: {code: "not_found_or_not_authorized", ...}}.
Expected
tool --json has one refusal shape for every tool: the typed problem record at structuredContent.problem and isError: true, as the #2651 note documents. Case 3 is not a typed problem record at all (reason_code/retryable instead of code/kind/retry/legal_actions), so a script cannot read one field to find out whether it should retry.
Impact
Scripts written against the #2651 note (jq .structuredContent.problem) miss the problem for application-primitive tools such as callers, and they miss it for daemon-down refusals.
Summary
On v1.0.0-beta.63 (
e531946c8c), a refusingtracedecay tool <name> --jsonprints one of three different shapes depending on the tool and on where the refusal is produced. The #2651 breaking-change note saystracedecay tool --jsonoutput carries the typed problem "only atstructuredContent.problem; the top-levelproblemmember is removed". That holds forsearch, but not forcallersor for a daemon-connect refusal. The MCP wire itself is consistent:tools/call tracedecay_callersanswersisError: truewithstructuredContent.problemonly.Repro (installed beta.63, run from
/fast/projects/tracedecay)For comparison, the same callers refusal over MCP (
tracedecay serve --path /fast/projects/tracedecay,tools/calltracedecay_callers{"node_id":"no-such-node-beta63"}) returnsresultkeys_meta, content, isError, structuredContent, withisError: trueandstructuredContent={problem: {code: "not_found_or_not_authorized", ...}}.Expected
tool --jsonhas one refusal shape for every tool: the typed problem record atstructuredContent.problemandisError: true, as the #2651 note documents. Case 3 is not a typed problem record at all (reason_code/retryableinstead ofcode/kind/retry/legal_actions), so a script cannot read one field to find out whether it should retry.Impact
Scripts written against the #2651 note (
jq .structuredContent.problem) miss the problem for application-primitive tools such ascallers, and they miss it for daemon-down refusals.