Repository navigation
refactor(plugin-security): move SecurityPlugin's controlled-by-parent read leaf into security-plugin-cbp-read-filter.ts (#25223 cut 4) - #25360
Conversation
… read leaf into security-plugin-cbp-read-filter.ts The bodies of computeControlledByParentFilter (the ADR-0055 §2 read derivation, answering the engine's ReadableIds leaf over the master) and controlledByParentChainRefusal (its chain refusal) move unedited to a new module beside the plugin, with CBP_LEAF_READ_VERBS, the one module-level declaration only they read. Each method keeps its name, signature, docblock and position and hands its body one host literal of exactly the members it reads; ql and logger go as getters. CBP_MAX_CHAIN_DEPTH stays (ADR-anchored) and is imported by value. Move-only: no verdict changes. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa
📓 Docs Drift CheckThis PR changes 1 package(s): 25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cd4c75dc4037aaa80375ecaf95f6b2f1b5dcf191 && git checkout cd4c75dc4037aaa80375ecaf95f6b2f1b5dcf191
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9127f23ea111f1535c9c6abadca117ada69cba83 d2b23e286bc364c2db43aaad1ec3a70e64536443 && git checkout -B drift-repro 9127f23ea111f1535c9c6abadca117ada69cba83 && git merge --no-ff d2b23e286bc364c2db43aaad1ec3a70e64536443
node scripts/docs-audit/affected-docs.mjs --json 9127f23ea111f1535c9c6abadca117ada69cba83
|
席位核对 · ACCEPT · 2026-10-23T03:30Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25223
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6353073966
Cut 4 of the
security-plugin.tssplit: the rest of cut 2. The claim is 6355875966. The bodies of two synchronous methods move unedited to the new modulepackages/plugins/plugin-security/src/security-plugin-cbp-read-filter.ts:computeControlledByParentFilter, the ADR-0055 §2 read derivation. It answers acontrolled_by_parentobject's read with the engine'sReadableIdsleaf over its master (ADR-0139 D6).controlledByParentChainRefusal, the chain refusal that derivation runs over the registry before it writes the leaf.The one module-level declaration only those bodies read moves with them:
CBP_LEAF_READ_VERBS. TheReadableIdsname leaves the@objectstack/spec/dataimport ofsecurity-plugin.ts, because no code left there reads it.It is move-only, and the card stays open for the later cuts. Branch:
claude/project-thread-3fzya4, the harness-pinned branch for this session, restarted fromorigin/mainat3e514912ad(cut 3's merge). It names no issue, so the claim comment is the identity record.Why these two now. Ruling 6353073966 item 4 held them until the Tier H docs PR for #25314 landed. That PR, #25316, merged as
97967a9a06before cut 3's merge, and #25314 is closed as completed. On this base, ADR-0055:26 is a file-level anchor and ADR-0055:58 anchors theassertControlledByParentWritemethod shell, so no ADR sentence resolves on a token inside these two bodies any more.composeWriteTenantWall, which item 4 also held, belongs to a different section (the tenant wall) and is not in this cut.What stays in
security-plugin.ts.CBP_MAX_CHAIN_DEPTHstays: ADR-0055:135 anchors it by symbol in that file (ruling item 5). It has been exported since cut 3, and the new module imports it by value, as cut 3's module does, sosecurity-plugin.tsgains no new export. It is read only inside a function body, at call time, never while either module evaluates.CbpRelationkeeps its one declaration. Both files importRLS_DENY_FILTERfromrls-compiler.js.The published
.d.tsdoes not changeUnder the series bar (ruling 6348891160 item 1), measured on base
3e514912adand on the head (the tree of this PR's commit):dist/index.d.tsanddist/index.d.mts, base and head, all four the same:803f5bfb7da0b00f8cd8d8f25172463d8dfb07d93fbdd8a9c6f4d4b7435e429a. The raw diff is 0 lines, so no union order moved either.b6b5c1b7fa6fa188f6e2f7e4cca3816a246b570f20d708643d116012aa86fa77.packages/plugins/plugin-security(it sorts the members of each string-literal union and changes nothing else):dist/index.d.ts, line 2027 (private computeControlledByParentFilter;):computeControlledByParentFilter;): raw133464a499c14936dfef45acfbfc4dc06f474a637d04a41af9c96cd69467e57b, normalised18feab96bc21c749ed55f60542c9564a6f80de841a2e650325229a8167ced6ff.private computeControlledByParentFilterRenamed;): raw702460748f17c01330fd55483961d149a1972622317ba54c89f03a99c2a045f7, normaliseda7ef310b2e289b480123f13b49aa60391b0e0ee5951cb650296a5f2d757a5759.f607fd765d6b277e08b3b46ccb16d1e2d133ec55658ffe40155aef25e1bf54f0on all four, the same as at cuts 2 and 3.index.js4f2b1fcbto7179686c,index.mjs8a9277aeto474e62d2), because code moved within them.No changeset:
skip-changeset, per ruling 6348891160 item 4.What moved
Base line numbers are each member's signature at
3e514912ad. The last column lists the members the body reads, which is exactly what its host carries.computeControlledByParentFilter:9058-:9099controlledByParentChainRefusal,logger,ql,resolveCbpRelationcontrolledByParentChainRefusal:9123-:9140ql,resolveCbpRelationBoth methods are synchronous, so no stub changes
async.CBP_LEAF_READ_VERBS(base:661, 11 lines with its docblock) moves verbatim. No code left insecurity-plugin.tsreads it.The shape (#23050 shape B, as cuts 1 to 3 applied it)
.d.ts. Each body is now one host literal plusfn.call(host, …), marked[#25223 cut 4].CbpReadFilterHosttypes every member as the class member itself (SecurityPlugin['m']). Each moved function types itsthisas thePickof the host over exactly the members its body reads, and each stub annotates its literal with the samePick. There is no cast, and no member becomes public or protected.SecurityPlugintype-only, and namesCbpRelationthrough the class's own signature, as cuts 2 and 3 do (an alias over the non-null return type ofSecurityPlugin['resolveCbpRelation']).this.m(...a)on the plugin at call time. A spy or test double set on the plugin is still the one the leaf reaches. The leaf still reaches its chain refusal through the plugin, and both still reachresolveCbpRelationthrough it. No moved body calls a function-valued field, so no receiver changes.qlandloggergo as getters, because neither is declaredreadonly.start()assigns both after construction. The host declares bothreadonly, so a moved body cannot assign a member through it; the typecheck passes, so none does.Proof of move-only
asyncmatch. A negative control,!context?.userIdchanged tocontext?.userIdincomputeControlledByParentFilter, fails the check.CBP_LEAF_READ_VERBSis verbatim in the module and absent fromsecurity-plugin.ts.CBP_MAX_CHAIN_DEPTHandCbpRelationare byte-identical to base. Every other class member, 123 named members plus the constructor, is byte-identical to base,composeWriteTenantWallincluded.git diff --color-moved=plain --color-moved-ws=allow-indentation-change 3e514912ad HEADover the two source files): 60 of 61 deleted lines and 75 of 179 added lines are detected as moved.@objectstack/spec/dataimport line, which losesReadableIds. The module importsReadableIdsfrom the same package.CbpRelationalias,CbpReadFilterHost, one pointer docblock per function and the rewritten signature heads. The rest are lines git does not pair with a moved block, such as blanks and closing braces.security-plugin.tsgoes from 10,240 to 10,205 lines.ADR anchors (ruling 6353073966 item 2)
node scripts/check-adr-symbol-anchors.mjs: exit 0 at base3e514912adand exit 0 at head, both with2383 anchors across 142 records resolve — 466 symbol (440 declaration, 26 literal), 1889 file-level, 28 cross-repo, 6 exempt, 18 continuation. The counts differ from cut 3's report because docs(adr): repoint three security-plugin.ts symbol anchors to declarations the class keeps #25316 made one symbol anchor file-level. CI runs this gate (lint.yml), butdispatch-gates --commandsdoes not derive it for this change set ([finding] dispatch-gates derives the symbol-anchor sweeps only from their corpus paths, so a change that removes an anchored symbol from a TARGET file passes every local gate and reds CI #21697), so it is run by hand here.pnpm check:adr-anchors: exit 0, 78 anchored files (77 at base, plus the new entry). The new entry isscripts/adr-anchors/packages__plugins__plugin-security__src__security-plugin-cbp-read-filter.ts.json. It carries ADR-0055 and the §1 invariant paragraph of thesecurity-plugin.tsentry, verbatim, as the entries of cuts 2 and 3 do. Its "reads answer RLS_DENY_FILTER" half is this leaf. The moved comments also cite ADR-0139. Like thesecurity-plugin.tsentry, the new entry does not list every ADR its file cites, and the gate passes. Thesecurity-plugin.tsentry is unchanged.docs/adr/**is not touched.security-plugin.ts: 27 anchors, 18 symbols, 10 ADRs on this base. It was measured withextractAnchorsandsymbolSegmentResolutionfromscripts/symbol-anchors.mjs, which is measurement, not a new gate. It was read on3e514912adBEFORE the cut, with the two bodies blanked: no anchor changed class. Every anchor resolves at head insecurity-plugin.tswith its base class.The table gives each anchor's class at base and head, the line it resolves on, and the member that line sits in. Every anchor stays in
security-plugin.ts.#CBP_MAX_CHAIN_DEPTH:649(top-level):657#SecurityPlugin:1338(class):1334#SecurityPlugin.getMetadataReadableFields:2487(start),:6106(method):2483,:6102#SecurityPlugin.resolvePermissionSetsForContextUnmemoized:7017(method):7013#assertControlledByParentWrite:9497(method shell):9462#assertMasterRowEditable:9623(method shell):9588#collectRLSPolicies:9677(method):9642#composeWriteTenantWall:8412(method):8408#computeControlledByParentFilter:9058(method):9054(method shell)#computeDelegatedReadFilter:5984(method):5980#computeRlsFilter:5236(explainAccessForCaller),:7639(method):5232,:7635#getReadFilter:2250(start),:5862(method):2246,:5858#hasPlatformAdminPosture:7666(method):7662#isPrincipalLessContext:503(top-level):511#manage_metadata:252(PLATFORM_ADMIN_ONLY_CAPABILITIES):260#nav_permission_sets:1743(init):1739#rlsFilter:4313(local instart):4309#writeCheckPolicies:1233(top-level):1229No resolving site moves. The three anchors that name
computeControlledByParentFilterresolve on its declaration, which stays as a shell.assertControlledByParentWrite,assertMasterRowEditable,collectRLSPoliciesandcomputeRlsFilteralso appear as host-literal keys inside cut 3's stubs, as cut 3's report noted; this cut adds no such line for an anchored name.Readers
No test, script, gate, baseline or docs anchor reads the moved lines by path, so nothing else is re-pointed.
isSystemread, socheck-system-context-censushas no row to move. Unlike cut 3,content/docs/permissions/system-context.mdxis not touched. The gate exits 0 at base and head with the same summary except the file count. The bodies hold no engine write, socheck-tenant-audit-censussees the same 237 write call sites.check-scripts-symbol-anchors,check-spec-docblock-symbol-anchors,pnpm --filter @objectstack/spec check:liveness, andcheck-platform-checklist --changed-from origin/main.computeControlledByParentFilteras the mechanism (controlled-by-parent-master-object-read.test.ts,readable-ids-nested-compose.test.ts), and so do two comments inpackages/lint/src/validate-security-posture.tsand its test. The method still exists under that name.packages/qa/dogfood/test/authz-conformance.matrix.tsdescribes the enforcement asplugin-security/security-plugin.ts computeControlledByParentFilter + assertControlledByParentWrite. Both are still methods of that class in that file, now as shells, as cut 3 already leftassertControlledByParentWrite.security-plugin.ts:Ncitation in the repo points into a moved range. The moved ranges at3e514912adare:651-:662and the two method bodies,:9064-:9098and:9127-:9139.:138-:650(the new import), by -4 for base:663-:9063, by -29 for base:9099-:9126and by -35 after:9139. Lines:1-:137do not move.:132,:303,:309,:1614,:1690,:1736,:3215,:3281,:3519and:4344are the same list cuts 2 and 3 read. Each lands in an unmoved or shifted range, and each already pointed at an unrelated line before cut 2, so this cut does not make any of them wrong.node scripts/docs-audit/affected-docs.mjs --json 3e514912ad(advisory): 37 pages, 9 of them release-owned. Every one is reached only through a string literal inside the moved code:'controlled_by_parent'in the two bodies, or'findOne'inCBP_LEAF_READ_VERBS. The literals moved unchanged and no behaviour changed, so there is nothing to edit on those pages.3e514912adand head and compared:check-platform-object-tenancy-census,check-position-name-fold-loaders,check-registry-log-declared,check-rest-log-spy-declared,check-system-context-census(148 elevation read sites in 70 files at both),check-tenant-audit-census(237 write call sites at both),check:cross-package-test-inputs,check:driver-memory-censusandcheck:query-options-erasure. The last of these reports the same counts at both, andsecurity-plugin.tsis not a baseline key.check-undeclared-dep-imports: 3291 to 3292 files, and 2835 to 2836 specifiers. The one is the new module's@objectstack/spec/dataimport, a declared dependency.check:doc-authoring: 1413 to 1414 sources.check:org-identifier: 3633 to 3634 files.check:logger-receiver-detach: 3607 to 3609 files. The second extra file isapps/docs/.next/types/validator.ts, a gitignored local build output of the docs app, as at cuts 2 and 3. It is not in the tree CI checks out.check-issue-citationsjudges a change set, so it has nothing to read at base. At head it judged 5 citations across 2 files, and all resolve.Acceptance notes
docs/adr/**.security-plugin.ts, with one import after:47and one block at:1765. This cut has no hunk near either.security-plugin.tsafter this cut: the method shells,CBP_MAX_CHAIN_DEPTH,CbpRelationandcbpRelCache.composeWriteTenantWallgets its own cut.Gates
All gates ran at the head, the tree of
d2b23e286b, on base3e514912ad.pnpm --filter @objectstack/plugin-security test(vitest run --maxWorkers=3, under the verify lock): Test Files 212 passed (212), Tests 4442 passed, 45 skipped (4487). Base3e514912adgives the same counts. VERDICT command-exit 0 on both.pnpm --filter @objectstack/plugin-security typecheck: exit 0. It runstsc --noEmit, the scripts tsconfig, andcheck:test-typecheck.eslint --no-inline-configover the two touched source files: exit 0.pnpm --filter @objectstack/plugin-security build: exit 0. It produced the.d.tsand bundles measured above.node scripts/check-adr-symbol-anchors.mjsexit 0,pnpm check:query-options-erasureexit 0, plus the three symbol-anchor gates under Readers. None of these is derived for this change set; each was run by hand.dispatch-gates --commands: 62 commands, the same list as cut 3's cut commit. The--ranreconciliation shows 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. The zero is derived: all 62 recorded an exit code and none is 3.Per command (exit code):
node scripts/check-ci-filter-parity.mjsexit 0node scripts/check-closing-keyword-parity.mjsexit 0node scripts/check-closing-keyword-parity.mjs --self-testexit 0node scripts/check-comment-mask-adoption.mjsexit 0node scripts/check-comment-mask-adoption.mjs --self-testexit 0node scripts/check-comment-mask-corpus.mjsexit 0node scripts/check-dts-emitted.mjs --self-testexit 0node scripts/check-issue-citations.mjsexit 0node scripts/check-keyed-text-bounds.mjsexit 0node scripts/check-keyed-text-bounds.mjs --self-testexit 0node scripts/check-platform-object-tenancy-census.mjsexit 0node scripts/check-platform-object-tenancy-census.mjs --self-testexit 0node scripts/check-plugin-teardown-shape.mjsexit 0node scripts/check-plugin-teardown-shape.mjs --self-testexit 0node scripts/check-position-name-fold-loaders.mjsexit 0node scripts/check-position-name-fold-loaders.mjs --self-testexit 0node scripts/check-registry-log-declared.mjsexit 0node scripts/check-registry-log-declared.mjs --self-testexit 0node scripts/check-rest-log-spy-declared.mjsexit 0node scripts/check-rest-log-spy-declared.mjs --self-testexit 0node scripts/check-scripts-symbol-anchors.mjsexit 0node scripts/check-scripts-symbol-anchors.mjs --self-testexit 0node scripts/check-system-context-census.mjsexit 0node scripts/check-system-context-census.mjs --self-testexit 0node scripts/check-tenant-audit-census.mjsexit 0node scripts/check-tenant-audit-census.mjs --self-testexit 0node scripts/check-undeclared-dep-imports.mjsexit 0node scripts/check-undeclared-dep-imports.mjs --self-testexit 0node scripts/docs-audit/check-affected-docs.mjsexit 0node scripts/docs-audit/check-drift-comment.mjsexit 0pnpm --filter @objectstack/spec run check:duration-unit-keysexit 0pnpm check:adr-anchorsexit 0pnpm check:agent-test-spellingexit 0pnpm check:bash32-floorexit 0pnpm check:cli-command-idsexit 0pnpm check:closed-ceremony-textexit 0pnpm check:cross-package-test-inputsexit 0pnpm check:doc-authoringexit 0pnpm check:driver-memory-censusexit 0pnpm check:dts-closureexit 0pnpm check:dual-build-cjs-loadsexit 0pnpm check:entry-guardexit 0pnpm check:error-status-conformanceexit 0pnpm check:gitlink-declaredexit 0pnpm check:i18nexit 0pnpm check:i18n-stale-fillexit 0pnpm check:issue-citationsexit 0pnpm check:lean-entry-closureexit 0pnpm check:logger-receiver-detachexit 0pnpm check:nul-bytesexit 0pnpm check:org-identifierexit 0pnpm check:page-declaration-shapeexit 0pnpm check:parse-guardexit 0pnpm check:pnpm-filter-targetsexit 0pnpm check:pnpm-flag-spellingsexit 0pnpm check:published-filesexit 0pnpm check:refd-timer-probeexit 0pnpm check:slot-lookupexit 0pnpm check:sourcemap-no-sources-contentexit 0pnpm check:test-source-aliasexit 0pnpm check:tier-file-adoptionexit 0pnpm check:watch-hint-literalexit 0pnpm check:query-options-erasureexit 0node scripts/check-adr-symbol-anchors.mjsexit 0node scripts/check-spec-docblock-symbol-anchors.mjsexit 0pnpm --filter @objectstack/spec check:livenessexit 0node scripts/check-platform-checklist.mjs --changed-from origin/mainexit 0Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa