Skip to content

Commit 945ffbe

Browse files
claude[bot]claude
andauthored
fix(cli): carry the undeclared-authoring-key warnings in the os build --json payload (#11726)
* fix(cli): carry the undeclared-authoring-key warnings in the `os build --json` payload Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR * docs(changeset): describe the `os build --json` warning-parity fix Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 9086761 commit 945ffbe

3 files changed

Lines changed: 380 additions & 14 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
'@objectstack/cli': patch
3+
---
4+
5+
Carry the undeclared-authoring-key warnings in the `os build --json` payload,
6+
so its `warnings` list matches `os validate --json` on the same tree
7+
8+
`os build --json` reported a strictly smaller `warnings` list than
9+
`os validate --json` did for the same stack, and the missing members were
10+
exactly the "your key was dropped at load" ones (#3786 / ADR-0087). A CI job
11+
gating on `os build --json` therefore could not see the class of warning that
12+
silently discards authored metadata — while the identical job gating on
13+
`os validate --json` did.
14+
15+
Measured over one temp project at `origin/main` `4ceae8ab0`, three faces of one
16+
authored stack whose field carries an undeclared key nested in `visibleWhen`:
17+
18+
```
19+
os build ⚠ Undeclared authoring keys (1) — dropped at load (#3786)
20+
os validate --json warnings: [ {rule record}, "…zzzUnknownKey…" ]
21+
os build --json warnings: [ {rule record} ] ← the dropped list
22+
```
23+
24+
`compile.ts` computed the findings and then formatted them **inside** the
25+
`if (… && !flags.json)` print block, which put them structurally out of reach
26+
of the payload: computed, then discarded, for the one audience `--json` exists
27+
to serve. `os validate --json` had this exact defect on its own face and fixed
28+
it by mapping the findings through `formatUnknownAuthoringKey` at the
29+
computation site; `os build` now does the same, so one list feeds both faces
30+
and they cannot report different sets.
31+
32+
**No new key.** The findings land in the `warnings` key the payload already
33+
declared — its own comment has always said "the whole registry's advisory set,
34+
in the shape `os validate --json` reports" — carried as formatted strings
35+
beside the authoring-rule records, which is byte-for-byte the heterogeneous
36+
shape `os validate --json` already ships. Consumers reading `warnings` off
37+
either command now read one shape for one class of warning. The payload's
38+
top-level key set is unchanged and pinned as unchanged.
39+
40+
This also makes an existing promise true. The truncation notice added in
41+
17.2.0 ends with "re-run with `--json` for the full list"; that pointer was
42+
honest about the authoring-rule advisories and would have been false about the
43+
undeclared-key list, which is why that change left the second list without a
44+
notice.
45+
46+
Text output is unchanged.

‎packages/cli/src/commands/compile.ts‎

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -285,14 +285,26 @@ export default class Compile extends Command {
285285
// through it; this covers the ones that skip it (a plain object
286286
// default-export, `strict: false`) and would otherwise emit an
287287
// artifact with the key quietly gone. Advisory, never fatal.
288-
const unknownKeyFindings = [
288+
//
289+
// [#11643] FORMATTED HERE, once, and consumed by BOTH faces — the
290+
// text block just below and the `--json` payload at the end of this
291+
// command. The findings used to be formatted inside that print
292+
// block, i.e. under `!flags.json`, which made them structurally
293+
// unreachable for the payload: computed, then discarded, for the one
294+
// audience `--json` exists to serve. `os validate --json` had this
295+
// exact defect and fixed it this exact way —
296+
// `.map(formatUnknownAuthoringKey)` at the computation site, beside
297+
// its own `normalized` — so hoisting the formatting rather than
298+
// restating it at the payload is what keeps the two faces from
299+
// reporting different sets. One list cannot drift from itself.
300+
const unknownKeyWarnings = [
289301
...lintUnknownStackKeys(normalized as Record<string, unknown>, ObjectStackDefinitionSchema),
290302
...lintUnknownAuthoringKeys(normalized as Record<string, unknown>, ObjectStackDefinitionSchema),
291-
];
292-
if (unknownKeyFindings.length > 0 && !flags.json) {
293-
printWarning(`Undeclared authoring keys (${unknownKeyFindings.length}) — dropped at load (#3786)`);
294-
for (const f of unknownKeyFindings.slice(0, 50)) {
295-
console.log(` • ${formatUnknownAuthoringKey(f)}`);
303+
].map(formatUnknownAuthoringKey);
304+
if (unknownKeyWarnings.length > 0 && !flags.json) {
305+
printWarning(`Undeclared authoring keys (${unknownKeyWarnings.length}) — dropped at load (#3786)`);
306+
for (const w of unknownKeyWarnings.slice(0, 50)) {
307+
console.log(` • ${w}`);
296308
}
297309
}
298310

@@ -464,15 +476,39 @@ export default class Compile extends Command {
464476
// The whole registry's advisory set, in the shape `os validate --json`
465477
// reports. This key used to carry the widget rule's warnings alone —
466478
// one gate out of the twenty-odd that raise them.
467-
warnings: ruleAdvisories,
479+
//
480+
// [#11643] …and then, still, only the RULE advisories: the #3786
481+
// undeclared-authoring-key findings were computed above and dropped,
482+
// so a CI consumer reading `warnings` off this command saw a strictly
483+
// smaller set than the same consumer reading it off
484+
// `os validate --json` on the same tree — missing exactly the "your
485+
// key was dropped at load" members. Two costs, both real: the machine
486+
// faces of two commands disagreed about one class of warning, and
487+
// #11529's truncation notice points the reader at `--json` "for the
488+
// full list", which was true of one advisory list and false of the
489+
// other.
490+
//
491+
// MIXED BY DESIGN, because that is what parity means here. This list
492+
// now carries the rule advisories as RECORDS and the undeclared-key
493+
// findings as formatted STRINGS — byte-for-byte the shape
494+
// `os validate --json` has shipped since it fixed this on its own
495+
// face (`warnings: [...ruleAdvisories, ...docWarnings,
496+
// ...unknownKeyWarnings, …]`, likewise a heterogeneous list). The
497+
// homogeneity this key used to have was not a contract; it was the
498+
// symptom of the omission.
499+
warnings: [...ruleAdvisories, ...unknownKeyWarnings],
468500
// [#10678] Body-extraction failures that made a callable fall back to
469-
// the legacy .mjs bundle. A SEPARATE key on purpose: `warnings` above
470-
// is author-time RULE advisories (`{where,message,rule,path,hint}`)
471-
// and is the shape `os validate --json` shares — folding a different
472-
// record shape (`{origin,reason}`) into it would break that contract
473-
// for every consumer that reads one shape from either command. Empty
474-
// array when every callable lowered cleanly, so a CI consumer can read
475-
// the key unconditionally.
501+
// the legacy .mjs bundle. A SEPARATE key on purpose, and the reason is
502+
// parity too — the opposite way round from `unknownKeyWarnings` just
503+
// above. `{origin,reason}` extraction records have NO counterpart in
504+
// `os validate --json`: that command lowers no handlers, so there is
505+
// no cross-command list for these to join, and folding a shape only
506+
// ONE command can ever emit into the shared key would teach consumers
507+
// a shape the other command never ships. The undeclared-key findings
508+
// are the mirror case — validate already carries them, in `warnings`,
509+
// so that is where build has to carry them too. Empty array when every
510+
// callable lowered cleanly, so a CI consumer can read the key
511+
// unconditionally.
476512
bodyExtractionWarnings: lowering.bodyExtractionWarnings,
477513
// Same key `os validate --json` uses, so a CI consumer reads one shape
478514
// from either command rather than learning two.

0 commit comments

Comments
 (0)