Skip to content

refactor(plugin-security): delete the position write-through and the row-only position backfill (C3 stage 7b) - #23302

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-22975-7b-recut
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-22975-7b-recut

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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 (feat(core): pure row-to-definition mappers for the ADR-0131 upgrade ceremony (C7b build-ahead, PR 1 of 2) #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

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

…row-only position backfill, and stop the boot reference report reading position rows (ADR-0131 D3, C3 stage 7b)

Stage 8 stopped registering the sys_position row object, its data-door
write-through and the row-only position backfill. Nothing references
position-write-through.ts or position-environment-backfill.ts any more,
so both go. PositionNameConflictError, which only the write-through
raised, goes with its module.

The catalog reference report drops its two position-row categories (a
position row with no definition, and a position name several rows
carry) and the posture input only they used. No composition provisions
sys_position after stage 8, so those categories could never read a row;
legacy position rows are the upgrade ceremony's (ADR-0131 D10). The
report still lists every unresolved assignment, every unresolved grant
and every grant that names nothing.

The system-context page loses row 11b, and the tenant-audit census drops
the write-through's untyped-receiver row; the census and its prose
figures are regenerated.

Claude-Session: https://claude.ai/code/session_01287UbjvrigpWwNfGDmdwpS
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 13, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-security, touching 79 documentable anchor(s).

44 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 9c7cfbf0a962ad690f1b4cbe271c95d909000d64.

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

What this run could not see
  • 1 cross-cutting symbol(s) contributed no route anchor: logError (9 routes)
  • 2 anchor(s) matched too much of the corpus to be a work list: created_at (literal, 38 pages), organization_id (literal, 35 pages)
  • 18 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 — 15 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 9c7cfbf0a962ad690f1b4cbe271c95d909000d64 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c1315fc943e8ace4d8f6bd74d97794c2042f85e8 — the merge of head 8c7ee7f06aaf4cc75d3a1b2536a2ac18196de4dd into base 9c7cfbf0a962ad690f1b4cbe271c95d909000d64, 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 c1315fc943e8ace4d8f6bd74d97794c2042f85e8 && git checkout c1315fc943e8ace4d8f6bd74d97794c2042f85e8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9c7cfbf0a962ad690f1b4cbe271c95d909000d64 8c7ee7f06aaf4cc75d3a1b2536a2ac18196de4dd && git checkout -B drift-repro 9c7cfbf0a962ad690f1b4cbe271c95d909000d64 && git merge --no-ff 8c7ee7f06aaf4cc75d3a1b2536a2ac18196de4dd

node scripts/docs-audit/affected-docs.mjs --json 9c7cfbf0a962ad690f1b4cbe271c95d909000d64

⚠️ 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 9c7cfbf0a962ad690f1b4cbe271c95d909000d64 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 8c7ee7f06aaf4cc75d3a1b2536a2ac18196de4dd
Local-runs: none

PR #23302 (#22975 slice 7b, re-cut) on branch claude/issue-22975-7b-recut, base main. The head's merge-base with origin/main is 9c7cfbf0a9, which IS the current origin/main, so the merge ref c1315fc943 carries the head's tree unchanged (zero-line diff between the two). Three-dot diff: 12 files, +74 / -1,338 = 1,412 changed lines, under 3,000. Inputs read: the card body, comments 6120370976, 6124054616, 6148409420, 6150584500, 6150594560 and 6150657199, the PR body and file list, the net diff, S8's squash 66949b7729 (#23222), ADR-0131 and ADR-0087 on origin/main, and the head's check-runs. The body opens Refs #22975 then Clause-②: no; no closing keyword in the body or in the three commits.

① Derived judgments

Dead before deletion — yes, every symbol, measured on origin/main across the whole tree (every package, examples and scripts).

  • position-write-through.ts: the only importers on main are catalog-reference-report.ts (POSITION_OBJECT, metadataDoorAcceptsPositionName; this PR removes the import) and position-environment-backfill.ts (deleted here). S8's own diff on security-plugin.ts removed the createPositionWriteThrough import and its sys_position registration, so the middleware has had no caller since 66949b7729. No other file names createPositionWriteThrough, packageHoldsPosition, isPositionMetadataDoor, positionBodyFromRow or POSITION_METADATA_TYPE.
  • position-environment-backfill.ts: no importer on main. S8 removed the runOneTimePositionEnvironmentBackfill import and the kernel:bootstrapped hook that ran it. No file names any of its exports.
  • PositionNameConflictError (errors.ts): its only thrower was the write-through; the only other mention is the FLOOR list in errors.test.ts, which this PR trims. The class is a non-exported sibling of the retained PermissionSetNameConflictError and stamps that class's constants, so no code or status spelling leaves with it.
  • The report's categories 4 and 5 were already inert on main: collectCatalogReferences reads sys_position only when engine.getObject('sys_position') answers, and after S8 nothing on main registers that object outside the test kit __tests__/legacy-catalog-objects.testkit.ts. The report already listed sys_position under absent on a post-S8 boot.
  • The objectui pinned sibling (.objectui-sha 1fb1ced362) is NOT checked out in this container and is NOT MEASURED. Structurally it cannot be affected: see the package entry below.

The package entry — no deleted symbol was ever reachable from it. src/index.ts on main re-exports nothing from position-write-through.js, position-environment-backfill.js or catalog-reference-report.js, and from errors.js only PermissionDeniedError and isPermissionDeniedError. package.json publishes one export, . (no subpaths), so a deep import cannot reach the deleted modules either. The PR does not touch index.ts or package.json. Therefore no ADR-0087 runtime-export-retired entry and no FROM → TO are owed, and the changeset's patch with no marker is the correct shape. This corrects the claim 6120370976's expectation that 7b "retires plugin-security exports": measured, 7b retires none; that expectation holds for 7a only.

The reference report (catalog-reference-report.ts). Categories 4 and 5 are dropped as one unit: the readPositions scan, the two logError blocks, the posture input (CatalogReferenceReportDeps.posture, CatalogReferenceReport.posture), the PositionWithoutDefinition and ConflictingPositionName interfaces, the positionsWithoutDefinition bucket field and the conflictingPositionNames top-level field, the postureEnforcesWall import, and the sys_position entry of absent. The only two consumers of posture were those categories; security-plugin.ts stops passing this.tenancyPosture. Categories 1–3 (unresolved assignments, unresolved grants, unnamed grants) are untouched line for line: the assignment and grant blocks, scanAll, catalogResolver, the ReportStop handling and perOrganization are not in the diff. The clean-boot info line changes text (drops "and no position row is without a definition") and its guard on absent.length goes from "fewer than three" to "fewer than two", the same meaning over two objects instead of three. Tests: 12 it blocks on main, 9 on the head; the three removed pin only the dropped categories (walled positions without definition, a walled position leaving the report after a door save, and conflicting names under single). The positive half of the second one — a position saved through the metadata door resolves in the catalog and is not reported — survives in the first test (s3_env_lead saved via saveMetaItem, then an assignment to it, findings empty). The remaining tests lose only the dropped fields and the absent entry. No doc, README, message or test on the head still names the two dropped lines; the two surviving comments (catalog-reference-report.test.ts:437, grant-permission-set-name-backfill.test.ts:679) say the backfill ran "until C3 stage 8 unwired it", which stays true. The seedHolder helper still inserts a sys_position row and a junction row as a deliberate control that a leftover row keeps nothing alive; that is a control, not dead setup.

Behaviour a deployment loses — each already unreachable after S8; no guarantee leaves without a door-side home.

  1. The Setup-create / rename-into refusal 409 UNIQUE_VIOLATION (PositionNameConflictError) for a name the environment catalog already holds. It answered a sys_position data-door write; after S8 that object is not registered and the middleware is not wired, so no request could reach it. On the metadata door the uniqueness is structural rather than a refusal: one name is one environment item (PUT /api/v1/meta/position/:name over an existing name is an edit, never a second holder), a package-held or built-in name is refused by the door's own packagedBaseRefusal (403 NOT_OVERRIDABLE; position takes no environment overlay), and a code/environment collision is refused at boot with NAMESPACE_CONFLICT. The write-through's module note itself describes the 409 as the row-side mirror of those door rules.
  2. The position name rule (metadataDoorAcceptsPositionName) was a derived mirror of the door's own two checks, METADATA_ITEM_NAME_PATTERN enforced at saveMetaItem (protocol.ts around line 20480) and PositionSchema.name; both stay on the door.
  3. The two boot report lines (categories 4 and 5) and the walled-posture remedy text: unreachable since S8 as shown above. Legacy rows are the upgrade ceremony's (ADR-0131 D10, fates 2 and 4), which this PR's changeset says.
  4. The one-time backfill and its sys_migration verdict row adr-0131-position-environment-backfill: unregistered by S8; existing verdict rows stay inert, with no reader left on the head.
  5. The one observable delta on a post-S8 deployment: absent no longer lists sys_position, and the clean-boot info line is shorter. Neither is a published surface.

Regenerated files — consistent with the diff. The census script loses the write-through's UNTYPED_RECEIVERS row (2 sites: delete, update on sys_position), and the backfill's one readable-receiver insert goes with its file: 228 → 225 sites, UNTYPED_RECEIVERS 8 → 6, readable receiver 186 → 185, any receivers 42 → 40, const-named 52 → 50, decidable 155 → 153, runtime-named 73 → 72, elevated 123 → 120, threading a tenant context 167 → 164; the counts file drops exactly the three rows. Row 11b leaves system-context.mdx; no other page cites 11b. CI's check-tenant-audit-census and check-system-context-census (in lint.yml) are green on this head. The "Measured on" stamp reads 66949b772, the regeneration base rather than the head; the page's own rule is that the corpus-scale figures say when they were true and are not compared, so this is permitted, and the dev re-ran both checks on 8c7ee7f06a.

Published text. Corrected here: system-context.mdx row 11b, the report's module JSDoc, the write-through JSDoc in errors.ts (deleted with the class), the changeset's own account. No ADR names either module or any of their functions, so no governed text goes false. Not corrected and not carried by name: packages/core/src/security/position-row-definition.ts lines 15–16 still say the mapping is what "the security plugin's row-only position backfill applies today (positionBodyFromRow, beside its position write-through)". After this PR that sentence is false and positionBodyFromRow no longer exists. packages/core/src/security/ is read-only for this card (claim 6120370976), so the fix is not 7b's to make; it must be carried by name (③).

The deletion clause. No test file is deleted. The three deleted it blocks pin only code this PR deletes, and the one assertion with a live half is pinned at the same layer in the same file. The FLOOR entry in errors.test.ts is the enumeration floor of a class that leaves; the floor is a lower bound, so trimming it with the class is the clause's "together with the code it pins". Clause satisfied.

② Semver level

The changeset grades @objectstack/plugin-security patch and the body declares Clause-②: no. Against WHICH LEVEL in .github/workflows/pr-automation.yml: a fix( that changes no public surface stays patch; minor is for an additive widening of a published surface. Measured: the entry exports no deleted symbol, package.json has no subpath, CatalogReferenceReport's narrowed shape is internal, and nothing widens. No narrowing or removal is hidden: the removals a deployment can observe (the 409, the report lines, the backfill) were taken out of reach by S8, whose changeset 15204-s8-catalog-row-objects-retired.md declares Clause-②: no (narrowing) and carries the ADR-0087 semantic entry catalog-row-objects-retired. check-changeset-no-major and the ADR-0087 registration step stand down on no + patch, and both passed in CI (Check Changeset: success). The PR title says refactor( while the changeset says fix(; the act, not the type, sets the level, and neither type raises it. Level upheld: patch, Clause-②: no.

③ Boundary flags

  1. Dev deviation, accepted: the derived sweep (114/114), dogfood (254 files, 2,106 tests) and the CLI integration tier (119 files, 1,036 tests) were measured on 05dee10c89, one merge of main before the head. Verified: 05dee10c89..8c7ee7f06a is 51 files from main (spec/ui, cli, three changesets, the engine-double pin), none in plugin-security, the censuses or the docs pages here. The plugin-security suite, typecheck and both census checks were re-run on the head.
  2. Dev deviation, no bearing: a mis-spelled first full build (turbo flag forwarded); re-run 74/74.
  3. 7a overlap and the 7a blocker: 7b shares security-plugin.ts, the census page and script, system-context.mdx and the counts file with 7a. 7b lands first (7a is blocked on the C measurement, 6150657199); 7a's re-cut must then merge main and regenerate. 7b has no code dependency on 7a or on its blocker: it deletes nothing live, and the echo hazard concerns the permission-set projector, which 7b does not touch. Judged independent.
  4. Out-of-scope observation, escalate to the C measurement: the 7a blocker's premise (applyRegistryWriteThrough hydrates no stored row into the engine registry on a kernel with an environmentId) is stated type-agnostically, while 7b's changeset asserts "A position is written through the metadata door alone". Whether a position saved through the door resolves in the catalog on a real environment kernel is the same open fact for positions as for permission sets. NOT MEASURED here (read-only; no booted stack). Suggest C step 1 include one position saved through the door, since that is cheap and settles the sentence 7b publishes.
  5. Residue the PR leaves in gate vocabularies, carry by name or fix here: scripts/check-durability-degradation-log-level.mjs:416 and scripts/measure-durability-swallow-family.mjs:334 still list persistPositionBackfillRecord, a private function of the deleted backfill. The durability gate's own header calls a name in a Map that no seam consumes the "declared ≠ enforced" shape it rejects, but only FAILURE_PROPAGATION_SITES carries the staleness check, so this leaves no red (Lint is green). Two one-line deletions in scripts/, the surface this PR already edits; otherwise the 8-post dispatch must name both lines, since the "comment sweep" it carries does not cover gate vocabularies.
  6. Published JSDoc made false, carry by name: packages/core/src/security/position-row-definition.ts:15-16 (①). Core is read-only for this card; name it in the 8-post dispatch or on the C7b card that owns the core mappers.
  7. Not this PR's, already 8-post's by the card: delegated-admin-gate.ts:1330, security-plugin.ts:6782 and :7075 still read or match sys_position rows (the delegated-admin catalog cases and the system-row gates the card assigns to 8-post).
  8. Check-runs on the head (read at 2026-10-13T17:05Z): 32 success, 5 skipped, 3 still running. Still running: Test Core (2/6), Test Core (3/6), Test Core (4/6). Skipped by path filter: Console Pin Gate, Temporal Conformance, Dogfood Regression Gate shards, Dogfood Verify CLI, Packed-tarball smoke. The dev's "CI's Console Pin Gate covers it" is therefore not true on this head; the console is covered structurally instead (no entry export moves, no subpath exists). Green includes TypeScript Type Check, Type Check · workspace, Lint · ESLint, Lint · docs & code guards, Lint & Repo Gates, Check Changeset, Check PR Size, Spec property liveness, Governed Surface Queue Guard, the single-writer and card-claim guards, Build Core, Build Docs, Test Core (5/6), Test Core (6/6). The seat reads the three shards before the queue.
  9. NOT MEASURED: the objectui pinned sibling's imports (not checked out here); check:platform-checklist's 8 reds as pre-existing (the dev's diff of the red lists against bae5841bce is the evidence, not repeated); check:dual-build-cjs-loads (dev: exit 0 on 74/74; not repeated).

Adopted by domain:engine seat 2 at 2026-10-13T17:08Z, after a tool-use audit of the isolated reviewer's transcript. The reviewer made reads, git fetch into review refs, git grep, REST GETs, and record writes only. It made no GitHub write and ran no build, test or gate. The seat takes the verdict as PASS. The seat queues the PR only after the two Test Core shards still running at adoption are green. The findings are carried:

  • Finding 2 (persistPositionBackfillRecord still listed at scripts/check-durability-degradation-log-level.mjs:416 and scripts/measure-durability-swallow-family.mjs:334) goes to 8-post's dispatch by name. It produces no red, so it is not worth a re-run here.
  • Finding 3 (the JSDoc of packages/core/src/security/position-row-definition.ts:15-16 names the deleted backfill) also goes to 8-post by name.
  • Finding 4 (also measure a position saved through the metadata door on an environment kernel) is added to 7a's C measurement.
  • Finding 6 (7a merges main after 7b and regenerates the shared census files with the repo's tools) is already in the dispatch 6148409420.
  • Findings 1 and 5 are noted: the objectui sibling is NOT MEASURED, and no ADR-0087 marker is owed for 7b.

Implemented-by: claude/issue-22975-7b-recut
Reviewed-by: session_01ADzJtzYTLUfgrRZHxkagkX

VERDICT: PASS

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 13, 2026 17:11
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 13, 2026 17:11
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 13, 2026
Merged via the queue into main with commit 5c9bfb0 Oct 13, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-22975-7b-recut branch October 13, 2026 17:42
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.

2 participants