Spotted by the domain:spec execution seat while working #16228 (the permissionForm docstring / section description). ⛔ Out of scope for that PR and deliberately not touched there: repairing this is a metadata/schema change, which that dispatch fences off. ⛔ domain:*, priority and type are triage's.
The contract
ADR-0090 D2 (docs/adr/0090-permission-model-v2-concept-convergence.md), Accepted 2026-07-09:
D2 — The Profile concept is removed (isProfile deleted, not deprecated).
The spec enforces that on the security side, in strings it ships to authors. packages/spec/src/security/permission.zod.ts:
- the
PermissionSetSchema header: "There is no Profile concept (ADR-0090 D2)."
- the
isProfile tombstone: "isProfile was removed by ADR-0090 D2 — there is no Profile concept."
- the
profiles wrong-layer pointer: "profiles is not a PermissionSet field (ADR-0090 D2: no Profile concept)."
And it is pinned. packages/spec/src/kernel/capability-metadata-kind.test.ts asserts, for each of role / profile / policy, that it is not a MetadataTypeSchema kind, has no registry entry and resolves no schema. packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.ts asserts METADATA_FORM_REGISTRY carries no profile key and that no locale bundle carries a profile form group.
What contradicts it
packages/spec/src/ui/page.zod.ts keeps an authorable key on the published PageSchema named for exactly that concept:
- The key.
assignedProfiles: z.array(z.string()).optional() — untyped strings, no .describe().
- The alias map steers authors INTO it. The same file maps
profiles: 'assignedProfiles' and assignedTo: 'assignedProfiles'. An author who writes profiles: on a page is not told the concept is gone — they are corrected into the retired vocabulary. Two lines away, PermissionSetSchema answers the same word profiles with "no Profile concept". The platform gives opposite answers to one word depending on which schema receives it.
- Two shipped guidance strings teach it. The
visibleWhen pointer ends "or gate the page with assignedProfiles"; the permissions pointer says "reach it through assignedProfiles". Both are prescriptions handed to an author at parse time.
- It ships translated.
page.form.ts gives the field helpText: 'Profiles that can access this page', which the extractor has carried into all four locale bundles — e.g. zh-CN label: "指定配置文件" / helpText: "此页面对哪些 Profile 可用", ja-JP "割り当てプロファイル", es-ES "Perfiles asignados".
What is NOT claimed here
⛔ Not claimed dead. A repo-wide grep for assignedProfiles outside the generated bundles finds no code reading it in this repository — only prose mentions in packages/spec/src/ui/view.zod.ts and packages/spec/src/api/protocol.zod.ts, plus a comment in packages/metadata-protocol/src/protocol.ts that states the key "is measured to have no backend consumer on the read door today; it is enforced where it is enforced now, at page render". Page render lives in objectui, which this reading does not cover. ⇒ Whether this is also an ADR-0049 enforce-or-remove case is a second, unmeasured question; the contract violation above stands on its own regardless of the answer.
⛔ No repair is proposed. Renaming the key, retargeting the aliases, or removing it are three different decisions with different blast radii (published PageSchema accept set, an ADR-0087 conversion, four locale bundles), and picking one is not this seat's call.
Boundary cases deliberately excluded
Final Profile / Profile Source in *.objects.generated.ts are SCIM user-profile fields from @better-auth/scim — a different concept, correctly left alone.
Refs: #16228 · packages/spec/src/ui/page.zod.ts · packages/spec/src/ui/page.form.ts · docs/adr/0090-permission-model-v2-concept-convergence.md
Generated by Claude Code
Spotted by the
domain:specexecution seat while working #16228 (thepermissionFormdocstring / section description). ⛔ Out of scope for that PR and deliberately not touched there: repairing this is a metadata/schema change, which that dispatch fences off. ⛔domain:*, priority and type are triage's.The contract
ADR-0090 D2 (
docs/adr/0090-permission-model-v2-concept-convergence.md), Accepted 2026-07-09:The spec enforces that on the security side, in strings it ships to authors.
packages/spec/src/security/permission.zod.ts:PermissionSetSchemaheader: "There is no Profile concept (ADR-0090 D2)."isProfiletombstone: "isProfilewas removed by ADR-0090 D2 — there is no Profile concept."profileswrong-layer pointer: "profilesis not a PermissionSet field (ADR-0090 D2: no Profile concept)."And it is pinned.
packages/spec/src/kernel/capability-metadata-kind.test.tsasserts, for each ofrole/profile/policy, that it is not aMetadataTypeSchemakind, has no registry entry and resolves no schema.packages/platform-objects/src/apps/translations/metadata-forms-vocabulary.test.tsassertsMETADATA_FORM_REGISTRYcarries noprofilekey and that no locale bundle carries aprofileform group.What contradicts it
packages/spec/src/ui/page.zod.tskeeps an authorable key on the publishedPageSchemanamed for exactly that concept:assignedProfiles: z.array(z.string()).optional()— untyped strings, no.describe().profiles: 'assignedProfiles'andassignedTo: 'assignedProfiles'. An author who writesprofiles:on a page is not told the concept is gone — they are corrected into the retired vocabulary. Two lines away,PermissionSetSchemaanswers the same wordprofileswith "no Profile concept". The platform gives opposite answers to one word depending on which schema receives it.visibleWhenpointer ends "or gate the page withassignedProfiles"; thepermissionspointer says "reach it throughassignedProfiles". Both are prescriptions handed to an author at parse time.page.form.tsgives the fieldhelpText: 'Profiles that can access this page', which the extractor has carried into all four locale bundles — e.g. zh-CNlabel: "指定配置文件"/helpText: "此页面对哪些 Profile 可用", ja-JP"割り当てプロファイル", es-ES"Perfiles asignados".What is NOT claimed here
⛔ Not claimed dead. A repo-wide grep for
assignedProfilesoutside the generated bundles finds no code reading it in this repository — only prose mentions inpackages/spec/src/ui/view.zod.tsandpackages/spec/src/api/protocol.zod.ts, plus a comment inpackages/metadata-protocol/src/protocol.tsthat states the key "is measured to have no backend consumer on the read door today; it is enforced where it is enforced now, at page render". Page render lives inobjectui, which this reading does not cover. ⇒ Whether this is also an ADR-0049 enforce-or-remove case is a second, unmeasured question; the contract violation above stands on its own regardless of the answer.⛔ No repair is proposed. Renaming the key, retargeting the aliases, or removing it are three different decisions with different blast radii (published
PageSchemaaccept set, an ADR-0087 conversion, four locale bundles), and picking one is not this seat's call.Boundary cases deliberately excluded
Final Profile/Profile Sourcein*.objects.generated.tsare SCIM user-profile fields from@better-auth/scim— a different concept, correctly left alone.Refs: #16228 ·
packages/spec/src/ui/page.zod.ts·packages/spec/src/ui/page.form.ts·docs/adr/0090-permission-model-v2-concept-convergence.mdGenerated by Claude Code