Skip to content

feat(types): retire page.assignedProfiles on both faces, in step with the installed spec (objectui#9409) - #11284

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-9409-retire-page-assigned-profiles
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-9409-retire-page-assigned-profiles

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #9409
Clause-②: no

What this does

@objectstack/spec 17.5.0 retired page.assignedProfiles: ADR-0090 D2 deleted the Profile concept the key was named after, and under ADR-0049 enforce-or-remove the spec's PageSchema now declares the key as a retiredKey() tombstone that refuses any value. @object-ui/types still declared it as an authorable string[], described as "Profiles that can access this page", on both published faces. Nothing in this repository ever read the key, so the key read as access control and enforced nothing.

Both faces now take the spec's tombstone by reference, the same way App version and Dashboard refreshInterval already do:

  • zod (packages/types/src/zod/layout.zod.ts, PageNodeSchema): the local assignedProfiles: z.array(z.string()) override is deleted. It shadowed the tombstone that SpecPageFields already carries in from the spec. An authored value, an empty list included, now fails to parse at path assignedProfiles with code invalid_type and the spec's own message, which names the remedy (permission sets bound through positions). The describe text "Profiles that can access this page" is gone, as triage ruled it must be under any route.
  • TypeScript (packages/types/src/layout.ts, PageNodeSchema): assignedProfiles is dropped from the Omit over SpecPage, and the hand-written string[] member is deleted. The twin now inherits the spec's member, whose type admits no value, so authoring one is a tsc error. The "FORWARD-COMPAT omission" docblock is rewritten to state the retirement.
  • Pin (twins-spec-by-reference-9736.test.ts): the pin "Page assignedProfiles keeps the hand-written string[]" is inverted into a refusal pin inside the existing "spec tombstones surface on the twin as a refusal" group. It asserts that the twin's member equals the spec's and equals undefined, that the key is still DECLARED (so the @ts-expect-error cannot be absorbed by the index signature), two @ts-expect-error authoring sites, and a parse whose issues are exactly [{ code: 'invalid_type', path: ['assignedProfiles'] }], with a message naming the key. An empty list gets the same refusal. A control case shows the same document without the key, and the admitted spec keys, pass both faces. The stale case "Page carries no tombstone on the installed pin" (true on 17.4.0, false on 17.5.0) is replaced by that control.
  • Docs (content/docs/api/schema-reference.md): the assignedProfiles row is rewritten as a retired-key row, following the table's existing body precedent (type *retired*, "Refused by name").
  • Changesets: .changeset/9409-retire-page-assigned-profiles.md is minor with a BREAKING (authoring) banner, per the version policy in AGENTS.md §9. The pending .changeset/9736-twins-spec-by-reference.md said the key is withheld "for forward compatibility" with its string[] member unchanged. That is now false, so it gets a dated, append-only note; its frontmatter is byte-identical (md5 of the first three lines is the same before and after).

Premises re-measured at origin/main e420df31 before any edit

  • H1, the installed spec refuses the key by name: TRUE. In this worktree @objectstack/spec resolves to 17.5.0. With PageSchema.safeParse in one run: a fully valid page without the key parses (success=true); the same page with assignedProfiles: ['admin'] is refused with exactly one issue, invalid_type at ['assignedProfiles'] (expected never); a never-declared key is refused as unrecognized_keys. ⚠️ The card's own probe document (name: 'p', type: 'object') fails for three other reasons too (name too short, label missing, type not a page kind). Its exit code is right, but its "lit control" is not a green parse. The readings here use a valid page.
  • H2, nothing in objectui reads the key: TRUE. A tree-wide git grep (lockfile excluded) finds only the declaring sites, the pin, the docs row, and the pending 9736 changeset. skills/** has zero mentions, against a lit control of 19 files there that mention pages. A case-insensitive variant probe also finds nothing. Every non-test use of PageNodeSchema is a TypeScript type position; nothing walks the zod shape, and the key set is unchanged anyway (the key is still present, now as the tombstone).
  • H3, no zod-mirror-parity.test.ts row names the key: TRUE. That file is not edited.

Route: by reference, not a local retirementTombstone (the suggested route, changed on measurement)

The dispatch suggested the repository's retired-key idiom (retirementTombstone plus ?: never). Measurement points the other way for this key. tombstone.zod.ts's own docblock, under "Not @objectstack/spec's retiredKey", reserves that helper for keys objectui retires itself and says the two are not to be swapped. For keys the SPEC retires, this package's idiom is to take the spec's tombstone by reference: App version and Dashboard refreshInterval are pinned exactly that way in the same test file. So the refusal message is the spec's own text, with no second copy to drift. The local override was the only thing standing between the node and that tombstone.

Tests (all at 59c78cbe5, the head of this PR)

  • pnpm --filter @object-ui/types build, then pnpm --filter @object-ui/types type-check (tsc --noEmit, the examples project, and tsconfig.test.json): lock VERDICT command-exit 0. tsc -p tsconfig.test.json --listFiles includes the pin file, so the @ts-expect-error lines are enforced.
  • pnpm exec vitest run packages/types/ (repo-root form): Test Files 298 passed (298), Tests 7465 passed (7465), zod-mirror-parity.test.ts and page-app-dashboard-spec-parity.test.ts included. The pin file on its own: 9 passed, and the new case is listed by name.
  • Consumers of the changed published type, downstream direction (the importers of PageNodeSchema): @object-ui/components (its dependency closure built first) and @object-ui/runner (--filter '@object-ui/runner^...' build first, since it reads built .d.ts). type-check passes on both.
  • pnpm --filter @object-ui/types lint: 0 errors. The 300 warnings are all no-explicit-any in files this PR does not touch.

Ablation (committed first, restored through ablation-replace.mjs, and every restore proven by blob hash plus an empty git diff HEAD)

  • zod face: the old z.array(z.string()) override is re-inserted, and the pin file is run. 1 failed, 8 passed: the refusal case fails with expected true to be false (the mirror parsed the value), and the controls stay green. Restored; the blob equals HEAD. ⚠️ The first attempt at this leg was a no-op: the tool refused it before running anything, because my replacement text contained the anchor. It was re-run with an anchor the replacement consumes. That refusal is how the tool is supposed to work.
  • TS face: 'assignedProfiles' is put back in the Omit list and the string[] member is restored (two anchors: hold, then wrap), and tsc -p tsconfig.test.json is run. Exit 2, with 4 errors in the pin (both Equal assertions turn false, and both @ts-expect-error directives become unused), plus zod-mirror-parity.test.ts reporting layout.zod.ts#PageNodeSchema as new drift between the faces. Restored; the blob equals HEAD.

Gates (hand-derived: dispatch-gates.mjs derives nothing for objectui)

Exit 0: check:spec-symbols, check:doc-types, check:doc-fences, check:doc-example-ids, check:doc-example-readers, check:new-line-citations (0 new citations), check:control-bytes, check:changeset-claims, check:pending-changeset-literals, check:installed-pin-claims (pin 17.5.0, OK), check:component-surface-parity, check:unreferenced-sources, check-changeset-presence.mjs, check-changeset-no-major.mjs, and check-governed-queue-guard.mjs --test over the six paths (NOT GOVERNED).

NOT MEASURED: check:doc-snippets and check:doc-examples both exited 2 with PRECONDITION NOT MET, because 21 or more packages outside this diff's closure are unbuilt. Neither gate reads anything this diff changed: the only content/docs edit is one table row with no fence lines, and .changeset/ is excluded from the snippet corpus by that gate's own rule. CI runs both.

check:changeset-claims (report-only) listed 28 pending changesets that name a file this PR touches. I grepped each one's body for PageNodeSchema, profiles, Omit, forward-compat, and string[]: none is made false by this change. The one that IS made false, 9736, names no file path, and it carries the dated note.

Acceptance notes

  • The presence-only drift guard (triage's question): is its gap worth its own card? Measured, and it is not a card today. The gap is "a mirror-local override that shadows a spec retiredKey() tombstone is invisible to a presence-only guard", and assignedProfiles was its only instance. I compared all six spec-by-reference mirrors (PageNodeSchema, AppComponentSchema, DashboardComponentSchema, ListViewSchema, DashboardWidgetSchema, NavigationAreaSchema) against their spec schemas: 24 spec tombstones, 0 shadowed at this head. The detector fires on the pre-change shape (it reports assignedProfiles:array, 1 shadowed, with the old override ablated back in). With zero live instances the class is dormant. The four-axis frame defaults new gates to "no", so I do not recommend a card or a widened guard. The guard was not widened here, per the triage fence.
  • Tree-wide reach: this changes PageNodeSchema's published TypeScript surface. Code that wrote assignedProfiles used to compile and now fails tsc. No workspace source writes the key (see H2), and the two workspace importers type-check clean.

This PR was produced by a dispatched dev run under the domain:spec @ objectui seat, session https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT. It stays draft: flipping it ready, the contract review record and enqueueing belong to the seat.

Patch round 2, at HEAD e423cc3ad (written by the seat)

  • CI Test (shard 8/8) went red on 59c78cbe5 in scripts/__tests__/doc-version-claims.test.ts. The new retired assignedProfiles row in content/docs/api/schema-reference.md spelled the spec version ("@objectstack/spec 17.5.0 retired the key"), and nothing in the repository re-verifies that literal.
  • e423cc3ad DELETES the literal, the ratchet's preferred remedy, with no KNOWN_CLAIMS entry. The row is otherwise byte-identical (+1/−1).
  • Red → green: the test file gave exit 1 ("1 failed | 28 passed") at 59c78cbe5, then exit 0 at e423cc3ad, together with the twins-spec-by-reference-9736 pin (38 tests). @object-ui/types type-check exit 0.
  • Widened: every root scripts/__tests__ file that reads the docs or changeset surfaces (56 files, 2639 tests) passes at e423cc3ad.

Generated by Claude Code

…th the installed spec (objectui#9409)

`@objectstack/spec` 17.5.0 turned `PageSchema.assignedProfiles` into a
`retiredKey()` tombstone (ADR-0090 D2 deleted the Profile concept; ADR-0049
enforce-or-remove). objectui still declared it as an authorable `string[]`
described as "Profiles that can access this page", which nothing enforced.

- zod: drop the `string[]` override so the spec's tombstone reaches
  `PageNodeSchema` by reference through `SpecPageFields`.
- TS: drop `assignedProfiles` from the `Omit` list and the hand-written member,
  so the twin takes the spec's member by reference; rewrite the
  forward-compat docblock as the retirement.
- Pin: invert the twins-spec-by-reference pin into a refusal pin on both faces.
- Docs: mark the schema-reference row retired.
- Changeset (minor, breaking authoring) plus a dated note on the pending
  objectui#9736 changeset whose `assignedProfiles` sentences this makes false.

Claude-Session: https://claude.ai/code/session_01VhxTqosz7wn54ahqyxgERT
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation package: types tests labels Sep 30, 2026
@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

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

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

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

.changeset/11068-grid-declared-keys.md

  • names schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was wrong. ObjectGridSchema declared eight keys the grid never read. An author who wrote description, emptyState, name, placeholder, rowSpecActions or bulkSpecActions on an object-grid got no type error, no validator refusal and no effect. The reference example in schema-reference.md taught description and showFilters as if they worked. It now authors only keys the grid reads.

.changeset/3917-retire-action-condition-branch.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • ActionCondition is removed from @object-ui/types (and from the barrel export). - ActionSchema.condition is retyped to the predicate the runtime actually honours: boolean | string | { dialect?: string; source: string } — the same three arms ActionRunner's own ActionDef.condition carries, and the same vocabulary visible and disabled use. - ActionConditionSchema is removed from @object-ui/types/zod (and from the zod barrel); the condition key now validates against that predicate union. - The two teaching sites (content/docs/core/enhanced-actions.mdx Conditional Execution, content/docs/api/schema-reference.md ActionSchema table) are rewritten to the live vocabulary: condition is a gate; a branch is expressed as separate actions with mutually exclusive conditions.

.changeset/4895-retire-block-schema-family.md

  • names packages/types/src/layout.ts → packages/types/src/layout.ts — edited by this change

    ⚠️ Not this family, and not touched. The live slotted record-page vocabulary — PageNodeSchema.kind === 'slotted' with slots?: PageSlotMap (packages/types/src/layout.ts), rendered by usePageAssignment / PageBlockCanvas / PageBlockInspector in @object-ui/app-shell — shares the words "block" and "slot" with the retired family and shares no declaration, type or file with it. Neither is the type: 'component' NAVIGATION item kind ({ type: 'component', componentRef }, NavigationItemSchema in zod/app.zod.ts), a different declaration in a different module.

.changeset/5632-svg-host-dom-passthrough.md

  • names packages/types/src/layout.ts → packages/types/src/layout.ts — edited by this change

    Both renderers forwarded their whole prop bag to the SVG they render, so every authored SDUI key on the node became an attribute — 14 per target, and icon="check" on all 71 icon nodes in the schema catalog. The two nodes declare different keys, and each renderer consumes its own node's keys by name: IconSchema (packages/types/src/layout.ts) against the pass-through docblock in renderers/basic/icon.tsx, and SpinnerSchema (packages/types/src/feedback.ts) against the one in renderers/feedback/spinner.tsx. icon and color are IconSchema's alone — SpinnerSchema declares neither. Read those two declarations for what they carry; this paragraph deliberately copies no member list. So the SDUI pass-through list withholds nothing they need.

.changeset/6170-retire-timeline-dead-keys.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Also in this change: the in-repo example packages/types/examples/data-display-examples.json (its timeline node authored all three) is migrated to items / variant; the two content/docs/api/schema-reference.md snippets that authored events: [] now author items: []; and the plugin-timeline docs callout says retired rather than deprecated.

.changeset/6896-retire-chart-inline-data.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ One correction to the record the ruling rests on. The ruling states zero authorship of a populated series[].data outside tests across packages/ / apps/ / examples/. The re-measurement finds one such site inside those roots — packages/types/examples/data-display-examples.json (2 series) — plus four outside them, in documentation: content/docs/api/schema-reference.md (3) and content/docs/core/report-schema.mdx (1).

.changeset/6939-kanban-column-cards.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/6951-text-value-retired.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    Two published faces, one retirement. The TypeScript interface TextSchema (@object-ui/types, layout.ts) declares value?: never; the Zod mirror TextSchema (@object-ui/types/zod, layout.zod.ts) declares value as a retirementTombstone(), so the key stays DECLARED and is refused BY NAME — a plain deletion would have let an authored value ride BaseSchema's .passthrough() into a silent blank, which is worse than the tolerated fallback it replaces. The value?: string members of TextSpanSchema and TabsSchema in the same file are other schemas' contracts and are unchanged.

  • names layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    Two published faces, one retirement. The TypeScript interface TextSchema (@object-ui/types, layout.ts) declares value?: never; the Zod mirror TextSchema (@object-ui/types/zod, layout.zod.ts) declares value as a retirementTombstone(), so the key stays DECLARED and is refused BY NAME — a plain deletion would have let an authored value ride BaseSchema's .passthrough() into a silent blank, which is worse than the tolerated fallback it replaces. The value?: string members of TextSpanSchema and TabsSchema in the same file are other schemas' contracts and are unchanged.

.changeset/6951-tree-view-data-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What was measured, on this branch's base. TreeViewSchema declared two spellings for its one inline-nodes slot — nodes (read second) and data (read third: boundData || schema.nodes || schema.data || [] at renderers/data-display/tree-view.tsx:105), both declared by objectui#6150. data had been REQUIRED until 777e5c6f4 (PR fix(types): tree-view mirror stops requiring the limb it reads third (group 2 of objectui#6939) #7533) made it optional, so this retirement starts from a declared-and-optional member on both faces. The in-repo corpus at the retirement: seven tree-view nodes under examples/schema-catalog and packages/types/examples plus one content/docs fence — six on nodes, two on data (packages/types/examples/data-display-examples.json and content/docs/api/schema-reference.md), both rewritten; no package source authored either spelling.

.changeset/7664-kanban-arm-plugin-dialect.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Migration. Author boards in the plugin dialect — objectName + groupBy for an object-bound board, or columns[].cards[] with badges for a static one. Replace DeclarativeKanbanSchema imports with KanbanSchema (from @object-ui/types, or the Zod KanbanSchema from @object-ui/types/zod; @object-ui/plugin-kanban re-exports the same KanbanSchema type). Delete draggable (drag-and-drop is always on) and column color (style a lane through className). content/docs/api/schema-reference.md's kanban section now documents this dialect.

.changeset/7735-zod-mirrors-stop-authoring-defaults.md

  • names layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    What changed. All 41 .default() call sites under packages/types/src/zod/ are removed — layout.zod.ts 22, crud.zod.ts 11, form.zod.ts 5, views.zod.ts 2, app.zod.ts 1. @object-ui/components reconciles the third face a separate finding found: flex's registration defaultProps.align seeded 'center', the value its own renderer never applies, so a designer-made node laid out differently from a hand-authored one; it now seeds 'start'.

.changeset/7997-detail-view-related-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Documentation. packages/plugin-detail/README.md and content/docs/api/schema-reference.md stop teaching the retired array and gain a migration block each.

.changeset/8268-testid-emitted-as-data-testid.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    The other direction — retiring the promise — was considered and declined. It is what c1fe272ad did for BaseSchema.hidden, but that key had a working behaviour to describe and zero named consumers, and the ruling's decline turned on exactly that. This promise already has carriers outside the type declaration: content/docs/api/schema-reference.md states it as a table row and authors testId in that page's own base-schema example, @object-ui/cli's OBJECTUI_STRUCTURAL_KEYS identifies a file as an ObjectUI schema node by this key, at this change ObjectGridSlotKey / ObjectFormSlotKey pin it, SchemaBuilder.testId() writes it, and ADR-0054 C4 — shipped — reads "the renderer emits data-testid … derived from metadata".

.changeset/8478-describe-line-addresses.md

.changeset/8478-zod-pins-form-layout.md

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

  • names zod/layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    • SemanticElementSchema (zod/layout.zod.ts) — the seven HTML sectioning tags renderers/layout/semantic.tsx registers: aside main header nav footer section article. - HtmlElementSchema (zod/layout.zod.ts) — the 37 safe flow/inline tags renderers/basic/html-elements.tsx registers (h1…h6, p, a, ul, img, …), plus the per-tag keys that module forwards to the DOM (href, target, rel, title, src, alt, width, height, dateTime, cite). ⚠️ Dated note, 2026-09-27 — that set has since gained code — objectui#10756. At this change TAGS and this arm both named 37 tags; both now name 38, and the parity pin counts 38. The rest of this entry is kept as the reading of this change. - InputShorthandSchema (zod/form.zod.ts) — email / password, the two aliases renderers/form/input.tsx registers onto the input renderer with inputType pinned. inputType is deliberately NOT declared on this arm: the wrapper spreads its own value last, so an authored one is overwritten. ⚠️ Dated note, 2026-09-28 — inputType is now declared on this arm, as a refusal — objectui#8762. Later in this same release the arm declares inputType on both faces and refuses it by name (?: never on the TypeScript face, a retirementTombstone on the zod mirror, at path inputType), with guidance pointing at { "type": "input", "inputType": "email" }. So "inputType is deliberately NOT declared on this arm" no longer holds; the reason does, since the wrapper still spreads its own value last. The rest of this entry is kept as the reading of this change. - UiCalendarSchema (zod/form.zod.ts) — ui:calendar, the date-picker primitive renderers/form/calendar.tsx registers under exactly that key (skipFallback, because bare calendar belongs to the plugin-calendar view).

.changeset/8505-grid-columns-breakpoint-narrowing.md

.changeset/8516-8556-mirror-partial-record-narrowing.md

  • names zod/layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    | key | mirror was | mirror is | | :-- | :--------- | :-------- | | GridSchema.columns (zod/layout.zod.ts) | z.record(z.string(), z.number()) | a PARTIAL record over the six breakpoints | | ReportComponentSchema.exportConfigs (zod/reports.zod.ts) | z.record(z.string(), ReportExportConfigSchema) | a PARTIAL record over ReportExportFormat |

.changeset/8653-listview-title-retired-rowactiondefs-pinned.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    title — retired. ListView resolved its export filename through schema.label || (schema as any).title. @objectstack/spec/ui's ListViewSchema refuses title by name (unrecognized_keys: ['title']) while ObjectGridPropsSchema accepts it; packages/types mirrors the platform contract rather than ruling over it, so declaring title on ListViewSchema would have made this repo accept what the platform save gate rejects. That asymmetry is also why objectui#6639 could take the declare branch for ObjectGridSchema.title one package over and this site could not. A parse-based census of apps/ examples/ content/ and packages/ found zero list-view nodes authoring title, so the retirement costs no author a filename. Over that same corpus the instrument reports three object-grid nodes carrying the key: two authored ones, both in content/docs/api/schema-reference.md, plus one that is not authored at all — packages/plugin-view/src/ObjectView.tsx composes title: schema.table?.title onto a grid node it builds, so it is a producer writing the key rather than an author declaring it. ObjectGrid's own title reads are untouched — they remain declared, ruled and read.

.changeset/8801-object-kanban-allow-collapse-retired.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

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

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

  • names zod/layout.zod.ts → packages/types/src/zod/layout.zod.ts — edited by this change

    What was measured, on this branch's base 93127bd6f. Zero readers, with a point-access probe rather than a bare word: on that base \.breadcrumbs scores 0 tree-wide (exit 1) against \.breadcrumb\b's 12 files tree-wide (10 under packages/) as the lit control. At head the same two probes read 16 and 13 and \.breadcrumbs is exit 0 over 4 files — every hit one of this branch's own four files (this changeset, the refusal pin, layout.ts, zod/layout.zod.ts) quoting the probe string, and the pin's own exclusions put head back at exit 1. The base reading is the measurement; the head reading is this branch's echo of it. The bare word would have lied — it also names Sentry's own unrelated concept (app-shell/src/observability/sentry.ts) and appears in two comments listing UI surfaces (core/src/utils/record-title.ts, layout/src/NavigationRenderer.tsx), so a bare probe reports five readers that do not exist.

.changeset/9092-inline-locale-declared-face.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9187-record-highlights-layout-two-values.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    ⚠️ The census behind this narrowing covers this repository only, and it found no in-repo authoring to migrate: every in-tree layout: 'grid' belongs to a different component (detail-view in content/docs/api/schema-reference.md and phase2-schemas.test.ts, ai-recommendations in packages/plugin-ai/README.md), and the one in-repo consumer of this interface that writes a layout (p1-spec-alignment.test.ts) writes 'horizontal'. So no document in this repository stops type-checking. A TypeScript consumer outside this repo that wrote grid is not observable from here and gets a compile error (TS2322) naming the key — which is why the FROM/TO is spelled out above.

.changeset/calendar-view-schema-converge.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Runtime renderer behaviour is unchanged. @object-ui/plugin-calendar's README and content/docs/api/schema-reference.md are repaired to the converged surface in the same change, so no copy of the old contradiction survives.

.changeset/issue-5373-retire-crud-schema.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    crud had four declaration faces and no registered renderer, for the whole life of the key: the TS interface (packages/types/src/crud.ts), the zod mirror (packages/types/src/zod/crud.zod.ts), a dedicated branch in validateSchema that affirmatively PASSED it, and CRUDBuilder in @object-ui/core. A node spelling it painted the OBJUI-001 "Unknown component type" panel, and content/docs/api/schema-reference.md published it as reference material — so a reader (or an AI author) who copied the page got a red panel.

  • names api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    Authoring crud is now REFUSED BY NAME rather than passed or silently ignored. validateSchema returns an error with code: 'RETIRED_TYPE' on schema.type — at any depth, since it is what validateChildren recurses with — so assertValidSchema throws and isValidSchema answers false. The message names the migration: object-grid for the record table with its toolbar, filters, pagination and row/batch actions, object-form for the create/edit form, and detail for the record view. api/schema-reference.md is rewritten around those shapes.

.changeset/layout-default-jsdoc-7361.md

  • names layout.ts → packages/types/src/layout.ts — edited by this change

    The published @default documentation on two layout.ts members now matches the value the renderer actually applies. ContainerSchema.maxWidth documented 'lg' while container.tsx applies schema.maxWidth ?? 'xl', and the shared FlexLayoutProps.align documented 'center' while flex.tsx applies schema.align || 'start' and stack.tsx applies schema.align || 'stretch'. The renderers are unchanged — they are the authority for what runs — so only the docblocks moved; align now states both consumers in prose instead of carrying a single @default, because one member shared by two deliberately divergent component types cannot have one correct default.

.changeset/object-view-unmirrored-keys-7779.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    listViews stays unmirrored, on the ruling's own fallback clause. The declaration's value is the local NamedListView — 64 declared top-level members (⚠️ re-taken at objectui#8980, which added the seventeen the protocol declares on this surface to the 47 this entry first measured), of which the renderer reads 21 off a named view. data is one of the 21 now: it used to reach the renderer through an as any cast on the named-view config in packages/plugin-view/src/ObjectView.tsx and be declared nowhere, and the objectui#8980 ruling declared it by name — objectui#7928's open half, answered. The spec's ViewSchema.listViews is a record of the STRICT ObjectListViewSchema, which requires columns and refuses options, ObjectQL tuple filters and default — that is, it refused the named views this package's own README and content/docs/api/schema-reference.md taught when this entry was written ({ label: 'All Users' } fails at columns; filter: [["owner", "=", "..."]] fails at filter.0), and objectui#8255 has since rewritten them in the spec shape. Mirroring the spec value would have lost documented behaviour; mirroring the local value would enforce 43 unread members (64 declared, minus the 21 that are both declared and read) into the contract — the very thing ruling B refused for the six local keys. The key therefore stays in the parity ledger with that measurement, pinned, until the maintainer decides its value type. It is not papered over with z.any().

.changeset/table-renderer-declared-column-contract-5350.md

  • names content/docs/api/schema-reference.md → content/docs/api/schema-reference.md — edited by this change

    What makes this site different from its three siblings is that the alias was not merely tolerated, it was published. content/docs/api/schema-reference.md §TableSchema shipped a copyable { "name": "id", "label": "#" } example and a property row reading "Column definitions with name, label, …", while packages/types declared the opposite pair. Docs and type disagreed about one type they both call TableColumn, each internally consistent. Retiring the alias without correcting the page would have turned a documented, working example into a silently broken one, so both halves land together: the page now authors accessorKey/header. The same row also advertised a render property that TableColumn has never declared — the renderer's hook is cell — and that claim is dropped rather than re-spelled.

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

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

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

Compared the checked-out tree with 1b30c0fe0 (merge-base with origin/main): 4 file(s) changed outside .changeset/, read against 1854 pending declaration(s) that publish a body (2466 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3584.0 KB 3607.4 KB
Main entry chunk (gzip) 150.0 KB 350 KB
Entry file index-MtjcssNs.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
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.17KB 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) 40.70KB 10.94KB
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.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.18KB 136.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 41.23KB 11.53KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.27KB 37.26KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 244.72KB 64.45KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 48.62KB 15.21KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
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.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

…d `assignedProfiles` row (objectui#9409)

`doc-version-claims.test.ts` refuses a version literal on a doc surface that
nothing in this repository re-verifies. The row now says `@objectstack/spec`
retired the key, with no version number; the rest of the row is unchanged.

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

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3584.5 KB 3607.4 KB
Main entry chunk (gzip) 150.0 KB 350 KB
Entry file index-sY3hKuzY.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
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.17KB 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) 40.70KB 10.94KB
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.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 570.18KB 136.44KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 228.86KB 63.53KB
fields (index.js) 261.11KB 66.26KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.35KB 9.18KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 41.23KB 11.53KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 52.17KB 15.06KB
plugin-charts (index.js) 84.09KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.27KB 37.26KB
plugin-designer (index.js) 216.32KB 44.56KB
plugin-detail (index.js) 244.72KB 64.45KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.61KB 44.59KB
plugin-gantt (index.js) 173.03KB 43.07KB
plugin-grid (index.js) 231.61KB 63.61KB
plugin-kanban (index.js) 48.62KB 15.21KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.50KB 7.82KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.05KB 9.67KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.81KB 3.58KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
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.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
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) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

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

Inputs, and nothing else: card #9409 (body and all 10 comments, rulings 5654961945 and the two os-dev-reports 5921335739 / 5921716119 included), PR #11284 (body, 6-file list, net diff against main), and the check-runs on the head, read last. Governing texts: objectui AGENTS.md at origin/main (§9 版本号策略, Housekeeping changeset rule, 多 agent 协作纪律 trailer rule), ADR-0049, ADR-0090 D2. The spec's own source at the 17.5.0 tag commit 0f6dcac5e9 was read for the tombstone (packages/spec/src/ui/page.zod.ts, packages/spec/src/shared/retired-key.ts). Files at the head were read with git show, never checked out. Nothing was built, run, re-run or ablated here; every "measured" number below that is not a read of text is the dev's, and is marked as such.

① Derived judgments

Every accept-set and public-surface change the diff implies, named right or wrong:

  1. Zod face — PageNodeSchema in packages/types/src/zod/layout.zod.ts narrows: RIGHT. The diff deletes the local assignedProfiles: z.array(z.string()).optional().describe(...) override and nothing else in that shape. What then reaches the key, derived by reading: PAGE_SPEC_EXCLUDED is name, label, description, type, regions (the key is not in it); specFieldsExcept keeps every other shape entry and applies .partial(); stripImportedDefaults strips ZodDefault wrappers only; and the spec at 0f6dcac5e9 declares assignedProfiles: retiredKey(PAGE_ASSIGNED_PROFILES_RETIRED) inside PageSchema, where retiredKey is z.never({ error }) made .optional() with a [REMOVED] ... describe. So the projected member is the spec's tombstone: an authored value, an empty list included, fails at path assignedProfiles with code invalid_type, and absent still passes. The mirror's key set is unchanged (the key is still declared, now as the tombstone), which is why the presence-only drift guard stays green exactly as the card predicted, and no zod-mirror-parity.test.ts row moves. The refusal message is the spec's own text, which names the remedy (permission sets bound through positions). The describe() that reached generated docs changes from "Profiles that can access this page" to the spec's [REMOVED] prescription: the arm triage ruled mandatory under every route.

  2. TypeScript face — the PageNodeSchema interface in packages/types/src/layout.ts narrows: RIGHT. 'assignedProfiles' leaves the Omit over SpecPage, and the hand-written assignedProfiles?: string[] member is deleted. SpecPage is the spec's Page, which is z.input of PageSchema, so the inherited member is undefined: the published type goes from string[] | undefined to undefined, authoring a value is a tsc error, and the key stays DECLARED, so BaseSchema's [key: string]: any index signature cannot absorb it. Nothing else on the interface moves; slots stays omitted with its hand-written type. The head's Type Check check-run is success, consistent with the PR's claim that the two workspace importers (components, runner) type-check clean.

  3. Pin — twins-spec-by-reference-9736.test.ts: RIGHT, and stronger than its siblings. The acceptance pin "keeps the hand-written string[]" is inverted into a refusal pin, not deleted (the claim's fence held). It asserts the Equal of the twin member against the spec member and against undefined, declared-ness through DeclaredKeys, two @ts-expect-error authoring sites, the exact issue list [{ code: 'invalid_type', path: ['assignedProfiles'] }], a message that names the key, the empty-list refusal, and a control (bare page and the admitted spec keys pass both faces). The App version / Dashboard refreshInterval pins beside it assert only success === false; this one pins the path and the code. The stale case "Page carries no tombstone on the installed pin" is replaced by the control; it was recorded against 17.4.0 and is false on 17.5.0, so keeping it would have been the wrong pin.

  4. Docs row — content/docs/api/schema-reference.md: RIGHT. The row follows the table's own body precedent (*retired*, "⛔ Refused by name (objectui#NNNN)"), names the remedy in the spec's words, and at this head carries no spec version literal (the doc-version-claims ratchet that reddened shard 8/8 on the prior head; e423cc3ad deletes the literal with no KNOWN_CLAIMS entry, and the row is otherwise byte-identical, +1/−1). Its sentence "a page that listed profiles stayed open to everyone who could reach it" is the spec tombstone's own wording and stays inside triage's fence: it says the key gated nothing, not that any page is wrongly exposed.

  5. Triage's ruled-out arm (5654961945) is MET; the seat's count is WRONG. The describe() is gone and the docs row is rewritten, so the arm triage ruled out under every route, leaving that text as-is, is not taken. But the R1 ACCEPT (5921365151) says the sentence has "0 hits in the diff's post-image". At e423cc3ad the exact sentence survives in two places: the PageNodeSchema interface docblock in layout.ts (the line reading ...the hand-written string[] member, described as "Profiles that can access this page", is gone...) and the body of .changeset/9409-retire-page-assigned-profiles.md, line-wrapped so a single-line grep misses it. Both are past-tense quotations inside a sentence that states the retirement; neither is a describe(), a type, a docs row, nor an instruction to an author. I judge the ruling met and the "0 hits" claim wrong, and I record it so the next reader does not inherit the zero. Not blocking. If the seat wants a literal zero, the docblock quotation is the one line to trim; this record does not require it.

  6. Tree-wide reach: nothing else declares, reads or teaches the key. git grep assignedProfiles at the head (lockfile excluded) hits only the six diff files' own lines; skills/** has none. H2 holds as read. zod-mirror-parity.test.ts is untouched (the claim's hot-file fence held). No content/docs/releases/ edit. Fixes #9409 is the body's only closing keyword.

  7. Nothing is widened. No new key, no loosened type, no new export, no changed default. The diff is a pure narrowing on both published faces, in step with the installed spec: check:installed-pin-claims reads pin 17.5.0 per the PR, and the head's Spec Main Shape Gate is success.

  8. Two claims in the PR body that this record does not re-derive, stated as the dev's: the drift-guard detector figures (24 spec tombstones over 6 by-reference mirrors, 0 shadowed at head, 1 shadowed with the old override ablated back in) and both ablation legs going red. They are consistent with the code as read and with the pin's shape; they were not re-run here.

② Semver level

  • Changeset .changeset/9409-retire-page-assigned-profiles.md: '@object-ui/types': minor under a BREAKING (authoring) banner, with a before/after example and the remedy. RIGHT. @object-ui/types is in the fixed group and the diff changes its src/, so a changeset is required (AGENTS.md §9 Housekeeping, the check-changeset-presence.mjs rule): present. The level follows AGENTS.md §9 版本号策略 verbatim: objectui's own breaking changes are marked minor with the breaking semantics written in the body, and major is refused by check-changeset-no-major.mjs (the one exception, following an objectstack major, is not this). patch would understate a published-type narrowing; major is banned; minor is the only right reading. The head's Changeset Bump Policy, Changeset Declaration, Changeset Fixed Group Check and Changeset Claim Re-read check-runs are all success.
  • The pending .changeset/9736-twins-spec-by-reference.md: a 3-line dated, append-only note; frontmatter untouched (the hunk starts at the last line of the file, and the '@object-ui/types': minor declaration is not in the diff). RIGHT. The diff does falsify that changeset's "withheld for forward compatibility, string[] member unchanged" sentences, and the dispatch's changesets clause requires the note. The note's residual claim that PageNodeSchema.slots is still withheld is true at the head (the Omit still names 'slots'; the slotsNotSpec pin remains).
  • Clause-②: no — RIGHT. The fleet's one reader of that line (clause2-line.mjs) defines it as the widening question: does this card widen an accept set or enlarge the public surface. The diff narrows both faces and adds nothing, so no is the correct declaration; the claim's "why no" (5920927900) says the same and is accurate.

③ Boundary flags

Round 1 os-dev-report (5921335739), open_questions: []; five deviations and two out-of-scope findings, each answered:

  • Route by reference, not Zone 3's local retirementTombstone + ?: never. ANSWERED, accepted. tombstone.zod.ts's docblock, under "Not @objectstack/spec's retiredKey", reserves the local helper for keys objectui retires itself (its describe strings are already-published metadata and stay byte-identical, no [REMOVED] prefix) and says the two are not to be swapped. This key is spec-retired, and App version / Dashboard refreshInterval in the same pin file already reach both faces by reference. One refusal, the spec's own message, no second copy to drift. The measured route is the right one.
  • A second changeset file edited. ANSWERED, accepted. The claim's "One changeset" is one NEW declaration, which is what was added; the 9736 edit is a correction to a pending changeset the diff falsifies, frontmatter byte-identical. Mandated by the dispatch, not a scope breach.
  • Commit trailer model-free. ANSWERED, accepted. Both commits on the PR (59c78cbe5, e423cc3ad) end with the Claude-Session line naming session_01VhxTqosz7wn54ahqyxgERT and the model-free Co-authored-by: Claude line, per AGENTS.md §9 多 agent 协作纪律. No model identifier in the PR title or body, the changesets, the docs row or the code comments (checked over the diff).
  • Ablation zod leg: first attempt refused by the tool, re-run with a consuming anchor. ANSWERED, accepted. Reporting only; the re-run direction is the one the pin predicts. Not re-run here.
  • dispatch-gates.mjs --ran reconciliation not run. ANSWERED, accepted. The dispatch said it derives nothing for objectui; the hand-derived union is superseded here by the head's check-runs, which are the gate verdicts.
  • Out-of-scope 1: the presence-only drift-guard gap triage asked the PR to rule on. ANSWERED in the PR body; concur. The class is "a mirror-local override that shadows a spec retiredKey() tombstone is invisible to a presence-only guard"; the dev measured 0 live instances at the head and the detector firing on the ablated pre-change shape. Dormant: no card, no widened guard, and triage's ⛔ on widening the guard on this card held. The figures are the dev's (① item 8).
  • Out-of-scope 2: the card's Restart-when: probe document fails for three unrelated reasons. ANSWERED; concur with the seat's drop. The probe's exit code keyed on the key's own issue, so the unlock read correctly, and the probe retires with the card. H1 was re-measured by the dev on a fully valid page.

Round 2 os-dev-report (5921716119), open_questions: []; three deviations, each answered:

  • Round-1 miss: the root scripts/__tests__ suite was outside the hand-derived gate list, and CI shard 8/8 went red on 59c78cbe5. ANSWERED. e423cc3ad takes the ratchet's preferred remedy (delete the literal, no KNOWN_CLAIMS entry). The verdict on THIS head is CI's, and Test (shard 8/8) is success at the read below. The dev's widened local run (56 root test files) is the dev's figure.
  • No PR-body edit by the dev. ANSWERED. The seat wrote the "Patch round 2" section; it is in the body and matches the commit.
  • Worktree re-created from the pushed head, no merge, no rebase. ANSWERED. Reporting only. The PR reads mergeable: true, mergeable_state: behind; the queue rebuilds on current main.

Fences from triage and the claim, all held: describe() text gone (① item 5), guard not widened, zod-mirror-parity.test.ts untouched, the pin inverted rather than deleted, no content/docs/releases/ edit, PR left draft for the seat. Escalations: none.

Check-runs on the head, read as the last input of this act: 42 in all; 35 success, 3 skipped (the path-filtered Test (coverage) pair and dependabot), 4 in progress (Test (shard 1/8), Test (shard 2/8), Test (shard 4/8), Test (shard 7/8)), 0 failures. The four in-progress shards are recorded as in progress, ⛔ not as verdicts: this record judges the contract, and the seat's own "every check green before enqueue" still stands on its own.

Implemented-by: claude/issue-9409-retire-page-assigned-profiles
Reviewed-by: session_01VhxTqosz7wn54ahqyxgERT

VERDICT: PASS

Contract review · CONTRACT_REVIEW_TIER · read-only · 2026-09-30T23:52Z

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 30, 2026 23:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 02f1813 Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-9409-retire-page-assigned-profiles branch October 1, 2026 00:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation package: types tests

Projects

None yet

2 participants