Skip to content

feat(plugin-security)!: no seeder or materializer writes a sys_permission_set row; the boot seeds no row for a declared set (#15204 stage 6b-2) - #22900

Merged
objectstack-fleet[bot] merged 28 commits into
mainfrom
claude/issue-15204-s6b2-declared-permissions
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 28 commits into
mainfrom
claude/issue-15204-s6b2-declared-permissions

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #15204
Clause-②: yes (narrowing)

Stage 6b-2: no seeder or materializer writes a sys_permission_set row, and the boot seeds no row for a declared set. The declared-permission seeder, the platform-default materialization, the permission publish materializer, the metadata:reloaded re-seed and the organization-creation hook are deleted, with drift, the seed refusal diagnostics and the managed_by normalizer ("only delete, never fix", batch #310 item 4). An uninstall still revokes every grant of the package's sets, now by name (option 1, the maintainer's ruling 6123311533; mechanism B, the seat's choice under option 1). Until stage 7 (7a, #22975) retires the ADR-0094 projector, a permission-set publish still has it write a transitional admin projection row (see "The ADR-0094 projector until stage 7" below). This PR leaves #15204 open: stages 7a, 7b, 8, 8-post, C7b and 6c remain.

Landing — draft until the seat marks it ready

What changes

Surface Before Now
bootstrap-declared-permissions.ts (522 lines) seeded every declared set as a managed_by:'package' row, once per organization under a wall deleted
bootstrap-platform-admin.ts seeded the platform defaults insert-once; resync reconciled them writes no catalog row. It reads the legacy admin_full_access row only for leg C (a grant written before the name column; restrict-only). A refused read withholds the promotion and says so once. No resync option, no seeded / resynced / resyncSkipped
permission publish materializer (SP) upserted a package-door publish into a row; ran the suggestion reconcile and everyone's re-derivation only when it materialized and the body carried isDefault deleted. The reconcile and the re-derivation hang off onMetadataMutation: every landed permission publish, direct save or delete, unconditionally. Drafts and other types do nothing (closes U2 contract flag A, 6109964380). The ADR-0094 projector still writes an admin projection row on the publish until stage 7 (next section)
metadata:reloaded re-seed, organization-creation hook (SP) re-seeded rows after a hot install and per new organization deleted
permission-set-drift.ts, normalize-managed-by.ts, seed-refusal-diagnostics.ts boot diagnostics over the seeded rows deleted; their public exports go (listed in the changeset)
warnOrganizationLessRows, catalogIsPerOrganization, listSeedOrganizationIds (organization-scope.ts, internal) the per-organization sweep and the organization-less row guard; the seeder was their one caller deleted (re-measured: no live caller, no test)
sys_permission_set.drift_status / drift_detail and the Needs Attention view written by drift removed with their writer (translations regenerated). The packaged-overlay report still names a shadowed set at boot
readDeclared exported by the seeder re-homed into declared-capability-context.ts (its two readers: the capability context and the managed-object write denies). It is a read of the registry, the catalog's one home, never of a row
Uninstall (security.package-permissions, both doors) found a package's grants through its managed_by:'package' rows see the next section

The ADR-0094 projector until stage 7 (measured)

With the permission materializer gone, the awaited ADR-0094 projector (projectPermissionMutation, retired in stage 7 / 7a #22975) is the one writer left on a publish. Measured on the CRM stack, fresh database, this head against main:

this PR main
package-door publish (draft, then publishPackageDrafts) 1 row: managed_by:'admin', no package_id 1 row: managed_by:'package', package_id
writable base's direct saveMetaItem publish 1 row: admin, no package_id the same (pre-existing)
admin data-door PATCH accepted; written through into the base's own sys_metadata row (no environment overlay); the catalog serves the edit the same
admin data-door DELETE deletes the row, the base's sys_metadata row and the served set answers success:false; the row stays, the sys_metadata row goes
deletePackage the row is removed (the projector's delete leg) the row is removed
boot with a base-published set and no row (file-backed DB, re-boot) the boot reconcile re-creates one admin row not measured

No authorization path reads the row (the resolver reads set bodies from the catalog). The one change from main is the provenance of a package-door publish's row (package → admin) and what a data-door delete of it does. Stage-plan rule: permission-set-projection.ts retires in 7a, so it is not fixed here ("only delete, never fix"). The seat carries "after 7a, a package-door publish writes no row" on 7a's card. The alternative, a projector guard that creates no row for a package-held name, is offered to the maintainer as (A).

Uninstall: the grants naming the package's sets (ADR-0090 D5)

With no row standing for a package's set, the cleanup found no grant to revoke: a grant naming the set survived the uninstall, inert while nothing served the name, and live again on reinstall. Option 1 (the maintainer's ruling on #15204, 6123311533): revoke by the package's catalog set names, read before the package is withdrawn. Mechanism B (the seat's choice under option 1): capture before the withdrawal and hand the names to the cleanups; the lifecycle order is untouched.

  • One capture, both doors, the same point. captureUninstallDeclared(registry, packageId, stored?) (@objectstack/metadata-protocol) runs just before registry.uninstallPackage, in deletePackage and in install-local's DELETE /api/v1/marketplace/install-local/:id. The withdrawal still precedes the cleanups ([finding] After an install-local uninstall, a later hot install of ANOTHER package re-projects the uninstalled package's permission set, which survives the restart as an orphan package-managed row #21576 / [finding] An install-local uninstall (DELETE /api/v1/marketplace/install-local/:id) leaves the package's permission sets in sys_permission_set: the "no ghost grants" uninstall cleanup never runs on that door #21490 race guard unchanged); the capture is synchronous, so install-local still has no await between the ledger removal and the withdrawal.
  • Two sources, unioned:
    • the registry: SchemaRegistry.securityCatalogNamesHeldBy(packageId, 'permission') (new; the package's claims and <packageId>:<name> item slots, unfiltered by disable, since a disabled package still holds its names). This names an installed (artifact / install-local) package's sets.
    • on deletePackage, the permission rows of the package's sys_metadata that the door already read before any delete. A base-authored item is held in the registry's bare slot with no _packageId (provenance org) (measured on the CRM stack: securityCatalogNamesHeldBy(base) answers [], while sys_metadata holds { type: 'permission', name }), so the registry alone cannot attribute it to the base.
  • The hand-off is additive and optional: UninstallCleanup's args gain declared?: { permission?: string[] }, passed by runUninstallCleanups only when the capture named a set. A package that declared none hands its cleanups exactly the request they had before. PACKAGE_JOBS_UNINSTALL_CLEANUP is unchanged.
  • The cleanup revokes every sys_user_permission_set grant naming a captured set. A name has one holder (the registry refuses a second at install), so each such grant was a grant of this package's set.
  • The legacy row path stays: an already-seeded database keeps its managed_by:'package' rows until C7b (this PR's changeset says so). Grants written before the name column and sys_position_permission_set bindings (which carry only permission_set_id, no name column) are still reached through such a row.
  • Not touched: a position declared in the catalog that lists the uninstalled package's set is metadata an author wrote, not a grant row; it stays as authored.

Pins

  • builtin-positions.boot.test.ts (real plugin boot, real SQLite, every scenario): zero sys_permission_set rows after boot, with a positive census control. The first platform administrator (single) and the organization administrator are still granted. The grant golden is unchanged, with no row at all.
  • grant-permission-set-name.writers.test.ts: the promotion leaves zero rows and the holder reads by name. The 6b-2-pre pins stay green.
  • everyone-rederivation-on-permission-mutation.test.ts: one listener even across a bootstrap re-run. Publish and delete of a permission re-derive; a draft or another type does not.
  • showcase-permission-seeding.dogfood.test.ts (the declarative-permission-seeding authz row, rewritten): the showcase's sets are served by the catalog with their package, and the boot writes no row.
  • bootstrap-platform-admin-refused-existence-read.test.ts: a refused legacy read withholds the promotion and writes nothing.
  • Uninstall, each source with its own ablation:
    • registry-held source (install-local door). marketplace-install-local-uninstall-withdrawal.test.ts: the order is capture → withdraw → cleanups, and the cleanups get the names; control: a registry that cannot say hands nothing. End to end, package-install-local-uninstall-cleanups.integration.test.ts (CLI, four orders of events, rewritten to the catalog): grant by name → DELETE → the grant is gone → after a restart, another package's hot install, and a reinstall of the same package, the grant is not live again ("the grant revoked by the DELETE does not come back with the set"). Ablation: the capture answering nothing turned the door pin red (1 of 8); the controls stayed green.
    • base-authored source (deletePackage door). package-uninstall-revokes-named-grants.dogfood.test.ts (CRM stack): a writable base publishes a set, a user is granted it by name, deletePackage → the grant is gone; the base publishes the set again → it is served, and the grant does not come back. Ablation: dropping the capture in deletePackage turned 2 of 3 red. Before the stored-row source existed, this pin was red with the registry-only capture.
    • protocol.uninstall-declared-capture.test.ts: the helper (both sources, the union, nothing when nothing names a set), the runner's hand-off, and deletePackage's order. protocol.uninstall-cleanups-runner.test.ts's control (a request with no capture is passed through unchanged) stays green.
    • registry-security-catalog-names-held-by.test.ts: the names a package holds, a disabled package included, none after uninstall.
    • cleanup-package-permissions.test.ts: the named grants go, with a no-names control in which they survive.

Tests this branch rewrites (they pinned the rows it removes)

Also in this PR

  • Liveness ledger. permission.packageId re-points to its surviving reader (the CLI's compile-time collision door). permission.managedBy is corrected to dead (see Acceptance notes).
  • Tenant-audit census regenerated after merging main: main has 227 write sites, and this branch's removal of the normalizer's and the platform-admin bootstrap's update sites takes it to 225. The hand-written figures on tenant-audit-census.mdx follow.
  • Docs: permission-sets.mdx (built-in sets, provenance, the drift section reduced to overlay shadow) and authorization.mdx.
  • Gate ledgers: the double-contract ledgers drop the retired suites and pin the new one.

Changeset

One file, .changeset/15204-s6b2-declared-permission-seeders.md:

  • @objectstack/plugin-security minor, BREAKING, Clause-②: yes (narrowing), ADR-0087 not-required (no-migration-prescription). The level is unchanged by the uninstall work: nothing an author writes moves.
  • @objectstack/metadata-protocol minor and @objectstack/objectql minor, additive: the optional declared argument of the exported UninstallCleanup type and of runUninstallCleanups' request, the UninstallDeclared type, captureUninstallDeclared, and SchemaRegistry.securityCatalogNamesHeldBy. @objectstack/cloud-connection patch (its door hands the names on). cleanupPackagePermissions takes an optional fourth argument.
  • check:changeset-no-major and check:adr-0087-registration exit 0.

Verification (local, at this head)

At ecfb54f8b8, main df7a137923 merged in (c60e08810). The merge had two conflicts: install-local's imports, where both sides' imports were kept, and the declaration-copy test's persisted-row case, which #22994 had rewritten and which keeps this branch's "no row" side.

  • Build: turbo run build, every task green. Typecheck green for the 21 packages below and dogfood.
  • Suites (turbo run test, 21 packages: plugin-security, platform-objects, runtime, metadata-protocol, cli (unit and integration), objectql, cloud-connection, plugin-auth, organizations, plugin-approvals, plugin-sharing, mcp, rest, hono, client, http-conformance, core, example-crm, example-showcase, example-todo, example-multi-package): 82 of 82 tasks green. plugin-auth: 136 files, 2,780 passed. cloud-connection: 43 files, 519 passed (from source; see below).
  • Dogfood, in full: 258 files passed, 1 skipped; 2,117 tests passed, 9 skipped (the deletePackage door pin, the grant-name pickers and main's walled-catalog suites included).
  • The rewritten suites (before this merge, on a fresh full build): (a) runtime 13/13; (b) cli boot-steps 7/7; (c) cli uninstall-cleanups 17/17. All three are inside the suite run above.
  • check-i18n-bundles: green after the full build.
  • dispatch-gates --commands: 117 derived families, all run and reconciled by --ran, 0 not measured.
    • check:test-source-alias went red on this diff earlier (cloud-connection's new runtime import of @objectstack/metadata-protocol). It is now aliased to source and green.
    • check-engine-split-ratio needs history covering its 90-day window; the clone was deepened to it, exit 0.
    • The one non-zero: check:platform-checklist, the same 8 absent-symbol anchors on T1 (which touches no checklist file). That check is the scheduled watchdog's (checklist-status.yml), not a per-PR gate, and main's checklist cites none of T1's deleted files.
  • check:changeset-no-major and check:adr-0087-registration: exit 0. check-tenant-audit-census: OK, 225 sites.

After ecfb54f8b8, three text rounds from the contract reviews: 5cc2ef8629, 70a42bf2f5 and 320ce20b74. They change docs prose, code comments, the changeset's wording, two sys_permission_set field descriptions (with the English translation bundle regenerated) and two install-local warn strings; no code path. At 320ce20b74:

  • plugin-security: 191 files, 3,973 passed;
  • cloud-connection: 44 files, 538 passed;
  • the protocol capture pins: 10/10;
  • check-i18n-bundles, the docs checks, check:changeset-no-major and check:adr-0087-registration: all 0;
  • dispatch-gates: 119/119 run, the only non-zero being the checklist watchdog.

Acceptance notes

  • Data-door reach on a package-door-published set (a writable base, package installed; measured on the CRM stack, this PR vs main):

    • a data-door PATCH is 200 on both and lands in the base's own sys_metadata row, with no environment overlay;
    • a data-door DELETE deletes end to end here (the row, the sys_metadata row and the catalog entry), while main half-applies it (success:false; sys_metadata gone, the row and the served body kept);
    • a grant naming the deleted set stays and goes dead, and is live again if the base republishes that name, as it would be on main after a reload;
    • under single the wall is inert, so the door is open to any caller with CRUD on sys_permission_set (the platform operator in practice); under group / isolated the wall refuses everyone but the operator (ADR-0131 D3);
    • the provenance key: the row's managed_by:'package' is what the projector's delete leg, the data-layer gate's transfer / restore / purge refusals and the uninstall cleanup's legacy row path key on, and a publish no longer stamps it here. The packaged-set lock (packaged-permission-set-lock.ts) keys on the artifact, not the row, and answers the same on both trees (delta review 6130075012 ①8);
    • carrier: 7a retires the projector and both write-throughs (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, seat note 6129253247).
  • Stale text left to its carriers: packages/spec/src/security/permission.zod.ts:845 (managedBy's JSDoc, "seeded by bootstrapDeclaredPermissions, re-seeded on upgrade"): the spec seat, with the managedBy enforce-or-remove item. seed-name-lookup.ts:9 and packages/objectql/src/engine-capability-provenance.test.ts:164–165 (comments): C7b.

  • permission.managedBy is an authored key with no reader (pre-existing; the ledger now says dead). Retiring it is an enforce-or-remove item for the spec seat. It is not done here.

  • The name-fold census instrument (scripts/measure-position-name-fold-census.mjs, not a CI gate) refuses to report since 6b-2a deleted the binders. Carrier: whoever next runs it re-aims its junction scan at PositionSchema.permissionSets.

  • Two dogfood suites (two-doors-permission, showcase-permission-projection) keep their data-door cases on an existing database (the fixture writes the legacy row), because the gate still guards such rows until 7a / stage 8.

  • plugin-auth's two grant-reader suites read the legacy platform-bucket rows; their fixture is T2's item (d) (test(dogfood): fixtures read permission sets from the security catalog (#15204 stage 6b-2, T2) #22898, a38b54f6f), carried here byte-identical (8e40fb06b) as this branch carries T2's other bytes, so it cancels out of the measured size.

维护者速读(草稿)

改了什么:不再有任何播种器或物化器向 sys_permission_set 表写行,启动时也不再为声明的权限集播种行(在阶段 7 删除 ADR-0094 投影器之前,发布权限集时投影器仍会写一条 admin 投影行,鉴权不读它,卸载包时会被删掉)。删除了:包声明权限集的播种器、平台默认权限集的物化、包发布时的物化器、热安装后的重播种、新建组织时的播种,以及基于这些行的漂移诊断、managed_by 归一化和播种拒绝诊断。drift_status/drift_detail 两个字段及「需要关注」视图随写入者一并删除。卸载包时仍会撤销所有指向该包权限集的授权:两个卸载入口在把包从注册表撤下之前,先记下该包持有的权限集名称(注册表中的名称,加上 deletePackage 已读到的该包已存储的权限集行),交给清理步骤按名称撤销授权(方案 1 是你的裁定 6123311533;机制 B 是席位在方案 1 之下的选择)。

为什么改:ADR-0131 D3 规定权限集只有一个家——注册表。鉴权解析器早已从注册表读定义,授权按名称写入,这些行已不再决定任何权限。卸载按名称撤销,是为了守住 ADR-0090 D5「不留幽灵授权」:否则授权会在卸载后残留,并在重新安装后重新生效。

风险与代价(含回滚):已有数据库保留旧行(直到 C7b),旧行路径的撤销照旧。删除了若干公开导出和 bootstrapPlatformAdmin 的 resync 选项,changeset 已逐项列出;新增的卸载交接字段是可选的,不读它的清理步骤行为不变。本 PR 单独计 3,601 行(超过 3,000),因此为 Tier H。回滚:直接 revert,无数据迁移。

席位意见:

你要做的:T1、T2、本 PR 依次合并;本 PR 因体量为 Tier H,需要你批准。


🤖 Generated with Claude Code

https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49

claude added 14 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 <[email protected]>
…the legacy platform-bucket rows themselves (#15204 stage 6b-2)

Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
Co-authored-by: Claude <[email protected]>
…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 <[email protected]>
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/cli, @objectstack/cloud-connection, @objectstack/metadata-protocol, @objectstack/objectql, @objectstack/plugin-security, @objectstack/spec, touching 118 documentable anchor(s). ⚠️ 10 changed file(s) yielded no anchor (packages/cli/src/utils/permission-set-name-collisions.ts, packages/cloud-connection/vitest.config.ts, packages/metadata-protocol/src/index.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

54 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json c9b5b24b0f0457b19132dd2f9870230e42cbd9a5.

⛔ 16 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 10 changed file(s) yielded no anchor (packages/cli/src/utils/permission-set-name-collisions.ts, packages/cloud-connection/vitest.config.ts, packages/metadata-protocol/src/index.ts, …) — pages documenting those are invisible to this run
  • 10 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 152 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c9b5b24b0f0457b19132dd2f9870230e42cbd9a5 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 467b21d24a8b3b0ea8922d2a85edc01884398bfc — the merge of head 4da59484413e6f21c1ef1248f099bafb609b0e7e into base c9b5b24b0f0457b19132dd2f9870230e42cbd9a5, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 467b21d24a8b3b0ea8922d2a85edc01884398bfc && git checkout 467b21d24a8b3b0ea8922d2a85edc01884398bfc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin c9b5b24b0f0457b19132dd2f9870230e42cbd9a5 4da59484413e6f21c1ef1248f099bafb609b0e7e && git checkout -B drift-repro c9b5b24b0f0457b19132dd2f9870230e42cbd9a5 && git merge --no-ff 4da59484413e6f21c1ef1248f099bafb609b0e7e

node scripts/docs-audit/affected-docs.mjs --json c9b5b24b0f0457b19132dd2f9870230e42cbd9a5

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs c9b5b24b0f0457b19132dd2f9870230e42cbd9a5 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

… 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 <[email protected]>
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 <[email protected]>
Claude-Session: https://claude.ai/code/session_013XKtmoR5QCUSTMjiQ3os49
@os-zhuang
os-zhuang marked this pull request as ready for review October 12, 2026 21:36
@os-zhuang
os-zhuang enabled auto-merge October 12, 2026 21:36
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 12, 2026
@os-zhuang
os-zhuang removed this pull request from the merge queue due to a manual request Oct 12, 2026
…b2-declared-permissions

# Conflicts:
#	scripts/engine-double-contract.pinned.json
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat note: D re-merged after T1 and T2, checked on the tree, and queued on green · epic PM session_01Rerax7QTjKMPCUZxQUtPFR (marchtian) · 2026-10-13T01:20Z

Landing authority:

The re-merge, checked on the tree:

  • 4da5948441 is a merge commit of origin/main 07bc34ac05 (which carries T1 and T2) into 320ce20b74. No rebase, amend or force push.
  • Unchanged files: 49 of D's 52 files are blob-identical to 320ce20b74, the accepted and approved head.
  • The other three: marketplace-install-local-plugin.ts, metadata-protocol/src/protocol.ts and objectql/src/registry.ts. They are the only D files main moved since D's base, and each auto-merged.
    • D's own change to each is intact: the same added and removed lines as at 320ce20b74 (34, 75 and 22), compared as sorted line sets.
    • None of main's hunks in them names a symbol D removes or sys_permission_set.
  • The one conflict: scripts/engine-double-contract.pinned.json, regenerated with check-engine-double-contract --write. D's net change over main is its own 10 lines, the two pins of everyone-rederivation-on-permission-mutation.test.ts, as before.
  • Removed exports: the tree has no new consumer of any export D removes. The remaining hits are comments and three translated help strings, identical to 320ce20b74 and covered by 6130075012 (a).
  • Size: D against main is 52 files, +1,001 / −2,696 = 3,697 changed lines, all D's own now that T1 and T2 are on main. No governed path is touched (check-governed-merges: 0 of 52), so it is Tier H by size alone, and the approval above covers it.
  • Against main's tip (c9b5b24b0f): git merge-tree is clean, and nothing that landed after 07bc34ac05 touches D's files.

The developer's local runs at 4da5948441:

  • install, build and typecheck of the 7 touched packages: 0;
  • six of those packages' suites: 0, and D's two dogfood files: 6/6;
  • check-engine-double-contract, check:objectql-double-limit, i18n and census: 0;
  • dispatch-gates: 119/119 ran. The only non-zero is check:platform-checklist, the watchdog, on the same 8 anchors that are absent on main.

CI on 4da5948441: running at the time of this note. Auto-merge enqueues only on green.

Then:

Noted, not changed: packages/cloud-connection/vitest.config.ts carries two identical aliases for @objectstack/metadata-protocol, one from main and one from D. It is harmless and already in the reviewed bytes. A follow-up can drop one.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 13, 2026 01:21
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit c8a9783 Oct 13, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-15204-s6b2-declared-permissions branch October 13, 2026 02:19
os-steve pushed a commit that referenced this pull request Oct 13, 2026
…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 <[email protected]>
os-steve pushed a commit that referenced this pull request Oct 13, 2026
…m the security catalog

Since D (#22900) no boot writes a sys_permission_set row for a set declared
through defaultPermissionSets (ADR-0131 D3), so this suite's grant helper,
which asserted the seeded row, failed on main's full battery. It now asserts
the set the security catalog serves by name (catalogPermissionSet, the
fixture helper T2 added); the grant row it writes is unchanged.

Claude-Session: https://claude.ai/code/session_01ADzJtzYTLUfgrRZHxkagkX
Co-Authored-By: Claude <[email protected]>
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 size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants