Repository navigation
feat(spec)!: retire the permission set's authored managedBy, a provenance key nothing read - #23989
Conversation
Claude-Session: https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr Co-authored-by: Claude <noreply@anthropic.com>
…anagedBy tombstone Claude-Session: https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr Co-authored-by: Claude <noreply@anthropic.com>
…s bundles Claude-Session: https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr Co-authored-by: Claude <noreply@anthropic.com>
…label Claude-Session: https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr Co-authored-by: Claude <noreply@anthropic.com>
…rmission-set-managedby-retired
📓 Docs Drift CheckThis PR changes 2 package(s): 6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 142 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 ad66f04f8de198826b20d11b3e786b341f7976e4 && git checkout ad66f04f8de198826b20d11b3e786b341f7976e4
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 022c6e799e88ae192e124c80e1cc343bda54d7bf 076958cb25d7092d7ebbb6151497719893f97247 && git checkout -B drift-repro 022c6e799e88ae192e124c80e1cc343bda54d7bf && git merge --no-ff 076958cb25d7092d7ebbb6151497719893f97247
node scripts/docs-audit/affected-docs.mjs --json 022c6e799e88ae192e124c80e1cc343bda54d7bf
|
Contract reviewServed-tier: Inputs: card #23891 (body; triage ① Derived judgmentsRefused by name at every authoring, save and publish door — RIGHT. A stored row and an installed artifact still load, by the existing ADR-0087 seams — RIGHT, and the seams are named.
The column, its seeders, the ownership ceremony and Ledger row follows the tombstone route — RIGHT. The key stays in the walked shape, so the row stays ( The four ratchets read as the route predicts — RIGHT. Tombstone route: ADR-0087 registration — RIGHT. D2 Form, bundles, docs — RIGHT. The ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对 · ACCEPT · PR #23989 @
|
Fixes #23891
Clause-②: yes
Retires the AUTHORED key
PermissionSetSchema.managedBy(remedy 1 of the card; the liveness ledger'sdeadverdict of 2026-10-11, ADR-0049 enforce-or-remove) with the fullspec-property-retirementkit. The key's Setup form row, its help text, its three option labels and their translations in four locales leave with it. Thesys_permission_set.managed_byCOLUMN, its seeders and the ownership ceremony are untouched, and so isObjectSchema.managedBy.What changes for an author
managedByon a permission set is aretiredKey()tombstone:tscfails at the authoring site, anddefinePermissionSet(),defineStack({ permissions })andPUT /api/v1/meta/permission/:namerefuse it at its own path with the prescription (delete the key; a set's provenance is the package that ships it, decided from that package's artifact and recorded aspackageId).permissionrow (what Setup's form row wrote) and an installed package's built artifact still load. The rehydration seams (applyConversionsToStoredItem,applyArtifactForwardConversions) replay the D2 conversionpermission-set-managed-by-removedand strip the key, with a notice that names the set. Only the authoring, save and publish doors refuse it. This is the existing ADR-0087 mechanism (retiredFromLoadPath: true, replayed withincludeRetiredat the data-at-rest seams), so no kernel built from an installed package or a stored row stops on it. Kernel build refuses an INSTALLED package page with isDefault on a non-record page (a69a40a572 / #23708): every hosted environment with HotCRM 4.0.1 fails to boot #23904 landed after this head as PR fix(spec,objectql): installed pages carrying a retired declaration convert at load instead of stopping the kernel build #23962 (d050a8fc63); itsObjectQL.convertPackageBodyForwarddoor runs the same conversion table, so this entry rides it with no second mechanism.Measured before removal (base
6d1660dc3b)bootstrapDeclaredPermissionsstamped the column and never readps.managedBy.packaged-permission-set-lock.tsdecides provenance from the artifact.database-loader.ts:906maps thesys_metadatarecord envelope'smanaged_by, not a set body.permissionSetDefinitionFromRow(core) excludes the column from the body.plugin-security's default sets: 0. EverydefineStackfixture: 0 (the tombstone typed every authoring site, and spec's typecheck and the platform-objects typecheck are green). HotCRM 4.0.1 at1d7148bf2d(thev4.0.1tag): 0 in its seven*.profile.tssets, against the controlobjects:at 7. The one writer was Setup's form row, into stored rows, which the D2 strip heals.daeb77fc84(.objectui-shaunchanged since dispatch):PermissionMatrixEditor.tsx:1365and:1606readdraft.managedByfor the Platform / Package / Custom badge;permission-slice.ts:62declaresmanagedBy?: stringon objectui's OWNPermissionSetDrafttype;permission-set-clone.ts:13lists it in a comment;PermissionMatrixEditor.badge.test.tsx:56-74andPermissionMatrixEditor.provenanceBadge-7611.test.tsxtest the badge. None imports the spec's type for that field, so the pinned objectui build does not break (Post-Task Checklist step 4). Its reader is a display, and its other two inputs (packageId,codeIsArtifact) are the real provenance. No objectui file is touched; the follow-up card is the seat's to file at landing.The kit
packages/spec/src/security/permission.zod.ts: the tombstonemanagedBy: retiredKey(PERMISSION_SET_MANAGED_BY_REMOVED)with its TSDoc; thepackageIdTSDoc no longer cites the key.packages/spec/src/security/permission.form.ts: themanagedByrow and its decision-record bullet leave, and a one-line note stays in their place.permission-set-managed-by-removed(conversions/registry.ts,MAJOR_18_CONVERSIONSorder 67, defined aboverecordChatterPositionVocabularyper the merge rule); D3permission-set-managed-by-retired(migrations/entries/semantic/);RETIRED_KEYS_BY_MAJOR[18]security/PermissionSet:managedBy(migrations/entries/retired-keys/); aSTEP18_RATIONALEfragment (order 99) inregistry.ts.template; a JOIN row inretired-key-removal-version.pin.test.ts.liveness/permission.json: the row stays (dead, a REMOVED note,verifiedAt: 2026-10-16). The status does not move, so the README's per-type counts do not either.authorable-surface/security.json(one row becomes[RETIRED]) andcontent/docs/references/security/permission.mdx(gen:docs). The four*.metadata-forms.generated.tsbundles were regenerated bynode scripts/check-i18n-bundles.mjs --write, which drops 4 lines per locale, all generator-owned. Nothing generated was hand-edited.content/docs/permissions/permission-sets.mdx(the clone door's dropped-key list no longer namesmanagedBy). Inplatform-objects, two pins follow the bundle:setup-copy-vocabulary.test.tsdrops the leaf from its help-text population and dark leg, andobject-lifecycle-panel-echo-decisions.test.tsmoves its catalog count from 674 to 673 for the one label that left.packages/spec/vitest.repo-tests.json: the new pin's tree walk reads outside the package, so it runs under therepoproject (check:cross-package-test-inputs)..changeset/23891-permission-set-managed-by-retired.md:@objectstack/specminor (BREAKING, FROM → TO, the one-line fix) and@objectstack/platform-objectspatch, with the ADR-0087registeredmarker.The four ratchets: what the route predicts, and what was read
On the tombstone route, the key stays in the walked shape and no def is deleted. The route predicts a
[RETIRED]mark on one authorable-surface row and no change to the other three. Read on076958cb:authorable-surface+1/−1 (the mark);api-surface,api-surface-signaturesandjson-schema.manifestunchanged;check:generatedreports all 14 artifacts up to date.Pins, and the ablations that name them
packages/spec/src/security/permission-set-managed-by-retirement.test.ts(17 cases): the refusal at the key's path with the prescription, for every value; thepermissionwrite door; thedefineStackenvelope (STACK_SCHEMA_INVALID, 422); a@ts-expect-errorat an authoring site; the form offers no row; a stored row replays clean throughapplyConversionsToStoredItem; an installed package's artifact is refused before the conversion and accepted after it, with a lit control; strip, idempotence, and retirement from the load path; ADR-0087 registration; and a tree-scoped absence walk. BecauseObjectSchema.managedByis live, the walk flagsmanagedByonly in an object literal that also carries a key only a permission set declares, and it states that bound.Ablations (
scripts/ablation-replace.mjs, each restored to the HEAD blob withgit diff HEADempty):z.enum(['package','platform','user']).optional(): 7 tests go red (the parse refusal, every value, the write door,defineStack, the tsc-site parse leg, the artifact before/after, and the removal-version JOIN row).MAJOR_18_CONVERSIONS: 4 go red (the stored row, the artifact, strip/idempotence, chain wiring).@ts-expect-errordeleted:check:test-typecheckgoes red, naming the file. The type channel is really compiled.Verification (on
076958cb, after mergingmain)@objectstack/spec:buildexit 0;typecheckexit 0;check:generatedexit 0;check:liveness,check:empty-state,check:exported-any,check:dual-source-exportsandcheck:variant-docsexit 0. Vitest runs:src/in four batches, 681 files and 20,447 tests, all passing. After the merge:src/security,src/conversions,src/migrationsandsrc/sharedplus the two major-18 merge tests andscripts/liveness, 83 files and 2,445 tests, all passing.@objectstack/platform-objects: vitest 73 files and 1,136 tests, all passing;typecheckexit 0.@objectstack/runtimepermission-set-clone.test.ts: 23 passing.check:i18nexit 0.dispatch-gates --commandson this diff: 113 families;--ran: 113 accounted, 110 run, 3 NOT MEASURED. NOT MEASURED (prerequisites of the local tree, not readings of this diff):check:plugin-teardown-shape --self-test(exit 3, shallow clone),check:skill-examples(exit 3,client-reactdeclarations not built),check:dual-build-cjs-loads(exit 3, packages never built here).check:dts-closureexited 1 on 54 packages missing.d.ts, which an earlierOS_SKIP_DTS=1closure build left in this tree. None of these is a package this diff changes, and CI checks out fresh.Acceptance notes
packages/runtime/src/domains/permission-set-clone.tsstill listsmanagedByinPERMISSION_SET_CLONE_DROPPED_KEYS. After this PR, no set body that reaches it can carry the key, so the entry is inert, not wrong. It is outside this card's file surface and is left alone. Carrier: none.@objectstack/specbeyond HotCRM 4.0.1 is NOT MEASURED.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_01D3Ur6XgfkkaDUCCTkviXAr