Skip to content

Commit 37a19d4

Browse files
fix(cli): objectui check prints the refused key and path, and never calls an unvalidated file passed (objectui#11007) (#11053)
Fixes #11007 Clause-②: no `domain:ui` seat 2, branch `claude/issue-11007-check-prints-key`, session `https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec`. Placeholders are spelled in capitals (`FILE`) because GitHub strips tag-shaped text from a body. ## What this changes `objectui check` recognises a JSON file in one of two ways: a structural root key (`children`, `className`, …) admits it without parsing it, or the whole document validates. Measured on `origin/main` at `bf7ab35ce` before this change, with the card's two documents: - A root `object-map` whose `map` block carries `latitudeFieId` was listed under "did not validate" as `map.json (type "object-map")`. The validator's issue was already in hand from the recognising parse, and was dropped. - The same node under a page's `children` was admitted on `children` and never parsed. The run ended `✓ All checks passed`. No exit call in either run. Per the triage ruling (both halves, narrowly): - **(a)** Each "did not validate" entry now carries one indented line: the first issue's path, spelled by the formatter behind `objectui validate`'s `Path:` line, and its message, with the issue count when there is more than one. For the card's root case it reads `Issue at map: Unrecognized key: "latitudeFieId"`. It reuses the issues of the parse the recogniser already ran. Skipped (foreign) files still get no diagnosis. - **(b)** The no-error close is a tally: `ObjectUI schema files: V validated, S recognised but not validated, U did not validate. This sweep is advisory; run objectui validate FILE for a verdict.` Nothing reads as a pass. The parse-error close (`Found N errors`, exit 1) is untouched. - No nested walk in `check`. A structurally admitted file is still not parsed, and a pin holds that boundary: a later walk turns it red. - Exit codes do not move. ## Surface beyond the claim's file list, and why - **`packages/cli/src/utils/issue-path.ts` (new, `formatIssuePath`), and `validate.ts` switched to it.** The suggested route said to reuse `validate`'s formatter rather than copy it. It was an inline ternary with no export, so it moved into one helper that both commands call. `validate`'s output is byte-identical: same expression, and its three suites are green below. - **A comment in `validate.ts` and the header of `utils/union-arm-diagnostics.ts`** each said `validate` is "the repository's only zod-issue printer". This change makes that false, so both now say what `check` prints (one first-issue line, no arm selection). - **Three pending changesets get an appended dated note.** Their existing text and declarations are unchanged. Each would otherwise publish a sentence this change makes false, in the same release: `7004-cli-union-arm-selection.md` (`check` "has no zod-issue printer"), `7694-chart-series-chart-type-alias-refusal.md` ("without the issue") and `6475-gantt-block-face-declared.md` ("without naming the missing field"). The shape follows the 2026-09-25 dated notes (objectui#10524) on the same files. `check-changeset-overwrite` reports the three as modified, with declarations unchanged (report-only). **Closing text pinned next to an exit code.** The order asked for this to be reported, not acted on. Four existing tests asserted `All checks passed` beside their exit-code assertion: `check-jsonc-parse` (two), `check-schema-marker` and `check-validity-recogniser`. `check-known-types` used the same line as its "not a failure" signal. Each text assertion now reads the tally through `closingLine(...)`. Every exit-code assertion is unchanged. ## Tests `packages/cli/src/__tests__/check-first-issue-11007.test.ts`, 8 tests: - The root typo prints the refused key and the path `map`. - A two-segment first-issue path prints as `map → zoom`. - The issue count appears when there is more than one issue. - There is still one entry per file. - The nested typo is not reported as passed: no printed line matches `/pass/i`, the tally is 0 / 1 / 0, and there is no exit call. - Each bucket is counted apart. - The parse-error close is unchanged. - There is no nested walk: a broken page and a clean page are counted the same, while `safeValidateSchema` tells them apart. Expected text comes from `describeFirstIssue` and `closingLine`, fed with real `safeValidateSchema` issues. The assertions pin the subject (key, path, counts), not the wording. **Ablations.** Each was one-shot, run on the committed tree at `762723318` through objectstack's `scripts/ablation-replace.mjs`. Each restore was proven: the blob equals HEAD and `git diff HEAD` is empty. No permanent test was added. - **i. Drop the first-issue print:** 4 failed, 48 passed. The four first-issue pins failed. - **ii. Put the old pass line back at the call site:** 8 failed, 44 passed, across all five files. That is the five updated sibling assertions plus the three new close pins. The first attempt was a no-op: the tool refused before running because the replacement contained its anchor. It was rerun with a different anchor. - **iii. Parse structurally admitted files (a nested walk):** 1 failed, 51 passed. Only the no-walk pin failed. ## Gates, all at `762723318` (the final commit) - `pnpm exec vitest run packages/cli/` → `Test Files 22 passed (22)`, `Tests 302 passed (302)`. The base has 21 files. - `pnpm --filter @object-ui/types build` → exit 0. That is the only workspace package the CLI's source imports (`@object-ui/types/zod`); the other `@object-ui/*` hits are template strings. - `pnpm --filter @object-ui/cli type-check` (`tsc --noEmit`) → exit 0. `--listFiles` includes the new test, `issue-path.ts` and `check.ts`. - `pnpm --filter @object-ui/cli build`, then `pnpm check` (the repo-tree run `lint.yml` makes) → exit 0. Output is below. - eslint, run the way the package `lint` script runs it (`eslint src` in `packages/cli`, inline config honoured): 48 files, 0 errors, 5 warnings. All five are `no-explicit-any` in untouched files. `eslint.config.js` enables no type-aware linting (no `parserOptions`, no type-checked preset), so this diff cannot move any untouched file's verdict. - Changeset and byte gates: `check-changeset-presence` ✅ (1 added), `check-changeset-no-major` ✅, `check-control-bytes` ✅, `check-new-line-citations` 0 new, `check-pending-changeset-literals` ✅. - `check-changeset-claims` is report-only. It names two pending changesets that mention touched files: 6320 (the ignore list in `check.ts`) and 7658 (headings in `cli.mdx`). Both still hold. - Other gates: `check-test-path-roots` ✅, `check-unreferenced-sources` ✅, `check-doc-fences` ✅, `check-doc-types` ✅, `check-spec-symbols` ✅, `check-doc-example-ids` ✅, `check-doc-example-readers` ✅, `check-doc-links` ✅. - NOT MEASURED: `check:doc-examples` and `check:doc-snippets`. Their prerequisite is not met: they need a scoped build of 34 packages. This diff adds no TypeScript block to any document; its one new fence is `text`. CI runs both. `pnpm check` on this tree (`FILE` stands for the placeholder the command prints): ```text Object UI Schema Check Analyzing 633 files... ⚠️ 1 file carries a registered ObjectUI component type but did not validate as an ObjectUI schema: packages/vscode-extension/schemas/objectui-schema.json (type "object") Issue at type: Invalid input Run `objectui validate FILE` on any of them for the full diagnosis. Either the document is off-spec, or its component type is not modelled by @object-ui/types. These are NOT counted as skipped: a schema that fails validation is a broken schema, not a foreign file. Skipped 46 files with a root "type" that no ObjectUI recogniser admitted. A file is checked when its root carries an ObjectUI structural key: body, children, className, style, placeholder, visible, visibleWhen, visibleOn, hidden, hiddenOn, disabled, disabledOn, testId, ariaLabel. It is also checked when the whole document validates as an ObjectUI component schema. ObjectUI schema files: 265 validated, 167 recognised but not validated, 1 did not validate. This sweep is advisory; run `objectui validate FILE` for a verdict. ``` ## Acceptance notes None of these was filed. - **A document whose `type` selects no arm gets the bare message.** Its first issue is the discriminator's `Issue at type: Invalid input`. The only hit on this tree is `packages/vscode-extension/schemas/objectui-schema.json`, a JSON Schema document with `"type": "object"`, which is the bucket's known false positive. `objectui validate` adds its "No arm accepts type" note from arm selection, which the ruling keeps out of `check`. This is polish. - **`content/docs/plugins/plugin-chatbot.mdx` says `check` "passes" a structurally admitted chatbot document.** It still exits 0 but no longer prints a pass line. Left as is. - **YAML files are counted but never read.** They appear in `Analyzing N files...`, as before and as the source already states. The tally counts JSON only. --- _Generated by [Claude Code](https://claude.ai/code/session_011p7ikEivgXefNDaE5S5Uec)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 2a1779f commit 37a19d4

15 files changed

Lines changed: 547 additions & 35 deletions
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
'@object-ui/cli': patch
3+
---
4+
5+
`objectui check` prints the key and path a file was refused for, and no longer ends with 「✓ All checks passed」 over files it never validated (objectui#11007).
6+
7+
`check` recognises a JSON file in one of two ways: by a structural root key (`children`,
8+
`className`, …), which admits the file without parsing it, or by the whole document
9+
validating.
10+
11+
- **A file that did not validate now carries its reason.** Each entry in the "did not validate"
12+
list gets one indented line: the validator's first issue, its path spelled the way
13+
`objectui validate` prints it, and its message, with the issue count when there is more than
14+
one. A root `object-map` whose `map` block carries the typo `latitudeFieId` was listed by file
15+
and type only. It now reads `Issue at map: Unrecognized key: "latitudeFieId"`. The issue comes
16+
from the parse `check` already ran to recognise the file. Skipped files, which are foreign,
17+
still get no diagnosis.
18+
- **The closing line is a tally, not a pass.** A run with no parse errors used to end on
19+
「✓ All checks passed」, including over a page whose nested node carries a refused key: that
20+
page is admitted on `children` and never parsed. The line now counts the recognised files
21+
that validated, those recognised by a structural key and not validated, and those that did
22+
not validate, and points to `objectui validate <file>` for a verdict.
23+
24+
`check` still does not parse a file its structural key admits. The verdict on a document stays
25+
`objectui validate`'s, and `check` still exits non-zero on unreadable JSON only, so no exit code
26+
changes. `objectui validate`'s output is unchanged: its path spelling moved into a helper both
27+
commands share.

‎.changeset/6475-gantt-block-face-declared.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ parses against the schema a file whose root carries a structural key (`children`
4343
not validate, without naming the missing field, and it exits non-zero on
4444
unreadable JSON only. The refusal is `objectui validate`'s.
4545

46+
⚠️ **Dated note, 2026-09-29 — objectui#11007.** The note above says `check`
47+
lists such a file "without naming the missing field". It now prints the file's
48+
first issue under it, path and message, so the missing field is named there
49+
when it is the first issue. `check` still never parses a file admitted by a
50+
structural key, and still exits non-zero on unreadable JSON only: the refusal
51+
is still `objectui validate`'s.
52+
4653
**This is a `declared = enforced` restoration, not new requiredness.**
4754
`getGanttConfig`'s block branch already fed the block to
4855
`GanttConfigSchema.safeParse` and logged `[ObjectGantt] Invalid gantt

‎.changeset/7004-cli-union-arm-selection.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,10 @@ arms, so one mistyped `type` would have produced hundreds of lines.
3636
*negative* recognition would flood its report with diagnoses of non-ObjectUI files, the
3737
failure objectui#5127 and objectui#6075 exist to prevent.
3838

39+
⚠️ **Dated note, 2026-09-29 — objectui#11007.** `check` now prints one line under each file in
40+
its "did not validate" list: the first issue's path, spelled by the formatter behind `validate`'s
41+
`Path:` line, and its message, with no arm selection. That list is the only place it prints
42+
one. Files counted as skipped, the foreign files the paragraph above guards against, still get
43+
no diagnosis.
44+
3945
Nothing about which documents are ACCEPTED changes — this is diagnostic output only.

‎.changeset/7694-chart-series-chart-type-alias-refusal.md‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ name when the file does not validate, without the issue, and it exits non-zero o
4141
only. The refusal is `objectui validate`'s. The source citations in this entry now name files
4242
rather than line numbers.
4343

44+
⚠️ **Dated note, 2026-09-29 — objectui#11007.** The note above says `check` lists such a file
45+
"without the issue". It now prints the file's first issue under it, path and message, so a
46+
document with none of the structural keys can show this refusal there when it is the first
47+
issue. `check` still never parses a file admitted by a structural key, and still exits non-zero
48+
on unreadable JSON only: the refusal is still `objectui validate`'s.
49+
4450
This repository's `major` is a cross-repo pin to `@objectstack`'s major, not a severity dial; the
4551
break is announced here, which is the channel that carries it.
4652

‎content/docs/plugins/plugin-chatbot.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ to apply. They are documented in their own table after this one, under
188188
| `model` | string | - | AI model identifier (e.g., `gpt-4o`) |
189189
| `streamingEnabled` | boolean | `true` | Enable SSE streaming for AI responses |
190190
| `headers` | object | - | Additional headers for API requests |
191-
| `requestBody` | object | - | Additional body parameters sent with each API request. Authored on the node as `requestBody`; the renderer forwards it to the chat runtime under its own `body` option. Writing `body` on a `chatbot` node instead is **refused by name** - that key is retired there (objectui#8572) and the refusal names `requestBody` as the replacement. The refusal is delivered when the document is parsed (`objectui validate`) or type-checked, not while it renders. `objectui check` does not deliver it: a file whose root carries a structural key (`body` is one, `children` another) is recognised by that key and never parsed against the schema, so `check` passes it |
191+
| `requestBody` | object | - | Additional body parameters sent with each API request. Authored on the node as `requestBody`; the renderer forwards it to the chat runtime under its own `body` option. Writing `body` on a `chatbot` node instead is **refused by name** - that key is retired there (objectui#8572) and the refusal names `requestBody` as the replacement. The refusal is delivered when the document is parsed (`objectui validate`) or type-checked, not while it renders. `objectui check` does not deliver it: a file whose root carries a structural key (`body` is one, `children` another) is recognised by that key and never parsed against the schema, so `check` counts it as recognised but not validated and exits 0; `objectui validate` delivers the refusal |
192192
| `surface` | `'card' \| 'plain'` | `'card'` | **`chatbot-enhanced` only** (declared on `ChatbotEnhancedSchema`). Controls whether the chat renders as a bordered panel (`'card'`) or a frameless full-page workspace (`'plain'`). `chatbot` renders the plain chat component, which has no such chrome to switch, and does not read this key. `chatbot-floating` has no named read for it and `ChatbotFloatingSchema` does not declare it; its panel is a `ChatbotEnhanced`, and an authored value used to reach that panel through the registration's unfiltered props spread, an accidental channel objectui#7708 fenced - it is dark there now, so switch the node to `chatbot-enhanced` if you need the frameless chrome |
193193
| `processVisibility` | `'hidden' \| 'summary' \| 'debug'` | `'summary'` | **`chatbot-enhanced` only** (declared on `ChatbotEnhancedSchema`; `ChatbotSchema` still declares it too, though the `chatbot` registration has no read for it). Controls how much agent reasoning and tool detail is shown. `chatbot` renders the plain chat component, which has no agent-process display to configure at all - switch the node to `chatbot-enhanced` if you need one. `chatbot-floating` has no named read for it and `ChatbotFloatingSchema` does not declare it; an authored value used to reach its panel through the registration's unfiltered props spread, an accidental channel objectui#7708 fenced - it is dark there now, and there is no floating-side substitute to author |
194194
| `onError` | function | - | Error callback for streaming/API errors |

‎content/docs/utilities/cli.mdx‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,36 @@ structural key (`children`, `className`, …) **or** when the whole
199199
document validates as an ObjectUI component schema. Recognised files have their
200200
`type` checked against the component types this build registers.
201201

202+
The two ways in are not equal. A document recognised by validating was parsed
203+
in full, nested nodes included. A document recognised by a structural key was
204+
**not parsed at all**: a page whose `children` hold a node with a refused key
205+
is recognised on `children` alone, and `check` says nothing about that node.
206+
`objectui validate` is the command that parses it.
207+
202208
Files that are recognised by neither arm fall into one of two reports, and the
203209
distinction matters:
204210

205211
- **Reported by name** when the root `type` *does* name a registered component.
206212
That is ObjectUI content the validator could not accept — either the document
207-
is off-spec, or its component type is not modelled by `@object-ui/types`. Run
208-
`objectui validate <file>` for the reason.
213+
is off-spec, or its component type is not modelled by `@object-ui/types`.
214+
Under each file, `check` prints the validator's first issue: its path, spelled
215+
as `objectui validate` spells it, and its message. Run
216+
`objectui validate <file>` for the full diagnosis.
209217
- **Counted as skipped** otherwise. A root `type` heads several unrelated JSON
210218
vocabularies — `package.json`'s `"type": "module"` most of all — and those are
211219
not ObjectUI files.
212220

221+
```text
222+
⚠️ 1 file carries a registered ObjectUI component type but did not validate as an ObjectUI schema:
223+
stores.json (type "object-map")
224+
Issue at map: Unrecognized key: "latitudeFieId"
225+
```
226+
227+
A run with no parse errors ends on a tally rather than a pass: how many
228+
recognised files validated, how many were recognised by a structural key and not
229+
validated, and how many did not validate, followed by a pointer to
230+
`objectui validate` for the verdict.
231+
213232
Only unreadable JSON makes `objectui check` exit non-zero; everything above is a
214233
report. Use `objectui validate` when you want a failing exit code.
215234

0 commit comments

Comments
 (0)