Skip to content

feat(plugin-security)!: retire the four security-catalog row objects (#15204 stage 8) - #23222

Merged
os-zhuang merged 53 commits into
mainfrom
claude/issue-15204-s8-catalog-objects
Oct 13, 2026
Merged

os-zhuang merged 53 commits into
mainfrom
claude/issue-15204-s8-catalog-objects

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Part of #15204
Clause-②: no (narrowing): it retires four declared objects with their data and REST routes, Setup object pages and exports. It adds no export and widens no accept set; the one widening of the early build (a new metadata-protocol export) is D's landed captureUninstallDeclared now.

C3 stage 8, the code half (S8), card #23056. It lands after S8-T (#22962, the test half) and D (#22900). #15204 stays open: 7a/7b (dead modules, #22975), 8-post (residue), C7b (#22937 / #15211, the id-to-name rewrite and the column drop) and the docs sweep follow.

What this changes

The four security-catalog row objects (sys_position, sys_permission_set, sys_position_permission_set, sys_capability) are no longer registered (ADR-0131 D3). Their data and REST routes go, and so do their Setup object pages. Tables and rows stay for the organization-ownership migration (ADR-0131 D10).

  • E1 (ruling 6110777865, E1 → A): the permission mutation projector and its reconcile, the sys_permission_set and sys_position data-door write-throughs and the row-only position backfill are no longer registered. A metadata-door save reaches the catalog through the door's own registry write-through. S8-T's permission-metadata-door-reaches-catalog.dogfood.test.ts pins this. The modules stay for 7a/7b to delete.
  • The walled catalog data-door refusal (ADR-0131 D3) is unregistered with the write-throughs. It landed after this stage was first built and guards only the three retired objects.
    • On a group or isolated deployment, a data-door write on those names now answers 404 OBJECT_NOT_FOUND (protocol.assertObjectRegistered) for every caller. S8-T's wall-catalog-refusal dogfood pins this.
    • The walled refusal at /meta, at the clone door and at the /packages doors is unchanged.
    • walled-catalog-data-door.test.ts pinned the registration and is deleted with it. The module walled-catalog-data-door.ts stays, unreferenced, for 7a/7b.
  • E3 (E3 → A): sys_user_permission_set.permission_set_id is now Field.text (maxLength 255) and keeps B2's hidden. The column, its ids and both indexes stay the same.
  • E6 (seat amendment 6116546399): the first-admin promotion reads no catalog row. An unscoped grant that names no set and carries a legacy id still withholds the promotion. Both pins hold: S8-T's fresh-boot golden and grant-readers-unnamed-grant.
  • Uninstall cleanup, on D's landed form: cleanupPackagePermissions keeps D's signature (declared: { permission }, captured by captureUninstallDeclared in both uninstall doors) and drops the row legs: the catalog-row read, the position-binding delete and the by-id grant leg. It revokes by the declared names alone. The by-id leg is C7b's.
  • Carried items:
    • Setup nav entries open /apps/setup/metadata/TYPE?scope=environment.
    • These drop the four names: the grants-cache watched set, plugin-sharing bypassObjects, the organization_admin and showcase object-permission entries, SystemObjectName.POSITION / PERMISSION_SET, the platform-object name lists and the tenancy entry.
    • The lint capability-reference-unknown seed-row source goes.
    • Refusal prose names the clone door instead of the sys_permission_set data route.
    • Settings and spec help text are updated, and isRowActive is deleted.
    • The published plugin-security README stops naming the retired objects.
  • Regenerated:
    • plugin-security translations and the platform-objects form bundles;
    • spec reference docs;
    • the tenancy and tenant-audit censuses;
    • the keyed-text-bounds MEASURED record.
    • The platform-checklist item access-security.packaged-permission-set-lifecycle is retired.
    • The ADR-0087 semantic entry is catalog-row-objects-retired.
  • Kept true by this retirement:
    • ADR-0086's D3 status row anchored to the deleted sys-permission-set.object.ts#managedBy. It now says the row object is retired and anchors to 18.catalog-row-objects-retired.ts; check-adr-symbol-anchors would otherwise red. That one line puts docs/adr/** in this diff.
    • The ADR-0087 entry 18.walled-catalog-tenant-writes-retired no longer promises a data-door 403 on the three retired objects: its surface and acceptanceCriteria say the refusal held while the objects were registered, and that the data door now answers 404.
    • The changeset names @objectstack/objectql's PLATFORM_OBJECT_TENANCY losing its sys_permission_set key (objectql is graded minor), and os migrate plan listing the four tables as unmanaged on an existing database (report-only).
    • scripts/check-keyed-text-bounds.mjs's MEASURED record and header name the tree its five counts hold on.
  • Test re-pins for suites that reached main after S8-T (each still pins its own predicate):

Reconciliation with D (#22900, c8a9783eca)

This branch was first built on an early D (fcaedfdaa1). The main merge resolves it as main plus this stage's own commit (dbae752f6e against its first parent), replayed three-way, so no line of the early D that the landed D dropped survives.

  • D's captureUninstallDeclared / UninstallDeclared { permission } supersedes this stage's earlier declaredCatalogItemsOf / UninstalledCatalogItem. As a result this diff does not touch packages/metadata-protocol/src/protocol.ts or its index, nor cloud-connection's install-local door or vitest config.
  • The cleanup registration in security-plugin.ts is D's. The resolveCbpRelation region is untouched.

Verification

Branch head 23e450a684: main 6d7f4df3c9 plus this diff, 75 files, +456 / −3352. All heavy runs went through os-verify-lock; times are shared-box readings.

  • Whole repo: turbo run build (74 of 74), and turbo run typecheck (147 of 147, on 016d329a36). plugin-security and cli typecheck are green after the merge of main that brought fix(plugin-approvals,plugin-security): the approval request actions name the request in their success copy, and Explain access names the principal as a person #23202's suite. pnpm --filter @objectstack/spec check:generated reports all 14 artifacts current. The tenant-audit census (regenerated on the merged tree), the platform-object tenancy census and check-i18n-bundles are in sync.
  • Dogfood, full, on b22a9ee6bf (main 4f5679c261 plus this diff; the later main merge touches no package this diff does): 254 files plus 1 skipped, 2099 tests. CI skips the dogfood shards on PRs.
  • Suites:
    • on 976023f0da:
      • plugin-security: 187 files, 3851 tests;
      • plugin-auth: 136 files, 2786 tests;
      • the two re-pinned cli integration files: 17 tests, with cli typecheck green;
    • on a29bede970, @objectstack/cli whole (unit and integration, as CI runs it): 403 of 404 files. The one red was schema-migrate.host-composition.integration.test.ts, re-pinned since;
    • on 016d329a36:
      • spec: 662 files, 20114 tests;
      • lint: 138 files, 6665 tests;
      • metadata-protocol: 233 files, 28284 tests;
    • on earlier heads: the consumer suites (core, platform-objects, plugin-sharing, service-settings, objectql, rest, runtime, plugin-approvals, plugin-hono-server, cloud-connection, verify, client, organizations, app-crm, app-showcase).
  • Premise (6110777865): a metadata-door permission save reaches the catalog with the projector unwired. permission-metadata-door-reaches-catalog.dogfood.test.ts is green in the full dogfood run above, and no live caller of the projector, either write-through, the backfill or the walled door remains.
  • Fresh boot: an empty sqlite file booted through the migrate path with real DDL, composing SecurityPlugin, holds 36 tables and none of the four retired ones.
  • Gates: the list derived three-dot against origin/main (150 families), unioned with the dispatch's lead list, ran on the final head. dispatch-gates --ran reconciles every derived family, and every gate exits 0 except check:platform-checklist.
    • That gate's 8 ABSENT SYMBOL / anchor-floor findings are identical on main 150da61aa5 (line-for-line comparison), and the gate is kept out of per-PR CI.
    • check-adr-symbol-anchors, check:adr-anchors, check:adr-0087-registration, check:spec-changes, check:upgrade-guide, check:keyed-text-bounds and check:pm-governed-merges are green.
    • check-governed-merges --test: GOVERNED, docs/adr/** ×1, and over 3000 changed lines.
  • Not run locally: pnpm lint (the repo-wide scan is CI's).

Acceptance notes

  • Out of scope here, and carried elsewhere:
    • 7a/7b (C3 #15204 stages 7a ∥ 7b → 8-post, build ahead: delete the permission-set projection and the position write-through that stage 8 leaves unreferenced, then the post-stage-8 catalog gates and residue #22975): the unwired permission-set-projection.ts, position-write-through.ts, position-environment-backfill.ts, walled-catalog-data-door.ts (and its ADR anchor) and the writesExecutedByDataDoor WeakSet.
      • Two of these modules are still imported for helpers. catalog-reference-report.ts and position-environment-backfill.ts take POSITION_OBJECT and metadataDoorAcceptsPositionName from position-write-through.ts; packaged-permission-set-overlay-detection.ts takes tryFind and ProjectionLogger from permission-set-projection.ts.
      • plugin-security's index.ts still exports the projector functions.
      • 7a/7b must re-home those four symbols before deleting the modules.
    • 8-post: comment and description residue, the per-object gates in security-plugin.ts and delegated-admin-gate.ts, and SYSTEM_ROW_PROVENANCE.
    • C7b: the CLI organization-ownership inventory, auto-org-admin-grant resolvePermissionSetIdsForName, the grant-name backfill (now a permanent objects-absent no-op), the by-id uninstall leg, and the blank-name leg-C edge of the first-admin promotion (not measured).
    • The docs sweep: the hand-written content/docs pages and ADRs that name the objects, including system-context.mdx row 55b's data-door clause.
  • 18.0.0-next.0's CHANGELOG entry for the walled refusal names a data-door 403 on the three objects. That is true of next.0, and this diff's changeset records the move to 404 for the next release; the unreleased ADR-0087 entry is corrected in this diff.
  • Governed path: the ADR-0086 anchor line puts docs/adr/** in this diff, so the PR is Tier H by path as well as by size (check-governed-merges --test: GOVERNED, docs/adr/** ×1). The line is here because the anchor names a file this diff deletes, and check-adr-symbol-anchors reds without it.
  • lint/src/validate-capability-references.seed-row.test.ts pins new behaviour that cannot pass on main before this lands, so it rides here rather than in S8-T.
  • The objectui pin 1fb1ced362 has the metadata/:type catalog route with ?scope=environment. It imports none of the removed exports (SystemObjectName.POSITION / PERMISSION_SET, SysPositionDetailPage, isRowActive); its sys_permission_set string checks become dead branches, not build breaks.

维护者速读(草稿)

改了什么

  • 岗位、权限集、权限集-岗位绑定、能力这四张“目录行对象”不再注册。对应的数据接口、REST 路由和 Setup 里的对象页面随之下线。数据表和行都保留,留给之后的组织归属迁移处理。
  • 权限集保存后不再写行镜像,由元数据门直接写入安全目录。两个数据门回写、一次性岗位补数,以及多租户下数据门那一半的目录写入拒绝都已摘除。
  • 首位平台管理员的提升不再读取目录行。
  • 卸载包时只按包声明的权限集名称撤销授权,不再读写目录行。

为什么改

风险与代价(含回滚)

  • 破坏性变更:直接调用 /api/v1/data/sys_permission_set 等四个对象接口的客户端会收到 404。changeset 写明了 FROM → TO 迁移:改用 /api/v1/meta/permission|position|capability 和克隆接口。
  • 多租户隔离部署下,目录写入的拒绝仍在 /meta、克隆门和包门生效。数据门那一半随对象一起消失,统一答 404。
  • 不删表、不删数据。回滚就是 revert 本 PR,无数据迁移需要回退。
  • 受管路径:本 PR 改了 ADR-0086 状态表 D3 行的一处锚点。原锚点指向本 PR 删除的对象文件,不改的话 ADR 锚点检查会红。因此 diff 触及 docs/adr/**,按路径也属 Tier H,不只是按规模。
  • 规模:75 个文件、+456/−3352,删除为主(对象定义、生成的翻译、随门一起删除的一份测试),加上为主线新进测试做的几处改钉。超过 3000 行。

席位意见

你要做的

  • 本 PR 既触及受管路径 docs/adr/**,又超过 3000 行,需要你批准后由席位落地。批准前席位会附上合约复核结论。

Generated by Claude Code

claude added 30 commits October 11, 2026 19:05
…efusal diagnostics and managed_by normalizer (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…log row; os meta resync is retired (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…us retires with its writer (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…ift_status / drift_detail (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…the legacy platform-bucket rows themselves (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…face-only disposition (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…ed seeders (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…new one (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…wn changeset (#15204 stage 6b-2a)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…ermission_set row; existing-database cases write the legacy row themselves (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…places did (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
…r, a command that exists (#15204 stage 6b-2a)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <noreply@anthropic.com>
… seeder was the only caller of (#15204 stage 6b-2)

warnOrganizationLessRows, catalogIsPerOrganization and listSeedOrganizationIds
had one caller, bootstrap-declared-permissions.ts, deleted in this PR. Re-measured:
no live caller and no test. Internal (never exported from the package index).
Also carries T1's buildExistingByName key-set fence byte-for-byte.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
…15204 stage 6b-2)

No assertion changes; keeps the residual under the 3,000-line ruling after the
organization-scope deletion.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
# Conflicts:
#	content/docs/permissions/tenant-audit-census.mdx
#	docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
#	examples/app-showcase/test/inert-wirings.test.ts
#	packages/cli/src/utils/schema-migrate.lazy-driver-import.test.ts
…og row objects (#15204 stage 8, S8-T)

Every touched suite stays green on main and on main + D + S8, where
sys_position, sys_permission_set, sys_position_permission_set and
sys_capability are no longer registered (ADR-0131 D3).

- a testkit holds the retired tables' shapes for suites that model an
  existing database;
- pins of the retired declarations, the unwired data-door write-throughs,
  the projection, the row lock and the junction gates are deleted;
- fixtures author permission sets through the metadata door;
- prose and floor pins span the route move and the retired page;
- new: a fresh-boot promotion golden (E6) and a metadata-door-reaches-
  the-catalog dogfood test (the premise of E1).

Claude-Session: https://claude.ai/code/session_01MGoWgeE8rhsrig77V4oUZn
Co-authored-by: Claude <noreply@anthropic.com>
…15204 stage 8)

sys_position, sys_permission_set, sys_position_permission_set and
sys_capability are no longer registered (ADR-0131 D3): their data and
REST routes and their Setup object pages go; the tables and rows stay
for the organization-ownership migration (D10).

- E1: the permission mutation projector and its reconcile, both
  data-door write-throughs and the row-only position backfill are
  unwired; a metadata-door save reaches the catalog through the door's
  own registry write-through.
- E3: sys_user_permission_set.permission_set_id is Field.text
  (maxLength 255): same column, ids and indexes.
- E6: the first-admin promotion reads no catalog row; an unscoped grant
  that names no set and carries a legacy id still withholds it.
- Uninstall revokes every grant naming a set the package declared; the
  doors read the names before the withdrawal (declared).
- Setup nav opens the registry lists; residue lists, prose, help text,
  the lint seed-row source and isRowActive go; translations, censuses,
  reference docs and the keyed-text-bounds record are regenerated; an
  ADR-0087 semantic entry registers the retirement.

Claude-Session: https://claude.ai/code/session_01MGoWgeE8rhsrig77V4oUZn
Co-authored-by: Claude <noreply@anthropic.com>
…8 test half resolved onto main

Conflicts resolved by the stage-8 report's D-merge recipe:
- S8-T's deletions taken over main's edits: bootstrap-platform-admin-
  refused-existence-read, position-write-through, and the dogfood files
  permission-set-lock-row-provenance, permission-set-write-through-
  package-binding, showcase-permission-projection, two-doors-permission;
  rbac-objects keeps S8-T's removal of the retired declaration pins.
- main's catalogPermissionSet reads kept in activity-text-predicate,
  approval-snapshot-masked-field, sharing-rule-org-less-caller and
  showcase-crud-persona-matrix.
- showcase-permission-zoo takes S8-T's side; showcase-fls-read-mask-strip
  takes S8-T's metadata-door authoring and keeps main's catalog read.
- plugin-security imports keep main's seedLegacyPlatformBucket and point
  the object shapes at the legacy-catalog-objects testkit.
- ensureLegacyPermissionSetRow leaves the dogfood catalog helper: every
  caller is deleted. showcase-permission-seeding's row census runs only
  where sys_permission_set is still registered.
- the runtime seeder-copy suite keeps main's "no row" pin beside S8-T's
  registered-object guard; the record-page label-map floor is restated
  for B2's two dropped maps and the retired position page.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
…s declared names shape

D (#22900) landed the uninstall cleanup's declared names as
`UninstallDeclared` ({ permission: string[] }), not the bare name array the
stage-8 branch was built against. The suite now passes that shape and drops
its signature cast; D's two named-grant pins are restated over S8-T's fixture
so each holds whether the sweep also reaches the package's catalog rows
(main) or reaches its sets by the declared names alone (after C3 stage 8).
The runner suite's comment attributes `declared` to the names D captures.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
… D's final form

The branch carried an early D (fcaedfd) merged by hand; main now carries
D as landed (c8a9783). The resolution is main plus S8's own commit
(dbae752 against its first parent 1d5c2b2), replayed as a three-way
cherry-pick, so no line of the early D that the landed D dropped survives.

- Uninstall cleanup: D's landed capture (captureUninstallDeclared,
  UninstallDeclared { permission }) supersedes S8's declaredCatalogItemsOf /
  UninstalledCatalogItem. S8 no longer edits metadata-protocol's protocol.ts
  or index.ts, nor cloud-connection's install-local door or vitest config.
  cleanupPackagePermissions keeps D's signature and drops the row legs
  (catalog-row read, position bindings, by-id grant leg), as stage 8 rules.
- security-plugin: the walled catalog data-door refusal registered since
  S8 was built (ADR-0131 D3) guards only the retired objects; it is
  unregistered with the write-throughs. The cleanup registration is D's.
- sys_user_permission_set.permission_set_id: Field.text (E3) keeps B2's
  hidden flag.
- Generated files (translations, tenant-audit census, tenancy census,
  reference docs) took main's side here and are regenerated next.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
…he four catalog row objects

Three files that reached main after S8-T's base read the retired objects:
- walled-catalog-data-door.test.ts is deleted: it pins the walled
  catalog refusal's data-door registration on sys_position,
  sys_permission_set and sys_capability, which stage 8 unregisters with
  the write-throughs (the precedent of position-write-through.test.ts);
- wall-catalog-refusal's data-door case answers 403 PERMISSION_DENIED
  while the row objects are registered and 404 OBJECT_NOT_FOUND once
  they are retired, and asserts no definition either way;
- mail-message-pool-read-gate reads its fixture sets from the security
  catalog: no boot has written a sys_permission_set row since D, so the
  row read failed on main as well.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
…e merged tree

The merge took main's side of every generated file; regenerated here from
the merged sources (main + stage 8):

- plugin-security en object translations (check-i18n-bundles --write; the
  other locales and every other bundle were already in sync),
- the spec reference page for permission (gen:docs),
- the platform-object tenancy census (four retired objects leave it),
- the tenant-audit census page and its counts ledger.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
…h: the stage-8 test half under the code half

S8-T is draft PR #22962 and carries a merge of main at f931e76, after D
(c8a9783). Clean merge, no file in common with stage 8's own diff; every
one of S8-T's 93 files here is byte-identical to its head. Until S8-T lands
on main this branch's diff also carries its hunks.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
…catalog objects

The README ships in the package (files[]), and stage 8 makes three of its
sentences false: sets resolve against SysPermissionSet metadata, SysPosition
and SysPermissionSet are exported objects, and sys_position /
sys_permission_set are system objects the plugin registers. It now names
the security catalog as the home of positions, permission sets and
capabilities (ADR-0131 D3), and lists the three objects the plugin still
registers.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
…door behaviour on D's landed form

- metadata-protocol is a patch (refusal prose only): the uninstall capture
  is D's captureUninstallDeclared, so stage 8 exports nothing new there,
  and cloud-connection is no longer touched.
- The uninstall bullet says what stage 8 changes on top of D: the cleanup
  revokes by the declared names alone and reads or removes no catalog row.
- The walled catalog refusal's data-door half goes with the objects: a
  data-door write on the three retired names answers 404 OBJECT_NOT_FOUND
  (protocol.assertObjectRegistered) for every caller.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
On D's landed form stage 8 adds no export and widens no accept set: the
one widening the early build carried, the declaredCatalogItemsOf export,
is D's captureUninstallDeclared now. What remains narrows (routes, object
pages, exports, the walled data door's admitted operator writes, the lint
seed-row source), so the value is no and the arm is narrowing.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
Measured on a local main + S8-T + S8 tree:
- plugin-auth's two grant-reader suites seed the legacy platform-bucket
  rows only where sys_permission_set is still registered;
- builtin-positions expects the walled tenant create refusal while the
  plugin declares the sys_position row object, which stage 8 retires
  together with that refusal;
- the record-page label-map floor is 38, the count with B2's two maps and
  the retired position page gone.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 · PR #23222(S8,C3 第 8 阶段的代码半边)· domain:engine 2 号席 · session_01ADzJtzYTLUfgrRZHxkagkX · 2026-10-13T14:09Z

改了什么
把四个已退役的安全目录行对象(sys_position、sys_permission_set、sys_position_permission_set、sys_capability)从平台上取消注册。权限集、职位、能力从此只有一个归宿:安全目录(注册表),通过 /api/v1/meta/permission|position|capability 读写。具体包括:

  • 取消对象注册、删掉对象定义和职位详情页,Setup 里的三个导航改为打开元数据页;
  • 断开权限集投影器、两条数据入口写穿透、职位回填,以及隔离墙下数据入口的拒绝。这些模块只断开接线,代码留给 7a/7b 删除;
  • permission_set_id 改成文本字段,列和数据都保留,留给 C7b 的迁移仪式处理;
  • 改一份 v18 迁移条目,并把 ADR-0086 里一行指向已删文件的锚点改成文字说明。

共 75 个文件,+456 / −3352 行,删除为主。

为什么改
ADR-0131 D3 规定这三类定义只能有一个来源。S8-T(测试半边)已经先落地,测试不再依赖这四个对象。S8 落地后,C7b 的迁移仪式(#23034)、7a/7b 的模块删除,以及文档和 ADR 的清理,才能接着做。

风险与代价(含回滚)

席位意见
建议批准。依据:

  • 契约审查完整一轮 PASS(6145593929),它提出的入队前事项都已改完,增量审查 PASS(6146702396),最终头只多了重新生成的审计统计,席位已核对;ACCEPT 6147447002;
  • CI 在补丁过程中查出的四处红灯(ADR 锚点、main 新进的一个测试、两个 CLI 集成测试)都已修好,并且都只动测试或锚点,没有绕过任何检查;
  • 和 main 无冲突。

要你批准的原因有两个:超过 3000 行;改了 ADR-0086 的一行,碰了受管路径 docs/adr/**。

你要做的
用 os-zhuang 或 hotlong 批准本 PR(或者亲自合并)。批准后,席位转 ready、送进合并队列;落地后立即推进 #23034(C7b 迁移仪式)和 7a/7b。

@os-zhuang
os-zhuang marked this pull request as ready for review October 13, 2026 14:14
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 66949b7 Oct 13, 2026
56 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-15204-s8-catalog-objects branch October 13, 2026 14:44
github-merge-queue Bot pushed a commit that referenced this pull request Oct 13, 2026
…row-only position backfill (C3 stage 7b) (#23302)

Refs #22975
Clause-②: no

C3 (#15204) stage 7b: delete the position write-through and the row-only
position backfill. Stage 8 left both unreferenced, so this is dead-code
deletion. It is the parallel half of 7a; 8-post follows once both have
landed. #22975 stays open.

## What this changes

Stage 8 (#23222) stopped registering the `sys_position` row object, the
`sys_position` data-door write-through and the row-only position
backfill. Their modules stayed in the tree with no caller. This PR
deletes them.

- **Deleted:** `position-write-through.ts` and
`position-environment-backfill.ts`. Measured on `main` before this PR:
no file imports `position-environment-backfill`, and only
`catalog-reference-report.ts` imports `position-write-through` (for
`POSITION_OBJECT` and `metadataDoorAcceptsPositionName`). The backfill
is deleted rather than switched to core's `positionDefinitionFromRow`.
That mapper (#22959) stays in core for the upgrade ceremony; no boot
path calls it.
- **`PositionNameConflictError`** goes with the write-through, its only
thrower (ruling Q4 → A). It was never exported from the package's entry.
- **The catalog reference report** (`catalog-reference-report.ts`) drops
categories 4 and 5:
  - category 4: a position row with no definition;
  - category 5: a position name that several rows carry.
  
It also drops the `posture` input, which only those two categories read.
No composition registers `sys_position` after stage 8, so those
categories could never read a row. Legacy position rows are the upgrade
ceremony's to convert and report (ADR-0131 D10). The report still lists,
per organization:
  - every assignment whose position the catalog does not resolve;
  - every grant whose permission set the catalog does not resolve;
  - every grant that names no permission set.
  
  The report is not exported. Seat answer Q1 → A.
- **Generated, regenerated with the repo's own tools:**
  - row 11b leaves `content/docs/permissions/system-context.mdx`;
- the write-through's `UNTYPED_RECEIVERS` row leaves
`scripts/tenant-audit-census.mjs`;
- the tenant-audit census page and counts are regenerated with `node
scripts/tenant-audit-census.mjs --write`, and the prose figures the gate
names are updated (228 → 225 sites).

No export of `@objectstack/plugin-security`'s entry changes. The
changeset is a `patch`, so no ADR-0087 marker is needed.

## Acceptance notes

- **Overlap with 7a.** 7a edits the same census, system-context and
`security-plugin.ts` regions. Whichever PR lands second merges `main`,
re-runs `node scripts/tenant-audit-census.mjs --write` and updates the
census prose figures.
- **Not here:**
  - the comment sweep and the remaining catalog gates (8-post);
- the catalog reference report's sixth class (8-post, director ruling
#23229 → B).

## Verification

On head `8c7ee7f06a` (`main` `026391d5d8` merged in), unless noted:

- `plugin-security` unit suite: 188/188 files, 3,854 passed (45
skipped). Typecheck: exit 0. eslint on the changed files: exit 0.
- `check:tenant-audit-census` and `check:system-context-census`: exit 0.
- The derived gate sweep (`dispatch-gates --commands`): 114 derived and
114 run, all exit 0; `--ran` reconciles with 0 NOT-MEASURED and 0 UNRUN.
This was measured on `05dee10c89`, the head before the last merge of
`main`, whose five commits touch none of these files.
- The three symbol-anchor sweeps (adr, scripts, spec-docblock): exit 0.
- `check:dual-build-cjs-loads` on a full build (74/74 tasks): exit 0.
- `check:platform-checklist`: exit 1 with 8 reds. The same 8 reds appear
on `main` (they are pre-existing).
- Dogfood: 254 files passed (1 skipped), 2,106 tests passed.
- CLI integration tier (`--project integration`): 119/119 files, 1,036
tests passed.
- objectui pinned sibling: no export of `@objectstack/plugin-security`'s
entry changes, so nothing the console imports moves. CI's Console Pin
Gate covers it.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01287UbjvrigpWwNfGDmdwpS)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants