Commit 4921a95
Two follow-ups to #3780. Both were smaller in the work and larger in the
finding than the issue text implied.
## 1 — `--json` truncation: every command, and the second document it hid
#3780 routed THREE commands through `emitJson`. The other ~100 emission sites
still wrote machine output with `console.log`, which on a PIPE is cut off at one
64 KiB buffer when the command exits right after. Invisible to whoever writes
it: stdout to a TTY is synchronous, so every interactive run looks perfect while
every scripted consumer — the only audience `--json` has — gets invalid JSON.
The exit need not be explicit. oclif ends failing commands with `handle()` →
`Exit.exit()` → `process.exit()` and flushes nothing on that path (`flush()`
runs only on `execute()`'s success path), so a plain `this.exit(1)` or any
thrown error truncates identically. Measured on a 300 009-byte payload:
`process.exit(1)` 65 536, `throw` 65 536, natural return 300 009,
`process.exitCode = 1` 300 009. 73 of the 104 sites had that shape, and `lint`
was itself only half fixed by #3780 — `--eval --json` writes a whole corpus
report and was still on `console.log`.
- All 104 sites go through `emitJson`; `formatOutput`'s json/yaml branches
through the same drain-aware `emitText` (`--format yaml` truncated too),
making it async — 32 call sites now await it.
- Control flow untouched: a following `this.exit(1)` stays, simply safe once
the buffer has drained.
- Output bytes unchanged. Roughly half the sites emitted compact and half
indented; `{ compact: true }` preserves whichever each had, keeping this a
truncation fix rather than an output-format change.
- An ESLint rule (root script runs `--no-inline-config`, so no per-site opt-out)
rejects `console.log(JSON.stringify(…))` under packages/cli/src and
un-awaited `formatOutput`. It earned its place immediately: #3789 landed
`os validate`'s authoring lints with the old pattern and the rule caught it
on the merge.
Draining the write exposed a second defect underneath. Because `this.exit(1)`
THROWS, a command whose body sits in one `try` unwinds its inner "report and
stop" into the outer `catch`, which reports again — `os validate --json` printed
TWO JSON documents, neither valid JSON nor valid JSONL. Truncation had been
hiding the second one. Nine commands had this shape; their catches now re-throw
the exit signal (`isExitSignal`).
`os validate --json`, 900 schema errors, piped: 131 072 bytes unparseable
before; 1 514 711 in two documents with the truncation fix alone; 1 514 648 as
one parseable document with both.
Deliberately NOT fixed by forcing stdout into blocking mode process-wide, which
would be one line and cover everything: the same binary runs `os serve` /
`os dev`, and a blocking write to a pipe with a slow reader blocks the event
loop — trading truncated JSON for a server that stalls on its own logs.
## 2 — #3762's remainder: the debt was 1 string, not 231
The open item read "77 strings short per locale in apps.*/dashboards.*, needs an
emit decision (drop --objects-only, or a companion .apps.generated.ts)".
Measured, the premise did not hold. Of the 77 declared keys per locale, 76 were
already translated in the hand-authored `<locale>.ts` files and had been for
months. Exactly one was genuinely missing:
`apps.studio.navigation.nav_app_builder.label`, absent in all four locales
including `en`. The 231 was a measurement artifact — the config declares
SETUP_APP / STUDIO_APP / ACCOUNT_APP and SystemOverviewDashboard, but its
`translations` merge baseline listed only the two GENERATED subtrees.
Neither proposed emit is right, and the second would have caused damage. The
Setup app is a shell of empty group anchors; its ~25 entries are contributed at
RUNTIME by SETUP_NAV_CONTRIBUTIONS and by capability plugins (ADR-0029 D7), so
a bundle generated from a static walk is structurally incomplete —
regenerating over the hand-authored files would have DELETED 40 live nav
translations per locale. Dropping `--objects-only` fails differently:
`kind:'full'` folds all 803 metadata-form keys into the objects bundle and
renames the export the baseline imports.
The split is correct and is now written down: objects/metadataForms are
generated and gated by the bundle-drift check; apps/dashboards/pages are
hand-authored and gated by the coverage ratchet. Only the baseline was wrong.
- Extract config's `translations` carries the per-locale assemblers, with
objects/metadataForms still pinned to the committed generated files.
- nav_app_builder translated in all four locales, wording harvested from the
repo's own precedent for "builder" (构建器 / ビルダー / generador).
- nav_workflows removed from all four — its entry is gone from STUDIO_APP and
nothing contributes to that app, so the translation was dead.
- Ratchet baselined 231 -> 0 (repo total 996 -> 765), making platform-objects
the ninth package where the ratchet is a strict gate.
- A CLI-independent parity test walks the statically declared Studio and
Account navigation plus the dashboard's widgets and asserts a translation in
every locale, and the reverse — no translation outlives its nav item.
An untranslated nav id is invisible in the UI: it falls back to the app's own
English label, so a Chinese Studio menu shows one English entry among thirty.
That is why this needs a gate and not a one-time sweep.
## On the gates themselves
Every new gate was checked in BOTH directions — red before the fix, green after.
That discipline caught a defect in my own test: the truncation control case
initially raced the reader's drain, passing locally at 65 536 bytes and failing
on a runner whose reader kept up. A gate that goes red by machine speed is no
better than one that cannot go red, so it was rewritten to remove the race —
never read the pipe until the child exits, 4 MB payload, assert only the
shortfall. The technique is valid only for the broken pattern: pointed at
`emitJson` it hangs forever, because awaiting the write callback is what the fix
does. Verified rather than assumed.
Closes #3762.
Co-authored-by: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
1 parent c7f4417 commit 4921a95
47 files changed
Lines changed: 904 additions & 245 deletions
File tree
- .changeset
- packages
- cli
- src
- commands
- cloud
- data
- environments
- i18n
- meta
- migrate
- utils
- test
- platform-objects
- scripts
- src/apps/translations
- scripts
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
81 | 131 | | |
82 | 132 | | |
83 | 133 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| |||
105 | 105 | | |
106 | 106 | | |
107 | 107 | | |
108 | | - | |
109 | | - | |
110 | | - | |
| 108 | + | |
111 | 109 | | |
112 | 110 | | |
113 | 111 | | |
| |||
145 | 143 | | |
146 | 144 | | |
147 | 145 | | |
148 | | - | |
| 146 | + | |
149 | 147 | | |
150 | 148 | | |
151 | 149 | | |
| |||
157 | 155 | | |
158 | 156 | | |
159 | 157 | | |
160 | | - | |
| 158 | + | |
161 | 159 | | |
162 | 160 | | |
163 | 161 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| |||
39 | 39 | | |
40 | 40 | | |
41 | 41 | | |
42 | | - | |
| 42 | + | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
49 | | - | |
| 49 | + | |
50 | 50 | | |
51 | 51 | | |
52 | 52 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
29 | | - | |
| 29 | + | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
| 57 | + | |
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| |||
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| 73 | + | |
73 | 74 | | |
74 | | - | |
| 75 | + | |
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
| |||
0 commit comments