Skip to content

fix(plugin-detail): compile the reference rail's parent scope by relationship arity - #9200

Merged
os-tesla merged 7 commits into
mainfrom
claude/issue-8883-reference-rail-parent-scope-arity
Sep 11, 2026
Merged

fix(plugin-detail): compile the reference rail's parent scope by relationship arity#9200
os-tesla merged 7 commits into
mainfrom
claude/issue-8883-reference-rail-parent-scope-arity

Conversation

@os-tesla

@os-tesla os-tesla commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #8883

record:reference_rail compiled an author-supplied relationshipField as bare
equality twice, in two different grammars — the $filter it puts on the
wire and the filter[FIELD]=VALUE URL behind its "View All" link. On a
multiple: true relationship the stored value is an array, = asks whether
that whole array is one id, and driver-sql refuses with
400 INVALID_FILTER while prescribing $contains.

What changed

Half one — the $filter, repaired. The condition is composed by
composeParentScopeFilter from @object-ui/core: the ONE compiler the related
list's rows (#7299) and its tab badge (#8882, PR #9184) already share, whose
arity verdict is @objectstack/spec/data's own isMultiValueField. No local
arity rule was written at this call site, and no authoring key was added.

Half two — the link, refused. No URL spelling on this surface carries
membership, so the link is suppressed on a multi-value relationship and the
reason is logged once. Losing that affordance is the cost of the repair,
not its purpose — which is why it is a seat-level call while growing the URL
operator set is not.

Landing half one alone was explicitly out: it would show the right rows above a
link that goes somewhere else, a disagreement invisible until the user clicks.

Measurements

M1 — does the rail's dataSource expose getObjectSchema? YES, and it is
the same object the rows read it off.
getObjectSchema is a required
(non-optional) member of DataSource in packages/types/src/data.ts:539.
record-related-list.tsx:248 hands ctx?.dataSource straight into
RelatedList, which reads dataSource.getObjectSchema at
RelatedList.tsx:515 — the rail holds that same value at
record-reference-rail.tsx:113. ObjectStackAdapter.getObjectSchema
(data-objectstack/src/index.ts:4660) is cached per object name, so N rail
entries cost N cached metadata reads, not N round trips. Measured, not assumed
from the badge's result. Counter-case measured too: PagePreview.tsx:255
mounts RecordContextProvider with no dataSource, so the rail's fetch
effect early-returns there and nothing changes.

Third round — the merge queue ejected this PR, and the cause was a second uncited file

The PR was green on its own head and red in the queue, which is the tell that
main moved under it. 1f4e02995 (objectui#8937 / PR #9195) landed a residue
sweep in
packages/plugin-detail/src/__tests__/relatedListParentScopeResidue-8937.test.ts:
every tracked file under packages naming BOTH the spec arity predicate and
the driver must cite objectstack#17469. Round two cleared the renderer. The
sweep's population is the CHANGED SET, and this PR's test file was in it
too.

Reproduced locally rather than inferred — origin/main merged in, then the pin
run on the combined tree before any edit:

× cites objectstack#17469 in every file that relates the two sides
AssertionError: a file relates the spec predicate to the driver without citing
the card that owns their divergence: expected [ Array(1) ] to deeply equal []
Tests  1 failed | 7 passed (8)

Readings on the test file, and the population re-derived with the sweep's own
enumeration (git grep -l isMultiValueField packages, minus CHANGELOG.md,
split by /driver/i):

reading on record-reference-rail.parentScopeArity-8883.test.tsx before after
isMultiValueField 1 1
driver (case-insensitive occurrences) 4 7
objectstack#17469 0 1
population on the merged tree before after
files naming the predicate (minus CHANGELOG) 10 10
carriers (also naming the driver) 6 6
uncited carriers 1 — this test file 0
predicate-only (the sweep's own discriminator side) 4 4

The docblock now states the relationship it was implying: the spec gates on the
field TYPE, driver-sql decides storage on !!field.multiple whatever the
type, the two part company for a multiple: true def on a type outside the
spec's multi-capable set, and objectstack#17469 owns which is right. It also
records why these cases sit clear of that disagreement — they relate through a
lookup field, which both rules call multi-valued.

Pin three on the merged tree: Tests 8 passed (8). And it discriminates on
THIS file, proved rather than assumed — leg G below replaces the citation token
with plain words (occurrences 1 → 0, driver count unchanged at 7 so the file
stays classified as a carrier) and the pin reds again with exactly one uncited
file, then restores under a trap.

The diff adds docblock lines and removes none; no predicate, assertion or
executable line moved. The 7-leg rail matrix was re-run afterwards and no leg
moved
— same six assertions, same red combinations, pre-fix leg still
3 failed | 3 passed (6). pnpm --filter @object-ui/plugin-detail test on the
merged tree at 33bb17450: Test Files 172 passed (172) / Tests 1595 passed (1595) (one file and eight cases more than round two — the sweep this PR now
satisfies came in with the merge).

Rework (2026-09-11) — prose only, no executable line moved

The file header carried a sentence that was FALSE: that the seam's verdict is
"the same predicate the driver that executes the query decides on". Measured at
source in the sibling checkout, packages/drivers/driver-sql/src/sql-driver.ts:

protected isJsonField(type: string, field: any): boolean {
  if (!field.multiple && FILE_REFERENCE_TYPES.has(type)) return this.mediaColumnIsJson();
  return JSON_COLUMN_TYPES.has(type) || !!field.multiple;
}

!!field.multiple on ANY type, against the spec's
MULTI_OPTION_TYPES.has(type) || (MULTI_CAPABLE_TYPES.has(type) && multiple === true).
So { type: 'master_detail', multiple: true } answers false to the spec and
true to the driver. The paragraph now states that measured relationship
instead of an identity, names which rule this renderer follows and why, and
cites objectstack#17469, which owns the divergence. The ⛔ "no local arity
rule here" instruction is kept — that half was true and load-bearing.

The file is now outside the in-flight citation sweep's failure set by
construction rather than by luck. Readings on this head:

reading on record-reference-rail.tsx before after
isMultiValueField 1 1
/driver/i 2 3
objectstack#17469 0 1

Every changed line begins with * inside the file docblock — verified by
reading the raw diff, not by intent. The 7-leg ablation matrix was re-run
afterwards and no leg moved: the same six assertions go red in the same
combinations, and the pre-fix leg still reads 3 failed | 3 passed (6).
pnpm --filter @object-ui/plugin-detail test re-run at 8fd5fba02:
Test Files 171 passed (171) / Tests 1587 passed (1587).

M2 — which trade? Resolve arity FIRST, and for the rail's own reason. Not a
copy of the badge. The rail's fetchedSigRef latch keys on
(parentId + entries); the arity is in neither, so unlike RelatedList
whose fetch effect re-runs on the verdict and can attempt equality, be refused,
and refetch — a probe-then-correct design would make the refused first attempt
the only attempt, leaving the entry on its error state until the user
navigates away. The rail also renders a navigational link decided by the same
verdict, so deferring the verdict ships the right rows under the wrong link.
The resolution is not a gate: an adapter without getObjectSchema, or one
whose fetch rejects, still reads rows on the historical equality wire
(pinned as DEGRADATION CONTROL).

M3 — the URL grammar claim, re-measured, and the card's version is
incomplete.
The rail's link targets /apps/:appName/:objectName
(AppContent.tsx:999ObjectView), not the /data surface the card and
RelatedList.tsx:671-692 describe. Both parsers were run on the same input:

input drillUrlFilters.parseUrlFilterTriples (the /data surface) ObjectView.urlFilters (this link's target)
filter[owners][contains]=u1 [] — dropped [["owners][contains","=","u1"]]mangled into a filter on a nonexistent field
lit control filter[owners]=u1 [["owners","=","u1"]] [["owners","=","u1"]]
lit control filter[amount][gte]=100 [["amount",">=","100"]] [["amount][gte","=","100"]]

Both zero/garbage readings have a lit control in the same command. Conclusion
unchanged and strengthened: neither surface can express membership, and the one
the rail actually links to does not even drop the suffix — it emits a condition
on a field name that does not exist. There is no third spelling either: both
readers only look at keys starting with filter[
(ObjectDataPage.tsx:228, ObjectView.tsx:2088), so no JSON-AST param is
available on this route.

M4 — is a multi-value relationshipField reachable? YES, and with zero
authoring.
ReferenceRailEntrySchema.relationshipField is a bare
z.ZodString (no enum, no arity constraint), and the registry manifest
(plugin-detail/src/index.tsx:796) publishes only hideEmpty as an input, so
nothing upstream narrows it. Stronger: the synthesised path reaches it without
an author at all — deriveRelatedLists.ts:244 admits every lookup field with
no multiple filter, lookup is in the spec's MULTI_CAPABLE_TYPES, and
buildDefaultPageSchema.ts:1059 copies that relationshipField straight onto
a rail entry. The card's premise holds and is wider than it states.

Instrument discipline

Pre-fix reading — the rail source restored to origin/main's bytes
(git checkout c5b0cecf6 -- packages/plugin-detail/src/renderers/record-reference-rail.tsx,
blob a5086aa2 on disk, verified by hash and by a grep of the injected tokens
going 3 -> 0 and 2 -> 0), same test file, same command:

Tests  3 failed | 3 passed (6)
  x SUBJECT - a multi-value relationship is queried by MEMBERSHIP, selectively
  x SUBJECT - the "View All" link is SUPPRESSED on a multi-value relationship, loudly
  x MEASURED DIFFERENCE - the rail resolves the arity BEFORE its one read

Restored by git checkout HEAD -- ... under an EXIT INT TERM trap, proved by
blob hash e6f44dd1 and an empty git diff HEAD.

Ablation matrix — every leg mutates one thing, proves it reached disk
(anchor token count before/after plus a changed blob hash, so a zero-hit edit
cannot pass as a run), runs the same file, and restores under a trap:

leg mutation goes RED
PRE-FIX the whole rail file at its origin/main bytes MEMBERSHIP, LINK, ARITY-FIRST
A drop !suppressViewAll from the link's condition LINK
B withhold fields from composeParentScopeFilter MEMBERSHIP, ARITY-FIRST
C insert if (fieldsFor.size === 0) return; — turn the resolution into a GATE DEGRADATION CONTROL
D suppress the link unconditionally LIVE CONTROL, DEGRADATION CONTROL
E put membership on the wire unconditionally LIVE CONTROL, DEGRADATION CONTROL
F make the fake backend PERMISSIVE (answer equality against an array) FIXTURE

All six assertions discriminate; none stays green while the thing it names is
deleted. Leg F is the one that matters most for reading the rest: the FIXTURE
case is what stops a permissive evaluator from making this whole file lie, and
it is red the moment the evaluator stops refusing.

Commands (from the repo root, at 3ab5db03c):

  • pnpm exec vitest run packages/plugin-detail/src/renderers/__tests__/record-reference-rail.parentScopeArity-8883.test.tsx -> Test Files 1 passed (1) / Tests 6 passed (6)
  • pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-detail^...' build -> VERDICT command-exit 0, 12 of 47 workspace projects
  • pnpm --filter @object-ui/plugin-detail type-check -> VERDICT command-exit 0. It covers the new test: tsc -p tsconfig.test.json is the leg that reported the two TS7006s this branch then fixed, so the coverage claim is a reading and not an assumption.
  • pnpm --filter @object-ui/plugin-detail test -> Test Files 171 passed (171) / Tests 1587 passed (1587), VERDICT command-exit 0, at 3ab5db03c (git rev-parse --short HEAD at that run)
  • Derived gates, each read off its own printed verdict line, not off a bare $?: check:control-bytes (OK, 7393 files), check:spec-symbols (three green lines, incl. "nothing cites a key its spec symbol does not declare" — the gate that caught record:related_list cannot bind to a multi-value relationship field — RelatedList builds its parent filter as bare equality, which the driver refuses with 400 INVALID_FILTER #7299's hand-written arity rule), check:changeset-claims (exit 0, report-only), check:new-line-citations (0 new citation(s)), check:phantom-deps (Every in-scope import is declared by the package that publishes it — the one that would have caught an undeclared @object-ui/core), check:i18n-keys (exit 0).
  • Repo-wide scans (pnpm lint, the full pnpm test) are CI's runs, not this branch's.
  • Narrowed lint, as a supplement and not a substitute for CI's turbo run lint:
    eslint --no-inline-config --format json over the two changed files reports
    2 files, 0 errors (27 warnings, all @typescript-eslint/no-explicit-any
    plus one pre-existing react-hooks/exhaustive-deps). The rail file's own
    baseline was read the same way with its origin/main bytes on disk —
    0 errors / 13 warnings, identical rule breakdown — so this change adds no
    lint finding to it. This config enables no type-aware rules
    (eslint.config.js declares no project / projectService), so the diff
    cannot move the verdict on any file it does not contain.
  • NOT MEASURED: check:sdui-registration-pins — it exits 2
    (PREREQUISITE NOT MET) without a console bundle, and building
    @object-ui/console needs that app's whole workspace closure, which is CI's
    run. This diff edits no ComponentRegistry.register call and adds no
    registration, so nothing it changes is in that gate's subject; CI measures it.

Acceptance notes

Found while measuring, deliberately not fixed here and not widened into
this PR:

Scope declarations


Generated by Claude Code

…tionship arity

`record:reference_rail` compiled an author-supplied `relationshipField` as bare
equality twice, in two different grammars: the `$filter` on the wire and the
`filter[<field>]=<value>` URL behind "View All". On a `multiple: true`
relationship the stored value is an array, equality asks whether that whole
array is one id, and the driver refuses with `400 INVALID_FILTER`.

The filter half now goes through `composeParentScopeFilter` from
`@object-ui/core`, the one compiler the related list's rows and tab badge
already share. The child schemas are resolved before the reads, not after a
refusal: the rail dispatches once per (record + entries) and has no second
attempt to correct itself with. Resolution is best-effort, never a gate.

The link half cannot be expressed: no URL spelling on this surface carries
membership, so it is suppressed on a multi-value relationship and the reason is
logged once. Single-value entries keep both halves byte for byte.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
…arity

Brings in objectui#8882 (PR #9184): `composeParentScopeFilter` /
`isMultiValueRelationship` in `@object-ui/core`, the seam this branch is the
first caller of.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
`tsc -p tsconfig.test.json` reported TS7006 on the two inline
`warnSpy.mock.calls.filter` predicates. One typed reader now serves both
assertions.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
@os-tesla os-tesla added domain:ui objectui ui stream: fix lands on the published library or apps — objectui execution seat needs:contract-review package: plugin-detail labels Sep 11, 2026 — with Claude
@github-actions github-actions Bot added plugin tests and removed domain:ui objectui ui stream: fix lands on the published library or apps — objectui execution seat needs:contract-review package: plugin-detail labels Sep 11, 2026
@os-tesla os-tesla added domain:ui objectui ui stream: fix lands on the published library or apps — objectui execution seat needs:contract-review package: plugin-detail labels Sep 11, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3112.4 KB 3134.8 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-B7xcxhUJ.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.69KB 6.21KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
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.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.68KB 114.78KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 209.38KB 57.92KB
fields (index.js) 247.41KB 62.43KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.18KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 253.02KB 65.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.94KB 41.04KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.10KB 14.33KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.54KB 3.31KB
plugin-view (index.js) 84.42KB 20.80KB
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.66KB 3.50KB
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) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
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.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
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) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 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

…arity

Routine sync before the objectui#8883 rework; no conflict with the rail.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
…in the rail header

The header claimed the seam's verdict was "the same predicate the driver that
executes the query decides on". It is not. Measured at source: the spec asks
`MULTI_OPTION_TYPES.has(type) || (MULTI_CAPABLE_TYPES.has(type) && multiple ===
true)`, while the SQL driver's `isJsonField` asks `JSON_COLUMN_TYPES.has(type)
|| !!field.multiple` — the flag on any type — so `{type:'master_detail',
multiple:true}` answers false to one and true to the other.

The paragraph now states the relationship it measured instead of an identity,
names which rule this renderer follows and why, and cites objectstack#17469,
which owns the divergence. The ⛔ "no local arity rule here" instruction is
kept; it was the true half.

Prose only: every changed line is inside the file docblock. No predicate, no
assertion and no executable line moved.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ

Copy link
Copy Markdown
Collaborator Author

PM review — ACCEPT on substance, one prose rework before arming

Read against git merge-base origin/main <head> = 759606e1d: 3 files, +555/−18.

Every ruling-zone item holds, measured here: R2 — composeParentScopeFilter / isMultiValueRelationship imported from @object-ui/core, no local arity rule anywhere in the diff. R3 — URL_FILTER_OPS untouched; the changed set is three files and none is the parser. R4 — both halves delivered: the $filter at the read and the link suppressed via !suppressViewAll. R5 — packages/plugin-detail/package.json untouched.

M2 was the question I most expected to be answered by resemblance, and it wasn't. The rail resolves arity before the reads for a reason that is its own, not the tab badge's: fetchedSigRef latches on (parentId + entries), the arity is in neither, so a probe-then-correct design would make the refused first attempt the only attempt. And ablation leg C — inserting if (fieldsFor.size === 0) return; — turns the DEGRADATION CONTROL red, which is what proves resolution is best-effort rather than a gate. That distinction is the whole difference between this card's fix and a silently empty rail on every entry in the app. Seven legs, all discriminating; leg F (permissive backend → FIXTURE red) closes the one hole that would make the file lie.

⛔ Rework: the file header states something false, and it collides with a pin in flight

record-reference-rail.tsx:33-34 says the spec's isMultiValueField is "the same predicate the driver that executes the query decides on." It is not. Measured in objectstack, packages/drivers/driver-sql/src/sql-driver.ts:17276:

protected isJsonField(type: string, field: any): boolean {
  if (!field.multiple && FILE_REFERENCE_TYPES.has(type)) return this.mediaColumnIsJson();
  return JSON_COLUMN_TYPES.has(type) || !!field.multiple;
}

!!field.multiple on any type ⇒ {type:'master_detail', multiple:true} answers false to the spec and true to the driver. Owned upstream by objectstack#17469.

⚠️ Not the author's error. That sentence was in objectui#9184's seam when this branch was cut, the measurement falsifying it arrived from objectui#8937 afterwards, and my dispatch brief never carried the correction. This is the third copy of one false claim and I am the reason it spread.

The collision, measured rather than predicted. PR #9195 adds a sweep: every tracked file under packages naming both isMultiValueField and matching /driver/i must cite objectstack#17469.

reading on this head value
isMultiValueField in record-reference-rail.tsx 1
/driver/i in the same file 2
objectstack#17469 in the same file 0
CONTROL — 17469 in parent-scope.ts on PR #9195 1

⇒ whichever of the two lands second, that pin goes red. Correcting the paragraph here (prose only, ⛔ no predicate and no assertion moves) is cheaper than a round on either PR.

⭐ Instrument note for anyone chasing this claim: git grep "same predicate the driver" returns nothing on this file — the sentence wraps at the / driver, so a line-oriented grep cannot see it. That is the same degeneracy PR #9195's author hit and reported; it is why their pin keys on the two subjects instead of the wording.

Arm held until the rework lands and CI reads pending: 0, failing: 0.


Generated by Claude Code

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3112.4 KB 3134.8 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-B7xcxhUJ.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.69KB 6.21KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
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.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.68KB 114.78KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 209.38KB 57.92KB
fields (index.js) 247.41KB 62.43KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.18KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 253.02KB 65.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.94KB 41.04KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.10KB 14.33KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.54KB 3.31KB
plugin-view (index.js) 84.42KB 20.80KB
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.66KB 3.50KB
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) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
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.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
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) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 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

Copy link
Copy Markdown
Collaborator Author

Rework ACCEPTED — the pin collision is cleared, measured with a lit control

Head 8fd5fba02.

reading on record-reference-rail.tsx before after
isMultiValueField 1 1
/driver/i 2 3
objectstack#17469 0 1

⇒ the file is now outside PR #9195's pin-three failure set by construction, not by luck.

The false claim is gone, and the probe is proven to be looking: grepping the pre-rework head 3ab5db03c for the same phrase returns 1; the same grep on this head returns 0. An empty reading with a dark probe would have been worthless here, which is the whole reason for the control.

Prose only, verified rather than asserted: every changed line in that file begins with * inside the docblock — filtering the diff for any changed line that is not docblock prose returns nothing. No predicate, no assertion, no executable line moved, and the 7-leg ablation matrix re-ran identical (3 failed / 3 passed pre-fix; legs A–F each reddening the same assertions). ⭐ None of the legs stopped discriminating, so nothing was quietly dropped — which is the failure mode a prose-only round is most likely to hide.

⭐ Two things the seat did that I want on the record. It re-measured the driver rule at source (sql-driver.ts:17276) instead of taking my brief's word for it — the brief was right, but it should not have been trusted, and it wasn't. And the rewritten paragraph does not merely delete the falsehood: it separates the query rule from the storage rule, states where they part company ({type:'master_detail', multiple:true}false to the spec, true to the driver), says which one this renderer follows and why (the spec is what the authoring surface is validated against), and ⛔ declines to decide the divergence, citing objectstack#17469 as its owner. That is strictly better than the sentence it replaces, which was confidently wrong in one clause.

⛔ Arm still held: 36 checks, 9 in progress, 0 failing. Not-red-yet is not green.


Generated by Claude Code

@os-tesla
os-tesla marked this pull request as ready for review September 11, 2026 17:53
@os-tesla
os-tesla added this pull request to the merge queue Sep 11, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 11, 2026

Copy link
Copy Markdown
Collaborator Author

⛔ Ejected from the merge queue — cause found, and it is my miss

This PR entered the queue at 17:54 as gh-readonly-queue/main/pr-9200-1f4e02995…. It is now gone from the queue with main unchanged at 1f4e02995 — ejection, not a propagation delay (an absent ref does not disprove enqueueing, but an absent ref plus an unmoved main after the entry was seen does).

The PR is open, unmerged, and green on its own head: 36 checks, 0 pending, 0 failing at 8fd5fba02. Green on the head and red in the queue is the tell — the queue merges main in, and main moved.

The causal chain, every link measured

1f4e02995 is PR #9195, which landed pin three: every tracked file under packages naming both the spec predicate and the driver must cite objectstack#17469. Its population is git grep -l isMultiValueField packages, minus CHANGELOG.md, split by /driver/i.test(sourceOf(rel)).

This PR's new test file falls into it:

reading on record-reference-rail.parentScopeArity-8883.test.tsx value
isMultiValueField 1 → inside git grep -l's set
/driver/i 4 → classified a carrier
objectstack#17469 0 → fails the assertion

This PR's base is 7696daac0, which predates pin three — which is exactly why its own CI is green while the combined tree is not.

⚠️ The miss is mine, and it is a miss against a rule I wrote

I checked the pin-three collision on record-reference-rail.tsx, measured 17469 going 0 → 1 there, and told the seat the collision was cleared. But the sweep's population is the changed set, not the one file I happened to look at — and "the scope must cover every file in the changed set" is the standing rule in this lane's own notes, added earlier today after PR objectui#9192's author swept for .ts readers and missed a README the same PR edited. I restated that rule and then failed to apply it one PR later.

⭐ Worth recording for whoever reads this thread: a cross-PR pin collision has to be measured against the whole changed set on the merged tree, not against the file that first raised the alarm. The alarm file is the one you notice; the population is what the sweep actually enumerates.

The seat is reworking: merge main, cite the card in the test file's docblock as a real sentence rather than a grep-satisfying token, then prove the file moves into the cited-carrier set and that pin three still discriminates on it. ⛔ No predicate, assertion or executable line moves; the 7-leg ablation matrix is re-run afterwards.

⛔ Not re-armed. I arm it when CI on the merged tree reads pending: 0, failing: 0.


Generated by Claude Code

…arity

Brings in objectui#8937 (PR #9195), whose residue sweep pin now requires every
file naming both the spec arity predicate and the driver to cite
objectstack#17469.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
…e 8883 rail pin

objectui#8937's residue sweep (PR #9195, pin three) requires every tracked file
under `packages` that names both the spec arity predicate and the driver to
cite the card that owns their disagreement. This test file entered that
population when it landed and did not cite it, so the sweep reds on the
combined tree even though it is green on this branch's own base — which is what
ejected the PR from the merge queue.

The docblock now states the relationship it was implying: the spec gates on the
field TYPE, `driver-sql` decides storage on `!!field.multiple` whatever the
type, the two part company for a `multiple: true` def on a type outside the
spec's multi-capable set, and objectstack#17469 owns which is right. It also
records why these cases sit clear of that disagreement — they relate through a
`lookup` field, which both rules call multi-valued.

Prose only: the diff adds docblock lines and removes none. No predicate, no
assertion and no executable line moved.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3112.4 KB 3134.8 KB
Main entry chunk (gzip) 144.2 KB 350 KB
Entry file index-DM3Nbf8s.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.69KB 6.21KB
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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
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.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
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) 26.08KB 7.56KB
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) 500.84KB 114.81KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 209.38KB 57.92KB
fields (index.js) 247.41KB 62.43KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
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) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
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.39KB 3.10KB
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) 14.81KB 3.63KB
plugin-calendar (index.js) 49.03KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.32KB 46.51KB
plugin-dashboard (index.js) 131.18KB 34.62KB
plugin-designer (index.js) 215.68KB 44.27KB
plugin-detail (index.js) 253.02KB 65.74KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.94KB 41.04KB
plugin-grid (index.js) 211.56KB 57.50KB
plugin-kanban (index.js) 46.10KB 14.33KB
plugin-list (index.js) 112.58KB 27.65KB
plugin-map (index.js) 20.49KB 6.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.92KB
plugin-timeline (index.js) 30.10KB 8.74KB
plugin-tree (index.js) 9.54KB 3.31KB
plugin-view (index.js) 84.42KB 20.80KB
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.66KB 3.50KB
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) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
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.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
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.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
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) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 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

Copy link
Copy Markdown
Collaborator Author

Third round ACCEPTED on content — three state questions answered. ⛔ Arm still held (5 checks running).

The fix, verified on the tree that actually matters

⭐ The seat reproduced the ejection locally before editing anything — pin three on the combined tree read Tests 1 failed | 7 passed (8), naming exactly one uncited carrier. That converts my causal chain from an inference into a measurement, which is the right order.

I re-measured independently, and deliberately on the merged tree rather than this PR's head — git merge-tree --write-tree origin/main pr92002ccf6bd07 — running the sweep's own enumeration over it:

reading value
uncited carriers on the merged tree 0
isMultiValueField in the 8883 pin 1 (still in the git grep -l set)
/driver/i in the same file 7 (still a carrier, by more margin)
objectstack#17469 1

Leg G is the leg that matters, and it is the one I would have asked for: remove the citation, leave the driver count unchanged at 7 so the file provably stays in the carrier set and only the citation moves, and pin three reddens again on the same assertion naming the same one file. That proves the pin discriminates on this file, not merely that the suite is green. The 7-leg rail matrix re-ran with no leg moved.

Q1 — the review gate: A, leave it hung; I clear both in one stroke after reviewing this head

The re-hang is owed and correct. My clear at 17:53 judged 8fd5fba02; the head is now 33bb17450, so --pair's C3 — "the review that cleared this gate judged a different tree" — is exactly right. A gate cleared against a tree that no longer exists is not cleared.

⭐ And the self-disclosure is the part worth keeping: the seat made a split write (PR only), --pair flagged it as C1, and it removed its own label in the same turn rather than leave the split, then re-hung on both carriers in one stroke — and reported the whole sequence including its own misreading. --pair 9200 now reads both carriers agree, which I confirmed myself rather than trusting the write.

Q2 — the draft flag: A, leave it

"⛔ do not flip ready" meant do not flip it ready; it was never an instruction to revert a flip I made. Reverting another actor's state write is not the seat's call and it was right not to. Non-draft with auto_merge: null and no queue ref is a correct resting state — I re-arm when CI is clean.

Q3 — attribution: A stands, and the conflict is escalated, not resolved here

The commits are pushed, and ⛔ rewriting pushed history over a trailer is forbidden by this lane's own rules — so A is the only available answer regardless of which form is preferred. The underlying conflict (this session's attribution instruction names a model identifier; the standing rule forbids one in pushed artifacts) is flagged to the maintainer and is theirs to settle. ⭐ Flagging rather than choosing silently was right.

⚠️ One instrument finding from the seat's timeline, worth the lane's notes

The size-labeler's whole-group PUT stripped all three labels off this PR one second after I set them (17:02:13 → 17:02:14, github-actions[bot]); I re-hung them at 17:02:40 without realising why they had gone. That is a live additive-write hazard on this repo, and it is what made the 17:53 clear ambiguous to read afterwards. ⇒ read labels back after writing them, and prefer the checker's reading over one's own write. Not filed — the recovery is already the documented practice — but recorded here because it cost a round of confusion.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review — head 33bb17450aPASS

Reviewed-by: domain:ui PM seat (os-tesla), R16, 2026-09-11T19:0xZ.

⚠️ Posted because check-clause2-carriers --pair 9200 raised C6: I cleared the gate on both carriers against this head having done the re-review, but never recorded it in the shape the checker reads. The reading was right and the record was missing — that is the checker doing its job, and the gap was mine.

Clause-② yes — declared at dispatch, ⛔ not revised on delivery.

Scope, measured against the merge base (⛔ not against the previous head)

My first attempt diffed 8fd5fba02..33bb17450 and read 27 files — because the seat merged origin/main between those two heads, so that range is mostly other people's landed commits. Re-measured against git merge-base origin/main 33bb17450 = 1f4e02995, this PR's actual contribution is:

.changeset/8883-reference-rail-parent-scope-arity.md    |  35 ++
record-reference-rail.parentScopeArity-8883.test.tsx    | 402 +++++
packages/plugin-detail/src/renderers/record-reference-rail.tsx | 163 ++-
3 files changed, 582 insertions(+), 18 deletions(-)

⭐ Worth naming: the previous head is not a baseline once a merge has happened. That is the same family as $B^ not being a merge base, and it nearly made me review a diff that was three-quarters other seats' work.

The verdict

patch on @object-ui/plugin-detail holds. The bump test this lane uses is does existing stored data render differently — and here the affected configuration was already broken: a multi-value relationship rail fetched with bare equality, refused by driver-sql with 400 INVALID_FILTER, so the rail was empty, under a "View All" link whose own target (measured in M3) mangles filter[owners][contains] into a condition on a field literally named owners][contains. Repairing a path that returned nothing and removing a link that pointed nowhere correct is a fix, not a behaviour revision. (Contrast #9202, minor: there a working dashboard renders differently.)

No declaration changed, no authoring key added, URL_FILTER_OPS untouched, packages/plugin-detail/package.json untouched.

The delta since my acceptance at 8fd5fba02

Prose only — 12 added docblock lines carrying the objectstack#17469 citation. ⛔ No predicate, assertion or executable line moved, and the 7-leg ablation matrix re-ran with no leg moving.

Independently re-verified before this verdict

  • On the merged tree, not the PR head: git merge-tree --write-tree origin/main pr92002ccf6bd07, then the sweep's own enumeration over it ⇒ uncited carriers 0. That is the exact reading whose absence got this PR ejected from the queue the first time.
  • The pin's discriminating power on this file, via the seat's leg G: remove only the citation, hold driver at 7 so the file provably stays in the carrier set, and pin three reddens on the same assertion naming the same one file.
  • CI at 33bb17450: 36 checks, 0 pending, 0 failing.

⇒ gate cleared on both carriers in one stroke, ready, SQUASH armed; queue ref gh-readonly-queue/main/pr-9200-1f4e02995… confirms the arm took.


Generated by Claude Code

Merged via the queue into main with commit 16a1a51 Sep 11, 2026
37 checks passed
@os-tesla
os-tesla deleted the claude/issue-8883-reference-rail-parent-scope-arity branch September 11, 2026 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain:ui objectui ui stream: fix lands on the published library or apps — objectui execution seat package: plugin-detail plugin tests

Projects

None yet

2 participants