Skip to content

finding: the ADR-0087 conversion notice has three distinct prose forms across four renderings, and the CLI's text face disagrees with its own --json payload #15095

Description

@os-trump

Observation

An ADR-0087 D2 conversion notice is rendered into prose in four places, in three
different sentences
. Measured on d17f352b1b with a probe not tuned to any known site —
a whole-repo git grep for the prose fragment converted at load, which is what turned up
the sites the count did not predict:

# site sentence
1 packages/spec/src/conversions/apply.ts (message field, built for every notice) [protocol] converted SURFACE at PATH: 'from' to 'to' (deprecated; ADR-0087 conversion 'ID', retires from the load path in protocol N). Update the source to 'to'.
2 packages/spec/src/stack.zod.ts (warnConversionNotice, the defineStack face) defineStack: PATH: 'from' to 'to' (converted at load; conversion 'ID', retires in protocol N). Update the source to the canonical shape — the conversion stops running then.
3 packages/cli/src/utils/format.ts (formatConversionNotice, the text face of os build / os validate / os lint) PATH: 'from' to 'to' (converted at load; conversion 'ID', retires in protocol N)

Form 3 was three verbatim copies until #13743 gave it one source; that card counted those
three and is not affected by this one. What this records is the layer above it: the
three forms above are held equal by nothing at all
, and two of them are in packages/spec
where the CLI's formatter cannot reach (@objectstack/cli depends on @objectstack/spec,
not the reverse).

The sharper half — one command, two sentences

ConversionNotice.message (form 1) is what os build --json, os validate --json and
os lint --json publish inside each entry of their conversions array. Form 3 is what the
same three commands print to a human. So a single os validate run describes the same
conversion two different ways depending on which face the reader is looking at:

text face : pages[0].kind: 'jsx' to 'html' (converted at load; conversion 'page-kind-jsx-to-html', retires in protocol 12)
--json    : [protocol] converted page.kind at pages[0].kind: 'jsx' to 'html' (deprecated; ADR-0087 conversion 'page-kind-jsx-to-html', retires from the load path in protocol 12). Update the source to 'html'.

Both are truthful and the structured fields agree; only the prose differs. But an author
who greps a CI log for the sentence they saw in the terminal will not find it in the JSON,
and vice versa.

Why it is worth recording rather than fixing on sight

The notice is the only warning an old-shape author gets before a conversion retires and
their metadata stops loading, so the sentence is close to a contract — which is exactly why
its wording should not be changed casually. Three plausible dispositions, none obviously
right:

  • One source, in packages/spec. The only home every consumer can reach. Costs a
    prose renderer in a package whose Prime Directive says schemas/types/constants — though
    formatZodIssue is already exported from there, so the precedent exists.
  • Two sources by design, declared. The registers genuinely differ: form 2 warns once
    per process while an author is composing; form 3 is a line in a command's report. If that
    is deliberate, it should be written down where each is defined, and the message field's
    relationship to both should be stated.
  • Leave all three. Defensible if nobody reads two faces of the same run — but that is a
    claim about usage nobody has measured.

Provenance

Noticed while implementing #13743 (the three-copy consolidation in packages/cli). That
card's dispatch fenced packages/spec read-only, so forms 1 and 2 were out of reach; the
CLI-side pin packages/cli/src/utils/format.conversion-notice.test.ts records the form 1 /
form 3 split as a fact rather than leaving it to be discovered and "fixed" in one command.

Filed unassigned. Observation class, not a specific defect.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions