Confirmed while building Episode 20 ("Mist") demo material. Real,
silent, no-error-signaled data loss on the client side — distinct
from staging#212 (mist push crash), found in the same episode's
research pass.
Repro:
# server-side, confirmed correct via raw curl + hand-signed header:
$ curl .../api/mists/Es8ujVEEAd2c/forks -H "Authorization: ..."
[{"mistId":"Hq4kz66XdNnR","owner":"gabriel","forkParentId":"Es8ujVEEAd2c",...}]
# same data, via the CLI:
$ muse mist forks gabriel/Es8ujVEEAd2c --json
{}
The server returns a genuine, correct, non-empty JSON array. The
CLI prints an empty JSON object. No error, no warning, exit code 0
— indistinguishable from "this mist genuinely has zero forks," even
though forkCount: 1 on the same mist (confirmed via muse mist read)
proves the fork is real and the server-side data is intact.
result = _json_mod.loads(raw.decode("utf-8"))
...
if not isinstance(result, dict):
return {}
return result
Any endpoint whose top-level JSON response is a list (as
GET /api/mists/{id}/forks — and per this function's own return-type
annotation, -> "dict[str, object]", this was apparently never
expected to happen) gets silently coerced to {}. mist.py::run_forks()
then does print(json.dumps(data)) for the --json path with no
further validation, propagating the empty dict straight to output.
Blast radius beyond forks:hub_json() is the shared transport
helper for every muse mist * (and likely other) subcommand that
talks to MuseHub's JSON API. Any other endpoint that legitimately
returns a top-level array — not just /forks — would hit the exact
same silent discard. Worth auditing all _hub_api()/hub_json()
call sites for endpoints with array response bodies, not just fixing
forks.
Suggested fix: change hub_json()'s return type to
dict[str, object] | list[object] (or a union JSON-value type) and
remove the isinstance coercion entirely — let callers handle the
shape they expect, the same way run_forks() already has a
(currently unreachable) data if isinstance(data, list) else data.get("forks", []) fallback for the non---json render path,
suggesting the original author anticipated array responses but the
transport layer was never updated to allow them through.
Confirmed while building Episode 20 ("Mist") demo material. Real, silent, no-error-signaled data loss on the client side — distinct from staging#212 (mist push crash), found in the same episode's research pass.
Repro:
The server returns a genuine, correct, non-empty JSON array. The CLI prints an empty JSON object. No error, no warning, exit code 0 — indistinguishable from "this mist genuinely has zero forks," even though
forkCount: 1on the same mist (confirmed viamuse mist read) proves the fork is real and the server-side data is intact.Root cause:
muse/core/transport.py::HttpTransport.hub_json():Any endpoint whose top-level JSON response is a list (as
GET /api/mists/{id}/forks— and per this function's own return-type annotation,-> "dict[str, object]", this was apparently never expected to happen) gets silently coerced to{}.mist.py::run_forks()then doesprint(json.dumps(data))for the--jsonpath with no further validation, propagating the empty dict straight to output.Blast radius beyond forks:
hub_json()is the shared transport helper for everymuse mist *(and likely other) subcommand that talks to MuseHub's JSON API. Any other endpoint that legitimately returns a top-level array — not just/forks— would hit the exact same silent discard. Worth auditing all_hub_api()/hub_json()call sites for endpoints with array response bodies, not just fixingforks.Suggested fix: change
hub_json()'s return type todict[str, object] | list[object](or a union JSON-value type) and remove theisinstancecoercion entirely — let callers handle the shape they expect, the same wayrun_forks()already has a (currently unreachable)data if isinstance(data, list) else data.get("forks", [])fallback for the non---jsonrender path, suggesting the original author anticipated array responses but the transport layer was never updated to allow them through.Deferred per usual until after the season wraps.