Skip to content

docs: every json fence under content/docs parses; annotated ones are jsonc (objectui#10088) - #10942

Merged
objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-10088-json-fences-parse
Sep 28, 2026
Merged

objectstack-fleet[bot] merged 5 commits into
mainfrom
claude/issue-10088-json-fences-parse

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10088

Clause-②: no — published docs examples and a repo gate; no accept set or public contract surface moves

Every json-tagged fence under content/docs now survives JSON.parse. A fence that annotates JSON with comments is tagged jsonc, and it parses under that dialect too. No gate script and no workflow changed. Whether a gate should block on this is raised as its own decision in the report, not landed here, as the card's acceptance asks.

The convention (named, not invented)

It is the contract scripts/check-skill-examples.mjs already states in parseJsonFence for the skills tree (objectui#7474 brought that corpus to "parses under its own tag"):

  • json — JSON.parse and nothing else. It is what a reader copies into a .json metadata file.
  • jsonc — comments (and trailing commas) removed, then one JSON document. The tag tells the reader that the fence carries annotation, which is how a deliberate note is told apart from a mistake. A json fence that does not parse is a mistake.
  • No JSON dialect admits a raw newline inside a string, several documents in one fence, or .... Those shapes become one-line strings, one fence per document, and removed elisions.
  • Counter-examples still parse. In every "bad" example on these pages the fault is semantic (an expensive or opaque expression, an unknown prop), not syntactic, so the example should parse.

Per fence (lines at the base commit 733fd5ac65)

# page:line before-shape fix kind tag after
1 guide/deployment.md:277 // file-name caption (railway.json (excerpt)) retag; the caption is the page's only statement of the file name jsonc
2 guide/expressions.md:223 inline // member note ('item' is scoped data) retag jsonc
3 guide/expressions.md:337 raw newlines inside a ${…} string expression moved onto one line json
4 guide/expressions.md:351 raw newlines inside a ${…} string one line json
5 guide/expressions.md:364 raw newlines inside a ${…} string one line json
6 guide/expressions.md:380 raw newlines inside a ${…} string one line json
7 guide/expressions.md:529 // ❌ Bad label, one document, then a dangling // ✅ Good label for the tsx block below each label becomes the line above its block json
8 guide/expressions.md:673 bad/good pair: two documents plus // labels split into one fence per document, labels above json ×2
9 guide/expressions.md:687 bad/good pair split json ×2
10 guide/expressions.md:701 bad/good pair split json ×2
11 guide/layout.md:237 // member comment + [...] comment dropped (the page's Schema API block states maxWidth), [...] changed to [] json
12 guide/layout.md:507 three [...] [] json
13 guide/layout.md:660 two // comments + [...] comments dropped (Schema API block states the padding default), [] json
14 guide/layout.md:743 // comment + [...] comment dropped (the heading says "for Forms"), [] json
15 guide/schema-rendering.md:273 raw newlines inside a ${…} string one line json
16 plugins/index.md:355 trailing // standing for the elided keys ("on the node itself") retag; the comment carries the teaching jsonc
17 utilities/runner.mdx:213 package.json member excerpt (an object body) wrapped in braces json
18 utilities/runner.mdx:246 trailing // for elided keys ("not a props envelope") retag jsonc
19 utilities/runner.mdx:292 three "..." elision members removed json
20 utilities/runner.mdx:496 // file-name caption retag jsonc
21 utilities/vscode-extension.mdx:187 // ❌ Error: Unknown property, the counter-example's point retag jsonc
+ guide/troubleshooting.md:369 a jsonc fence holding three documents, rejected by both parseJsonFence(…, 'jsonc') and jsonc-parser split, labels above json ×3

The last row is outside the json population. It is in the claim's file surface and is the same defect class: its tag's grammar rejects its body. It gets the same mechanical split as rows 8–10, so the convention holds for the whole of content/docs with no exception.

The premise, re-measured

The instrument walks git ls-files content/docs (.md/.mdx), pairs fences with the repo's scripts/markdown-fence-scan.mjs, and runs strict JSON.parse on every fence whose language is exactly json. Controls run in the same command: {"a":1,} is REJECTED and {"a":1} is ACCEPTED.

  • base 733fd5ac65: 184 files, 203 json fences, 21 failing. The PM probe's 209 / 24 also counted the six jsonc fences, three of which fail a strict parse. fields/location.mdx and guide/record-edit-modes.md are correctly tagged jsonc and parse under it (both instruments). guide/troubleshooting.md is the + row above.
  • final head: 203 json fences, 0 failing; 11 jsonc fences, 0 failing under parseJsonFence(…, 'jsonc') and under jsonc-parser (with the error list read). Controls: a comment is ACCEPTED, while [...] and two documents are REJECTED by both.

Why the one existing gate was not tightened

The gate that reads these fences is scripts/check-doc-expression-carriage.mjs, which is report-only. It normalises away five shapes the 21 used, not two: comments, raw newlines, elisions, the object-body retry, and several top-level documents. It treats json and jsonc alike. At the base commit it printed 231 json/jsonc fence(s), 231 parsed, 0 UNPARSED (1 parsed as an object body). I left it alone for two reasons:

  1. Its tolerances are there on purpose. Its header says so: it judges ${…} carriage inside annotated fences, and "reaches 0 unparsed over the whole tree, which takes four tolerances beyond JSON.parse". The card says the same thing: "Not a defect in that gate — it was built for a different question".
  2. Tightening it would change how strong a gate is, and the card keeps that decision for a human. check-doc-expression-carriage.test.ts › "has no blind spot on the corpus it ships against" asserts census.unparsed is empty on the real tree. That test runs on every pull request that touches content/docs: node scripts/markdown-test-inputs.mjs --changed - lists all eight pages this PR touches, so the test job in ci.yml runs everything. Parsing json strictly would therefore make a non-JSON json fence block the merge. The card's acceptance covers exactly this: "if the answer is 「it should」, that is raised as its own decision rather than landed inside this card". The report raises it.

After the change, the census reads the same content. It printed 417 node(s) … 68 ${…} site(s) … 65 of them carried before and after, and the three findings are identical apart from line numbers. The fence count went from 231 to 236 (json 221 → 221, jsonc 10 → 15), and nothing is parsed as an object body any more.

The one test that changed, and a mutation that shows it can fail

packages/types/src/__tests__/page-actions-refusal-7926.test.ts pinned guide/layout.md's four unparseable json fences (the [...] elisions) as its blind spot, by exact count. That count is now zero. So the census judges those four page nodes (no actions, content under children) instead of skipping them. An unparseable json fence on that page was already red before, as 5 against the pinned 4, so the pin is no stronger or weaker than it was.

Mutation, run with objectstack's scripts/ablation-replace.mjs from the committed head f957ff9252: anchor "children": [] hit ×1 and was replaced by "children": [...] (anchor 1 → 0, blob 570a150066be → c26fd61cea62). The pin then failed with AssertionError: expected [ Array(1) ] to deeply equal [], naming content/docs/guide/layout.md:660 (Tests 1 failed | 10 passed). Restored with blob == HEAD (570a150066be) and git diff HEAD is empty.

Local verification (final head f1edd2dd3d, after merging origin/main 29c76e573c)

  • pnpm exec vitest run over the 25 tests scripts/markdown-test-inputs.mjs names as readers of the touched pages, plus 5 grep-found neighbours: Test Files 30 passed (30), Tests 1173 passed (1173), run under the verify lock.
  • pnpm --filter @object-ui/types type-check: exit 0. tsc -p tsconfig.test.json --listFiles lists the changed test (1 hit).
  • check:doc-fences, check:doc-types, check:doc-example-ids, check:doc-example-readers, check:control-bytes, check:new-line-citations (0 new citation(s)) and docs:check-links: all exit 0.
  • node scripts/check-changeset-presence.mjs: exit 0, with an empty-frontmatter changeset for the one src/ test file. check-changeset-no-major: exit 0.
  • node scripts/check-doc-expression-carriage.mjs: exit 0, 236 … 236 parsed, 0 UNPARSED, Blind spot: none.
  • NOT MEASURED: check:doc-snippets, check:doc-examples. Reason: THE GATE COULD NOT RUN, because 34 packages are unbuilt in this worktree. The narrowing was measured instead: across the 8 touched pages, scanFences from check-doc-snippet-types.mjs returns the same 52 ts/tsx/typescript blocks at the base and at the head. Body, quote depth and fragment-marker binding are all unchanged (28 are fragment-declared). A control confirms that a detached marker reads as changed. CI runs both gates in full.

Acceptance notes (observations, not filed)

  • packages/vscode-extension/README.md:125 is a json fence with // comments (VS Code settings are JSONC). It sits on the carriage surface, outside content/docs, and is the only json/jsonc fence there that fails under its own tag. Carrier: none.
  • guide/layout.md teaches maxWidth and padding on a page node. A source grep of renderers/layout/page.tsx finds no schema.maxWidth or schema.padding read, but AGENTS.md says a grep zero cannot answer "no renderer reads this key", so this is unmeasured, not asserted. Carrier: none.
  • guide/schema-rendering.md's Memoization claim is the card's unmeasured neighbour. It is still unmeasured: this PR did not take a reading in either direction.
  • The dispatch assumed that docs-only PRs skip the CI test shards. For content/docs, the test job's decision step reads markdown-test-inputs.mjs and runs everything.

Session: https://claude.ai/code/session_01EBx9rvB7dufCz4at53x35U


Generated by Claude Code

…e `jsonc`

A `json`-tagged fence is what a reader copies into a metadata file, so it
must survive `JSON.parse`. The convention applied is the one
`scripts/check-skill-examples.mjs` (`parseJsonFence`) already states for the
skills tree: `json` parses strictly; `jsonc` is JSON plus comments (and
trailing commas), one document per fence.

- raw newlines inside a `${...}` string: the expression moves onto one line
  (no JSON dialect admits a raw newline in a string);
- several documents in one fence (bad/good pairs): one fence per document,
  the `//` label becomes the line above it;
- `[...]` / `"..."` elisions: removed, leaving an empty list or the bare node;
- a package.json member excerpt: wrapped in braces;
- a comment that carries information the page states nowhere else (a file
  name, a member annotation, a counter-example's error): the fence is
  retagged `jsonc`; comments the page already states are dropped.

page-actions-refusal-7926.test.ts pinned layout.md's four unparseable
elided fences as its blind spot; the count is zero now, so those four page
nodes are judged by that census.

Refs objectui#10088

Claude-Session: https://claude.ai/code/session_01EBx9rvB7dufCz4at53x35U
Co-authored-by: Claude <noreply@anthropic.com>
The only source file the branch touches is a test in @object-ui/types.

Claude-Session: https://claude.ai/code/session_01EBx9rvB7dufCz4at53x35U
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 15 address(es) in this pull request's own prose name a tree it replaced

Each was read from a tree this change itself moves, so a reader who follows it lands somewhere else. ⛔ Nothing here blocks and nothing here says the sentence is false — the question asked is arithmetic: does this diff move the line that number points at?

  • in this body, utilities/vscode-extension.mdx:187 — this change rewrites content/docs/utilities/vscode-extension.mdx:187

    | 21 | utilities/vscode-extension.mdx:187 | // ❌ Error: Unknown property, the counter-example's point | retag | jsonc |

  • in this body, guide/expressions.md:223 — this change rewrites content/docs/guide/expressions.md:223

    | 2 | guide/expressions.md:223 | inline // member note ('item' is scoped data) | retag | jsonc |

  • in this body, utilities/runner.mdx:246 — this change rewrites content/docs/utilities/runner.mdx:246

    | 18 | utilities/runner.mdx:246 | trailing // for elided keys ("not a props envelope") | retag | jsonc |

  • in this body, guide/deployment.md:277 — this change rewrites content/docs/guide/deployment.md:277

    | 1 | guide/deployment.md:277 | // file-name caption (railway.json (excerpt)) | retag; the caption is the page's only statement of the file name | jsonc |

  • in this body, utilities/runner.mdx:292 — this change moves content/docs/utilities/runner.mdx:292 to :294

    | 19 | utilities/runner.mdx:292 | three "..." elision members | removed | json |

  • in this body, guide/expressions.md:351 — this change moves content/docs/guide/expressions.md:351 to :346

    | 4 | guide/expressions.md:351 | raw newlines inside a ${…} string | one line | json |

  • in this body, plugins/index.md:355 — this change rewrites content/docs/plugins/index.md:355

    | 16 | plugins/index.md:355 | trailing // standing for the elided keys ("on the node itself") | retag; the comment carries the teaching | jsonc |

  • in this body, guide/expressions.md:364 — this change moves content/docs/guide/expressions.md:364 to :355

    | 5 | guide/expressions.md:364 | raw newlines inside a ${…} string | one line | json |

  • in this body, guide/troubleshooting.md:369 — this change rewrites content/docs/guide/troubleshooting.md:369

    | + | guide/troubleshooting.md:369 | a jsonc fence holding three documents, rejected by both parseJsonFence(…, 'jsonc') and jsonc-parser | split, labels above | json ×3 |

  • in this body, guide/expressions.md:380 — this change moves content/docs/guide/expressions.md:380 to :366

    | 6 | guide/expressions.md:380 | raw newlines inside a ${…} string | one line | json |

  • in this body, utilities/runner.mdx:496 — this change rewrites content/docs/utilities/runner.mdx:496

    | 20 | utilities/runner.mdx:496 | // file-name caption | retag | jsonc |

  • in this body, guide/expressions.md:529 — this change moves content/docs/guide/expressions.md:529 to :513

    | 7 | guide/expressions.md:529 | // ❌ Bad label, one document, then a dangling // ✅ Good label for the tsx block below | each label becomes the line above its block | json |

  • in this body, guide/expressions.md:673 — this change moves content/docs/guide/expressions.md:673 to :658

    | 8 | guide/expressions.md:673 | bad/good pair: two documents plus // labels | split into one fence per document, labels above | json ×2 |

  • in this body, guide/expressions.md:687 — this change moves content/docs/guide/expressions.md:687 to :676

    | 9 | guide/expressions.md:687 | bad/good pair | split | json ×2 |

  • in this body, guide/expressions.md:701 — this change moves content/docs/guide/expressions.md:701 to :694

    | 10 | guide/expressions.md:701 | bad/good pair | split | json ×2 |

⛔ The repair is not to correct the number. Changing :246 to :274 is true today and born false again on the next insertion — objectui#9509 states that before anything else. Bind the number to the tree it was read from (`:246` at `b8a006883d`, `:274` at this head), which cannot re-stale because each number names its own tree; or state a rule instead of a coordinate, the way objectui#9495 replaced a file count with "every file in git diff --name-only against the merge base".

⚠️ 7 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6872-app-shell-branding-title-jsdoc.md

  • names content/docs/guide/layout.md → content/docs/guide/layout.md — edited by this change

    Correct the AppShellBranding.title doc comment. It read "Page title suffix (sets document.title)" while useAppShellBranding assigns document.title = title wholesale — nothing is appended; the caller composes the whole string (the console passes "App label — Product name"). That comment ships in dist/index.d.ts and is the only description a consumer sees on editor hover, so a reader who followed it passed a suffix-only fragment and got a truncated title with no error. The comment now carries the same wording as content/docs/layout/app-shell.mdx, and agrees with the AppShellProps tables in the package README and content/docs/guide/layout.md. No runtime behaviour changes; the wholesale assignment and the four-surface agreement are now pinned by tests.

.changeset/7658-toc-code-span-anchor-parity.md

  • names content/docs/utilities/runner.mdx → content/docs/utilities/runner.mdx — edited by this change

    Seven live headings in this repo's own docs were affected (content/docs/utilities/cli.mdx, content/docs/utilities/runner.mdx, packages/cli/README.md). Pinned against the real render pipeline rather than a second derivation of the slug rules: the new test renders each heading through MarkdownImpl and compares extractToc's id to the id attribute rehype-slug actually emitted.

.changeset/7926-page-node-refuses-actions.md

  • names content/docs/guide/layout.md → content/docs/guide/layout.md — edited by this change

    Scope. One key, by name; the node is NOT strict. A census over this tree read 91 authored page-tagged objects with a blind-spot reading of 8 unreadable sites, and found only actions (3 sites, all in content/docs/guide/layout.md) and breadcrumbs (1 site, its own question, untouched) surviving passthrough on a real page node — every other undeclared key belongs to a different declaration that merely spells type: 'page'. PageNodeSchema still passes unknown renderer props through.

.changeset/8284-content-channel-per-component.md

  • names content/docs/guide/expressions.md → content/docs/guide/expressions.md — edited by this change

    Migration. Nothing that renders today stops rendering: a document authoring the channel its renderer reads is unchanged, and a document authoring the other one rendered an empty element before and is now refused instead. The repo-wide census found five documents in this state — packages/react/README.md, content/docs/guide/expressions.md, two blocks in content/docs/guide/schema-rendering.md and packages/components/TESTING.md — every one of them a form or container authoring body; all five are corrected in this change. If your own metadata authors the refused channel on one of these twelve node types, the component was already drawing nothing there; rename the key to the one in the table.

  • names content/docs/guide/schema-rendering.md → content/docs/guide/schema-rendering.md — edited by this change

    Migration. Nothing that renders today stops rendering: a document authoring the channel its renderer reads is unchanged, and a document authoring the other one rendered an empty element before and is now refused instead. The repo-wide census found five documents in this state — packages/react/README.md, content/docs/guide/expressions.md, two blocks in content/docs/guide/schema-rendering.md and packages/components/TESTING.md — every one of them a form or container authoring body; all five are corrected in this change. If your own metadata authors the refused channel on one of these twelve node types, the component was already drawing nothing there; rename the key to the one in the table.

.changeset/8499-node-slot-registered-arms.md

  • names content/docs/utilities/runner.mdx → content/docs/utilities/runner.mdx — edited by this change

    The defect, and the direction it ran. Nine type spellings sat at DECLARED node slots in this repository's own corpora and resolved in no arm of the component union. Eight were registered renderers with fixtures proving they draw; the ninth (my-component) is the reader's own plugin component and carries a written exemption in scripts/check-doc-component-types.mjs. A reader following content/docs/utilities/runner.mdx's own instruction — "copy one, wrap it in a page document … and save it as src/app-data/pages/index.json" — got a document that renders correctly in the browser and is refused by objectui validate. That is the expensive direction: the likely reaction is to stop trusting the validator.

.changeset/8871-page-node-refuses-breadcrumbs.md

  • names content/docs/guide/layout.md → content/docs/guide/layout.md — edited by this change

    Three author sites, all teaching passages in content/docs/guide/layout.md, and that count corrects the actions refusal's "1 site": its census reads every git-tracked JSON file, every json fence in .md/.mdx, and every TS/TSX object literal via the TypeScript AST (PR fix(types,docs): refuse actions by name on the page node, teach the shape that draws (objectui#7926) #8870), and it undercounted for two different reasons. The Schema API block declared breadcrumbs?: ArrayANGLE-BRACKETS({ label, href }) outright and its literal does carry type: 'page', but that literal sits inside a markdown typescript fence — a fence language the census's json-fence reader never visits, so it was never read at all. Best Practices §2 authored it on a fragment inside a json fence the census does read, but that fragment never writes type, so a page-tagged filter correctly excluded it. No example app, catalog fixture, template or customer document writes the key, so the refusal strands no authored document in this tree.

.changeset/9308-data-root-unbound-from-adapter.md

  • names content/docs/guide/schema-rendering.md → content/docs/guide/schema-rendering.md — edited by this change

    The Data Context passages of content/docs/guide/schema-rendering.md and packages/react/README.md teach the scope channel accordingly.

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with af2221d45 (merge-base with origin/main): 10 file(s) changed outside .changeset/, read against 1674 pending declaration(s) that publish a body (2270 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3102.0 KB 3104.5 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-F8jgn-Gk.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.44KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.15KB 9.14KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f1edd2dd3d4130127fbb419857db16f1a01c0a90
Local-runs: none

Inputs: card #10088 (body and both comments), PR #10942 (body, file list, net diff against main at the head; merge-base 29c76e573c, 10 files, +74 / -72), the check-runs on the head, and the pages, the test and the two scripts read at the head with git show. Nothing built, run or re-run.

Check-runs on the head (the gate verdicts): all 43 completed, none failed — 40 success (Lint, Type Check, Test shards 1–8, Test, Test (dist pins), Build & E2E, Build Docs, Doc Snippet Type Check, Doc Component Type Check, Doc Fence Language Check, Doc Example Id Check, Internal Docs Link Check, Line Citation Gate, the five Changeset gates, Governed Surface Queue Guard, Spec Main Shape Gate, Skill Example Check and the rest) and 3 skipped by design (dependabot, Test (coverage), the coverage shard). Every derived gate family the diff touches is green on this head.

① Derived judgments

Each fence, judged on whether it still teaches what its page says it teaches.

  • guide/expressions.md › "Scoped Data" — retagged jsonc, the 'item' is scoped data note kept. Right: that note is the page's only naming of item, so it had to stay, and jsonc is the tag that carries it.
  • guide/expressions.md › the four ${…} strings that held raw newlines (the ternary content, the visibleOn role gate, the filter/map/join chain, the greeting) — each expression moved onto one line, text unchanged. Right: no JSON dialect admits a raw newline inside a string; the expression the reader copies is the same expression.
  • guide/expressions.md › "Expensive Computations" — the ❌ Bad label moved above the json fence, the ✅ Good label moved above the doc-snippet: fragment marker and its tsx block. Right: the label used to dangle inside the JSON fence pointing at a block below it; the marker still sits immediately above the tsx fence it binds (Doc Snippet Type Check green).
  • guide/expressions.md › Best Practices 1–3 — each bad/good pair split into one fence per document, the label line above each. Right: every "bad" half still parses, and should, because its fault is semantic (an over-complex chain, opaque names, unsafe access), not syntactic; a counter-example whose lesson is semantic must not be made unparseable to look bad.
  • guide/layout.md › "Max Width Options" — [...] became [] (right); the Centered content with max width comment was dropped. The PR's stated reason ("the Schema API block states maxWidth") is inexact: that block declares the key and the "Available values" list gives widths, and "centered" is stated nowhere else on the page. Judged right on outcome all the same: at the head, packages/components/src/renderers/layout/page.tsx takes its cap from getPageMaxWidth(pageType) and centers with mx-auto whatever the node says, and no schema.maxWidth read is visible there (a grep reading, not an instrument, exactly as the dev flagged). Keeping the comment under jsonc would have preserved a claim the key does not back. The carrier for that is the dev's own out-of-scope finding, answered in ③.
  • guide/layout.md › "Page Padding" — two comments dropped, [...] became []. Right: the Schema API block already says padding?: boolean with "Add padding (default: true)", and "className": "p-8" needs no gloss.
  • guide/layout.md › "Max Width for Forms" — Better for forms dropped. Right: the heading and "Use constrained width for forms and reading content" carry it.
  • guide/layout.md › "Settings Page with Tabs" — three [...] became []. Right.
  • guide/schema-rendering.md › "Complex Logic" — one line. Right: the paragraph beneath ("The branch sits on the nested text node's content") still describes it.
  • guide/troubleshooting.md › §11 — the jsonc fence holding three documents became three json fences with the ✗ / ✗ / ✓ lines above them. Right: no dialect admits several documents in one fence; each label keeps its glyph and its sentence, and the canonical form is still last. Outside the card's json population, inside the claim's file surface, same defect class — the bounded exemption reads as claimed.
  • guide/deployment.md › the railway.json (excerpt) fence — retagged jsonc, caption kept. Right: the caption is the page's only statement of the file name.
  • plugins/index.md › "Usage Pattern" — retagged jsonc, the "on the node itself" comment kept. Right: the comment is the fence's teaching and the prose below restates it.
  • utilities/runner.mdx › step 1 (the package.json member) — wrapped in braces. Right: it parses, and the prose ("Depend on it in packages/runner/package.json") reads as an edit, not a file to replace. Observation only: the same shape got a jsonc (excerpt) caption on deployment.md; either spelling is fine.
  • utilities/runner.mdx › step 5 — retagged jsonc, the "not a props envelope" comment kept. Right.
  • utilities/runner.mdx › "Integration Testing" — three "..." members removed. Right: three plugin nodes side by side is still the lesson.
  • utilities/runner.mdx › "Add Custom Schemas" — retagged jsonc, the file-name caption kept. Right.
  • utilities/vscode-extension.mdx › "Validation" — retagged jsonc, // ❌ Error: Unknown property kept. Right: that note is the counter-example's whole point; the node parses, and its fault (an unknown key) is what the extension is shown catching.
  • No example changed meaning, no counter-example became a positive example, and every removed comment's fact is stated elsewhere on its page or was a claim the code does not back.

The json / jsonc convention the PR names — is it stated where a reader meets it? WRONG, and this is the FAIL. The PR body states it well (json = JSON.parse and nothing else; jsonc = comments and trailing commas removed, then one document; no dialect admits a raw newline in a string, several documents in one fence, or ...). But after merge that statement lives nowhere an author of content/docs reads: the changeset has empty frontmatter, so its prose reaches no CHANGELOG; scripts/check-skill-examples.mjs states it for skills/**, a different tree; guide/ci-cd-pipeline.md › "Skill Examples" says "a marked json / jsonc fence must parse" about that same skills gate only; and no gate reads content/docs for it (that is the open question). The place a docs author meets fence-tag rules already exists — CONTRIBUTING.md › "Writing Documentation", the paragraph beginning "A code block inside one of those records is a SPECIMEN" — and it says nothing about json / jsonc. Card acceptance #1 asks that the convention be named so a reader can tell a deliberate spelling from a mistake; a rule stated only in a closed PR does not survive to the next author, which is how the corpus reached 22. Remedy, one commit: add one paragraph beside that CONTRIBUTING.md paragraph stating the four rules above, the three fixes this PR applied (one line; one fence per document with the label above; drop the elision), and — per AGENTS.md #9 — that nothing enforces it in content/docs today, pending the gate-strength decision in ③. ⛔ Not in skills/** or .claude/** (governed surfaces, which would change this PR's landing tier), not on a published page (the rule is for authors).

The moved test pin — packages/types/src/__tests__/page-actions-refusal-7926.test.ts › "BLIND SPOT — the fences this scan could not parse are counted, not ignored". Before: exactly four blind fences, each carrying .... After: the blind list equals [], printed on failure. Right, and still guards what it guarded: the pin exists so the census zero is measured rather than vacuous; at zero blind every page node the guide authors in a json fence is judged by "no page node on the page carries actions", and a new unparseable fence goes red by name instead of becoming a fifth blind entry. The "each blind is an elision" leg is vacuous at zero, so dropping it loses nothing. The reader's regex takes the tag json followed by a newline, so jsonc sits outside its population — the new comment says so, and it was already so. The three LIT CONTROL legs are untouched. Not a gate-strength change: a fifth unparseable fence was already red against the pinned four, and the PR's reported mutation ("children": [] to "children": [...]) turned the new pin red by name.

Accept set and public surface: no non-test src/ file, no exports, no schema moves; content/docs/** is the site, not a package. Clause-②: no on the PR is right.

② Semver level

.changeset/10088-json-fences-parse.md with empty frontmatter — right. The only src/ path is a test under packages/types, which ships nothing; the docs pages are not a published package; no bump is owed and one is declared not owed, which is the declaration check-changeset-presence asks for. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report and Changeset Claim Re-read are all green on the head. The changeset prose is accurate. One consequence, already counted in ①: with empty frontmatter that prose is not a durable statement of the convention.

③ Boundary flags

  • open_questions — should a non-parsing json fence under content/docs block a merge (A tighten the carriage census for json only / B no gate / C new gate)? Not this lane's to decide, and the dev was right not to land it: the carriage script's own header says its four tolerances exist to reach 0 unparsed for the ${…} question, and its pin "has no blind spot on the corpus it ships against" runs on every content/docs PR, so strict json there flips a blocking gate — the card's acceptance Add public roadmap, VitePress documentation site, and GitHub Pages deployment #3 reserves that. Escalated: the adopting seat files it as its own needs-user-decision card carrying the dev's A/B/C. My reading for that card: A is the coherent option (one json / jsonc contract across both doc trees, carried by an instrument and pin that already exist, no new script or workflow); the strength flip is the maintainer's word. This PR does not wait on it.
  • Deviation: the carriage gate was not tightened — right, same reason.
  • Deviation: page-actions-refusal-7926.test.ts and the changeset lie outside the named file surface — the claim was amended in the same round to name both; the pin had to move (4 to 0) or stay red; cleared.
  • Deviation: guide/troubleshooting.md was a jsonc fence, outside the json population — in the claim's file surface, same defect class, same mechanical fix; cleared.
  • Deviation: check:doc-snippets and check:doc-examples not measured locally — answered on the head by Doc Snippet Type Check, which by its own workflow header runs both scripts in one job; green; cleared.
  • Deviation: commit d27cb20292 carries a body line Refs objectui#10088 — no rule breached; the trailers are the model-free pair; noted.
  • Out-of-scope finding: packages/vscode-extension/README.md has a json fence with // comments — carrier none. Escalated: name it in the gate-strength card, since under option A it is the one carriage-surface fence outside content/docs that would go red, and a jsonc retag there is the same fix as this PR's.
  • Out-of-scope finding: guide/layout.md teaches maxWidth and padding on a page node, reader unmeasured — carrier none. Escalated: a follow-up card in the ADR-0049 enforce-or-remove class, the same class as this page's actions (objectui#7926) and breadcrumbs (objectui#8871) retirements; my own head reading of page.tsx (grep, not an instrument) points the same way and is why the dropped "centered" comment is not a loss.
  • Out-of-scope finding: guide/schema-rendering.md Memoization claim, still unmeasured — the card recorded it as unmeasured and did not require a reading; carrier none. Escalated: a follow-up card candidate for the seat; not a defect of this PR.
  • PR body and report cite fences as page:line — AGENTS.md [WIP] Update documentation for project #11 (the objectui#10048 clause) says a PR body has no same file, so every line address in it is the banned form. Mitigated here: every address is anchored to the immutable base commit 733fd5ac65, so none can rot. Flag only, no edit asked; the next body cites by heading or quoted string.

Implemented-by: claude/issue-10088-json-fences-parse
Reviewed-by: session_01EBx9rvB7dufCz4at53x35U

VERDICT: FAIL

One commit closes it: the CONTRIBUTING.md paragraph named in ①. Every other judgment on this head is right and the re-review is a diff of that one file.


Generated by Claude Code

…uthors read it

One paragraph under "Writing Documentation", beside the SPECIMEN-to-plaintext
fence rule. It says a `json` fence must pass strict JSON.parse, annotation
goes under `jsonc`, how to rewrite the shapes no JSON dialect admits, that the
skills tree enforces the same contract (`parseJsonFence`), and that nothing
enforces it for content/docs today; objectui#10943 is the open decision on a
gate.

Claude-Session: https://claude.ai/code/session_01EBx9rvB7dufCz4at53x35U
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3102.0 KB 3104.5 KB
Main entry chunk (gzip) 149.4 KB 350 KB
Entry file index-F8jgn-Gk.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.58KB 6.17KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.70KB 2.23KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 557.59KB 133.60KB
core (index.js) 9.93KB 3.94KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 226.46KB 63.00KB
fields (index.js) 261.01KB 66.28KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.32KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.96KB 14.83KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 197.67KB 46.90KB
plugin-dashboard (index.js) 136.93KB 36.48KB
plugin-designer (index.js) 215.03KB 44.21KB
plugin-detail (index.js) 233.48KB 61.79KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 161.21KB 41.41KB
plugin-gantt (index.js) 170.35KB 42.19KB
plugin-grid (index.js) 228.33KB 62.59KB
plugin-kanban (index.js) 48.43KB 15.11KB
plugin-list (index.js) 115.86KB 28.64KB
plugin-map (index.js) 22.90KB 7.62KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.17KB 12.20KB
plugin-timeline (index.js) 31.15KB 9.14KB
plugin-tree (index.js) 11.21KB 3.89KB
plugin-view (index.js) 88.55KB 22.21KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.16KB 39.05KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 7.50KB 3.05KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.16KB 2.71KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.22KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 3.83KB 1.49KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.27KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6da92addb3bade97664c954b742185ff2a60d0ff
Local-runs: none

Inputs: card #10088 (body and all three comments: the claim, the round-0 report, the REWORK 1 report), PR #10942 (body, all three comments including the earlier ## Contract review on head f1edd2dd3d, the 11-file list, the net diff against main at af2221d45e: +76 / -72), the check-runs on the head, and files read at the head with git show (CONTRIBUTING.md, scripts/check-skill-examples.mjs, scripts/check-doc-expression-carriage.mjs and its test, scripts/check-governed-queue-guard.mjs, the two page-census tests named below) plus objectui#10943's body, which the new paragraph names. Nothing built, run or re-run.

Check-runs on the head (the gate verdicts): 42 check-runs on 6da92addb3, read at post time: 37 completed, 5 still running. 34 success: Action Ref Convention, Build & E2E, Build Docs, Bundle Analysis, Changeset Bump Policy, Changeset Claim Re-read, Changeset Declaration, Changeset Fixed Group Check, Changeset Overwrite Report, Control Byte Scan, Doc Component Type Check, Doc Example Id Check, Doc Fence Language Check, Doc Snippet Type Check, Docs Route Eager Closure Check, Governed Surface Queue Guard, Inert vi.mock Specifier Check, Internal Docs Link Check, Line Citation Gate, Lint, Live E2E (informational), Pre-Install Import Graph Check, README Export Check, Shell Escape Residue Scan, Skill Eval Token Check, Skill Example Check, Skill Guide Path Check, Spec Main Shape Gate, Test (dist pins), Test (shard 2/8), Test (shard 3/8), Test (shard 7/8), Test (shard 8/8), label. 3 skipped by design: Test (coverage shard N/4), Test (coverage), dependabot (the coverage shard name is spelled with N for its matrix token). None failed, none cancelled. Still in_progress when this record was posted: Test (shard 1/8), Test (shard 4/8), Test (shard 5/8), Test (shard 6/8), Type Check — ⛔ not read as green: their conclusions are gate verdicts this record does not carry, and the landing waits for each to reach success (in_progress is not a pass). Every gate family the diff derives that has finished is green; the ten blobs the Test shards judge are byte-identical to f1edd2dd3d, on which all eight shards and Type Check were success, and CONTRIBUTING.md is the only new input to any test (its four ledger readers, run by the dev at this head, passed). That is context, not a verdict.

What moved since the earlier record's head f1edd2dd3d: two commits. 12f7d1490b merges origin/main (af2221d45e) into the branch, parents f1edd2dd3d and af2221d45e; 6da92addb3 adds one paragraph and one blank line to CONTRIBUTING.md. Every one of the ten files the earlier record judged has the same blob at this head as at f1edd2dd3d, and the net diff against main for those ten is the same +74 / -72 it judged; the delta is CONTRIBUTING.md alone. f1edd2dd3d is an unchanged ancestor of this head, so nothing already pushed was rewritten.

① Derived judgments

The ten files judged on the earlier head — every per-fence judgment there (retags to jsonc where the comment is the page's only statement, one-line ${…} strings, bad/good pairs split with labels above, elisions to [], the package.json member wrapped, the troubleshooting.md split, the moved test pin) stands unchanged on this head, since the blobs are identical. Not re-argued here.

The new CONTRIBUTING.md paragraph, claim by claim against this tree:

  • Placement — CONTRIBUTING.md › "### Writing Documentation", directly after the paragraph beginning "A code block inside one of those records is a SPECIMEN", before the pnpm site:dev bash fence. Right: it is the place the earlier record named, where a docs author already meets the fence-tag rule. CONTRIBUTING.md is not a governed surface (GOVERNED_SURFACES in scripts/check-governed-queue-guard.mjs lists docs/adr/, .claude/, skills/, AGENTS.md, CLAUDE.md; Governed Surface Queue Guard is green on the head), so the PR's landing tier is unchanged.
  • "A json fence … must pass a strict JSON.parse" — True. parseJsonFence(body, 'json') in scripts/check-skill-examples.mjs is JSON.parse on the raw body and nothing else; its docblock says so in those words.
  • "A block that annotates JSON with // or /* … */ comments or trailing commas is fenced jsonc instead, and it must still be one JSON document once those are removed" — True. The jsonc branch is stripTrailingCommas(stripJsonComments(body)) then one JSON.parse; stripJsonComments removes both the line and the block form outside strings, stripTrailingCommas removes a comma before } / ], and a fence holding two documents fails the single parse.
  • "a raw newline in a string is invalid under both tags" — True. Neither strip touches the inside of a string, so the raw newline reaches JSON.parse under both tags and is refused.
  • The three rewrites (a ${…} expression on one line; one fence per document with the label above; an elision removed, leaving an empty list or the bare node) and "a counter-example still parses" — Right: these are exactly the rewrites this PR applied, and each is a shape the contract admits. The elision sentence names a "..." member, which agrees with the census's own header (a dotted string in value position is data).
  • "This is the contract parseJsonFence in scripts/check-skill-examples.mjs enforces on the skills tree's marked fences" — True. SCAN_ROOTS = ['skills', '.claude/skills'], only fences under the opt-in MARKER are judged, and every marked json / jsonc block goes through parseJsonFence; a failure is exit 1. "Marked" is the load-bearing qualifier and it is present.
  • "Nothing enforces it for content/docs today" — True of every gate and pin over the corpus, said per objectui AGENTS.md 完善设计器的每一个细节 #9: no check:* script or workflow reads a content/docs json fence strictly, and the carriage census's pin "has no blind spot on the corpus it ships against" asserts census.unparsed is empty only after its tolerances. Observation, not a defect: two page-scoped tests do refuse a non-parsing json fence on the one page each reads — packages/types/src/__tests__/page-actions-refusal-7926.test.ts (this PR's own change: the blind list for guide/layout.md now equals []) and schema-reference-named-list-view-keys-7923.test.ts (an unguarded JSON.parse of the first json fence under "### ObjectViewSchema" in content/docs/api/schema-reference.md). Each asks a different question of one named page and refuses as a side effect; neither is enforcement of the convention over the tree, and the sentence errs in the safe direction (under-claiming enforcement). No edit asked.
  • "scripts/check-doc-expression-carriage.mjs reads these fences but normalises comments, raw newlines and elisions away by design" — True. Its header lists tolerances 1 (line and block comments), 2 (raw newlines and tabs re-escaped) and 4 (elision markers) and states the reason ("reaches 0 unparsed over the whole tree, which takes four tolerances"); it also has tolerance 3 (trailing commas), the object-body retry and splitTopLevel, which the paragraph does not need to list.
  • "whether a gate should block a json fence that does not parse is the open decision objectui#10943" — True. objectui#10943 exists, is open, carries needs-user-decision, and asks exactly that question with the dev's A / B / C.
  • No line addresses in the paragraph (Line Citation Gate green); the two script paths it names exist at the head (Internal Docs Link Check green; the check-doc-links reader lists CONTRIBUTING.md).

The earlier record's failing item — is it met? Yes. The convention is now stated where a content/docs author reads it, with the four rules, the three rewrites, the pointer to the instrument that enforces it on the sister tree, the statement that nothing enforces it here, and the card that carries the gate decision. Not in skills/** or .claude/**, not on a published page.

Accept set and public surface: unchanged from the earlier head — no non-test src/ file, no exports, no schema moves; CONTRIBUTING.md is not published source. Clause-②: no on the PR is right.

② Semver level

.changeset/10088-json-fences-parse.md, empty frontmatter, unchanged since the earlier head — right. The one src/ path is a test under packages/types; CONTRIBUTING.md and the docs pages publish nothing from any released package; no bump is owed and one is declared not owed. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report and Changeset Claim Re-read are all green on the head. The changeset prose is still accurate.

③ Boundary flags

  • REWORK 1 open_questions: none. The round-0 open question (should a non-parsing json fence block a merge — A tighten the carriage census for json only / B no gate / C new gate) is now carried by objectui#10943, filed by the adopting seat with the dev's options and recommendation. The escalation the earlier record asked for is discharged; this PR does not wait on the ruling.
  • REWORK deviation: the worktree was re-created on the existing local branch (sha matched origin at f1edd2dd3d) — no effect on the diff; cleared.
  • REWORK deviation: the merge commit message was amended locally before push — verified: 12f7d1490b has parents f1edd2dd3d and af2221d45e, both new commits end with the model-free trailer pair (Claude-Session: and Co-authored-by: Claude), and f1edd2dd3d is an unchanged ancestor of the head, so nothing already pushed was rewritten; cleared.
  • Round-0 deviations (carriage gate not tightened; the test pin and changeset outside the named surface; troubleshooting.md being a jsonc fence; check:doc-snippets / check:doc-examples not measured locally; the Refs objectui#10088 body line) — cleared in the earlier record on identical blobs; Doc Snippet Type Check is green again on this head; stand.
  • Earlier escalation: packages/vscode-extension/README.md carries a json fence with // comments — the earlier record asked that the gate-strength card name it, and objectui#10943's body does not. It matters to that card's premise: the carriage census's scan surface imports packageReadmePages from check-doc-component-types.mjs, so under option A as the card describes it that README fence is the one UNPARSED fence left on the surface and the census's "no blind spot" pin would go red — the card's "lands green (0 today)" holds for content/docs only. Re-escalated to the adopting seat: amend objectui#10943 to name that fence and its jsonc retag as part of option A's landing. Not a defect of this PR.
  • Earlier escalations, still carrier none: guide/layout.md teaching maxWidth / padding on a page node with no measured reader (an enforce-or-remove candidate, same class as the page's actions and breadcrumbs retirements), and guide/schema-rendering.md's Memoization claim (the card's unmeasured neighbour). Neither is this PR's to answer; re-escalated as follow-up card candidates for the seat.
  • PR body cites fences as page:line — unchanged since the earlier record (the body was not edited this round); every address is anchored to the immutable base 733fd5ac65, so none rots. Flag only, as before.

Implemented-by: claude/issue-10088-json-fences-parse
Reviewed-by: session_01EBx9rvB7dufCz4at53x35U

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 28, 2026 09:38
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 28, 2026
Merged via the queue into main with commit 5c94589 Sep 28, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10088-json-fences-parse branch September 28, 2026 09:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: types tests

Projects

None yet

2 participants