Repository navigation
feat(platform-objects): sys_organization declares a name/logo-only rename action at the record header - #24317
Conversation
…name action at the record header rename_organization collects name and logo, no slug, and targets better-auth organization/update exactly as update_organization does, gated on multiOrgEnabled. Declared before change_slug, so it takes the header's third inline slot. The spec books it in PUBLIC_AUTH_FEATURES.multiOrgEnabled.gatedInputs. Claude-Session: https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH Co-authored-by: Claude <noreply@anthropic.com>
…ooted showcase The bundles are regenerated by the extractor; the zh-CN, ja-JP and es-ES values are translated, so the provenance tables carry no copied leaf. The dogfood pin renames through the registered action's request shape and reads the slug back unchanged. Claude-Session: https://claude.ai/code/session_01Nk17Z26X5DB59iQztDBADH Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 2 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 7 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 143 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 5f9defbc20ab2ee3ae5602850daecf20879af165 && git checkout 5f9defbc20ab2ee3ae5602850daecf20879af165
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 52ff308b8686f1f5a304341534bd8b8eb1cdd59e 1c3722ca1a622f63c566c58903bf137150b956bc && git checkout -B drift-repro 52ff308b8686f1f5a304341534bd8b8eb1cdd59e && git merge --no-ff 1c3722ca1a622f63c566c58903bf137150b956bc
node scripts/docs-audit/affected-docs.mjs --json 52ff308b8686f1f5a304341534bd8b8eb1cdd59e
|
Contract reviewServed-tier: Inputs: card #24271 (body and all six comments, 6248084492 through 6249653353), PR #24317 (body, ten-path file list, net diff ① Derived judgments
② Semver level
③ Boundary flagsThe dev's final report (6249492189) has
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对ACCEPT ·
Generated by Claude Code |
Fixes #24271
Clause-②: yes
Dispatched by the
domain:servicesseat 1 (seat post #6021), claim6248546668as amended by the fork ruling6248721024. Branchclaude/issue-24271-organization-rename-door.What
sys_organizationdeclaresrename_organization("Rename Organization"), so an organization record page can rename the organization. The maintainer's question that filed the card: 「组织 slug 可以改,但是名称不能改吗」.type: 'api',target: '/api/v1/auth/organization/update',recordIdParam: 'organizationId',bodyShape: { wrap: 'data' },requiresFeature: 'multiOrgEnabled',locations: ['record_header', 'list_item']. Its params arename(required,defaultFromRow) andlogo(defaultFromRow). It has noslugparam. It also carriesvariant: 'secondary'like its header siblings,mode: 'edit'likeupdate_organization, iconpencil, and the success message "Organization renamed".change_slug. Both sort atorder0, so declaration order breaks the tie. The record header's inline row is nowinvite_user,set_active_organization,rename_organization.change_slugfolds into the⋯overflow, andleave_organization(order: 1) stays last. Theleave_organizationcomment that names the inline row is updated. So is theactionsblock's header comment: it said better-auth's columns are edited "ONLY throughupdate_organization", and it now names both doors.update_organizationkeepsname,slugandlogo, its target andlist_itemalone. It stays the slug door of a composition without the cloud orchestrator.change_slugis not edited. Nothing inplugin-authchanges.node scripts/check-i18n-bundles.mjs --write --filter=platform-objects. The zh-CN, ja-JP and es-ES values are translated: 重命名组织 / 组织已重命名, 組織名を変更 / 組織名を変更しました, Renombrar organización / Organización renombrada. A second extract then dropped the six provenance rows the first one had seeded, so no copied English leaf ships.The spec row (the contract face)
The only spec edit is one line in
packages/spec/src/kernel/public-auth-features.ts. It adds'sys_organization.actions.rename_organization'toPUBLIC_AUTH_FEATURES.multiOrgEnabled.gatedInputs, besideupdate_organization's row.requiresFeature: 'multiOrgEnabled'lowers at parse time tovisible: features.multiOrgEnabled != false.PUBLIC_AUTH_FEATURESrecords, for each flag/api/v1/auth/configserves, which spec inputs that flag gates.packages/platform-objects/src/feature-gate-guard.test.tsholds the registry and the objects' gates in lockstep in both directions. Forward: every booked path must carry the flag's predicate. Reverse: everyfeatures.Xreference must be booked. The action without the row is red in the reverse direction; the previous round measured it (sys_organization.actions.rename_organization is booked in multiOrgEnabled.gatedInputs: expected false to be true). The row without the action is red in the forward direction. So the two land together.requiresFeaturevalue and no flag.check:api-surfaceandcheck:authorable-surfaceare green with no regenerated artifact.@objectstack/spectakes apatch. Precedent:b0a1df044bookedsys_organization.actions.invite_userthe same way.Premises, verified before any edit
organization/updatetakesdataas a.partial()object wherenameandslugare optional andlogois nullish (dist/plugins/organization/routes/crud-org.mjs). The slug-taken check runs only whendata.slugis a string.adapter.updateOrganizationwrites...dataplusmetadata, so a body withoutslugleaves the column alone. Measured on a boot: a name-only body answered 200 and returned the old slug, and the booted pin below reads the slug back unchanged after a name and logo rename.beforeUpdateOrganizationreturns at its first line when no slug is sent (auth-manager.ts:3607–:3610). The ADR-0092 D2 identity write guard passes better-auth adapter writes: the adapter's writes run as attributed system, soisUserContextWriteis false. better-auth grantsorganization: ['update']toownerandadmin(access/statement.mjs), not tomember. Measured on a boot for the owner: the creator's rename answered 200. The admin grade is a source reading.Tests
All on head
1c3722ca1unless noted.packages/platform-objects/src/identity/sys-organization-rename-action.test.ts, 8 tests. It pins the params (exactlynameandlogo), the locations, the target,recordIdParamandbodyShape(equal toupdate_organization's), and the lowered gate. It pins one door per field: the onlyrecord_headeraction that writes throughorganization/updateis the rename, and it collects no slug. It pins the header order, with the rename inline andchange_slugpast the inline budget. Controls:update_organizationandchange_slugunchanged.platform-objects.test.ts's feature-gate lowering matrix gainsrename_organizationatfeatures.multiOrgEnabled != false.packages/qa/dogfood/test/organization-rename-door.dogfood.test.ts, 2 tests. It boots the showcase withmultiTenant: 'posture-only', creates an organization, builds the request from the REGISTERED action the way the console's api runner does, renames it, and readsslugback unchanged.organization-update-door.dogfood.test.tsran beside it as the control: 11 of 11 passed.pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2:Test Files 76 passed (76),Tests 1176 passed (1176). The package'stypecheckexits 0, andtsc -p tsconfig.test.json --listFilesOnlylists the new test.@objectstack/spec:vitest run --project localgivesTest Files 694 passed (694),Tests 21040 passed | 1 todo.typecheckexits 0. Therepoproject is CI's.plugin-authsrc/public-feature-registry.test.ts(the served-key drift guard): 5 of 5 passed.scripts/ablation-replace.mjs). Aslugparam was planted inrename_organizationand the anchor count went 1 to 0. The declaration pin then answeredTests 2 failed | 6 passed: "expected [ 'name', 'slug', 'logo' ] to deeply equal [ 'name', 'logo' ]" and "rename_organization sends a slug to better-auth". The restore was proven by blob equality:16a3dce65796equals HEAD, andgit diff HEADis empty. The pin imports the object from source, so no build sits between the mutation and the run.Gates
The local subset is
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackover this diff: 88 commands, each run with its exit code captured before any pipe. 87 exited 0.pnpm check:dual-build-cjs-loadsanswered PREREQUISITE NOT MET (exit 3): it reads every workspace package'sdist/, and eight packages outside this diff's build closure have none. It is recorded NOT-MEASURED, and CI runs it. The--ranreconciliation reads88 derived famil(ies) accounted for — 87 run, 1 NOT-MEASURED. The local-onlycheck:dev-prereqspassed after the spec build.Acceptance notes
change_slugin the open-source composition, measured:POST /api/v1/cloud/organizations/{id}/change-sluganswers404 ENDPOINT_NOT_FOUNDon a booted showcase. On the stock single-org boot,/auth/configservesmultiOrgEnabled: false, so the action is hidden there. On theposture-onlyboot it servestrue, so the record page offerschange_slug(now in the overflow) and the click answers that 404. This is handed to the seat as a finding, and nothing is changed here.organization/updatestill acceptsdata.slug, andbeforeUpdateOrganizationrefuses it only while active environments exist. The record page no longer reaches it (the rename sends no slug). The API door is objectstack-ai/cloud#3000's to carry.packages/platform-objects/src/pages/sys-organization.page.ts's header comment lists "Set Active, Edit, Delete, Leave" as the record-header actions. Edit and Delete arelist_itemonly. This is comment drift, outside this claim's file surface, and is left as it is.Generated by Claude Code