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.
Observation
An ADR-0087 D2 conversion notice is rendered into prose in four places, in three
different sentences. Measured on
d17f352b1bwith a probe not tuned to any known site —a whole-repo
git grepfor the prose fragmentconverted at load, which is what turned upthe sites the count did not predict:
packages/spec/src/conversions/apply.ts(messagefield, 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'.packages/spec/src/stack.zod.ts(warnConversionNotice, thedefineStackface)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.packages/cli/src/utils/format.ts(formatConversionNotice, the text face ofos 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/specwhere the CLI's formatter cannot reach (
@objectstack/clidepends on@objectstack/spec,not the reverse).
The sharper half — one command, two sentences
ConversionNotice.message(form 1) is whatos build --json,os validate --jsonandos lint --jsonpublish inside each entry of theirconversionsarray. Form 3 is what thesame three commands print to a human. So a single
os validaterun describes the sameconversion two different ways depending on which face the reader is looking at:
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:
packages/spec. The only home every consumer can reach. Costs aprose renderer in a package whose Prime Directive says schemas/types/constants — though
formatZodIssueis already exported from there, so the precedent exists.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
messagefield'srelationship to both should be stated.
claim about usage nobody has measured.
Provenance
Noticed while implementing #13743 (the three-copy consolidation in
packages/cli). Thatcard's dispatch fenced
packages/specread-only, so forms 1 and 2 were out of reach; theCLI-side pin
packages/cli/src/utils/format.conversion-notice.test.tsrecords 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.