Skip to content

--track --save --format json hands a parser two lines of prose and still exits 0 #84

Description

@vyncint

Today — the report alone is clean; the run that also saves the plan is not:

$ mossaic-art VYNCINT --year 2027 --track --plan /dev/null --format json --no-colour | jq . >/dev/null && echo ok
ok

$ mossaic-art VYNCINT --year 2027 --track --save --plan ./plan.json --format json --no-colour > out.json 2> out.err
exit=0  stdout=845 bytes  stderr=0 bytes
$ head -4 out.json
saved ./plan.json — from now on:
  mossaic-art --track

{
$ python3 -c "import json;json.load(open('out.json'))"
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Through a pipe, as a script would write it:

$ … --format json --no-colour 2>/dev/null | jq .
jq: parse error: Invalid numeric literal at line 1, column 6
art exit=101  jq exit=5

--format markdown is the same shape: the first three lines of stdout are the saved … block, then the ### … heading.

Why it is worth fixing--help describes --format as "text (default), json or markdown — what --track prints. The GitHub Action reads json and markdown", and CHANGELOG.md:842 calls json "a shape a machine and a message can carry". A machine shape means stdout carries the document and nothing else, and every other thing a track run says already obeys that: all five notes go to stderr (src/bin/mossaic-art.rs:205, :487, :589, :1191, :1664). The saved … confirmation at :233 and :613 is the single println! and the only message that can run ahead of the document.

Because the exit code is 0 and stderr is empty, there is no signal anywhere. A wrapper that checks $? before parsing sees success; a wrapper that pipes into jq sees jq's parse error plus mossaic-art turning into 101 from the resulting broken pipe, which points at the pipe rather than at the extra line. It bites only on the run that writes the plan — the run after it is clean — so it reads as a flake on first use. The binary is already careful about this class of pairing: src/bin/mossaic-art.rs:2057-2069 refuses --track --snapshot, --track --write and --track --repo with "…would do nothing. Drop one of them." --save is the one side-effect flag --track still permits, and it is the one that corrupts the machine stream.

Scoped honestly: the shipped Action is unaffected — action/action.yml:315-316 runs both formats without --save — and docs/ART.md:576-580 teaches the two flags as separate invocations rather than together, so the exposure is a hand-written script. This is filed on "machine-readable output silently non-parsable at exit 0", not on breadth.

Fixprintln!eprintln! at src/bin/mossaic-art.rs:233 and :613. That is the whole change, and it keeps the message in front of a human at a terminal. If the line is felt to belong on stdout for the text report, gate it on options.format == Format::Text instead; json and markdown are the two modes where stdout is a document. The --track guard list at :2057 is not the problem — leave --save permitted.

Done whenmossaic-art VYNCINT --year 2027 --track --save --plan p.json --format json | jq . succeeds and p.json is written; the same with --format markdown produces a document whose first line is the ### … heading; the saved … confirmation is still shown, on stderr, in all three formats; and a case in tests/art_cli.rs asserts stdout parses as JSON for a --track --save --format json run and starts with ### for markdown.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomers

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions