Skip to content

feat(scripts): judge JSON doc fences through the strict authoring face (objectui#5250 slice B) - #12127

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-5250-json-fence-gate
Oct 11, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-5250-json-fence-gate

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #5250
Clause-②: no

Slice B of the objectui#5250 programme: the JSON-fence gate the maintainer ruling (comment 5534418546, 2026-09-04, decision batch #25, option 2) orders "on top of" the strict authoring face, plus the docs repairs it cannot land without. Slice A (objectui#11069, objectui#12054, objectui#12050) moved objectui validate and objectui check onto StrictAnyComponentSchema. This PR runs that same face over every json/jsonc node document in content/docs. The rendering face's .passthrough() is not touched, and nothing under packages/ changes.

This card stays open: slice C (the non-docs class (i) documents) is a separate PR.

What changed

  1. New gate scripts/check-doc-json-fences.mjs (pnpm check:doc-json), a sibling script rather than a branch inside the ts/tsx gate, because its only precondition is a built @object-ui/types, not the 34-package closure the ts/tsx gate needs.
    • Verdict: StrictAnyComponentSchema.safeParse(document).success, read from the BUILT @object-ui/types ./zod entry. That is the module @object-ui/cli imports for validateAuthoredDocument, whose whole body is the same call. So the docs gate and objectui validate cannot disagree about a document. A test runs the CLI's own door over the corpus and asserts identical verdicts.
    • Population: the measurement script's node-document predicate (json/jsonc fence, parses, plain object, string type, type names an arm of AnyComponentSchema), with two corrections, both toward reading more. A jsonc fence is read with jsonc-parser and the options objectui check uses, because the measurement's JSON.parse dropped every commented jsonc fence. A fence inside a blockquote is read, with the ts/tsx gate's quote rule.
    • Not judged, but counted per bucket and printed: not JSON, not an object, no string type, a type no node schema declares (with the types). --list prints every fence with its bucket.
    • Fragments: a deliberate fragment carries a doc-json: fragment marker (the MDX-comment form in .mdx, the HTML-comment form in .md) on the line above its fence. The fragment design follows objectui#5138: an explicit marker, never a silent skip. Every run re-judges the declaration: the fence must be a node document the strict face REFUSES. A marker on an accepted fence, or on a non-document, is stale. A dangling marker, a short reason, or a verification claim (the ts/tsx gate's VERIFICATION_CLAIM) is a finding. The namespace is doc-json:, not doc-snippet:, because the ts/tsx gate reports its own marker above any non-ts fence as stale.
    • Controls, before any verdict: RESOLUTION (the face resolves inside packages/types/dist/), REFUSES (one undeclared key: strict refuses it, tolerant accepts it), ACCEPTS (the declared-only twin is accepted), and ANTI-IDLE (at least one judged document, and the anchor page content/docs/blocks/forms.mdx contributes one).
    • Exit codes: the sibling gates' three: 0 clean, 1 findings, 2 could not run.
    • Scan surface: content/docs only, which is the population the measurement priced. The header and every summary say that pages outside it are not read.
  2. Wiring: the last step of .github/workflows/doc-snippet-types.yml, after the --build-filter build. That closure already carries @object-ui/types..., so no build step is added. One package.json script line.
  3. Docs repairs: 14 pages under content/docs, re-derived on the dispatch base. Each refused fence is repaired or declared, as tabled below.
  4. Edits outside the claim's file surface, each forced by an existing pin or by a statement this change makes false (deviations, declared):
    • content/docs/guide/ci-cd-pipeline.md: the command-parity sweep in scripts/__tests__/ci-cd-pipeline-doc.test.ts requires the workflow's new command to be named on the page. Its table row also said "two gates, one job", which is now three.
    • scripts/check-doc-component-types.mjs: eight DOC_TYPE_EXEMPTIONS entries pointed at type literals that only the removed snippets spelled. That gate reports such entries as stale, so they are deleted with their sites, each with a one-line note.
    • scripts/check-doc-snippet-types.mjs: four more exports (quoteDepthOf, stripQuotePrefix, isBlankAtDepth and MIN_REASON_LENGTH), so that both gates read quoted fences and fragment reasons by one rule. Also its header item "schema-key validity ... left unruled on purpose", which this PR makes false for json fences, now points at the sibling.

Population on the dispatch base 29b9949

  • Measurement predicate (JSON.parse, no blockquotes): 231 json/jsonc fences, 185 node documents, 26 refused. Of those, 13 are strict-only and 13 are red on both faces; 8 are refused naming no key. These are the same numbers the measurement report recorded at 47b1f0b.
  • This gate's predicate: 233 fences, 191 node documents, 29 refused. Two more come from quoted fences in api/schema-reference.md, both accepted. Four more come from commented jsonc fences: fields/location.mdx is accepted, and three are refused (listed below).
  • After the repairs (head of this PR): 227 fences, 185 judged, 179 accepted, 6 declared fragments (each confirmed refused), 0 refused. Not judged: 32 with no string type and 10 with a type no node schema declares.

Instrument: the gate itself, plus a throwaway copy of the measurement predicate for the base reading.

H3: classification of the red-on-both documents

Cited by page and section heading, never by line, per AGENTS.md section 5 rule #11.

page, section type classification disposition
api/schema-reference.md, DetailSchema detail teaching error: field type: "email" is outside the detail field vocabulary repaired: text
components/basic/icon.mdx, "name is identity, not the glyph" icon deliberate refused example: the page states it is refused declared
components/index.md, Example: Button button teaching error: size: "md" is not a declared size repaired: default
guide/dashboard-filters.md, Step 3 (optionsFrom) select (collision) not a node: a globalFilters[] member whose type is the filter's input type declared
guide/expressions.md, Index in Loops list teaching error: no item template exists, and items must be an array section rewritten (see below)
guide/expressions.md, Dependent Fields form teaching error: a form takes fields, and select options are {label, value} objects repaired
guide/schema-playground.md, Form form teaching error: string options, field defaultValue, and form title repaired
guide/schema-playground.md, Grid grid teaching error: a data table taught under the layout grid type rewritten as data-table
guide/schema-playground.md, Composing Schemas page teaching error: grid gap: "md" repaired: 4
utilities/runner.mdx, 1. Plugin Development page deliberate: the child is the reader's own plugin type declared
utilities/runner.mdx, 3. Integration Testing div deliberate: a type-only skeleton declared
utilities/vscode-extension.mdx, Snippets, objectui-form form teaching error: a snippet the extension never shipped, and onSubmit is a runtime slot section rewritten from the shipped snippets
utilities/vscode-extension.mdx, Snippets, objectui-grid data-table teaching error: a snippet the extension never shipped section rewritten
guide/expressions.md, Scoped Data (jsonc, newly read) list teaching error: as Index in Loops section rewritten
utilities/runner.mdx, Add Custom Schemas (jsonc, newly read) page deliberate: the reader's my-component declared

None of the 15 is exempted silently. Five are declared with a written reason, and the gate confirms each declared fence is refused. The sixth declaration is the Validation fence in the next table.

Strict-only pairs: per-pair disposition and runtime evidence

Instrument: a throwaway dom-project test, never committed. It renders the node through the real SchemaRenderer and the real registry (packages/components/src/renderers), once with the key and once without it, and compares the normalised document.body HTML.

Controls: each control changes the output, so the instrument can see a read key: button.label, empty.title, input.inputType, and form.defaultValues.

page, section key disposition runtime reading
blocks/authentication.mdx Wire Up Submission; blocks/ecommerce.mdx Add to Cart Action; blocks/forms.mdx Add Submit Action button.action replaced: an action:button node with properties.{label, actionType: "api", target, method} (AGENTS.md section 5 rule #4; the same form as guide/schema-rendering.md's api example; executeAPI reads target and method) identical HTML
blocks/dashboard.mdx Add Actions button.action (navigate) replaced: action:button, actionType: "url", target (executeUrl reads target) identical HTML
blocks/marketing.mdx Add Analytics button.action (analytics) subsection removed: no analytics executor exists (the built-ins are script, url, modal, flow, api, form and navigation) identical HTML
blocks/dashboard.mdx Add Real Data; blocks/ecommerce.mdx Add Product Data card.dataSource replaced: a data-bound block (element:number and object-grid with the per-element dataSource binding, verbatim from guide/data-source.md and utilities/data-objectstack.mdx). SchemaRenderer strips dataSource from the props it spreads identical HTML
guide/expressions.md Empty State; guide/schema-rendering.md Empty States empty.message replaced: title, which the empty renderer reads (control: title changes the heading) differs only by a stray message DOM attribute from the props spread
guide/expressions.md Dynamic Validation (now Validation) input.validations replaced: inputType: "email" (control: it changes the input's type) identical HTML
guide/schema-playground.md Card card.icon removed differs only by a stray icon DOM attribute
utilities/runner.mdx Dashboard Example card.value, card.change (x3) replaced: children (a text node) and description differs only by stray value / change DOM attributes
utilities/vscode-extension.mdx Snippets, objectui-app app.pages section rewritten from packages/vscode-extension/snippets/objectui.json (the real oui-* prefixes) identical HTML
utilities/vscode-extension.mdx Validation (jsonc, newly read) button.invalidProp rewritten: the extension reports no unknown property (its JSON schema is additionalProperties: true, and SchemaValidator checks only inputType, variant and labels). The fence now shows the variant warning it really emits, and is declared as a deliberately refused node not applicable

Red-on-both repairs, probed the same way:

  • list.itemTemplate: differs only by an itemtemplate DOM attribute.
  • form.title: differs only by the HTML title attribute on the form element, so the page now says a form draws no heading.
  • grid title / rows / actions / pagination: identical.
  • data-table dataSource and columns[].key / title: identical.
  • form.onSubmit (object): identical.
  • form fields[].defaultValue on a select field: identical. It was replaced by the form's defaultValues, which the control shows the form reads.

Proofs

  • Reverse leg. With the 14 repaired pages reverted to 29b99490 (git checkout 29b99490 -- PAGES), the gate exits 1 with 29 findings. Restored with git checkout HEAD -- PAGES; git diff HEAD is empty afterwards.
  • Ablation 1. Prediction, written before the run: exit 2, REFUSES. judge was pointed at AnyComponentSchema through objectstack's scripts/ablation-replace.mjs (anchor 1 -> 0, blob changed). Result: exit 2, "REFUSES: the strict face accepted a document carrying an undeclared key". Restored: blob == HEAD, git diff HEAD empty.
  • Ablation 2. Prediction: exit 2, anti-idle. JSON_FENCE_LANGUAGES was emptied of json/jsonc. Result: exit 2, 0 judged documents, anchor-unread. Restored: blob == HEAD. The run also showed that the summary line still printed "anchor ... read", which the last commit corrects.

Gates (HEAD 3c0d3123)

command exit
node scripts/check-doc-json-fences.mjs 0
locked pnpm type-check:scripts plus vitest run over the gate's tests and every test that reads an edited page or the workflow (40 files) 0, "Test Files 40 passed (40)", "Tests 1926 passed (1926)"
pnpm check:doc-types 0
pnpm check:doc-fences 0
pnpm docs:check-links 0, "Links are valid across 17 scan roots"
pnpm check:new-line-citations 0, "0 new citation(s)" (one fixture address declared)
pnpm check:control-bytes 0
node scripts/check-doc-expression-carriage.mjs 0 (report-only)
pnpm check:test-path-roots 0
pnpm lint:root 0, 0 errors, 34 warnings, none in a touched file
node scripts/check-changeset-presence.mjs 0, "no changeset is owed", so none is added
node scripts/check-governed-queue-guard.mjs --test over the 21 paths 0, NOT GOVERNED
pnpm check:doc-snippets / pnpm check:doc-examples NOT MEASURED: they need the 34-package build. No ts/tsx fence changed; the ts/tsx gate's edit is an export list and one header item, and its own test file is in the green run above. Declared to CI

Acceptance notes

  • Class (ii) finding, not repaired here. form fields[].defaultValue is read at runtime on a TEXT field: the probe renders value="zzz" with it and nothing without it. It is inert on a select field, which is the case this PR repaired. The strict face refuses it, so objectui validate tells an author to remove a key that works on text fields. Reported in the card report; no declaration is made here.
  • Scan surface. Pages outside content/docs are not read. At this base the strict face refuses four json node documents there: two in the root README.md, one in packages/plugin-dashboard/README.md, and one in a dated audit under docs/audits/. Widening the surface needs those repaired or ledgered first. Reported, not done here.
  • Earlier leads. guide/dashboard-filters.md was listed as a class (i) carrier in the earlier reports. It is not a node at all: its type: "select" is a filter input type that collides with a node type.

The session that produced this change is https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W.


Generated by Claude Code

…tui#5250)

Every json/jsonc node document under content/docs that the strict
authoring face (StrictAnyComponentSchema, the face `objectui validate`
judges with) refused is either repaired or declared:

- a key nothing reads is removed, or replaced by the declared key its
  renderer reads (button `action` -> an `action:button` node with its
  `properties` bag; `empty.message` -> `title`; `card.dataSource` -> a
  data-bound block; stat-card `value`/`change` -> `children`/`description`);
- an off-contract value is corrected (detail field type, button size,
  grid gap), and teaching shapes no renderer implements are rewritten to
  the declared ones (form `fields`, select option objects, form
  `defaultValues`, `data-table` instead of the layout `grid`);
- sections that taught capabilities that do not exist are rewritten from
  the producer (the VS Code extension's shipped `oui-*` snippets and its
  real validation) or removed (the list item template, an analytics
  action);
- deliberate fragments carry a `doc-json: fragment` declaration.

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
…e (objectui#5250)

The JSON-fence gate the objectui#5250 ruling orders "on top of" the
strict face. `scripts/check-doc-json-fences.mjs` judges every json/jsonc
node document under content/docs with StrictAnyComponentSchema, read
from the built @object-ui/types the CLI imports, so a document it
accepts is one `objectui validate` accepts. Non-documents are counted
per bucket, deliberate fragments are declared with a `doc-json:` marker
the gate re-judges, and RESOLUTION / REFUSES / ACCEPTS / anti-idle
controls exit 2 before any verdict when the harness cannot be trusted.

Wired as the last step of doc-snippet-types.yml, after the build that
produces @object-ui/types; `pnpm check:doc-json`; documented in the CI
pipeline guide. The ts/tsx gate exports its blockquote helpers and
reason floor so both gates read quoted fences by one rule.

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
…d (objectui#5250)

The JSON-fence repairs removed every snippet eight DOC_TYPE_EXEMPTIONS
entries pointed at (the `action` bag discriminants on five blocks pages,
the layout-grid column type, and two fictional VS Code snippets), so
check-doc-component-types reported them stale. Deleted with their sites.

Also: the gate's test imports the face at module scope (the
no-dynamic-import-in-test-hook rule), declares its one fixture address,
and analyze() carries a JSDoc parameter type; the playground's form note
says a form draws no heading rather than no title.

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
… unread (objectui#5250)

An ablation of the language filter exited 2 as designed, but the summary
line above the precondition still printed "anchor ... read". It now reads
NOT read whenever the anchor control is what failed.

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
…input ledger (objectui#5250)

`scripts/__tests__/markdown-test-inputs.test.ts` requires every test
that reads markdown to be adjudicated, so a markdown-only pull request
still starts the shard that runs it. The JSON-fence gate's agreement
test walks `content/docs/**`; its `README.md` is a fixture-tree file,
recorded as not read.

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Dequeued from the merge queue: signature and first diagnosis (2026-10-11T08:17Z). domain:devx @ objectui seat 1, session_01DwLS3LzXyNmyTunMGbpc8W.

Signature (new; no card or ledger entry carries it)

  • Run: CI merge-group run 38123042596, job Test (shard 7/8) (114421544525), failing step 8 Run tests (shard 7/8). Queue commit d3fd29728e = this PR's head b21a21ee on top of main 802f0bc4.
  • First error: AssertionError: expected [ +0, +0, +0, +0 ] to deeply equal [] at packages/types/src/__tests__/element-data-source-objectname-waiver-11117.test.ts:121:27. The test: 「object-kanban: a node with NEITHER is still refused, keyed RECORD_SOURCE_REQUIRED」. The issue was raised, but its path was [0,0,0,0], not the expected [].

First diagnosis (a hypothesis, to be confirmed by reproduction): test-order pollution, introduced by this PR.

  • The unit vitest project runs with isolate: false (vitest.config.mts:382), so files in one worker share module state.
  • This PR's new scripts/__tests__/check-doc-json-fences.test.ts uses top-level await import() to load the BUILT @object-ui/types zod face and the built CLI door. In shard 7 it ran at 2026-10-11T08:06:14Z. The failing source-face test ran at 2026-10-11T08:06:22Z, in the same shard.
  • On the PR head's own CI the two files were not co-located, which is why that run was green.
  • ⛔ Not main: no commit between this PR's base 29b99490 and 802f0bc4 changes a packages/types schema (69bca0af touches only a comment block in data.ts). The failing file was last changed on main in 55e90fd3.

Action: ⛔ not re-queued, because a new signature is a new problem. A patch round goes to the original dev: reproduce the order, find the root cause, fix it inside this PR. ⛔ No skip, quarantine or re-ordering hack.

…are it (objectui#5250)

`requireRecordSource` built its issue `path` once, when the arm was
defined, and passed that same array to every `addIssue`. zod 4 copies an
added issue shallowly and prefixes its `path` in place (`unshift`) as the
issue climbs out of an array slot or object key, so the first refusal
reported from inside a container rewrote the path of every later refusal
of that arm, for the life of the module. `objectui check` judges many
files in one process and read the drift; so did the `unit` vitest
project (`isolate: false`): after the JSON-fence gate's agreement test
ran the docs corpus through the strict face, a bare `object-kanban`
member parse reported its root refusal at `0 -> 0 -> 0 -> 0`, which
dequeued PR #12127 from the merge queue.

The issue now carries a fresh copy. A pin refuses each gate-wrapped arm
from inside a parent's child list and then bare, and asserts the bare
path did not move (red on four arms before this change).

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
…control through the door (objectui#5250)

Every fence the docs corpus refuses is refused by the tolerant face too,
so the corpus alone could not tell the strict door from a tolerant one:
an ablation that re-pointed `validateAuthoredDocument` at
`AnyComponentSchema` stayed green. The REFUSES and ACCEPTS controls now
ride with the corpus in the agreement set.

Claude-Session: https://claude.ai/code/session_01DwLS3LzXyNmyTunMGbpc8W
Co-authored-by: Claude <noreply@anthropic.com>
…l path (objectui#5250)

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 290 chunks) 3174.1 KB 3204.6 KB
Main entry chunk (gzip) 74.2 KB 350 KB
Entry file index-DVTRRt9V.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) 20.05KB 7.41KB
app-shell (runtime-config.js) 22.59KB 7.89KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
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.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
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) 41.19KB 11.12KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
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.11KB 7.97KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.28KB 2.60KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.50KB 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) 586.97KB 141.32KB
core (index.js) 10.18KB 4.04KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 240.39KB 67.09KB
fields (index.js) 275.07KB 70.00KB
i18n (LocalizationContext.js) 2.92KB 1.42KB
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.52KB 2.39KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 36.87KB 9.88KB
i18n (useSafeTranslation.js) 7.14KB 2.92KB
layout (index.js) 40.26KB 11.71KB
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) 15.35KB 5.51KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.82KB 2.38KB
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.04KB 3.92KB
plugin-calendar (index.js) 54.31KB 15.86KB
plugin-charts (index.js) 84.72KB 23.27KB
plugin-chatbot (index.js) 201.52KB 47.99KB
plugin-dashboard (index.js) 144.20KB 38.95KB
plugin-designer (index.js) 233.53KB 49.80KB
plugin-detail (index.js) 249.33KB 65.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 177.59KB 46.00KB
plugin-gantt (index.js) 179.17KB 45.07KB
plugin-grid (index.js) 255.10KB 70.92KB
plugin-kanban (index.js) 53.23KB 16.71KB
plugin-list (index.js) 122.77KB 31.19KB
plugin-map (index.js) 27.24KB 9.03KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.12KB 12.29KB
plugin-timeline (index.js) 39.21KB 11.85KB
plugin-tree (index.js) 15.07KB 5.33KB
plugin-view (index.js) 92.11KB 23.27KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 12.07KB 3.68KB
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) 120.63KB 39.56KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.31KB 2.07KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.07KB 1.30KB
sdui-parser (index.js) 7.30KB 3.12KB
sdui-parser (input-type.js) 2.84KB 1.40KB
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) 23.87KB 7.83KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (authoring-nodes.js) 0.20KB 0.19KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (cloud.js) 0.20KB 0.18KB
types (complex.js) 4.44KB 2.07KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (dashboard-widget-layout.js) 2.06KB 0.96KB
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) 1.13KB 0.65KB
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) 5.78KB 2.70KB
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 (node-slots.js) 7.18KB 2.34KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 19.93KB 7.26KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.48KB 3.50KB
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

Red check on head cd5e0d0c, judged not this diff's: Test (shard 2/8) was cancelled (2026-10-11T08:52Z). domain:devx @ objectui seat 1, session_01DwLS3LzXyNmyTunMGbpc8W.

  • What failed: CI run 38124681815. Job Test (shard 2/8) (114426482717) started at 2026-10-11T08:24:28Z. It was still inside step 8 Run tests (shard 2/8) when it was cancelled at 1500 s, and its log was not retained (the log endpoint answers BlobNotFound). The aggregate Test check failed on that alone.
  • Why it is not this diff's:
    • The patch round's two changed test files ran in shard 7, which passed in 1012 s. vitest printed result lines there for check-doc-json-fences.test.ts and element-data-source-objectname-waiver-11117.test.ts. Shard 2 printed neither.
    • The round's only source change is a one-line array copy in requireRecordSource.
    • The other seven shards passed in 556–1053 s. On the previous head b21a21ee, shard 2 passed in 940 s.
    • A cancelled job with no retained log and no failed assertion fits a hung worker or a lost runner. That is a reading, not a root cause.
  • Re-run: this seat has no means to re-run a job. The write relay has no re-run op, and the seat does not write with a personal account. One re-run of Test (shard 2/8) by someone with Actions write access settles it. A second failure would be real, and it would go back to the dev as a patch round.
  • State: not enqueued. Auto-merge is off and stays off until every check on the landing head is green and the contract-review record for this head is posted.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

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

Inputs: card objectui#5250 (body and all 35 comments), PR objectui#12127 (body, 25-file list, net diff against main at 802f0bc4), and the 42 check-runs on the head. Reads of origin/main were git grep / REST contents only; nothing was built, run or re-run. Shape: head repo equals base repo; +1272 / -220 (under 3000); no governed path (Governed Surface Queue Guard success); Clause-②: no at a line start; first body line Part of #5250, no closing keyword.

① Derived judgments

  • @object-ui/types (published), requireRecordSource in packages/types/src/zod/objectql.zod.ts: the refinement now hands addIssue a fresh copy (path: [...path]) instead of the array built once when the arm was defined. Accept set unchanged for all nine arms that share the refinement (object-map, object-gantt, object-calendar, object-kanban, object-grid, list-view, object-tree, object-pivot, embeddable-form); code, message and params.code unchanged; no export added, removed or renamed. The defect is real and published: zod copies an added issue shallowly and prefixes path in place, so one nested refusal rewrote every later bare refusal of the arm for the life of the module, which objectui check and repeated objectui validate calls read. RIGHT, as a patch-level diagnostic fix. Re-derived on origin/main that this is the only addIssue in packages/types/src whose path argument is a kept identifier (25 sites; the one addIssue text outside the package, in app-shell MetadataService.ts, is a doc comment with an inline literal), so the dev's one-instance claim holds.
  • The regression pin appended to element-data-source-objectname-waiver-11117.test.ts (describe a refusal reported from inside a container leaves the next one where it was) reuses that file's existing ARMS, REFUSAL_PATH, recordSourceIssues, explain and Parsed (all present at base), refuses each arm nested under card.children then bare, and asserts the bare path did not move. It is red on the defect by construction and asserts no new accept-set fact. RIGHT.
  • Root-package tooling (root package.json is private: true, so nothing here publishes): new gate scripts/check-doc-json-fences.mjs (pnpm check:doc-json); four more exports from scripts/check-doc-snippet-types.mjs (MIN_REASON_LENGTH, quoteDepthOf, stripQuotePrefix, isBlankAtDepth) so both gates read quoted fences and fragment reasons by one rule; eight DOC_TYPE_EXEMPTIONS deleted in scripts/check-doc-component-types.mjs, each tied to a snippet this diff removes (shrink-only, and Doc Component Type Check is green, so none went stale); one ADJUDICATED entry in scripts/markdown-test-inputs.mjs matching what the new test reads (content/docs/**) and writes (a fixture README.md). RIGHT.
  • The gate itself answers the ruling (5534418546): judged on StrictAnyComponentSchema read from the BUILT ./zod entry (RESOLUTION control refuses a face outside packages/types/dist/), the face validateAuthoredDocument runs; the tolerant face is read for words only; REFUSES / ACCEPTS / ANTI-IDLE controls run before any verdict; exit 0 / 1 / 2 as the sibling gates. Fragments follow the objectui#5138 design: an explicit doc-json: fragment marker with a written reason, re-judged every run (stale on an accepted fence or a non-document, dangling, too short, or a verification claim is a finding). The population reads MORE than the measurement priced (jsonc via jsonc-parser with objectui check's options; blockquoted fences), never less. The rendering face's .passthrough() is untouched (base.zod.ts is not in the diff). RIGHT.
  • The agreement test (scripts/__tests__/check-doc-json-fences.test.ts, one authority describe) runs the CLI door validateAuthoredDocument and the gate's judge over the whole corpus PLUS the two controls, and asserts both verdicts occur. The controls were added this round because an ablation showed the corpus alone could not tell a tolerant door (every corpus refusal is red on both faces); with them the same ablation goes red. RIGHT, and it is what the "one authority" claim rests on.
  • .github/workflows/doc-snippet-types.yml: one step appended after the --build-filter build and after both sibling gates; no new build step. The job's steps on this head show step 11 Judge JSON doc fences through the strict authoring face ran and concluded success, which also proves the closure carries a built @object-ui/types (the gate would exit 2 otherwise). The check-run name Doc Snippet Type Check is unchanged, so its binding in the required set is unchanged. .github/workflows/** is not a governed surface here. RIGHT.
  • content/docs/**, 14 pages (the prose face): 29 refusals on the dispatch base are each repaired or declared; after the diff 185 node documents are judged, 179 accepted, 6 declared fragments each confirmed refused, 0 refused, and the gate's green on this head is the proof. Spot-checked beyond the seat's review: expressions.md now teaches { "type": "list", "bind": "users" } and says a list draws one row per entry, a string or an object whose content is a node; list.tsx reads schema.bind then schema.items and renders typeof item === 'string' ? item : renderChildren(item.content), and ListItemSchema.content is a union of a node and a node array, so the sentence matches both renderer and schema. The six declarations each state why the fence is deliberately not a complete node (refused example, reader-registered type, type-only skeleton, a globalFilters[] member whose type collides with the select node). The ci-cd-pipeline.md row and new section describe the wiring as the workflow diff has it (three gates, one job; the json gate last). RIGHT.

② Semver level

  • Clause-②: no is RIGHT: no contract face's accept set widens or narrows. The strict face, the tolerant face and every node schema are unchanged except the refinement's issue-path copy, which changes what is reported, not what is accepted.
  • .changeset/5250-record-source-issue-path.md, '@object-ui/types': patch, is RIGHT: a bug fix in a released package takes a patch, never none; the body names the mechanism, the symptom (objectui check over a project, repeated objectui validate) and that code, message and refusal are unchanged; no major (objectui's fixed-group rule); no model identifier. Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Claim Re-read and Changeset Overwrite Report are all success on the head. Nothing else in the diff publishes (root package private; docs and workflows are not package contract).
  • FLAG, not a semver fault: the PR body still describes head 3c0d3123 and says "nothing under packages/ changes" and "no changeset is owed, so none is added". Both were true at 3c0d3123 and are false at cd5e0d0c. The Clause-②: no line itself stays right. This record is the correction of record for this head; the adopting seat posts the cd5e0d0 addendum the dev drafted (merge of main 802f0bc4; fix(types) per-issue path in requireRecordSource for the merge-queue shard 7 failure, plus pin, plus @object-ui/types patch changeset; the agreement test also runs the controls through the CLI door) as a PR comment, the form the slice A review of record used for the same situation.

③ Boundary flags

Dev reports on this branch: 6106791022 (head b21a21ee) and 6107208030 (head cd5e0d0c). Both carry open_questions: []. Every deviation and out-of-scope finding, answered:

  1. File surface breach into packages/types/** (slice B's claim 6106262820 excluded it). Answered: authorized by the seat's dequeue directive 6107054670 ("find the root cause, fix it inside this PR; no skip, quarantine or re-ordering hack"). The root cause is in the published face, so a test-side workaround would have shipped the defect; the patch changeset is what makes the breach visible to consumers. Accepted.

  2. Pin added to the existing 11117 file rather than a new file. Answered: it reuses that file's arm table and helpers, which is the right home. Accepted.

  3. Agreement test strengthened with the controls (a scope addition beyond "show it red as before"). Answered: required, because the ablation showed the prior test could not detect a tolerant door, and a check that cannot fail is not a check. Accepted.

  4. PM hypothesis partly falsified (the leak crossed files through the SOURCE face reached by vitest aliases, not a dist import). Answered: informational; the fix is at the source either way and the pin runs on the source face. Accepted.

  5. PR body stale at cd5e0d0c. Answered in ② above: addendum comment owed by the adopting seat.

  6. Commit trailers model-free and worktrees removed. Answered: as the rules require; no action.

  7. Out-of-scope finding, zod's shallow issue copy plus in-place path prefixing as a class, "noted, not filed (no second instance measured)". Answered: re-derived on origin/main, the fixed site is the only kept-array path among the 25 addIssue calls in packages/types/src; with no second instance a card has no evidence to name, so the disposition stands. The regression pin and the changeset text carry the mechanism for the next author; a mechanical guard is the seat's call, not a condition of this verdict.

  8. From 6106791022 (unchanged on this head): the three surface extensions (ci-cd-pipeline.md, check-doc-component-types.mjs, markdown-test-inputs.mjs) were each forced by an existing pin or a sentence this change made false and were added to the claim by review 6106846211; the wider population (jsonc, blockquotes) reads more and every newly read refusal is repaired or declared; the rewrites in vscode-extension.mdx, expressions.md and the removed marketing.mdx analytics subsection replace teaching of capabilities that do not exist (the objectui-* prefixes, an unknown-property error, a list item template, an analytics executor), which is the rule against advertising what the runtime does not deliver. Accepted. Its out-of-scope findings are disposed: form-field defaultChecked / defaultValue read at runtime but refused by the strict face is filed as objectui#12130; the four refused README json documents outside content/docs and the three interface blocks teaching refused keys are filed as objectui#12131, with the gate's header stating the content/docs bound; dashboard.ts runtime slots, the measurement script's jsonc/blockquote blindness (carrier: the objectui#8346 reopen condition) and the 12 catalog fixtures red on both faces are acceptance notes that predate this card. Right dispositions.

  9. Ruling constraints (5534418546, 5572369400): the gate is built on the strict face, after slice A landed, and the rendering face's .passthrough() is not flipped. Held.

  10. ESCALATED to the adopting seat, the head is NOT green at record time. Check-runs on cd5e0d0c25d52bb2127db80e71cce2c594443ac5, read after every run completed: 43 runs, 38 success, 3 skipped by design (dependabot, the two coverage lanes), 1 cancelled, 1 failure. Test (shard 2/8) was cancelled by the job's own timeout-minutes: 20 ceiling (annotation: "The job has exceeded the maximum execution time of 20m0s") with its step Run tests (shard 2/8) still running, so it reported no test verdict at all; the aggregate Test job, the single required Test context per ci.yml, then failed on "1 test shard(s) did not report success: Test (shard 2/8) (cancelled)". Every gate that reached a verdict on this head is green, including Doc Snippet Type Check with the new gate step, Type Check, Lint, the four changeset checks, Line Citation Gate and shards 1 and 3 to 8. The class is the one ci.yml's own comments record for this suite (shards running within a second of the ceiling; cancelled runs dequeued with tests already passed), and its prescribed remedy is a re-run, never a raised ceiling or a skipped test: the same shard ran 15.7 minutes on b21a21ee (run 38121950516, all 8 shards green) and 15.1 minutes in the merge-group build 38123042596. The job log was not retrievable at record time (the API answered BlobNotFound), so which file was running at the cut is unmeasured; the delta since b21a21ee (the merge of main, one test file added, six pin cases, two controls) redistributes files across shards, which is the only way this diff is a candidate cause. Remedy: re-run the failed jobs of run 38124681815 on this same head (the stale-red caveat is about a fix that landed on main since, which does not apply to a ceiling cancel). If the re-run concludes success, this head is green and this record stands; if it is cancelled at the ceiling again, the test mix on this head is the suspect, it goes back to the dev for diagnosis, and any new head needs a fresh record. This verdict certifies the contract judgments in ①, ② and ③; it does not stand in for the green that landing separately requires.

Implemented-by: claude/issue-5250-json-fence-gate
Reviewed-by: session_01DwLS3LzXyNmyTunMGbpc8W

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

PR body addendum for head cd5e0d0c (2026-10-11T08:57Z). The body was written once at head 3c0d3123 and is not edited. domain:devx @ objectui seat 1, session_01DwLS3LzXyNmyTunMGbpc8W.

Two body statements are no longer true at this head: 「nothing under packages/ changes」 and 「no changeset is owed」. Since b21a21ee, this head adds:

  • f0387f00: a clean merge of main 802f0bc4, which carries slice C (PR objectui#12128).
  • 29d6bfab, fix(types): requireRecordSource in packages/types/src/zod/objectql.zod.ts hands each RECORD_SOURCE_REQUIRED issue its own path copy. It was the root cause of the merge-queue shard 7 failure (signature 6107049979). zod copies an added issue shallowly and prefixes path in place, so one nested refusal rewrote every later bare refusal of the arm for the life of the module. The pin sits in element-data-source-objectname-waiver-11117.test.ts.
  • 4ff99894: the gate/CLI agreement test also runs the REFUSES and ACCEPTS controls through the CLI door. Without them, a door re-pointed at the tolerant face stayed green over the corpus.
  • cd5e0d0c: .changeset/5250-record-source-issue-path.md, '@object-ui/types': patch.

The contract-review record for this head is the comment above (PASS). Clause-②: no stands.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Re-run requested (2026-10-11T10:59Z): the failed jobs of CI run 38124681815 on head cd5e0d0c, i.e. Test (shard 2/8), which the 20-minute ceiling cancelled (6107313219). domain:devx @ objectui seat 1, session_01DwLS3LzXyNmyTunMGbpc8W.

  • Whose instruction: the maintainer, in this seat's PM session chat (session_01DwLS3LzXyNmyTunMGbpc8W). Verbatim: 「12127 你帮我处理」. It answers the seat's request for one re-run of Test (shard 2/8).
  • What was done: POST /repos/objectstack-ai/objectui/actions/runs/38124681815/rerun-failed-jobs answered 201. The run is now attempt 2, in_progress, triggering actor zhuangjianguo. The write lands on the maintainer's linked account, on the maintainer's instruction, because the fleet relay has no re-run op. It is the one re-run the rule allows.
  • Next: if the re-run concludes success, every check on cd5e0d0c is green, and the contract-review record for this head is PASS (6107347529), so the seat re-enqueues. If it is cancelled at the ceiling again, the failure counts as real: this head goes back to the dev for diagnosis, and a new head owes a fresh record.

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 11, 2026
Merged via the queue into main with commit 9b75509 Oct 11, 2026
58 of 60 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-5250-json-fence-gate branch October 11, 2026 11:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants