Repository navigation
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
Copy link
Copy link
Open
Labels
domain:enginepm:dispatchedpriority:p1High: required for production / M2High: required for production / M2target:v18
Description
Part of #15204
Handed out of the #15194 epic subtree to the domain:engine lane. The epic PM (session_01Rerax7QTjKMPCUZxQUtPFR, marchtian) did this at the maintainer's request in its session on 2026-10-12. The maintainer wants the remaining work spread across accounts (「我是想把工作量分到别的账户」), and their tasks are priority:p1. This card is the lane's claim surface. Build ahead now. No PR opens before stage 8 (S8) is on main.
Where this sits
- Landing order (refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204, seat answer 6119027520): feat(plugin-security,platform-objects): grant forms pick a position or permission set by name from the catalog (#15204 B2) #22939 → T1 test(plugin-security): retire the declared-permission seeder suites (#15204 stage 6b-2, T1) #22897 → T2 test(dogfood): fixtures read permission sets from the security catalog (#15204 stage 6b-2, T2) #22898 → D 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 → S8-T test: the stage-8 test half — suites stop depending on the four catalog row objects (#15204 stage 8, S8-T) #22962 → S8 → 7a ∥ 7b → 8-post → C7b (feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211). - S8 is not yet a PR. It lives on branch
claude/issue-15204-s8-catalog-objects@dbae752f6e, which ismain+ Dfcaedfdaa1+ one commit. S8:- unregisters
sys_position,sys_permission_set,sys_position_permission_setandsys_capability; - unwires the projector, both write-throughs and the position backfill (E1).
- unregisters
- After S8,
permission-set-projection.ts,position-write-through.tsand thewritesExecutedByDataDoorWeakSet are unreferenced. See the stage-8 report 6118994290, carrier "7a/7b". - Build on a local integration tree:
main+ D (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 head) + S8-T (test: the stage-8 test half — suites stop depending on the four catalog row objects (#15204 stage 8, S8-T) #22962) + the S8 branch. Use the report's merge recipe (d_merge_recipe_for_s8tin 6118994290). - ⛔ No stacked PRs. Each PR opens from
mainonce S8 has merged.
Scope
Read these first: the stage-8 tier record 6110777865, the rulings it cites, and #15204's body.
7a (about 2.45k lines of code):
- Delete
permission-set-projection.ts(1,763 lines),seed-name-lookup.ts(506) andseed-refusal-sink.ts(83), with their index exports. - Re-home
tryFind, and keep the liveness proof. - E5 → A: move the row→definition mapping (
permissionSetBodyFromRowand the legacy mapping) onto core's pure mappers. Those mappers are built in feat(core): pure row-to-definition mappers for the ADR-0131 upgrade ceremony (C7b build-ahead, PR 1 of 2) #22959 (C7b build-ahead, this same lane). Then delete the copies. - Whole-file test deletions (about 3.3k lines, for example
permission-set-projection.test.tsat 1,884) go in a test PR ahead of it.
7b (about 2.2k):
- Delete
position-write-through.ts. catalog-reference-report.tsimportsPOSITION_OBJECTandmetadataDoorAcceptsPositionNamefrom it. Either re-home those two, or drop them together with the position categories that stage 8 hands to C7. This obligation is recorded in 6095755866.- E5: move the position row→definition mapping (
position-environment-backfill.ts) onto core's mapper. - The Q (a) refusal (
PositionNameConflictError) goes with the module (Q4 → A).
8-post (about 0.75k):
- In
security-plugin.ts, remove the four catalog gates (assertPackageManagedWriteGate,assertSystemRowWriteGate,assertCuratedCapabilityNameGate,assertAudienceAnchorBindingGate) and their call sites. - Remove
SYSTEM_ROW_PROVENANCEand the delegated-admin catalog cases. - Do the comment sweep. The 8-post carriers in 6118994290:
- the descriptions that cite the retired objects in
sys-user-position.object.ts,sys-invitation.object.tsandsys-audience-binding-suggestion.object.ts; - the describes in spec
protocol.zod.ts,eval-user.zod.tsandposition.zod.ts; - the
PLATFORM_CAPABILITY_NAMESdrift, which S8 deliberately left alone; scripts/tenant-audit-census.mjs: its twoUNTYPED_RECEIVERSrows go with the deleted modules.
- the descriptions that cite the retired objects in
Every PR:
- at most 3,000 changed lines, or it is Tier H;
- a
Clause-②line perscripts/pm/clause2-line.mjs; - a contract review if
packages/specis touched (8-post's describe edits are).
Coordination
- The epic seat lands D, S8-T and S8, and posts "S8 on
main" on this card. - The S8 branch is held by the epic seat's stage-8 developer (
session_01MGoWgeE8rhsrig77V4oUZn). Questions about that tree go on this card; the epic seat relays them. - ⛔ Do not touch files that D, S8-T or S8 own until they land.
- Done when 7a, 7b and 8-post are merged. The epic seat then updates refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204's stage list.
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
domain:enginepm:dispatchedpriority:p1High: required for production / M2High: required for production / M2target:v18
Activity
objectstack-fleet commentedon Oct 12, 2026
Claim: PM loop round 1 · 2026-10-12T08:14Z · build-ahead for all three slices (7a with its test PR, 7b, 8-post); no PR opens before S8 is on
mainSession:
session_01ADzJtzYTLUfgrRZHxkagkXAccount:
os-steve(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-22975-c3-7a7b-8post(the build-ahead integration branch). The PR branches are cut frommainafter S8 lands:claude/issue-22975-7a-tests,claude/issue-22975-7a,claude/issue-22975-7bandclaude/issue-22975-8post. The PR that closes the card gets a freshClaim:naming its branch.Worktree:
objectstack-issue-22975(in the cloud session's own checkout)Domain:
domain:engine(handed out by the #15194 epic PM at the maintainer's request: 「我是想把工作量分到别的账户」)Seat:
domain:engine#2(seat post #20966)File surface (read on
origin/main246edf5b39and the card; stop on a breach and explain it in the report). ⛔ Classes, positions and functions only, in every artefact.packages/plugins/plugin-security/src/(adomain:servicespackage):permission-set-projection.ts,seed-name-lookup.ts,seed-refusal-sink.ts,position-write-through.tsandposition-environment-backfill.ts, with their index exports and tests;SYSTEM_ROW_PROVENANCEinsecurity-plugin.ts;catalog-reference-report.tsreads.packages/core/src/security/: read only. It supplies the mappers from PR feat(core): pure row-to-definition mappers for the ADR-0131 upgrade ceremony (C7b build-ahead, PR 1 of 2) #22959 (C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937 PR 1), which is ACCEPTed and not yet onmain. The build-ahead tree carries that branch. ⛔ No edit to those files.packages/platform-objects(sys-user-position,sys-invitation,sys-audience-binding-suggestion);packages/spec(protocol.zod.ts,eval-user.zod.ts,position.zod.ts): describe text only,domain:spec, with a contract review owed;PLATFORM_CAPABILITY_NAMESdrift;UNTYPED_RECEIVERSrows inscripts/tenant-audit-census.mjs.claude/issue-15204-s8-catalog-objects) own until they land. ⛔ No stacked PRs. Each PR is at most 3,000 changed lines.Container & model:
XL(about 5.4k lines of code in three slices plus about 3.3k lines of test deletions),mode:cloud(a one-shotcreate_session),model: default(dispatch-gates --tier: no path-derived mandate)Clause-②: no (narrowing)
plugin-securityexports whose only callers S8 removes. That narrows a published surface and widens nothing, so each of those PRs carries its ADR-0087 disposition (runtime-export-retired, the categoryda9c2bf2f2added). 8-post removes gates on objects S8 unregistered and edits describe text. Each PR declares its own line perscripts/pm/clause2-line.mjs. Contract reviews are owed for the narrowing PRs and for 8-post'spackages/specdescribes.Responsibility: n/a — not a defect card (an execution card under refactor(plugin-security,platform-objects,spec): retire the catalog seeders, the per-organization catalog machinery and the four catalog objects; Setup creation is an environment write under
singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204, build ahead)Thread-read: none
Serial constraints cleared:
os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937 PR 1 (PR feat(core): pure row-to-definition mappers for the ADR-0131 upgrade ceremony (C7b build-ahead, PR 1 of 2) #22959, the core mappers) is this lane's and is ACCEPTed. 7a and 7b consume it, and their PRs open after it and S8 land.plugin-securityfiles (read at this stamp).singleand refused under a wall (ADR-0131 D2/D3/D5/D13) #15204 (6119027520): feat(plugin-security,platform-objects): grant forms pick a position or permission set by name from the catalog (#15204 B2) #22939 → T1 test(plugin-security): retire the declared-permission seeder suites (#15204 stage 6b-2, T1) #22897 → T2 test(dogfood): fixtures read permission sets from the security catalog (#15204 stage 6b-2, T2) #22898 → D 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 → S8-T test: the stage-8 test half — suites stop depending on the four catalog row objects (#15204 stage 8, S8-T) #22962 → S8 → 7a ∥ 7b → 8-post → C7b (feat(objectql,cli): inventory + migration — four fates per object, mirrors deleted only after the id→name rewrite is verified, per-table boot report (ADR-0131 D10) #15211).objectstack-fleet commentedon Oct 12, 2026
Note for the build-ahead ·
domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T09:40Z. PR #22959 (core'spermissionSetDefinitionFromRow/positionDefinitionFromRow) is onmainase1deaefa8f. The integration tree no longer needs its branch. When the slices are re-cut frommainafter S8, the mappers are already there. Nothing else changes for this card.objectstack-fleet commentedon Oct 12, 2026
os-dev-report
{ "issue": 22975, "status": "done", "branch": "claude/issue-22975-c3-7a7b-8post (reference: integration tree plus every slice) · slices: claude/issue-22975-7a-tests-1, claude/issue-22975-7a-tests-2, claude/issue-22975-7a, claude/issue-22975-7b, claude/issue-22975-8post", "pr": null, "session": "session_01287UbjvrigpWwNfGDmdwpS", "premise_still_valid": true, "summary": "Built ahead on an integration tree (main 712361b7a8 + S8 dbae752f6e carrying D fcaedfdaa1 + S8-T 813dfed93e by the report's recipe + PR #22959's mappers 9e864370ea; head 06d8f97f01). 7a deletes permission-set-projection.ts, seed-name-lookup.ts and seed-refusal-sink.ts with their 7 index exports and 4 types, re-homes tryFind into its one live caller (packaged-permission-set-overlay-detection.ts) with a door-level pin, and drops the writesExecutedByDataDoor WeakSet and its stand-down; its test half is split in two PRs (2,182 and 1,922 lines). 7b deletes position-write-through.ts and position-environment-backfill.ts (both unreferenced after S8) and PositionNameConflictError, and drops the catalog reference report's two position-row categories instead of re-homing POSITION_OBJECT and metadataDoorAcceptsPositionName (measured: no composition registers sys_position after S8). 8-post removes the four catalog gates and SYSTEM_ROW_PROVENANCE, the delegated-admin binding and set-authoring cases, the now-dead PermissionSetNameConflictError and its error-ledger row, and sweeps the object descriptions and spec describes. No PR opened, per the card.", "integration_tree": { "head": "06d8f97f01c9c10bd3260bce9b28317ad1647090", "recipe": "main 712361b7a8 → merge S8 (claude/issue-15204-s8-catalog-objects @ dbae752f6e) → merge S8-T (#22962 @ 813dfed93e) per d_merge_recipe_for_s8t → merge claude/issue-22937-core-row-definition-mappers @ 9e864370ea → one integration-only commit", "conflicts_resolved": [ "security-plugin.ts (S8 vs main): main's #22895 (walled catalog data-door refusal, landed after S8 was cut) was kept; S8's removal of both write-throughs and the backfill import was taken.", "S8-T recipe applied as written: S8-T's deletions over D's edits; D's catalogPermissionSet reads kept in the four named dogfood files; S8-T's side in showcase-fls-read-mask-strip and showcase-permission-zoo; ensureLegacyPermissionSetRow deleted from catalog-permission-set.ts; the showcase-permission-seeding census guarded on getObject('sys_permission_set'); seedLegacyPlatformBucket import kept beside the testkit import.", "Integration-only commit 06d8f97f01: walled-catalog-data-door.test.ts (main's #22895) imports the four object files S8 deletes, so it failed to load on main+S8; it now reads them from S8-T's testkit (12/12 green). S8 owes its own resolution of this when it merges main; nothing in any slice depends on it." ], "baseline": "plugin-security on the integration tree: 185/185 files, 3,820 tests passed (45 skipped) after the integration-only fix (184/185 before it: the one load failure above); typecheck exit 0." }, "slices": [ { "slice": "7a tests, part 1", "branch": "claude/issue-22975-7a-tests-1", "head": "2c04b423f24015a86eea40d4c4036c86aad06467", "base": "integration tree 06d8f97f01", "line_budget": "5 files, +0/-2,182 = 2,182 changed (generated included: the three ledger JSONs)", "content": "Whole-file deletions: permission-set-projection.test.ts (1,886), permission-set-projection-heal-by-id.test.ts (272); engine-double-contract baseline and pinned rows and the objectql-double-limit row of the deleted doubles.", "gates": "62 derived, 62 run, all exit 0; --ran reconciles 62/62, 0 NOT-MEASURED, 0 UNRUN. plugin-security on this tree alone: 183/183 files, 3,740 tests passed; typecheck exit 0.", "clause2_line": "Clause-②: no", "changeset": "none: only test files (not in the package's files[]) and scripts/ ledgers change, so nothing publishes; skip-changeset at PR time." }, { "slice": "7a tests, part 2", "branch": "claude/issue-22975-7a-tests-2", "head": "d5480a41b4a084e5cc3b1909b9fc22744865e2de", "base": "integration tree 06d8f97f01 (parallel to part 1)", "line_budget": "8 files, +90/-1,832 = 1,922 changed (generated included)", "content": "Whole-file deletions: packaged-permission-set-restore-leg (470), permission-set-duplicate-name-refusal (279), seed-name-lookup-degradation-audible (246). packaged-permission-set-lock.test.ts keeps the #21789 classifier pins and the end-user copy pins, now driven through the metadata door's lock gate (the lock's one remaining door); its data-door pins go. engine-find-bare-array drops the two seed-name-lookup blocks. The overlay detection suite gains one pin of tryFind's contract at its door (a refused sys_metadata read is a reading of nothing, both type spellings asked under the shared page cap). Ledger rows of the deleted doubles go (--write for the pinned ledger).", "gates": "62 derived, 62 run, all exit 0; --ran reconciles 62/62, 0 NOT-MEASURED, 0 UNRUN. plugin-security on this tree alone: 182/182 files, 3,783 tests passed; typecheck exit 0.", "clause2_line": "Clause-②: no", "changeset": "none, same reason as part 1; skip-changeset at PR time." }, { "slice": "7a", "branch": "claude/issue-22975-7a", "head": "e6bc18a4a1172c9ebd3dd7388efc93f1b6410acd", "base": "integration tree + both test parts (merge b593c07da)", "line_budget": "source slice alone: 14 files, +81/-2,457 = 2,538 changed (generated included: tenant-audit census page and counts, liveness ledger)", "content": "Deletes permission-set-projection.ts (1,763), seed-name-lookup.ts (506), seed-refusal-sink.ts (83) and their index exports; tryFind re-homed as a private helper of packaged-permission-set-overlay-detection.ts, OverlayDetectionOptions.logger narrowed to the warn channel it uses; the writesExecutedByDataDoor WeakSet and its stand-down removed from security-plugin.ts; the projection's ADR anchor file, system-context row 11, tenant-audit UNTYPED_RECEIVERS row (census and prose figures regenerated, 225 to 222), the tabPermissions liveness pointer and the swallow-family positive control removed.", "gates": "119 derived; --ran reconciles 119: 118 run, 1 NOT-MEASURED (check:dual-build-cjs-loads, exit 3, needs a full repo build), 0 UNRUN. Exit codes: 117 exit 0; check:platform-checklist exit 1 with 8 reds identical on a tree without 7a (symbols in metadata-protocol and service-storage this card does not touch; the same 8 S8's report lists as pre-existing). check:i18n, check:skill-examples and the teardown self-test first refused on unbuilt prerequisites (exit 3), then exit 0 after building them; check:type-check-debt timed out at 420s once, then exit 0. check:adr-0087-registration exit 0 (11 runtime-export-retired operands verified). plugin-security: 180/180 files, 3,703 tests passed; typecheck exit 0.", "clause2_line": "Clause-②: no (narrowing)", "changeset": ".changeset/15204-7a-permission-set-projection-retired.md — plugin-security minor (BREAKING on the v18 prerelease line), spec patch. ADR-0087 disposition: not-required (runtime-export-retired) naming the 11 operands permissionSetRowFields, permissionSetBodyFromRow, upsertEnvPermissionSet, projectPermissionMutation, registerPermissionSetProjection, createPermissionSetWriteThrough, reconcilePermissionSetProjection, PermissionSeedOutcome, ProjectionDeps, ProjectionReconcileOutcome, WriteThroughDeps. A consumer sees: each retired name with its FROM → TO line (permissionSetBodyFromRow → core's permissionSetDefinitionFromRow; upsertEnvPermissionSet → PUT /api/v1/meta/permission/:name; the rest 'delete the call'), and that the packaged-set lock and the overlay reading are unchanged." }, { "slice": "7b", "branch": "claude/issue-22975-7b", "head": "1bbcd057abaded9898995f1aad3472c85d0bb477", "base": "integration tree 06d8f97f01 (parallel to 7a)", "line_budget": "12 files, +73/-1,336 = 1,409 changed (generated included)", "content": "Deletes position-write-through.ts (583) and position-environment-backfill.ts (453); PositionNameConflictError goes with its module (Q4 → A); catalog-reference-report.ts drops its position categories 4 and 5 and the posture dependency they alone needed, with its suite trimmed to match; system-context row 11b and the tenant-audit row removed (census and prose regenerated).", "gates": "114 derived; --ran reconciles 114: 113 run, 1 NOT-MEASURED (check:dual-build-cjs-loads, same reason), 0 UNRUN; all 113 exit 0 (check:i18n and check:skill-examples after building their prerequisites, check:type-check-debt on a longer budget). plugin-security: 185/185 files, 3,817 tests passed; typecheck exit 0.", "clause2_line": "Clause-②: no", "changeset": ".changeset/15204-7b-position-write-through-retired.md — plugin-security patch. No export of the package's entry changes (measured: neither module, nor the report, nor the error class is exported from src/index.ts, the one entry), so no ADR-0087 marker. A deployment sees: the boot reference report reads no sys_position row and loses its two position-row lines, which no composition could reach after stage 8; the Setup-create 409 of the deleted write-through goes with it." }, { "slice": "8-post", "branch": "claude/issue-22975-8post", "head": "00d10febd815d8baffd6e28becc91c6562664c25", "base": "7b + merge of 7a (24d088d984), because 8-post lands after both", "line_budget": "27 files, +81/-2,083 = 2,164 changed (generated included: two reference docs pages, two en translation bundles)", "content": "security-plugin.ts: the four gates and their call sites, SYSTEM_ROW_PROVENANCE, the package-position carve-out statics, the PLATFORM_CAPABILITY_NAMES import (the drift item) and two now-unused imports. delegated-admin-gate.ts: the sys_position_permission_set and sys_permission_set cases and their only helpers (checkStrictContainment, loadSetRowById, positionById, assignmentAnchorsOfPosition, BLAST_RADIUS_CAP), header updated. errors.ts: PermissionSetNameConflictError and its two constants (dead after 7a+7b), and the spec error-code ledger's plugin-security UNIQUE_VIOLATION row. Descriptions: sys_invitation.positions, sys_user_position (object and position field), sys_audience_binding_suggestion's set name, with es-ES/ja-JP/zh-CN values updated by hand and en regenerated. Spec: protocol.zod.ts projection/materializer describes, eval-user.zod.ts positions describe and TSDoc, position.zod.ts guidance prose and the activation TSDoc. Tests: the pins of the removed gates (49 cases in 6 files).", "gates": "116 derived; --ran reconciles 116: 115 run, 1 NOT-MEASURED (check:dual-build-cjs-loads, same reason), 0 UNRUN; all 115 exit 0. spec check:generated exit 0 after --fix regenerated check:docs (the only stale artifact). Suites on this tree (which carries 7a and 7b): plugin-security 180/180 files, 3,610 tests; platform-objects 69/69 files, 1,078 tests; spec 706/706 files, 20,387 tests; typecheck exit 0 for all three.", "clause2_line": "Clause-②: no", "changeset": ".changeset/15204-8post-catalog-gates-retired.md — plugin-security, platform-objects, spec patch. A consumer sees: no export changes and no request answered differently; the removed gates guarded objects stage 8 unregistered; the delegated-admin gate governs the two assignment tables and sys_member; corrected description and describe text; the ledger drops one provenance row while UNIQUE_VIOLATION stays registered. A contract review is owed for the spec describe edits." } ], "tests": "All runs through os-verify-lock, exit codes read from its VERDICT line. Baseline (integration tree): plugin-security 185 files / 3,820 passed, typecheck 0. 7a-tests-1: 183 / 3,740, typecheck 0. 7a-tests-2: 182 / 3,783, typecheck 0. 7a: 180 / 3,703, typecheck 0. 7b: 185 / 3,817, typecheck 0. 8-post tree: plugin-security 180 / 3,610; platform-objects 69 / 1,078; spec 706 / 20,387; typecheck 0 each. Gate sweeps per slice as listed; check:pm-dispatch-gates (derived for 7a) exit 0, 1,828 fast-tier cases. Dogfood and repo-wide lint not run locally: no in-repo importer of any retired export exists (git grep over packages, examples, apps, scripts), so no consumer suite is owed; CI's Dogfood Regression Gate covers the rest. No ablation was owed: the slices delete code and add one pin (the tryFind door pin), which passes before and after the re-home by design.", "gates": { "7a-tests-1": "62/62 run, all 0", "7a-tests-2": "62/62 run, all 0", "7a": "119 derived: 117 exit 0, check:platform-checklist exit 1 (8 pre-existing reds, identical without 7a), check:dual-build-cjs-loads NOT MEASURED (exit 3, full build)", "7b": "114 derived: 113 exit 0, check:dual-build-cjs-loads NOT MEASURED", "8-post": "116 derived: 115 exit 0, check:dual-build-cjs-loads NOT MEASURED", "derivation_note": "Paths were passed explicitly as the slice's diff against its own fixed base sha (integration tree 06d8f97f01, the merged 0f32dc3d17 for 7a's source, 24d088d984 for 8-post), and the --base origin/main flags were run as --base on that sha: the merge-base derivation against origin/main would charge every S8, S8-T and D path to these slices." }, "line_budget": { "7a-tests-1": 2182, "7a-tests-2": 1922, "7a": 2538, "7b": 1409, "8-post": 2164, "note": "Generated files included. The unsplit 7a test half measures 4,104 and is the content of claude/issue-22975-7a-tests @ 0f32dc3d17 (see deviations)." }, "files_changed": { "7a-tests-1": [ "packages/plugins/plugin-security/src/permission-set-projection.test.ts (deleted)", "packages/plugins/plugin-security/src/permission-set-projection-heal-by-id.test.ts (deleted)", "scripts/engine-double-contract.baseline.json", "scripts/engine-double-contract.pinned.json", "scripts/objectql-double-limit.baseline.json" ], "7a-tests-2": [ "packages/plugins/plugin-security/src/packaged-permission-set-restore-leg.test.ts (deleted)", "packages/plugins/plugin-security/src/permission-set-duplicate-name-refusal.test.ts (deleted)", "packages/plugins/plugin-security/src/seed-name-lookup-degradation-audible.test.ts (deleted)", "packages/plugins/plugin-security/src/packaged-permission-set-lock.test.ts", "packages/plugins/plugin-security/src/engine-find-bare-array.pin.test.ts", "packages/plugins/plugin-security/src/packaged-permission-set-overlay-detection.test.ts", "scripts/engine-double-contract.pinned.json", "scripts/objectql-double-limit.baseline.json" ], "7a": [ ".changeset/15204-7a-permission-set-projection-retired.md", "content/docs/permissions/system-context.mdx", "content/docs/permissions/tenant-audit-census.mdx", "docs/audits/2026-08-tenant-audit-write-call-sites.counts.md", "packages/plugins/plugin-security/src/index.ts", "packages/plugins/plugin-security/src/packaged-permission-set-overlay-detection.ts", "packages/plugins/plugin-security/src/permission-set-projection.ts (deleted)", "packages/plugins/plugin-security/src/security-plugin.ts", "packages/plugins/plugin-security/src/seed-name-lookup.ts (deleted)", "packages/plugins/plugin-security/src/seed-refusal-sink.ts (deleted)", "packages/spec/liveness/permission.json", "scripts/adr-anchors/packages__plugins__plugin-security__src__permission-set-projection.ts.json (deleted)", "scripts/measure-durability-swallow-family.mjs", "scripts/tenant-audit-census.mjs" ], "7b": [ ".changeset/15204-7b-position-write-through-retired.md", "content/docs/permissions/system-context.mdx", "content/docs/permissions/tenant-audit-census.mdx", "docs/audits/2026-08-tenant-audit-write-call-sites.counts.md", "packages/plugins/plugin-security/src/catalog-reference-report.ts", "packages/plugins/plugin-security/src/catalog-reference-report.test.ts", "packages/plugins/plugin-security/src/errors.ts", "packages/plugins/plugin-security/src/errors.test.ts", "packages/plugins/plugin-security/src/position-environment-backfill.ts (deleted)", "packages/plugins/plugin-security/src/position-write-through.ts (deleted)", "packages/plugins/plugin-security/src/security-plugin.ts", "scripts/tenant-audit-census.mjs" ], "8-post": [ ".changeset/15204-8post-catalog-gates-retired.md", "content/docs/references/api/protocol.mdx (generated)", "content/docs/references/identity/eval-user.mdx (generated)", "packages/platform-objects/src/identity/sys-invitation.object.ts", "packages/platform-objects/src/apps/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts", "packages/plugins/plugin-security/src/objects/sys-user-position.object.ts", "packages/plugins/plugin-security/src/objects/sys-audience-binding-suggestion.object.ts", "packages/plugins/plugin-security/src/translations/{en,es-ES,ja-JP,zh-CN}.objects.generated.ts", "packages/plugins/plugin-security/src/security-plugin.ts", "packages/plugins/plugin-security/src/delegated-admin-gate.ts", "packages/plugins/plugin-security/src/errors.ts", "packages/plugins/plugin-security/src/errors.test.ts", "packages/plugins/plugin-security/src/security-plugin.test.ts", "packages/plugins/plugin-security/src/delegated-admin-gate.test.ts", "packages/plugins/plugin-security/src/delegated-admin-gate-holding-organization.test.ts", "packages/plugins/plugin-security/src/store-fault-fail-closed.test.ts", "packages/plugins/plugin-security/src/builtin-positions.boot.test.ts", "packages/spec/src/api/protocol.zod.ts", "packages/spec/src/api/error-code-ledger.zod.ts", "packages/spec/src/identity/eval-user.zod.ts", "packages/spec/src/identity/position.zod.ts" ] }, "mcp_calls": "0", "api_writes": "1 — this os-dev-report comment on #22975 through scripts/pm/post-stamped.mjs. git push (not a REST write): the integration tree and the slice branches listed above; every push a plain fast-forward.", "open_questions": [ { "question": "catalog-reference-report.ts imports POSITION_OBJECT and metadataDoorAcceptsPositionName from position-write-through.ts: re-home them, or drop the position categories? (settled here, recorded)", "options": [ "A: drop categories 4 and 5 (positionsWithoutDefinition, conflictingPositionNames) and the posture dependency only they used — taken", "B: re-home the two names into the report and keep both categories" ], "axes": { "business_need": "Measured on the integration tree: no production composition registers sys_position after S8 (the only registrations are in S8-T's test-only testkit), so has('sys_position') is always false at boot, both categories never read, and the report always lists sys_position as absent. Legacy position rows are the upgrade ceremony's (C7: the CLI organization-ownership inventory reads them by raw SQL). A has zero lost readers; B keeps a reader of a table nothing provisions.", "long_term": "A leaves the boot report reading only the two live assignment tables; B keeps dead branches keyed to a retired object, plus a re-homed name-grammar copy (metadataDoorAcceptsPositionName) that would be the report's alone.", "ai_error_proofing": "B is the trap AGENTS.md's 'one route, one owner' names: code grep finds and the runtime never runs, from which an agent reasons that position rows are still reported at boot.", "startup_focus": "Retire immediately; no staged window. A deletes; B maintains." }, "recommendation": "A, taken: no public contract is touched (the report is not exported from the package's entry), so no needs_decision." }, { "question": "The dispatch says to switch position-environment-backfill.ts's mapping to positionDefinitionFromRow, and 7a's mapping to permissionSetDefinitionFromRow, pinning each caller's door.", "options": [ "A: delete the modules and their copies, since no caller survives S8 — taken", "B: switch dead modules to core's mappers, then delete them" ], "axes": { "business_need": "Measured: after S8 every caller of permissionSetBodyFromRow and positionBodyFromRow is inside the deleted modules; position-environment-backfill.ts has no importer at all (S8 unwired it). There is no surviving door to pin. Core's mappers stay for C7b's ceremony with PR #22959's own pins.", "long_term": "E5 → A moves the mapping into core before the boot module goes; #22959 did that move, so the deletion completes E5.", "ai_error_proofing": "B would add a code path that runs nowhere.", "startup_focus": "A is less work and no dead switch." }, "recommendation": "A, taken; the only live door touched (tryFind's) is pinned." }, { "question": "'Keep its liveness proof' for tryFind: the reading taken here is that the live caller (the boot overlay reading) keeps a pin that drives tryFind. The spec liveness proof registry also has an unbound 'permission-set-projection' entry whose proofRef names showcase-permission-projection.dogfood.test.ts, which S8-T deletes. Is that entry the proof meant?", "options": [ "A: leave the registry entry for its owner (the proof's file was S8-T's) — taken", "B: delete the entry in 7a, as the projection invariant it describes retires there" ], "recommendation": "B at 7a's re-cut, if the PM agrees: check:liveness does not read proofRef paths (it stayed green), so the dangling citation is invisible to every gate." }, { "question": "Position and permission-set name describes say 'unique per organization', citing a 2026-08-13 ruling; under ADR-0131 D3 the catalog is environment-level and the catalog reference report spoke of a position name as unique per deployment under single. Not changed here: two readings, one ruling on record.", "options": [ "A: maintainer ruling that ADR-0131 D3 supersedes the 2026-08-13 scoping, then one describe edit per type", "B: keep the describes" ], "recommendation": "A, through the PM's contract review of 8-post's spec describes, because the generated reference pages publish this text to authors." }, { "question": "main's #22895 walled-catalog-data-door (registered on sys_position, sys_permission_set and sys_capability) becomes inert once S8 unregisters those objects, and its test imports the deleted object files. It is not in this card's list of four gates.", "options": [ "A: S8 resolves the merge and test imports when it merges main; the refusal module retires in a later card", "B: 8-post also removes the walled middleware and its test" ], "recommendation": "A for the merge (it is S8's file surface); B is a scope decision for the PM." } ], "out_of_scope_findings": [ "class: b · reach: AdminScope.manageBindings and AdminScope.authorEnvironmentSets are authorable on a permission set's adminScope (packages/spec security AdminScope), and after 8-post no write path honours either: the only writes they authorized were sys_position_permission_set bindings and sys_permission_set authoring, which stage 8 unregistered; the metadata door has no delegated-admin gate, so a delegate holding them can neither bind nor author anything · evidence: delegated-admin-gate.ts on 8-post @ 00d10febd8 reads them only in definition-containment comparisons and describeDelegableScope · Seam: spec:AdminScope.manageBindings → runtime: delegated-admin-gate containment only | consumer: none · dedupe words: manageBindings, authorEnvironmentSets, adminScope enforce-or-remove, delegated administration metadata door", "carrier: docs-sweep dispatch · ADR-0094 still describes permission-set-projection.ts as its machinery; 7a deletes the file and its ADR anchor (check:adr-anchors asks the ADR to say so). content/docs/permissions/permission-sets.mdx (two-doors), authorization.mdx and capabilities.mdx still describe the removed gates · noted, not filed", "carrier: none · comments in surviving files still name the deleted modules as live (packaged-permission-set-lock-gate.ts and packaged-permission-set-lock.ts headers, walled-catalog-data-door.ts, write-refusals.ts, permission-set-name-collision.ts, the overlay detection header, metadata-protocol's adr0005-org-override-rollback test, spec error-code-ledger, metadata-plugin.zod.ts and permission.zod.ts comments) · noted, not filed", "carrier: none · after 7a, assertPermissionSetNotPackageDeclared is called only with operation 'update' (the metadata-door gate), so its 'insert' guidance branch is unreachable · noted, not filed", "carrier: none · objectui pinned-sibling import check NOT MEASURED: no objectui checkout in this container; no in-repo importer of any retired export exists" ], "deviations": [ "Force-push was refused by this session's permission classifier, so claude/issue-22975-7a-tests still carries the unsplit 7a test half (0f32dc3d17, 4,104 lines). The two PR-sized parts are claude/issue-22975-7a-tests-1 and claude/issue-22975-7a-tests-2; their merge (one adjacency conflict in the pinned ledger resolved by --write's content) equals 0f32dc3d17's tree: git diff between them is empty.", "Slice branches carry 'wip' commit messages (no history rewrite was possible); the re-cut re-applies them onto main with proper messages.", "Branch topology: 7a = integration tree + both test parts + 7a source (its source cannot pass without the test deletions); 8-post = 7b + 7a merged (it lands after both). Each PR still cuts from main after S8 lands; no PR stacks.", "Two slices delete two things the dispatch asked to switch or re-home (position-environment-backfill.ts, the report's position categories); measured and recorded above. 8-post also removes PermissionSetNameConflictError with its constants and ledger row, dead once 7a and 7b land; placed in 8-post so 7a and 7b do not both edit errors.ts.", "The card names the three descriptions as platform-objects files; measured: only sys-invitation.object.ts is in platform-objects, sys-user-position and sys-audience-binding-suggestion are in plugin-security's objects/.", "Files S8, S8-T or D also touch (security-plugin.ts, index.ts, catalog-reference-report.test.ts, tenant-audit-census.mjs, the census page) are edited only on these build-ahead branches, on top of their state; none of their branches was touched.", "Re-cut notes: 7a and 7b each edit the tenant-audit census (row, census page, counts, prose figures) and system-context.mdx, so the second to land re-runs node scripts/tenant-audit-census.mjs --write and updates the prose figures the gate names; the two 7a test parts touch adjacent rows of engine-double-contract.pinned.json, so the second re-runs check-engine-double-contract.mjs --write.", "dispatch vs os-dev.md: os-dev.md's worktree-first, push-probe and report rules were followed; its draft-PR deliverable was not, because the dispatch and the card forbid a PR now (the dispatch states that build-ahead order)." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 12, 2026
Seat review: build-ahead report 6123324506 (no PR, per the card) ·
domain:engineseat 2 ·session_01ADzJtzYTLUfgrRZHxkagkX· 2026-10-12T12:13ZThis reviews report 6123324506, against claim 6120370976. The branches named below were checked on the remote.
06d8f97f01;main:check:platform-checklistexits 1 with 8 reds that are identical without 7a (the pre-existing set S8's report lists). Re-read onmain.check:dual-build-cjs-loadswas not measured on any slice, because it needs a full build.position-environment-backfill.tsis deleted rather than switched (no caller after S8);PermissionSetNameConflictErroris retired in 8-post;plugin-security, notplatform-objects.Seat answers (execution parameters inside the card; not the decision box)
main.permission-set-projectionentry: B, at 7a's re-cut. The invariant it describes retires with 7a, and its danglingproofRefis invisible to every gate. 7a already touchespackages/spec, and its contract review covers this.walled-catalog-data-door: for the epic seat. S8 resolves its own merge ofmain(A). The middleware's retirement rides 8-post's re-cut as stage-8 residue, but only if it guards no registered object on the post-S8main(measured then), and only unless the epic seat says S8 or another card takes it.Out-of-scope findings
AdminScope.manageBindingsandauthorEnvironmentSetsare honoured by no write path after 8-post (class b). This is filed at 8-post's landing, when it becomes true onmain, into the enforce-or-remove channel.permission-set-projection.tsas its machinery. That is governed text (Tier H), filed at 7a's landing. Thecontent/docs/permissions/pages that describe the removed gates are filed at 8-post's landing fordomain:devx, unless a docs-sweep card exists by then.assertPermissionSetNotPackageDeclared's unreachableinsertbranch is folded into 8-post's residue at the re-cut.State: the card stays
pm:dispatched. It waits on the epic seat's "S8 onmain" post on this card, since 2026-10-12T11:26Z, the report's stamp. Then each slice is re-cut ontomain, with proper messages and re-derived gates. One PR opens per slice, in landing order: 7a tests (two parts) → 7a ∥ 7b → 8-post. Contract reviews are owed for:runtime-export-retired, and the spec patch;objectstack-fleet commentedon Oct 12, 2026
Seat note for 7a: the ADR-0094 projector is now the only
sys_permission_setwriter on publish · epic PMsession_01Rerax7QTjKMPCUZxQUtPFR(marchtian) · 2026-10-12T17:27ZFrom stage 6b-2 D (#22900), delta contract review 6129112138, measured by D's developer on a fresh database (CRM stack):
permissionpublish materializer. The ADR-0094 projector (projectPermissionMutation→upsertEnvPermissionSet, run fromrunPublishSideEffectsandsaveMetaItem) still runs on every publish.managed_by:'admin'and nopackage_id. Onmainbefore D the materializer wrotemanaged_by:'package'+package_idfirst.adminrow onmaintoday.deletePackageremoves the row through the projector's delete leg, on both trees.reconcilePermissionSetProjectionre-projects base-published sets intoadminrows.Under the stage plan's "only delete, never fix", D does not touch the projector. D's texts state this transitional behaviour (option B). Moving D to (A), a projector guard, is open to the maintainer.
Added to 7a's acceptance, for the
domain:enginelane. 7a retires the projector and both write-throughs. When it lands:permissionleave zerosys_permission_setrows on a fresh database (the shape of the 块1 case D removed), with an ablation;content/docs/permissions/authorization.mdxand the header ofpackage-uninstall-revokes-named-grants.dogfood.test.ts.Generated by Claude Code