Repository navigation
fix(app-shell): a position approver is read through the spec's registry binding (objectui#7611 Q3b) - #12174
Conversation
…ry binding (objectui#7611 Q3b)
The approval panel, the override dialog and the flow designer's approver
Value cell read the spec's approver value bindings, but kept only the `data`
member. objectstack#22710 binds `position` to a `registry` member,
`{ source: 'registry', type }`, which reaches this console two ways: the
console objectstack ships bundles the server's own spec, and a server on
that spec publishes the member as the approval node's `xRef.sources`.
- approverValueSources: the binding as published, with the registry member
admitted by a type loosened for spec 17 (removed at spec 18; a pin says
when). Which kinds are registry-bound is read off the binding, never
listed here.
- approverIdentity / useApproverDirectory: a registry-bound reference is a
machine name (prettified, never truncated), is handed to the directory,
and takes its label from the registry item through the metadata store.
Staffing still reads sys_user_position.
- json-schema-to-fields keeps a published registry source; FlowReferenceField
renders a select over that type's items and commits the item's name,
with a manual-entry toggle.
- The data member is read exactly as before.
Claude-Session: https://claude.ai/code/session_01Rerax7QTjKMPCUZxQUtPFR
Co-authored-by: Claude <noreply@anthropic.com>
…low the spec's binding (objectui#7611 Q3b) Claude-Session: https://claude.ai/code/session_01Rerax7QTjKMPCUZxQUtPFR 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
|
…ferences that carry a machine name (objectui#7611 Q3b) An id-valued directory kind (team, department) whose row label does not resolve keeps the caller's existing formatting; only a reference whose binding commits a name (a position, under either binding) is prettified. Claude-Session: https://claude.ai/code/session_01Rerax7QTjKMPCUZxQUtPFR Co-authored-by: Claude <noreply@anthropic.com>
✅ 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
|
Seat ACCEPT: objectui PR #12174 (Q3b, the approver registry read) at
|
Part of #7611
Clause-②: no. No published acceptance set widens and no public surface grows: no authoring face, zod face or
objectui validateinput is touched, and none of the touched modules is re-exported from@object-ui/app-shell's index. The one reader that now keeps more is the designer's reading of the server-publishedxRef.sources, a renderer input rather than an accepted-metadata set.Q3b of objectstack-ai/objectstack#22865, ruled B (comment 6115690641; pointer on #7611, comment 6115700897). The approval read side lists positions for a
{ source: 'registry', type }approver binding from the registry and stores the name only. Item 3a (the decision-output position picker, objectui#12152) is not touched: none of its files are in this diff. #7611 remains open for its other parts.What changes
approverValueSources(new,packages/app-shell/src/utils). The spec'sAPPROVER_VALUE_SOURCESas published, typed so the registry member objectstack#22710 added can be tested for. The installed spec 17.7.0 predates that member, so the type is widened here and marked LOOSENED FOR SPEC 17, remove at spec 18. A pin (approverValueSources.test.ts, "the installed spec binds nothing to the registry yet") turns red on the bump to say so. Which kinds are registry-bound is read off the binding; nothing in the diff listspositionas a registry kind.approverIdentity,useApproverDirectory). A reference whose kind is registry-bound carries a machine name: it is prettified rather than truncated, it is handed to the directory, and its label comes from the registry item read through the console's metadata store (ensureType(TYPE), theGET /api/v1/meta/TYPEread the Approval Center and item 3a already make). Staffing still readssys_user_positionby name.DeclaredActionsBarpicks this up without an edit.json-schema-to-fields,flow-node-config,FlowReferenceField). A publishedregistrysource is kept instead of dropped. A kind bound that way renders a select over that metadata type's items (the designer's metadata client,GET /api/v1/meta/TYPE) and commits the item'sname. It keeps the pencil toggle for manual entry, flags a stored name the registry does not list as(not found), and leaves the text box when the registry lists nothing. With no published source, the fallback is derived from the bundled spec in the same way as the record lookup table.databinding (user,team,department, andpositionunder the installed spec) renders the same record lookup and resolves its label from the same directory object.engine.inspector.reference.pickFromRegistry, en and zh). The app-shell README and the flow-designer guide each gain one paragraph.Measured in a browser (both symptoms reproduced on base first)
Setup: objectstack
main7dbb89dc5,examples/app-showcasebooted withobjectstack dev --seed-admin --fresh(closure build exit 0).GET /api/v1/meta/positionserves 16 positions;GET /api/v1/data/sys_positionanswers"records":[]. Each console wasvite buildplusvite previewwith the/apiproxy pointed at that server, built two ways:OBJECTSTACK_SPEC_DISTset to objectstack main'spackages/spec, the way objectstack builds the console it ships. Bundle check: the registry spelling of the position binding appears 5 times, and the vendored spelling (sourcerecord, objectsys_position) appears 0 times;A probe flow,
q3b_position_probe, was saved withPUT /api/v1/meta/flow/q3b_position_probe. It triggers on an account update and has one approval node whose approvers are positionmanager(held by the admin) and positionfield_ops_delegate(held by nobody), withunanimousbehaviour. Updating Acme Retail opened a request whosepending_approversis the admin's id andposition:field_ops_delegate.d2500d8be, injectedDev Admin,positi…gate(titleposition:field_ops_delegate); no request to/meta/positionor/data/sys_positionmanager,field_ops_delegate) with no datalist, and no candidate requestd2500d8be, vendoredField Ops Delegate (no current holder); readsGET /data/sys_position(empty)sys_positionrecord lookup that hydrates nothing, so both stored names render blank (placeholder shown); twoGET /data/sys_positionreadsField Ops Delegate Admin (no current holder), the registry label;GET /api/v1/meta/positiononlyProject Manager (manager)andField Ops Delegate Admin (field_ops_delegate);GET /api/v1/meta/positiononlyPicking on the branch (injected): the select offers all 16 positions, labelled
LABEL (name). ChoosingOperations (ops)made the designer's save send"approvers":[{"type":"position","value":"ops"},{"type":"position","value":"field_ops_delegate"}], so the name is the only thing stored.Tests (head
61a2d3ed0)approverValueSources.test.ts,approverIdentity.registryBinding-7611.test.tsx(mocks the spec into the injected shape),FlowReferenceField.registrySource-7611.test.tsxandFlowReferenceField.injectedRegistryBinding-7611.test.tsx(the second mocks the spec into the injected shape).FlowReferenceField*,json-schema-to-fields*,flow-node-config*,FlowNodeInspector*,approver*,useApproverDirectory*,RecordApprovalsPanel*,DeclaredActionsBar*,ApprovalsInboxPage*,approvalsApi*test, plus the four other tests that import a touched module). Result:Test Files 75 passed (75),Tests 824 passed | 1 skipped (825).pnpm --filter @object-ui/app-shell type-check(bothtscpasses) exit 0, after building the@object-ui/app-shell^...closure (28/28 tasks). The test program's file list contains all four new test files.check:i18n-keys,check:i18n-drift,check:i18n-dead-keys,check:i18n-designer-parity,check-changeset-presence,check-changeset-no-major,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations(0 new),check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:test-path-roots,check:control-bytes,check:unreferenced-sources,check:spec-symbols,check:installed-pin-claims,check:doc-fences,check:doc-types,check:docs-route-closure.react-refresh/only-export-componentswarning inFlowReferenceField.tsx(8 against 7), the same class as the file's existing exported helpers. The repo-wide lint run is CI's.check:readme-exportsandcheck:doc-snippets. Reason: their precondition is a builtdistfor every documented package, and neither was met here. Neither reads the two paragraphs this diff adds, because both gates read fenced code blocks and the paragraphs have none.Ablations (each leg on committed
61a2d3ed0, mutation proven on disk, restored bygit checkout HEAD, blob hash equal to HEAD,git diff HEADempty)json-schema-to-fieldsdropsregistryagainAcceptance notes
ApprovalsInboxPage) already labelsposition:approvers from the registry, through its ownPOSITION_REF_PREFIXrather than the published binding. It reads correctly under both specs and is left as it is: routing it through the binding would drop its registry labels on the vendored console until spec 18. Carrier: none.sys_positionmirror, so a fresh environment shows the prettified name (Field Ops Delegate) rather than the registry label (Field Ops Delegate Admin). The ruling leaves the record arm unchanged, and this goes away at spec 18. Carrier: none.sys_positionhydration reads. That fallback is the installed spec's binding and goes away at spec 18. Carrier: none.Session:
https://claude.ai/code/session_01Rerax7QTjKMPCUZxQUtPFR(os-dev subagent of the epic PM seatepic:#15194).Generated by Claude Code