Repository navigation
feat(types): arm record:approvals and record:attachments from their spec rows (objectui#12363) - #12382
Conversation
…pec rows (objectui#12363) Both spec-18 page blocks were registered by app-shell and emitted by buildDefaultPageSchema, but AnyComponentSchema had no arm for them, so objectui validate refused the default record page at `type`. Each arm's `properties` bag restates its empty strict ComponentPropsMap row; body and children are refused by name. NAMESPACED_REFUSED_AT_TYPE falls 308 -> 306. Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB Co-authored-by: Claude <noreply@anthropic.com>
…hments arms (objectui#12363) Claude-Session: https://claude.ai/code/session_01XVp19E1MYUTFM3dnkwz8gB Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
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
Size Limits
|
Contract reviewServed-tier: Inputs read: card #12363 body, the first grade ( ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #12363
Clause-②: yes
What
@objectstack/spec18 declares two page blocks,record:approvalsandrecord:attachments.@object-ui/app-shellregisters a renderer for each, andbuildDefaultPageSchema(@object-ui/plugin-detail) emits both as the default record page's Approvals and Attachments tabs. ButAnyComponentSchemahad no arm for either. Soobjectui validaterefused each one withinvalid_unionattype, and the record page objectui builds itself failed validation.This PR reuses the construction of objectui#12342 (PR #12362,
52d6194):@object-ui/types/zod). New modulezod/record-panels.zod.tswithRecordApprovalsBlockSchemaandRecordAttachmentsBlockSchema. Both are wired intoAnyComponentSchema, soStrictAnyComponentSchemaandobjectui validateclaim them too. Eachpropertiesbag restates its spec row.body/childrenare refused by name withretirementTombstone, as on app-shell: spec 18'srecord:approval_decisionpage block renders nothing —ApprovalDecisionRendereris written but never registered (the objectui half of spec 18's three record blocks, after #12030) #12342's arm.@object-ui/types). Newrecord-panels.tswith interfaces of the same two names, exported from the entry and added toAnySchema.zod-mirror-parity.test.ts;EXPECTED_MIRROR_PAIRSgoes 196 to 198. The emit-headroom member list inany-component-emit-headroom-11573.test.tsgains both arms.@object-ui/cli).NAMESPACED_REFUSED_AT_TYPEgoes 308 to 306, lowered by exactly two. A new row names both keys as armed, with a control showing an unclaimedrecord:literal is still refused. The app-shell: spec 18'srecord:approval_decisionpage block renders nothing —ApprovalDecisionRendereris written but never registered (the objectui half of spec 18's three record blocks, after #12030) #12342 row's comment no longer says the siblings are unarmed.public-blocks.zod.ts. That union is pinned to the ADR-0080 curated list, and neither block is curated, the same reason app-shell: spec 18'srecord:approval_decisionpage block renders nothing —ApprovalDecisionRendereris written but never registered (the objectui half of spec 18's three record blocks, after #12030) #12342 gave..changeset/12363-approvals-attachments-arms.md,@object-ui/typesminor.Contract review: the accept set this widens (Clause-②)
Installed spec.
@objectstack/spec18.0.0-next.0, as this checkout resolves it (packages/types). Both types arePageComponentTypemembers, and both have aComponentPropsMaprow. This was measured at runtime and is re-read by the new test on every run.propertiesrecord:approvalsRecordApprovalsProps(not exported under that name from@objectstack/spec/ui)z.strictObject({}).optional(){}record:attachmentsemptyProps('record:attachments'), built inline and exported under no namez.strictObject({}).optional(){}Key by key: neither row declares a key, so neither bag declares one. ⛔ No key the spec does not declare, and no
passthrough/catchall. Both rows are unnamed exports, so they cannot cross the objectui#8317 import boundary by reference. They are restated, which iselement:divider's andrecord:approval_decision's situation.record-panels-arms-12363.test.tspins three things against the installed row on every run:{}and refuses a key.What is refused:
properties(unrecognized_keysatproperties). This includesapprovalsandcurrentUserId. The spec'sRecordApprovalsPropsguidance names both as the HOST's runtime channel, "not authorable surface".childrenorbody, refused by name (invalid_typeat the key).unrecognized_keys).What is accepted: the bare node
{ type }, and the node withid/className/ an emptypropertiesbag (theBaseSchemanode keys).Published exports added:
@object-ui/types/zod:RecordApprovalsBlockSchema,RecordAttachmentsBlockSchema.@object-ui/types: the TS types of the same names, plus two newAnySchemamembers.AnyComponentSchema/StrictAnyComponentSchemagain two discriminator literals.Premise checks (dispatch section 2)
PageComponentTypemembers withComponentPropsMaprows on the installed spec. Both rows are strict and declare no key (table above).buildDefaultPageSchemaemits{ type: 'record:attachments' }for anenable.files: trueobject, and{ type: 'record:approvals' }when the host passesapprovals. That page now validates whole on both faces (pinned).approvals.node, the synthesizer spreads it into the node'sproperties.RecordDetailViewdoes this with its liveuseRecordApprovalsread:approvalspluscurrentUserId. The arm refuses those two keys because the spec row does.record:history's hostentries/loading, which the existing arm already refuses.SchemaRendererand never reaches the validator. So I read it as a host runtime channel, not a producer defect, and pinned it as a refusal. The arm is not widened for it. It is reported below.packages/cli/src/__tests__/registered-types-validate-ratchet-10859.test.ts. It drops by exactly two, 308 to 306.origin/mainmoved by one commit,fc211e817, which touches neitherpackages/types, the lockfile nor the ratchet. So the rows are measured on next.0.Pins
packages/types/src/__tests__/record-panels-arms-12363.test.ts. Per type, it pins:type;children/bodyare refused by name;It also pins that
properties.approvals/properties.currentUserIdare refused.packages/plugin-detail/src/__tests__/buildDefaultPageSchema.validatesWhole-12363.test.ts:safeValidateSchemaandStrictAnyComponentSchemawith zero issues;unrecognized_keysat therecord:approvalsbag namingapprovalsandcurrentUserId.The cli ratchet row described above.
Verification (head
83d91d3)packages/types/andpackages/cli/, plus the plugin-detail pin. One run under the verify lock:Test Files 409 passed (409),Tests 10719 passed (10719). This includeszod-mirror-parity,any-component-emit-headroom-11573,record-approval-decision-arm-12342and the ratchet.@object-ui/types,@object-ui/cliand@object-ui/plugin-detailall exit 0. I built the dependency closure first.ablation-replace.mjs, run with--deleteon the two union members inzod/index.zod.ts):cd2ce50to0f4b39f;Test Files 3 failed (3),Tests 24 failed | 50 passed (74);the refused count equals the pin), the new ratchet row, all four default-page rows and every union-path arm row failed;git diff HEAD.check:phantom-deps,self-import,unreferenced-sources,doc-example-readers,handler-key-reads,side-effects-array,element-data-source-declaration,spec-symbols,action-forward-parity,designer-field-key-parity,icon-record-names,i18n-keys,i18n-drift,i18n-designer-parity,registry-bare-names,control-bytesandnew-line-citations. So docheck-changeset-fixed.mjs,check-changeset-presence.mjsandcheck-changeset-no-major.mjs.check:spec-floorsexits 1. It says "The scan collapsed: 0 package(s) declare @objectstack/spec in a consumer-facing field". That is the known failure onmain(objectui#12334), and this diff touches no dependency field.eslint --format jsonover the 9 changed.tsfiles gives 9 results, 0 errors and 0 warnings, the way the packagelintscripts run it (eslint .for types and plugin-detail,eslint srcfor cli).--no-inline-configit reports 3 findings, all on lines this diff does not touch: 2 inzod-mirror-parity.test.ts, 1 intypes/src/index.ts. Each is already covered by an inline disable.eslint.config.jssets noproject/projectService, so linting is not type-aware, and this diff cannot change the verdict on any untouched file.pnpm lintrun belongs to CI.Acceptance notes
packages/types/src/**, the cli ratchet and their tests. The ruled pin "buildDefaultPageSchema's page validates whole" cannot live in either package.@object-ui/typesand@object-ui/clido not depend on@object-ui/plugin-detail, and a reverse import is whatcheck:phantom-depsrefuses. So that pin is a new test file inpackages/plugin-detail/src/__tests__/, a package that already depends on@object-ui/types. No plugin-detail source changed.record:approvalsbag drops the spec's guidance text. The spec row's own unrecognized-key message explains whyapprovalsandcurrentUserIdare host channels. The restatedz.strictObject({})reports only a genericunrecognized_keys. The arm's.describe()carries the same guidance for readers of the schema. Carrying the row's message would need the row by reference, which the objectui#8317 boundary census does not admit for an unnamed export.NODE_ENVELOPE(responsiveStyles), as on app-shell: spec 18'srecord:approval_decisionpage block renders nothing —ApprovalDecisionRendereris written but never registered (the objectui half of spec 18's three record blocks, after #12030) #12342's and thecloud:arms. Neither block is offered by Studio, and no producer writes that key on these nodes.Generated by Claude Code