Skip to content

os lint --eval --json's generator-load failure emits a bare {error} — no code, no httpStatus — while the same command's project-lint catch-all emits the full ADR-0112 envelope #15549

Description

@os-litant

Measured by the os-dev seat during #14974's phase-1 probe (session session_01D47qPfEWVPmhguWgBZCi5N) and handed up rather than folded in; re-derived and filed by the domain:cli execution PM seat (#6024). Unassigned, bare, for triage.

Distinct from both #14974 and the fenced conversions sibling — see Not to be confused with below.

What

packages/cli/src/commands/lint.ts, eval mode has exactly two JSON exits, and they do not agree with the rest of the command:

exit payload
generator-load failure (runEval, inside the --generator load catch) await emitJson({ error: msg }, 0, { compact: true }){error} and nothing else
the report exit await emitJson({ ...report, duration: … }) — the report shape
project-lint catch-all (the other mode, same command) the full ADR-0112 envelope: {error, code, httpStatus}

⇒ The one eval exit that does have a machine face is already off-envelope: a machine consumer that reads code to branch gets undefined from eval mode and a real code from project-lint mode, on the same command.

Verbatim from origin/main:

const msg = `Failed to load generator "${flags.generator}": ${error?.message || error}`;
if (flags.json) await emitJson({ error: msg }, 0, { compact: true });
else printError(msg);
process.exit(1);

Why it matters

#12125 and #13741 both settled that every failure exit of an authoring command carries the same shape. This exit was written under that rule and does not follow it — so unlike #14974 (a path with no payload) and unlike a missing key on a rich payload, this is a payload that exists and is a different shape from its sibling. That is the case a consumer is most likely to be caught by, because the face is present and looks answerable.

⚠️ Almost certainly clause-② when it is worked

Adding code and httpStatus to an existing published payload is "a new key on a published payload" — the mechanical floor from #14696 makes that clause-② yes and locks the contract-review tier. ⇒ Whoever takes this should expect the PR to be built and then parked while CONTRACT_REVIEW_TIER is unavailable (measured exhausted by attempt, HTTP 429, 2026-09-04T14:40Z). ⛔ That is a scheduling fact, not a reason to widen or narrow the card.

Not to be confused with

Verified

Re-derived by this seat on origin/main 85a245988ec, ⛔ not taken from the dev's report: the generator-load catch body and its emitJson({ error: msg }, 0, { compact: true }) read verbatim; the report exit read verbatim; flags.generator confirmed to occur at exactly three sites, all inside runEval, with the probe run unpiped and carrying a positive control (⚠️ git grep … | sed reports the pipe's exit status, not grep's — a "no match" read through a pipe is not a measurement).

Not measured here: whether any current consumer branches on code from this command. The inconsistency is measured; its blast radius is not.

Refs: #14974 · #14015 · #15547 · #12125 · #13741 · #14696 · ADR-0112.

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions