Repository navigation
Commit f47f7f2
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuMYdF2uchcinMphEKW4sYxd)_
Part of #25223
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
Cut 1 of the `security-plugin.ts` split. The bodies of the 16 leaf
readers of `SecurityPlugin` move, unedited, to the new module
`packages/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.ts` saying
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.ts` does not change
Under the series bar (ruling 6348891160 item 1), measured on base
`72fad922` and head `a7cd7c62`:
- Raw sha256 of `dist/index.d.ts` and `dist/index.d.mts`, base and head,
all four the same:
`61c36a53c30dfccdd4a62280b922bfd1328c83e02253266c469f809c0a78679c`. The
raw diff is 0 lines, so no union order moved either.
- Normalised sha256, all four the same:
`942a52dc2afc950fdc2f89a720b08d4a8c584250f940db05d099c98f813c2359`.
- The normaliser, run from `packages/plugins/plugin-security` (it sorts
the members of each string-literal union and changes nothing else):
```sh
node -e 'const s=require("fs").readFileSync(process.argv[1],"utf8");process.stdout.write(s.replace(/"[^"\n]*"(?:\s*\|\s*"[^"\n]*")+/g,r=>r.split(/\s*\|\s*/).sort().join(" | ")))' dist/index.d.ts | sha256sum
```
- Positive controls on a copy of the head `dist/index.d.ts`, line 2410
(`private collectRLSPolicies;`):
- Made public (`collectRLSPolicies;`): raw
`05654fc4ea1aa556ce256aa3bee1f94c6ec9b5ae1a240c7744e6fe7225d8cd43`,
normalised
`9280d1c333e070fad6635d3adbb9dad308a9ad49a3e3d30cdf90002947f3f738`.
- Renamed (`private collectRLSPoliciesRenamed;`): raw
`f20c387525fecc3b2c65bd6113a3c83cfe0388d4defabfc0a5a0fb2149c449db`,
normalised
`312949d507e1943df1f357c43a6a85d97fe8205aefea057ded1bf17866943ef7`.
- Both differ from the real hashes, so the comparison does see a
visibility change and a rename.
- Export tables: the ESM and CJS bundles each export the same 63 names
at base and head, loaded at runtime rather than parsed. The sorted name
list hashes
`f607fd765d6b277e08b3b46ccb16d1e2d133ec55658ffe40155aef25e1bf54f0` on
all four.
- The bundle JS bytes differ (`index.js` `aba80183` to `b52cd4f1`,
`index.mjs` `aca41ade` to `cd55afa9`), 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.
| method | base lines | moved function async | host members |
|---|---|---|---|
| `collectRLSPolicies` | `:10366`-`:10400` | no |
`admitCollectedPolicy`, `ownershipFloorAlternates`, `ql`, `rlsCompiler`
|
| `admitCollectedPolicy` | `:10419`-`:10427` | no | `orgScopingEnabled`,
`warnAuthoredTenantPolicyOnce` |
| `warnAuthoredTenantPolicyOnce` | `:10437`-`:10450` | no | `logger`,
`warnedAuthoredTenantPolicies` |
| `resolveUnresolvedCause` | `:10579`-`:10582` | yes |
`probeMetadataOutage`, `probeUnpublishedDraft` |
| `probeMetadataOutage` | `:10628`-`:10639` | yes | `metadata` |
| `probeUnpublishedDraft` | `:10673`-`:10684` | yes | `ql` |
| `computePartialMaskRules` | `:10708`-`:10727` | no |
`permissionEvaluator` |
| `computeQueryGuardFieldPerms` | `:10753`-`:10773` | no |
`computePartialMaskRules`, `foldFieldRequiredPermissions`,
`permissionEvaluator` |
| `computeForbiddenFieldWrites` | `:10788`-`:10804` | no |
`fieldMasker`, `foldFieldRequiredPermissions`, `permissionEvaluator` |
| `computeReadPartialMaskRules` | `:10826`-`:10840` | no |
`computePartialMaskRules` |
| `maskOperationResult` | `:10856`-`:10885` | no |
`computeReadPartialMaskRules`, `fieldMasker`,
`foldFieldRequiredPermissions`, `permissionEvaluator` |
| `foldFieldRequiredPermissions` | `:10887`-`:10902` | no |
`permissionEvaluator` |
| `getObjectFieldNames` | `:10908`-`:10924` | yes | `fieldNamesCache`,
`loadObjectFieldNames` |
| `writeCheckFieldOptions` | `:10943`-`:10952` | yes | `metadata`, `ql`
|
| `loadObjectFieldNames` | `:10954`-`:11015` | yes |
`datetimeFieldNamesCache`, `numberComparandFieldsCache`,
`tenancyDisabledCache` |
| `extractTargetField` | `:11035`-`:11053` | no | none (called directly)
|
Plus `isDelegatedPrincipalLeg` (base `:471`-`:487`, with its docblock),
read only by `collectRLSPolicies`. Left in the class on purpose:
`getObjectSecurityMeta`, which sits among these methods, because
`packages/spec/liveness/object.json` anchors it next to
`normalizeRequiredPermissions` and that file belongs to another lane.
## The shape (#23050 shape B, as #25222 and #25224 apply it)
- The class keeps each private method with its name, signature, docblock
and position, because tsup emits private members with their JSDoc into
the `.d.ts`. Each body is now one host literal plus `fn.call(host, …)`,
marked `[#25223 cut 1]`.
- `ObjectInputsHost` types 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 imports
`SecurityPlugin` type-only, so there is no runtime import cycle.
- Each moved function types its `this` parameter as the `Pick` of
`ObjectInputsHost` over exactly the members its own body reads, and each
stub annotates its host literal with the same `Pick`. A missing or extra
member is a compile error, and the closures get their parameter types
from it.
- Methods go in as late-bound closures that call `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. Every `this.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.
- Each field was classified on its own declaration, one rule for all. A
field declared `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 the `orgScopingEnabled` accessor goes as a getter, so
each read happens when the body makes it, after any `await`, as before.
`start()` assigns `ql`, `metadata` and `logger` after construction, and
tests replace `permissionEvaluator` on a constructed plugin.
- The host declares every field `readonly`, so a moved body cannot
assign a member through it; the typecheck passes, so none does.
- Six methods lose `async`: `resolveUnresolvedCause`,
`probeMetadataOutage`, `probeUnpublishedDraft`, `getObjectFieldNames`,
`writeCheckFieldOptions` and `loadObjectFieldNames`. Each returns the
moved async function's own promise. The stub cannot throw before it
returns that promise: building the host reads only `readonly` fields 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.
- `extractTargetField` reads no member, so its method calls it directly.
- The moved bodies keep saying `this.`, so the moved lines are unedited.
Nothing re-exports the module, so the package entry does not move.
## Proof of move-only
- **Bodies, by AST token**: for all 16 methods, the moved function's
body has the same TypeScript token sequence as the base method body, and
each of its 234 lines is the base line with two leading spaces removed.
Parameters, return types and `async` match. A negative control, one
token changed in one body, fails the check.
- **Class side**: each method keeps its docblock and signature, with
`async` dropped on the six above. `isDelegatedPrincipalLeg` is verbatim
(17 lines) in the module and absent from `security-plugin.ts`.
- **git's own view** (`git diff --color-moved=plain
--color-moved-ws=allow-indentation-change 72fad92 HEAD` over the two
source files): 255 of 262 deleted lines and 318 of 621 added lines are
detected as moved.
- Deleted, not moved (7): the six `private async` signature lines, which
lost `async`, and the `withOwnershipFloorAlternates,` name taken out of
a multi-name import.
- Added, not moved: the stub bodies, the import of the new module with
its comment, and in the module the license header, the module docblock,
the imports, `ObjectSecurityMeta` (a type alias over
`getObjectSecurityMeta`'s return type, so `security-plugin.ts` needs 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.
- **Size**: 890 changed lines (+628 / -262, 3 files), under the
3000-line human-merge threshold. `security-plugin.ts` goes 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.
- The symbol-anchor gates still resolve every anchor into this file:
`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`, and
`check-platform-checklist --changed-from origin/main` (6 anchors judged,
0 lost). All exit 0. The ADR anchor for this file names
`resolveCbpRelation`, `getMetadataReadableFields`,
`claimSeedOwnershipOnSettle` and `readSeededRecordIds`, none of which
moved.
- Line citations: no `security-plugin.ts:N` citation in the repo points
into a moved range (base `:471`-`:487` and `:10366`-`:11053`). Base
lines `:56`-`:470` move by between -1 and +19 (the import block
changed), lines from `:10373` on move up, and lines `:489`-`:10372` keep
their numbers. Three citations land in the shifted head range:
`CHANGELOG.md` (`:303`, release history), `docs/launch-readiness.md`
(`:309`) and `packages/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.
- Census gates run on base and head, compared. These have identical
output: `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-citations` and `check: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) and
`check:org-identifier` (3626 to 3627 files).
## Acceptance notes
- Nothing outside this cut is touched: no other section of the class, no
behaviour, no other lane's file.
- The test file gains comment lines only.
## Gates
All at head `a7cd7c62`, on base `72fad922`. Between that base and the
current `origin/main` (`1eb1afae`), no file under
`packages/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). Base `72fad922`, 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, and `check:test-typecheck` with 0 files
/ 0 errors in the debt ledger).
- `eslint --no-inline-config` over the three touched files: exit 0.
- `pnpm --filter @objectstack/plugin-security build`: exit 0, which
produced the `.d.ts` and bundles measured above.
- `dispatch-gates --commands`: 58 commands, 890 changed lines, under the
threshold. `--ran` reconciliation: 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.mjs` exit 0
- `node scripts/check-closing-keyword-parity.mjs` exit 0
- `node scripts/check-closing-keyword-parity.mjs --self-test` exit 0
- `node scripts/check-comment-mask-adoption.mjs` exit 0
- `node scripts/check-comment-mask-adoption.mjs --self-test` exit 0
- `node scripts/check-comment-mask-corpus.mjs` exit 0
- `node scripts/check-dts-emitted.mjs --self-test` exit 0
- `node scripts/check-issue-citations.mjs` exit 0
- `node scripts/check-keyed-text-bounds.mjs` exit 0
- `node scripts/check-keyed-text-bounds.mjs --self-test` exit 0
- `node scripts/check-platform-object-tenancy-census.mjs` exit 0
- `node scripts/check-platform-object-tenancy-census.mjs --self-test`
exit 0
- `node scripts/check-plugin-teardown-shape.mjs` exit 0
- `node scripts/check-plugin-teardown-shape.mjs --self-test` exit 0 (the
first run exited 3: this shallow clone lacked the pinned fixture commit;
it passed after `git fetch --depth=1` of that commit)
- `node scripts/check-position-name-fold-loaders.mjs` exit 0
- `node scripts/check-position-name-fold-loaders.mjs --self-test` exit 0
- `node scripts/check-registry-log-declared.mjs` exit 0
- `node scripts/check-registry-log-declared.mjs --self-test` exit 0
- `node scripts/check-rest-log-spy-declared.mjs` exit 0
- `node scripts/check-rest-log-spy-declared.mjs --self-test` exit 0
- `node scripts/check-system-context-census.mjs` exit 0
- `node scripts/check-system-context-census.mjs --self-test` exit 0
- `node scripts/check-tenant-audit-census.mjs` exit 0
- `node scripts/check-tenant-audit-census.mjs --self-test` exit 0
- `node scripts/check-undeclared-dep-imports.mjs` exit 0
- `node scripts/check-undeclared-dep-imports.mjs --self-test` exit 0
- `node scripts/docs-audit/check-affected-docs.mjs` exit 0
- `node scripts/docs-audit/check-drift-comment.mjs` exit 0
- `pnpm --filter @objectstack/spec run check:duration-unit-keys` exit 0
- `pnpm check:closed-ceremony-text` exit 0
- `pnpm check:cross-package-test-inputs` exit 0
- `pnpm check:doc-authoring` exit 0
- `pnpm check:driver-memory-census` exit 0
- `pnpm check:dts-closure` exit 0
- `pnpm check:dual-build-cjs-loads` exit 0 (the first run exited 3,
PREREQUISITE NOT MET, before a full build; after a full `pnpm exec turbo
run build` under the verify lock (77 of 77 tasks), it passed: 112 entry
points across 69 packages load)
- `pnpm check:engine-double-contract` exit 0
- `pnpm check:error-status-conformance` exit 0
- `pnpm check:gitlink-declared` exit 0
- `pnpm check:i18n` exit 0 (the same: exit 3 before the full build, exit
0 after it)
- `pnpm check:i18n-stale-fill` exit 0
- `pnpm check:issue-citations` exit 0
- `pnpm check:lean-entry-closure` exit 0
- `pnpm check:logger-receiver-detach` exit 0
- `pnpm check:nul-bytes` exit 0
- `pnpm check:objectql-double-limit` exit 0
- `pnpm check:org-identifier` exit 0
- `pnpm check:page-declaration-shape` exit 0
- `pnpm check:published-files` exit 0
- `pnpm check:query-options-erasure` exit 0
- `pnpm check:refd-timer-probe` exit 0
- `pnpm check:slot-lookup` exit 0
- `pnpm check:sourcemap-no-sources-content` exit 0
- `pnpm check:test-source-alias` exit 0
- `pnpm check:tier-file-adoption` exit 0
- `pnpm check:type-check-coverage` exit 0
- `pnpm check:type-check-debt` exit 0
- `pnpm check:watch-hint-literal` exit 0
- `pnpm check:where-matcher` exit 0
---
_Generated by [Claude
Code](https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent b62f959 commit f47f7f2
3 files changed
Lines changed: 628 additions & 262 deletions
File tree
- packages/plugins/plugin-security/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
17 | 24 | | |
18 | 25 | | |
19 | 26 | | |
| |||
0 commit comments