Repository navigation
refactor(plugin-security): move SecurityPlugin's leaf readers into security-plugin-object-inputs.ts (#25223 cut 1) - #25290
Conversation
…curity-plugin-object-inputs.ts (wip) The bodies of sixteen private methods (the RLS policy collection and its admission, the unresolved-posture probes, the field-level masks and required-permission folds, the field-name tables) move unedited, re-indented one level, with isDelegatedPrincipalLeg, which only they read. The class keeps every method, its signature and docblock, and hands each body a host literal of exactly the members it reads. Claude-Session: https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa Co-authored-by: Claude <noreply@anthropic.com>
…he unwritable-master resolution (wip) Claude-Session: https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 31 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 10 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 0486b94fa4c9e11a404defd26dcf5816b90ece45 && git checkout 0486b94fa4c9e11a404defd26dcf5816b90ece45
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1eb1afae071530f66f4d93d416e7f452ceab3de3 a7cd7c6258a8d8eb81624ba0e05fba79ef8dbe23 && git checkout -B drift-repro 1eb1afae071530f66f4d93d416e7f452ceab3de3 && git merge --no-ff a7cd7c6258a8d8eb81624ba0e05fba79ef8dbe23
node scripts/docs-audit/affected-docs.mjs --json 1eb1afae071530f66f4d93d416e7f452ceab3de3
|
席位核对 · ACCEPT · 2026-10-22T21:08Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25223
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
Cut 1 of the
security-plugin.tssplit. The bodies of the 16 leaf readers ofSecurityPluginmove, unedited, to the new modulepackages/plugins/plugin-security/src/security-plugin-object-inputs.ts. These are the per-object inputs every gate layer reads: the row-level security policy collection with its ADR-0105 D3 admission and the authored-tenant-policy warning, the unresolved-posture probes, the field-level masks and required-permission folds, and the field-name tables. The one module-level declaration only those bodies read,isDelegatedPrincipalLeg, moves with them, and so do the four imports only they used.It is move-only, and the card stays open for the later cuts. The seam plan for the series is in the claim comment on #25223 (6349703046); each later cut is re-measured on the merged file first. Branch:
claude/project-thread-3fzya4, the harness-pinned branch for this session; it names no issue, so the claim comment is the identity record.The PR also carries the one-sentence rider this card owes: a docblock note in
controlled-by-parent-unwritable-master-write.test.tssaying why the raw registration door alone pins the resolution (ruling 6345869077 item 6, owed by the services seat's ACCEPT 6348830068 on PR #25256). Comment only.The published
.d.tsdoes not changeUnder the series bar (ruling 6348891160 item 1), measured on base
72fad922and heada7cd7c62:dist/index.d.tsanddist/index.d.mts, base and head, all four the same:61c36a53c30dfccdd4a62280b922bfd1328c83e02253266c469f809c0a78679c. The raw diff is 0 lines, so no union order moved either.942a52dc2afc950fdc2f89a720b08d4a8c584250f940db05d099c98f813c2359.packages/plugins/plugin-security(it sorts the members of each string-literal union and changes nothing else):dist/index.d.ts, line 2410 (private collectRLSPolicies;):collectRLSPolicies;): raw05654fc4ea1aa556ce256aa3bee1f94c6ec9b5ae1a240c7744e6fe7225d8cd43, normalised9280d1c333e070fad6635d3adbb9dad308a9ad49a3e3d30cdf90002947f3f738.private collectRLSPoliciesRenamed;): rawf20c387525fecc3b2c65bd6113a3c83cfe0388d4defabfc0a5a0fb2149c449db, normalised312949d507e1943df1f357c43a6a85d97fe8205aefea057ded1bf17866943ef7.f607fd765d6b277e08b3b46ccb16d1e2d133ec55658ffe40155aef25e1bf54f0on all four.index.jsaba80183tob52cd4f1,index.mjsaca41adetocd55afa9), because code moved within them.No changeset:
skip-changeset, per ruling 6348891160 item 4.What moved
Base line numbers are each method's signature at
72fad922. The last column is the members the body reads, which is exactly what its host carries.collectRLSPolicies:10366-:10400admitCollectedPolicy,ownershipFloorAlternates,ql,rlsCompileradmitCollectedPolicy:10419-:10427orgScopingEnabled,warnAuthoredTenantPolicyOncewarnAuthoredTenantPolicyOnce:10437-:10450logger,warnedAuthoredTenantPoliciesresolveUnresolvedCause:10579-:10582probeMetadataOutage,probeUnpublishedDraftprobeMetadataOutage:10628-:10639metadataprobeUnpublishedDraft:10673-:10684qlcomputePartialMaskRules:10708-:10727permissionEvaluatorcomputeQueryGuardFieldPerms:10753-:10773computePartialMaskRules,foldFieldRequiredPermissions,permissionEvaluatorcomputeForbiddenFieldWrites:10788-:10804fieldMasker,foldFieldRequiredPermissions,permissionEvaluatorcomputeReadPartialMaskRules:10826-:10840computePartialMaskRulesmaskOperationResult:10856-:10885computeReadPartialMaskRules,fieldMasker,foldFieldRequiredPermissions,permissionEvaluatorfoldFieldRequiredPermissions:10887-:10902permissionEvaluatorgetObjectFieldNames:10908-:10924fieldNamesCache,loadObjectFieldNameswriteCheckFieldOptions:10943-:10952metadata,qlloadObjectFieldNames:10954-:11015datetimeFieldNamesCache,numberComparandFieldsCache,tenancyDisabledCacheextractTargetField:11035-:11053Plus
isDelegatedPrincipalLeg(base:471-:487, with its docblock), read only bycollectRLSPolicies. Left in the class on purpose:getObjectSecurityMeta, which sits among these methods, becausepackages/spec/liveness/object.jsonanchors it next tonormalizeRequiredPermissionsand that file belongs to another lane.The shape (#23050 shape B, as #25222 and #25224 apply it)
.d.ts. Each body is now one host literal plusfn.call(host, …), marked[#25223 cut 1].ObjectInputsHosttypes every member as the class member itself (SecurityPlugin['m'], an indexed access that works on private members). A renamed or retyped member is a compile error. There is no cast, and no member becomes public or protected. The module importsSecurityPlugintype-only, so there is no runtime import cycle.thisparameter as thePickofObjectInputsHostover exactly the members its own body reads, and each stub annotates its host literal with the samePick. A missing or extra member is a compile error, and the closures get their parameter types from it.this.m(...a)on the plugin at call time. A spy or test double set on the plugin is still the one the body reaches, and the moved bodies that call one another do so through the plugin, as before. Everythis.m(call in the module goes to one of the eight methods a host carries this way; no moved body calls a function-valued field, so no receiver changes.readonly(the four caches,ownershipFloorAlternates,warnedAuthoredTenantPolicies) goes by value, because it is never reassigned and the Map or Set is the same object. Every other field (ql,metadata,logger,rlsCompiler,permissionEvaluator,fieldMasker) and theorgScopingEnabledaccessor goes as a getter, so each read happens when the body makes it, after anyawait, as before.start()assignsql,metadataandloggerafter construction, and tests replacepermissionEvaluatoron a constructed plugin.readonly, so a moved body cannot assign a member through it; the typecheck passes, so none does.async:resolveUnresolvedCause,probeMetadataOutage,probeUnpublishedDraft,getObjectFieldNames,writeCheckFieldOptionsandloadObjectFieldNames. Each returns the moved async function's own promise. The stub cannot throw before it returns that promise: building the host reads onlyreadonlyfields and defines getters without running them, and calling an async function never throws synchronously. So a caller awaits a promise that settles exactly as the old method's did.extractTargetFieldreads no member, so its method calls it directly.this., so the moved lines are unedited. Nothing re-exports the module, so the package entry does not move.Proof of move-only
asyncmatch. A negative control, one token changed in one body, fails the check.asyncdropped on the six above.isDelegatedPrincipalLegis verbatim (17 lines) in the module and absent fromsecurity-plugin.ts.git diff --color-moved=plain --color-moved-ws=allow-indentation-change 72fad922 HEADover the two source files): 255 of 262 deleted lines and 318 of 621 added lines are detected as moved.private asyncsignature lines, which lostasync, and thewithOwnershipFloorAlternates,name taken out of a multi-name import.ObjectSecurityMeta(a type alias overgetObjectSecurityMeta's return type, sosecurity-plugin.tsneeds no new export),ObjectInputsHost, 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 11,054 to 10,929 lines; it shrinks only by the bodies because every docblock stays for the.d.ts.Readers
No test, script, gate, baseline or docs anchor reads the moved lines by path, so nothing is re-pointed.
pnpm check:adr-anchors,check-adr-symbol-anchors(2383 anchors),check-scripts-symbol-anchors,check-spec-docblock-symbol-anchors,pnpm --filter @objectstack/spec check:liveness, andcheck-platform-checklist --changed-from origin/main(6 anchors judged, 0 lost). All exit 0. The ADR anchor for this file namesresolveCbpRelation,getMetadataReadableFields,claimSeedOwnershipOnSettleandreadSeededRecordIds, none of which moved.security-plugin.ts:Ncitation in the repo points into a moved range (base:471-:487and:10366-:11053). Base lines:56-:470move by between -1 and +19 (the import block changed), lines from:10373on move up, and lines:489-:10372keep their numbers. Three citations land in the shifted head range:CHANGELOG.md(:303, release history),docs/launch-readiness.md(:309) andpackages/spec/liveness/object.json(:132, inside a note that records it as an already-repointed stale cite). At base all three already point at unrelated lines, so this cut does not make any of them wrong.check-platform-object-tenancy-census,check-position-name-fold-loaders,check-registry-log-declared,check-rest-log-spy-declared,check-system-context-census,check-tenant-audit-census,check:cross-package-test-inputs,check:doc-authoring,check:driver-memory-census,check:issue-citationsandcheck:query-options-erasure. Only the scanned counts move, by the one new file:check-undeclared-dep-imports(3284 to 3285 files, 2817 to 2820 specifiers),check:logger-receiver-detach(3600 to 3601 files) andcheck:org-identifier(3626 to 3627 files).Acceptance notes
Gates
All at head
a7cd7c62, on base72fad922. Between that base and the currentorigin/main(1eb1afae), no file underpackages/plugins/plugin-security/changed.pnpm --filter @objectstack/plugin-security test(vitest run --maxWorkers=3, under the verify lock): Test Files 210 passed (210), Tests 4379 passed, 45 skipped (4424). Base72fad922, in a separate worktree: the same counts. VERDICT command-exit 0 on both.pnpm --filter @objectstack/plugin-security typecheck: exit 0 (tsc --noEmit, the scripts tsconfig, andcheck:test-typecheckwith 0 files / 0 errors in the debt ledger).eslint --no-inline-configover the three touched files: exit 0.pnpm --filter @objectstack/plugin-security build: exit 0, which produced the.d.tsand bundles measured above.dispatch-gates --commands: 58 commands, 890 changed lines, under the threshold.--ranreconciliation: 58 derived, 58 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero: all 58 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 0 (the first run exited 3: this shallow clone lacked the pinned fixture commit; it passed aftergit fetch --depth=1of that commit)node 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-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: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 0 (the first run exited 3, PREREQUISITE NOT MET, before a full build; after a fullpnpm exec turbo run buildunder the verify lock (77 of 77 tasks), it passed: 112 entry points across 69 packages load)pnpm check:engine-double-contractexit 0pnpm check:error-status-conformanceexit 0pnpm check:gitlink-declaredexit 0pnpm check:i18nexit 0 (the same: exit 3 before the full build, exit 0 after it)pnpm check:i18n-stale-fillexit 0pnpm check:issue-citationsexit 0pnpm check:lean-entry-closureexit 0pnpm check:logger-receiver-detachexit 0pnpm check:nul-bytesexit 0pnpm check:objectql-double-limitexit 0pnpm check:org-identifierexit 0pnpm check:page-declaration-shapeexit 0pnpm check:published-filesexit 0pnpm check:query-options-erasureexit 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:type-check-coverageexit 0pnpm check:type-check-debtexit 0pnpm check:watch-hint-literalexit 0pnpm check:where-matcherexit 0Generated by Claude Code