Repository navigation
fix(plugin-sharing,plugin-webhooks): by_object lists sort by object instead of grouping on its API name - #24733
Conversation
…nstead of grouping on its API name sys_sharing_rule.by_object and sys_webhook.by_object grouped on object_name, and the console heads a group with the stored value, so each group read an object API name. Both views keep their object_name sort, which holds one object's rows together, and their Object column, whose object-ref cell draws the object's label. A pin beside each object refuses a grouping on a field that stores an object API name. Claude-Session: https://claude.ai/code/session_01LR1cD9JqgkeqY7k9rrZH8s Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 15 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 2 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 97c8167b6d834257636b543f33babd5890946add && git checkout 97c8167b6d834257636b543f33babd5890946add
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0904d856e31fd065c71af725cf3ad4ad0363dc76 7fa022b591b0bdd4ac803ad0748d1e24eab071bc && git checkout -B drift-repro 0904d856e31fd065c71af725cf3ad4ad0363dc76 && git merge --no-ff 7fa022b591b0bdd4ac803ad0748d1e24eab071bc
node scripts/docs-audit/affected-docs.mjs --json 0904d856e31fd065c71af725cf3ad4ad0363dc76
|
席位核对ACCEPT · #24701 half (a) ·
Generated by Claude Code |
Part of #24701
Clause-②: no
This is half (a) of the card.
sys_sharing_rule.by_objectandsys_webhook.by_objectstop heading each group with an object API name. Both views now sort by object instead of grouping on it, the same route PR #24698 took forsys_record_share.by_object. Half (b), the record share's heading, is measured and analysed only, with no edit, and the card stays open for its decision. #24701 remains open after this PR lands.Responsibility: the sharing-rule and webhook list views' grouping (
plugin-sharing,plugin-webhooks) | a served surface names the target object by its label, never by its API name (the #24375 route; no console-side mapping) | admins reading Setup → Sharing Rules and Setup → Webhooks.Readings are against objectstack
d20bf9b09(this branch's base) and objectui at the console pin.objectui-sha=12dc7b47c3, bygit showandgit grep. No browser reading: there is no console build in this container.Premise, measured
object_name.sys-sharing-rule.object.tsandsys-webhook.object.tsdeclaredgrouping: { fields: [{ field: 'object_name', … }] }onby_object.object_nameis aField.textwithwidget: 'object-ref'on both objects, so it stores an object API name.ObjectGrid'sgroupValueFormattermaps a select value to its option label and a boolean to Yes / No, and returns nothing for anything else.useGroupedDatathen uses the raw value as the header label, andGroupRowdrawsgroup.label. Server-side grouping hands back raw values too (ListViewGroupHeaderRow). So each group readcrm_account,showcase_announcementand so on.widget: 'object-ref'(ObjectRefCellFace): the object's icon and its translated label. The Object column's cells were labelled, and the header above them was not.Route: option (ii), sort instead of group
object-refface labels a cell at render time, but the group header does not use cell faces. So any grouping key draws stored text.sort: object_name asc, label ascis unchanged, so one object's rows stay together. The Object column stays first, and its cell draws the object's icon and label.Four axes:
nav_sharing_rulesandnav_webhooksopen the object's first view (active).object-reffield, or a pointer pair'sreferenceViatype half. The sharing pin covers every objectobjects/index.tsships, so it also coverssys_record_shareandsys_share_link.Changes
packages/plugins/plugin-sharing/src/objects/sys-sharing-rule.object.tsby_objectlosesgrouping; the sort and columns are unchangedpackages/plugins/plugin-webhooks/src/sys-webhook.object.tsby_objectlosesgrouping; the sort and columns are unchangedpackages/plugins/plugin-sharing/src/objects/sys-sharing-rule.by-object.test.tspackages/plugins/plugin-webhooks/src/sys-webhook.by-object.test.ts.changeset/24701-by-object-group-headers.mdpatchfor@objectstack/plugin-sharingand@objectstack/plugin-webhooksNot touched:
sys-record-share.object.ts,packages/spec/**, and every stored column, index and query. Also not touched: the #15194 epic's files (sharing-rule-service.ts,bootstrap-declared-sharing-rules.ts,sharing-plugin.ts,declared-webhooks.ts,webhook-provenance.ts,webhook-outbox-plugin.ts).Pins
Each pin file has three cases:
object_nameon each sharing object, andobject_nameonsys_webhook. So the refusal below cannot pass on an empty set.by_objectstill sorts first onobject_name ascand still showsobject_nameas its first column. The sharing control also asserts thatobject_namekeeps itsobject-refwidget, the cell face's trigger.Ablation. Each leg ran once at
7fa022b59, underscripts/pm/os-verify-lock.sh, throughnode scripts/ablation-replace.mjsin wrap mode, with a shelltraprestoringHEADon EXIT, INT and TERM. The anchor waspagination: { pageSize: 100 },(1 hit in each file). The replacement putgrouping: { fields: [{ field: 'object_name', order: 'asc', collapsed: false }] }back in front of it. The tests import the object files by relative source path, and@objectstack/specwas built, so no rebuild sits between a mutation and its run.object_namegrouping back intoby_objectsys-sharing-rule.object.ts5597d023128a→23c624a69766no list view groups on a field that stores an object API name: received[ "sys_sharing_rule.by_object: object_name" ]·Tests 1 failed / 2 passed5597d023128a,git diff HEADemptyobject_namegrouping back intoby_objectsys-webhook.object.ts1e3bf6397b97→a946ffa35dc7[ "sys_webhook.by_object: object_name" ]·Tests 1 failed / 2 passed1e3bf6397b97,git diff HEADemptyVerification
All readings below are on head
7fa022b59, the final commit. Exit codes were captured before any pipe. Builds and tests ran underscripts/pm/os-verify-lock.sh.--filter '@objectstack/plugin-sharing^...'and--filter '@objectstack/plugin-webhooks^...'), then both packages: exit 0.pnpm --filter @objectstack/plugin-sharing test: exit 0,Test Files 50 passed (50),Tests 1172 passed (1172)(1169 + the 3 new).pnpm --filter @objectstack/plugin-webhooks test: exit 0,Test Files 20 passed (20),Tests 215 passed (215).pnpm --filter @objectstack/plugin-sharing typecheckandpnpm --filter @objectstack/plugin-webhooks typecheck: exit 0.check:test-typecheckholds both ledgers unchanged: sharing 2 files / 3 errors, webhooks 0 / 0.tsc -p tsconfig.test.json --listFileslists both new pin files, so the test layer compiles them. The sharing program's 3 errors are the ledgered ones, inposition-graph.test.tsandsharing-rule.test.ts, none in the new file.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, with no paths after the last commit, gave 67 commands. 65 exited 0 on the first pass.pnpm check:i18nfirst exited 3 (prerequisite not met: no CLI build). Its own prescribed turbo build was run, and the rerun exited 0:check-i18n-bundles: OK (10 package(s) — all bundles in sync, no undeclared authoring keys).pnpm check:dual-build-cjs-loads. Reason: exit 3, prerequisite not met. It reads every package'sdist/(54 packages unbuilt here), which needs a whole-workspace build. This diff changes no package entry, export or build config. CI'sLint & Repo Gatesruns it.--ranreconciliation.Run reconciliation — 67 derived, 66 run, 1 NOT-MEASURED, 0 UNRUN, exit 0.test:repo(no repo-project test or derived list names this diff),pnpm lint, the type-check lanes,Build Coreand the fullTest Coreshards.Half (b), measured only (no edit)
A record share's heading is
recipient_id(nameField, since PR #24698), aField.textwith no widget and no reference.sys_record_share, both inSharingService.grant()(sharing-service.ts, the update at :1550 and the insert at :1579).grant()refuses everyrecipientTypeexceptuser(ADR-0111 D7). The rule evaluator calls it withrecipientType: 'user'(sharing-rule-service.ts:2177), expanding team, business-unit and position recipients into one row per user. So every row written atd20bf9b09is ausergrant, and itsrecipient_idis asys_userid. The read gates match onlyrecipient_type: 'user'(sharing-service.ts:589, :665, :877). The select still declaresuser,group,positionandunit_and_subordinates, held equal toRecordShareRecipientType(packages/spec/src/contracts/sharing-service.ts:104). No writer produces the last three. No example app seeds the object.recipient_type, a required select defaulting touser.recordDisplayValueAt(data, declaredNameField(…))inpage:header(containers.tsx). That is the stored text, so the H1 reads a raw user id. A field's widget face is not consulted.all_shares,granted_by_me,by_object,manual_grantsandrule_grantsdraws the same raw id.sys_sharing_rule.recipient_iddeclareswidget: 'recipient-picker'. Its face (RecipientCellFace, objectui#12613) names auserid by an exact-idsys_userread, keyed by the row'srecipient_type.groupis not a kind it knows, so agrouprow would draw as stored text.resolveRecordPointer(packages/fields/src/index.tsx:3291) takes thereferenceViasibling's stored value as the object name.recipient_typestoresuser, notsys_user, so a pair on it would ask for an object calleduser. A declared pair needs a sibling column that stores an object name. Even then, the H1 draws the title field's stored id, and the Details tab hides the title field, so its pointer card would not draw.lookupanduserare title-ineligible types (packages/spec/src/data/display-name.ts). Aformulawith a text result is eligible.The options and the four-axis analysis are in the dev report on #24701. No edit was made to
sys-record-share.object.ts.Acceptance notes
groupValueFormatterlabels select and boolean values only, so a grouping on anobject-reforrecipient-pickerfield heads its group with the stored value while the cells below are labelled. After this PR, no served objectstack view groups on such a field (the pins hold that), so nothing reaches it today. Carrier: none.Generated by Claude Code