Repository navigation
refactor(plugin-security): move the everyone anchor, the explanation, the sharing verdicts and the authored-row write verdict out of security-plugin.ts (#25223 cut 10) - #25516
Conversation
… the sharing verdicts and the authored-row write verdict out of security-plugin.ts The bodies of declareEveryone and declareBuiltinPositions (the everyone anchor's definition and the built-in positions) move unedited to security-plugin-everyone-anchor.ts, with the four import names only they read. The body of explainAccessForCaller moves unedited to security-plugin-explain-access.ts, with the import names only it reads; the two caller-read classifications it shares with the write path stay in security-plugin.ts and are exported. The bodies of the six sharing verdicts (resolveSharingReadFilter, resolveSharingCanEdit, resolveWriteScopeForSharing, resolveSharingWriteVerdict, resolvePreImageFloorDrop, delegatorOwnWriteFloorOptions) move unedited to security-plugin-sharing-verdicts.ts. The body of checkAuthoredRowWrite moves unedited to security-plugin-authored-row-write.ts, with the probe scope constant only it reads. Each method keeps its name, signature, docblock and position and hands its body one host literal of exactly the members it reads. everyoneBaseline and destroy stay whole. The two readers that read the moved lines by path are re-pointed: the system-context page's explain anchor names the new module, and the delegator enumeration pin lists it. 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): 11 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 17 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 bedb07663c36fdd20005dbe453c11a49ae16d2cb && git checkout bedb07663c36fdd20005dbe453c11a49ae16d2cb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5188e7f18fea8d336d5f4a0f0a48c6af67dfc243 38392cb1b72803f2402f921e78a040e3ebbf1883 && git checkout -B drift-repro 5188e7f18fea8d336d5f4a0f0a48c6af67dfc243 && git merge --no-ff 38392cb1b72803f2402f921e78a040e3ebbf1883
node scripts/docs-audit/affected-docs.mjs --json 5188e7f18fea8d336d5f4a0f0a48c6af67dfc243
|
席位核对 · ACCEPT · 2026-10-23T18:37Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25223
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6353073966
Cut 10 of the
security-plugin.tssplit covers row 8 of the series plan: the everyone anchor, the explanation, the sharing verdicts and the authored-row write verdict. The claim is 6369634056. The row has four seams, so this one PR moves the bodies of ten methods out of the class, unedited, into four new modules. Cut 9 split its row the same way, over three.To
packages/plugins/plugin-security/src/security-plugin-everyone-anchor.ts:declareEveryone: theeveryoneanchor's definition, as a started kernel derives it. It is the deployment's baseline plus every package default amanage_metadataholder accepted. Each withheld acceptance is reported with its reason (ADR-0090 D5/D7, ADR-0131 D3, [Decision] 装包时包里声明的「默认权限集」怎么生效:管理员整部署接受一次、装包即自动生效,还是取消这条路(安全边界) #22801).declareBuiltinPositions: the six built-in positions, declared to the engine registry frominit().everyonenames the fail-closed baseline (ADR-0131 D2). It is synchronous and stays synchronous.To
packages/plugins/plugin-security/src/security-plugin-explain-access.ts:explainAccessForCaller(public): thesecurity/explainanswer for a caller. It covers:readverdict for a row the caller cannot read disagrees with the read door, and differs from its answer for an id no row carries (a controlled_by_parent detail under an unreadable master, measured) #22792, [Decision] explain: does an asker whose own read is REFUSED (a declared 4xx) count as an asker who cannot read, under ruling A of #22795? #22851).To
packages/plugins/plugin-security/src/security-plugin-sharing-verdicts.ts:resolveSharingReadFilter: the owner/share half of the read scope. It throws on a resolution failure, so its callers fail closed (analytics: /analytics/query ignores record-level scoping — a member counts and reads dimension values of records they cannot read #4467).resolveSharingCanEdit: the sharing gate of a single-record write. It fails closed when the probe throws (controlled_by_parentderivation ignores the master's ownership and share grants — children are readable (and writable) regardless of parent access #5386).resolveWriteScopeForSharing: the write-depth stash that both write probes hand the service (ADR-0057 D1).resolveSharingWriteVerdict: the tri-state write verdict (Row-level write gate consults neithermodifyAllRecordsnorsys_record_share.access_level— both declared write-widening mechanisms are inert #5492, ISharingService 写判定补三态(放行/不表态/拒绝)—— #5492 裁决 B 案的 step1,二态 canEdit 已实测产出 fail-open #6428).resolvePreImageFloorDrop: the floor decision of the by-id pre-image gate.security/explainasks it too (plugin-security: the record-grained explain verdict forupdateis not computed with the write path's inputs —record.visibleis false on rows the by-id PATCH admits, so every consumer hides Edit from permitted users #19963).delegatorOwnWriteFloorOptions: the composition knobs the delegator's own direct by-id write would get (ADR-0090 D10).To
packages/plugins/plugin-security/src/security-plugin-authored-row-write.ts:checkAuthoredRowWrite(public): theISecurityService.checkAuthoredRowWriteverdict,admitorabstain(On objects that carry sharing rules, sharing middleware answers FORBIDDEN before RLS update-wideners are consulted — the identical widener works on rule-less objects #5493, checkAuthoredRowWrite abstains on everyprivate-OWD cross-owner row, so #5493's by-id widener deferral is inert on the posture it was filed for — and its unit test cannot see it (fake engine bypasses middleware) #7281, [security] A check-onlyupdatepolicy suppresses the #7665 write-scope derivation — a contributor by-id-writes showcase_invoice rows it cannot read #8059). The scope its probe read runs under moves with it:AUTHORED_ROW_WRITE_PROBE_CONTEXT, which nothing else reads.The change is move-only, and the card stays open for
start(). The branch isclaude/project-thread-3fzya4, the harness-pinned branch for this session. It was restarted fromorigin/mainat05cc3ee953, which contains cut 9's mergedd124e9466. The branch name names no issue, so the claim comment is the identity record.What stays in the class. Each moved method keeps its name, signature, docblock and position, less
async. Every caller still reaches the methods through the plugin:securityservice binding (explain,checkAuthoredRowWrite);init()andkernel:ready;Two members of the row stay whole,
everyoneBaseline(6 body lines) anddestroy(3). Neither body is longer than its stub would be:everyoneBaseline's stub would also be 6 lines.Module-level declarations.
AUTHORED_ROW_WRITE_PROBE_CONTEXTis read only bycheckAuthoredRowWrite's body. It moves with its docblock, unedited and still unexported, as cuts 1 to 4 and 9 moved declarations that only their bodies read. Its docblock's{@link SecurityPlugin.checkAuthoredRowWrite}resolves through the module's type import of the class.absentUnderCallerReadandunreadableUnderCallerReadstay insecurity-plugin.ts, and each gainsexportand nothing else. The explanation reads both.absentUnderCallerReadis shared with the write path's step 2.7, which stays.unreadableUnderCallerReadis defined and documented beside it. Its docblock links toabsentUnderCallerRead, and cut 8'ssecurity-plugin-addressed-writes.tsheader names both as living insecurity-plugin.ts.requiredCapsForOperationis already exported. The explanation imports it by value, as the object-admission module does.Import names that only the moved bodies read leave
security-plugin.ts. The modules import them from the same specifiers:explainAccessForAskerandbuildContextForUser(./explain-engine.js);./builtin-positions.jsimport (declareEveryoneBaseline,registerBuiltinPositions);readAcceptedEveryoneSets(./suggested-audience-bindings.js);readDeclaredCapabilityContext(./declared-capability-context.js);vetOrganizationClaim(@objectstack/core);ExplainOperation(@objectstack/spec/security).No staying docblock links to any of them. Two staying docblocks name
readAcceptedEveryoneSetsandvetOrganizationClaim, in backticks only.The unexported interface
RlsFilterOptionsstays where every gate reads it. The explanation and the sharing verdicts name it through the class's own signature, as the type ofcomputeLayeredRlsFilter's fifth parameter lessundefined, so the moved text names it exactly as before. Cut 9's modules nameObjectSecurityMetathe same way.Each value import is read only inside a function body, at call time, and never while a module evaluates.
index.tsstill exports only what it did, so the published surface does not move (measured below).Two readers outside the moved code read the moved lines by path. Both are re-pointed in this PR, as cut 9 re-pointed the same two.
content/docs/permissions/system-context.mdx, row 9.check-system-context-censuskeys each elevation read by file and enclosing symbol, andexplainAccessForCaller'scallerContext?.isSystemread now sits in the explain module.security-plugin.ts#explainAccessForCallertosecurity-plugin-explain-access.ts#explainAccessForCaller. No row's text changes.packages/plugins/plugin-security/src/delegated-organization-membership.test.ts, the plugin-security: delegation probes and diagnostics (canReadObject / canWriteObject / canExport, field mask, explain) answer for an on-behalf-of context the operation prologue now refuses (PR #25101) #25107 enumeration pin. The explanation resolves a delegator and asks the organization check beside it. So the expected list gainssecurity-plugin-explain-access.ts, and the title says seven files instead of six.security-plugin.tscounts 1 and 1 (start(); it counted 2 and 2 at base).No ADR anchor entry is added. No paragraph of the
security-plugin.tsentry describes these bodies (see ADR anchors below).One receiver changes, disclosed.
resolveKernelServiceis a function-valued field. The explanation and three of the sharing verdicts call it through the host, so itsthisis now the host, not the plugin. Two facts keep every call answering as it did:start()assigns, which returnsctx.getService(name)and answersundefinedwhen that throws, and it reads nothis;Cut 9's report said that no body it moved calls a function-valued field. This cut's bodies do, so it is stated here.
The published
.d.tsdoes not changeMeasured under the series bar (ruling 6348891160 item 1), on base
05cc3ee953and on head38392cb1b7. Each was a fullturbo run buildin its own worktree. A package build of the same trees before the claim gave the same bytes.dist/index.d.tsanddist/index.d.mtsis the same for all four files, base and head:7d67966ce82e00a1531807b3b0008f16c489fc2c3199f9ad67078302eb2803da. The raw diff is 0 lines, so no union order moved either. These are the same bytes cut 9 measured, so neither cut 9's merge nor main's later commits moved this package's.d.ts.b53e9025f794e20debd2245afed5ceb1a0ad48109a2a21cc19232a562ccd9cb4.security-plugin.tsgains two exports,absentUnderCallerReadandunreadableUnderCallerRead. Each takes one read callback and answers a boolean promise, so its signature names no type from another module, so the cut 6 trap does not arise.index.tsre-exports neither.packages/plugins/plugin-security, sorts the members of each string-literal union and changes nothing else:Positive controls, each a one-line edit on a copy of the head
dist/index.d.ts:private resolveSharingReadFilter;) made public: raw8c8fad488058d08a54e23b5410b539b7eecad71a0e749b0133a61280e146bdde, normalisedf02f50957a57e31fd943e00fa0ef74113a2f0efdc4af8e3ee5b963c7e932a3c6;private declareEveryoneRenamed;): raw7af56294d477cdbc07b22567c87a110bbc2c36d37724cc47ed3652dc085a8ab4, normalised44d50a2b10e166373f11f31ef5e046c1a05ab9f5613e9d6b762c03c7ca15ec88;checkAuthoredRowWritesignature, withcontext?: anychanged tocontext?: unknown: rawb75e990785a61245092adf8414acba79b6fe27f330ebd7555e9a808642802556, normalised6b7331a568193fda94c1e45eabe85f5704d61c6ef99639bb197b7f11510b81b6.All three differ from the real hashes, so the comparison does see a visibility change, a rename and a change to a public signature.
The two public methods carry their full signatures and docblocks in the
.d.ts:explainAccessForCaller(line 525) andcheckAuthoredRowWrite(745). The eight private methods carry only their name and docblock:declareEveryone(447),declareBuiltinPositions(483), and the six sharing verdicts (557 to 676). Those lines are the same in both files.Export tables: the ESM and CJS bundles each export the same 66 names at base and at head. The lists were loaded at runtime, with
requireandimport()of the built bundles, not parsed. The sorted name list hashes to83576430f83cf60cecbcc5c0e80214f16c626a8e66f700294fc6a44361423116for all four, as at cuts 6 to 9.The bundle JS bytes differ, because code moved within them:
index.jsgoes fromdfa83a54to78d8f0eb, andindex.mjsfrom18c4d919to5d290484. Every value the new modules import comes from a file inside this package or from a specifiersecurity-plugin.tsalready imports. So the set of external specifiers each bundle loads is the same at base and head: 16 distinctrequirespecifiers inindex.jsand 19 distinctfromspecifiers inindex.mjs.Bundle order, disclosed:
builtin-positions.ts,manifest.tsandobjects/sys-audience-binding-suggestion.object.tsare now emitted just ahead of the four modules, where before they sat betweensecurity-plugin.ts's chunks. None of them importssecurity-plugin.ts, and their top level only declares constants.No changeset:
skip-changeset, per ruling 6348891160 item 4.What moved
The base line numbers are each method's signature and closing brace at
05cc3ee953. The last column lists the members the body reads, which is exactly what its host carries.declareEveryone(private, async):4912-:4946deploymentAcceptance,epoch,everyoneBaseline,metadata,qldeclareBuiltinPositions(private):4994-:5013everyoneBaselineexplainAccessForCaller(public, async):5061-:5373baselinePermissionSets,checkControlledByParentWrite,computeLayeredRlsFilter,computeReadPartialMaskRules,computeRlsFilter,delegatedAdminGate,foldFieldRequiredPermissions,getObjectSecurityMeta,permissionEvaluator,ql,readRowById,resolveKernelService,resolvePermissionSetsForContext,resolvePreImageFloorDrop,resolveSharingReadFilter,resolveWriteScopeForSharing,tenancyPostureresolveSharingReadFilter(private, async):5401-:5449getObjectSecurityMeta,permissionEvaluator,resolveKernelService,resolvePermissionSetsForContextresolveSharingCanEdit(private, async):5468-:5493logger,resolveKernelService,resolveWriteScopeForSharingresolveWriteScopeForSharing(private, async):5513-:5531getObjectSecurityMeta,permissionEvaluator,resolvePermissionSetsForContextresolveSharingWriteVerdict(private, async):5563-:5593logger,resolveKernelService,resolveWriteScopeForSharingresolvePreImageFloorDrop(private, async):5621-:5642collectRLSPolicies,resolveSharingWriteVerdictdelegatorOwnWriteFloorOptions(private, async):5667-:5684resolvePreImageFloorDropcheckAuthoredRowWrite(public, async):5754-:5838collectRLSPolicies,computeLayeredRlsFilter,logger,ql,resolvePermissionSetsForContextEach async stub drops
asyncand returns the moved function's promise, as cuts 1 and 3 to 9 did for their async methods.declareBuiltinPositionswas synchronous, and its stub returns its function'svoid. Two of the ten are public, so the.d.tscarries their full signatures. A body move does not touch a signature, and those lines do not change (above).The shape (#23050 shape B, as cuts 1 to 9 applied it)
The class keeps each method with its name, signature, docblock and position, because tsup emits members with their JSDoc into the
.d.ts. Each body is now one host literal plusfn.call(host, …), marked[#25223 cut 10].EveryoneAnchorHost,ExplainAccessHost,SharingVerdictsHostandAuthoredRowWriteHostare new. Each types every member as the class member itself (SecurityPlugin['m']). Each moved function types itsthisas thePickof its host over exactly the members its body reads, and the stub annotates its literal with the samePick. There is no cast, and no member becomes public or protected.Methods go in as late-bound closures that call
this.m(...a)on the plugin at call time. So a spy or test double set on the plugin is still the one the body reaches:No test replaces a moved method on the plugin instance today. One that did would still replace it for every caller.
Fields follow the rule cut 1 set, judged on each field's own declaration:
baselinePermissionSetsis declaredreadonly, so it goes by value.readonly:delegatedAdminGate,epoch,logger,metadata,permissionEvaluator,ql,resolveKernelServiceandtenancyPosture. So they go as getters, and each read happens when the body makes it.start()assigns all of them exceptpermissionEvaluatorafter construction, and tests replacepermissionEvaluatoron a constructed plugin.delegatedAdminGate,epoch,logger,permissionEvaluatorandqlare objects whose own methods the bodies call.resolveKernelServiceis the one function-valued field (disclosed above).readonly, so a moved body cannot assign a member through them. The typecheck passes, so none does.Nothing re-exports any of the modules, so the package entry does not move.
Proof of move-only
Bodies, by AST token. Each moved function's body has the same TypeScript token sequence as the base method body. Each of the 549 body lines is the base line with two leading spaces removed. Parameters, return types and
asyncmatch.explainAccessForCaller,callerContext?.isSystem === truechanged to=== false;resolveSharingCanEdit, the fail-closedreturn false;of the probe'scatchchanged toreturn true;;checkAuthoredRowWrite, the non-write abstentionreturn 'abstain';changed toreturn 'admit';;Class side. Each moved method keeps its docblock and signature, less
async. Every other class member is byte-identical to base: 116 named members plus the constructor, includingeveryoneBaselineanddestroy. The class has 127 members at base and at head.Top level of
security-plugin.ts. Of the 106 top-level statements at base other than the class, 97 are byte-identical at head. The other nine:./builtin-positions.jsimport is gone;AUTHORED_ROW_WRITE_PROBE_CONTEXTis gone from the file. The authored-row module holds the same statement and docblock, byte for byte;absentUnderCallerReadandunreadableUnderCallerReadare the base text withexportadded.The four new statements are the imports of the new modules.
The two re-pointed readers. The page diff is one line, the anchor's path. The pin diff is the title's count and one added list entry.
git's own view. This is
git diff --color-moved=plain --color-moved-ws=allow-indentation-change 05cc3ee953 HEADover the five source files. Of the 596 deleted lines, git sees 581 as moved. The other 15 are the nine signatures that loseasync, four of the import lines that change, and the two functions that gainexport. Of the 1,184 added lines, 679 show as moved: 669 in the four modules, and 10 insecurity-plugin.tswhose text git also finds among the deleted lines (nine stub};lines and the host linebaselinePermissionSets: this.baselinePermissionSets,). The rest are the modules' headers, imports, host interfaces, type aliases and function signatures, the ten stubs, and the four new imports with their comments.Size. 1,785 changed lines (+1,187 / -598) over 7 files, under the 3,000-line manual-merge line.
security-plugin.tsgoes from 8,852 to 8,406 lines.security-plugin-everyone-anchor.tshas 140 lines,security-plugin-explain-access.ts433,security-plugin-sharing-verdicts.ts277 andsecurity-plugin-authored-row-write.ts184.ADR anchors (ruling 6353073966 item 2)
node scripts/check-adr-symbol-anchors.mjsexits 0 and gives byte-identical output at base05cc3ee953and at head.dispatch-gates --commandsdoes not derive it for this change set, so it was run by hand.pnpm check:adr-anchorsexits 0 at base and at head, with 81 anchored files at both ends.No new entry. No paragraph of the
security-plugin.tsentry describes a moved body:So no module of this cut gets an entry, as the modules of cuts 1 and 6 to 8 have none.
The decision-letter count on the anchored surface goes from 1978 to 1969. That gate counts
ADR-NNNN Dkcitations in each anchored file and in each entry's own invariant text. Nine citations leave the anchored surface with the moved bodies:explainAccessForCaller7: ADR-0090 D6, ADR-0090 D10, ADR-0090 D12, ADR-0105 D3 twice, ADR-0111 D3, ADR-0111 D5;resolveSharingReadFilter1: ADR-0057 D1;checkAuthoredRowWrite1: ADR-0058 D4.The other bodies, the moved constant's docblock, the four module headers and the four new import comments cite none.
resolveSharingReadFilter'sdepthparameter comment also cites ADR-0090 D10. The parameter list belongs to the signature, which stays in the class, so that letter stays on the anchored surface. The move copies each signature unedited, so the function's parameter list carries it too.Measured directly:
security-plugin.tsholds 258 such citations at base and 249 at head, and the 9 that leave are exactly these.Every moved citation is still in the tree, in the module its body moved to. Citations that resolve tree-wide go from 52223 across 6651 files at base to 52224 across 6654 at head. Three of the modules cite an ADR. The one added citation is the
depthparameter comment, which both signatures now carry.docs/adr/**is not touched, and no anchor token is planted.The anchor map covers every ADR symbol anchor that names
security-plugin.ts: 27 anchors over 18 symbols.extractAnchorsandsymbolSegmentResolutionfromscripts/symbol-anchors.mjs. That is measurement, not a new gate.05cc3ee953BEFORE the claim, with the whole cut applied as a local trial, and no anchor changed class.security-plugin.ts, with its base class.The table gives each anchor's class at base and at head, and the line its own declaration resolves on. Every anchor stays in
security-plugin.ts.#CBP_MAX_CHAIN_DEPTH:712(top-level):744(top-level)#SecurityPlugin:1344(class):1346(class)#SecurityPlugin.getMetadataReadableFields:6030(method):5584(method)#SecurityPlugin.resolvePermissionSetsForContextUnmemoized:6624(method):6178(method)#assertControlledByParentWrite:8109(method):7663(method)#assertMasterRowEditable:8235(method):7789(method)#collectRLSPolicies:8289(method):7843(method)#composeWriteTenantWall:7447(method):7001(method)#computeControlledByParentFilter:7755(method):7309(method)#computeDelegatedReadFilter:5949(method):5503(method)#computeRlsFilter:7155(method):6709(method)#getReadFilter:5895(method):5449(method)#hasPlatformAdminPosture:7182(method):6736(method)#isPrincipalLessContext:585(top-level):617(top-level)#manage_metadata:338(top-level):370(top-level)#nav_permission_sets:1772(init):1774(init)#rlsFilter:4346(start):4348(start)#writeCheckPolicies:1239(top-level):1241(top-level)Readers
Two readers outside the moved code read the moved lines by path: the system-context page and the #25107 enumeration pin. Both are re-pointed in this PR (above). No other gate, baseline, docs anchor or test reads the moved lines by path. Each kind was read:
node scripts/isystem-census.mjs --jsongives the same 148 sites at base and at head, 0 differences. The comparison ignores line numbers and maps the four modules back tosecurity-plugin.ts.explainAccessForCaller'scallerContext?.isSystemread, which the page's re-pointed anchor names. The moved bodies'isSystem: truekeys and the probe constant's key are counted the same way at both ends.read153,declaration38,key438,other9), and so are the 5 non-elevation reads and the 22 packages. The files holding a site go from 74 to 75, and the scanned files from 397 to 399, the two modules that nameisSystem.check-system-context-censusand its--self-testexit 0 at base and at head. The totals are the same (148 sites, 127 symbols, 140 cited against 140 required, 163 anchors), and files go from 74 to 75. With the page left untouched, it reports 2 problems (claim).ql.findOneandql.find, and the everyone declaration handsqlto the derivation's readers.node scripts/tenant-audit-census.mjs --jsongives the same 238 write call sites at base and at head, compared the same way: 0 differences, none in the modules, and the same 175 non-engine calls. OnlyscannedSourcesmoves, from 730 to 734, the four modules.check-tenant-audit-censusexits 0 at base and at head with the same output, andtenant-audit-census.mdxis not touched.packages/core/src/security/row-serving-door-exposure.pin.test.tsandoperation-private-keys.pin.test.ts. No moved body names the read admission or the operation-private keys. Run on their own at head: 8 of 8 pass.access-security.json:2032citessecurity-plugin.ts#declareEveryone(the everyone derivation). The anchor resolves on the method, which stays with its docblock.check-platform-checklist(the script andpnpm check:platform-checklist) exits 0 at base and at head with the same output.plugin-security/src/security-plugin*.ts#…anchor outsidedocs/adr/was resolved in its own file at base and at head: 32 at both, 0 unresolved. At head, 22 point intosecurity-plugin.ts, 1 into the explain module (the re-pointed row 9), and 9 into the modules of cuts 3, 5 and 9, as at base.system-context.mdxand specconversions/registry.ts. They hold 22 anchors over 13 symbols that namesecurity-plugin.ts. WithsymbolSegmentResolution, each resolves at head with its base class: 0 changed.check-scripts-symbol-anchorsand its--self-test, andpnpm --filter @objectstack/spec check:liveness, give the same output at base and at head.rest-server.ts:5953, which namesSecurityPlugin.explainAccessForCallerin prose;{@link computeDelegatedReadFilter}inresolveSharingReadFilter'sdepthcomment, and the explanation's inner links toresolvePreImageFloorDropandcheckControlledByParentWrite. The class keeps the docblocks those links belong to.security-plugin.ts:Ncitation in the repo points into the moved ranges (base:760to:788and:4912to:5838).Gates
All gates ran against base
05cc3ee953and on head38392cb1b7, each in its own worktree after a fullturbo run buildthere (both exit 0).pnpm --filter @objectstack/plugin-security test(vitest run): Test Files 214 passed (214); Tests 4465 passed, 45 skipped (4510). Base05cc3ee953gives the same counts.pnpm --filter @objectstack/plugin-security typecheck: exit 0 at head and at base. It runstsc --noEmit, the scripts tsconfig, andcheck:test-typecheck.eslint over the six touched source and test files: exit 0.
pnpm --filter @objectstack/plugin-security build: exit 0. It produced the.d.tsand the bundles measured above.row-serving-door-exposure.pin.test.tsandoperation-private-keys.pin.test.ts(@objectstack/core): 8 of 8 pass.dispatch-gates --commands: 88 commands. The--ranreconciliation shows 88 derived, 88 run, 0 NOT-MEASURED and 0 UNRUN. The zero is derived: all 88 recorded an exit code, and none is 3.Nine commands are not derived for this change set, and the reconciliation names them as recorded outside the derivation. Each was run by hand:
node scripts/check-adr-symbol-anchors.mjs;pnpm check:adr-anchors;check-platform-checklist, both the script andpnpm check:platform-checklist;check-scripts-symbol-anchorsand its--self-test;--jsonreads;pnpm --filter @objectstack/spec check:liveness.pnpm check:query-options-erasureis derived for this change set this time.Seven more were run at base and at head. Six keep a roster in a directory this change touches, so
dispatch-gatesmarks their silence as no evidence:check-published-list-mirrors,check:authz-resolver,check:console-injection,check:error-code-casing,check:filter-alias-parityandcheck:published-readme-exports. The seventh is the package typecheck. All seven exit 0 at both.Base against head, all 97 commands. All 97 (the 88 derived and the 9 hand-run) exit 0 at head and at base. 72 give identical output at both, once the worktree path and timings are masked. The other 25, each read line by line:
check:adr-anchors,check-system-context-censusand the two census--jsonreads move as described above.check-issue-citationsjudges a change set, so it has nothing to read at base. At head it judged 42 citations across 5 files, and all resolve.check-closing-keyword-parity12029 to 12033 tracked files;check:gitlink-declared12029 to 12033 index entries;check:nul-bytes12020 to 12024;check-comment-mask-corpus10005 to 10009;check-plugin-teardown-shape9397 to 9401 sources;check-position-name-fold-loaders12370 to 12374 files, with the same 8 references;check-undeclared-dep-imports3316 to 3320 files;check:duration-unit-keys3321 to 3325;check:closed-ceremony-text4573 to 4577;check:doc-authoring1434 to 1438 parsed sources;check:engine-double-contract3625 to 3629 production files;check:error-status-conformance3413 to 3417;check:logger-receiver-detach3633 to 3637;check:org-identifier3658 to 3662;check:page-declaration-shape3666 to 3670;check:refd-timer-probe9998 to 10002;check:type-check-coverageandcheck:type-check-debt3542 to 3546 walked sources;check:vendor-version-stamps10397 to 10401.check-undeclared-dep-importsalso reads 2865 to 2872@objectstack/*specifiers. Those are the seven the modules import, all declared dependencies.security-plugin.tskeeps every specifier it had.check:doc-authoringalso reads 104377 to 104528 strings, the strings of the new headers and stubs.check:skill-identifier-livenessreads 139798 to 139805 implementation word tokens. Those are the seven words new to the tree: the four host names, the stubs' two getter localsreadEpochandreadResolveKernelService, and the word "abstentions" in the authored-row module's header. No citation changes.Per command (exit code at head, then at base), the derived first and the hand-run after:
node scripts/check-ci-filter-parity.mjsexit 0 (base 0)node scripts/check-closing-keyword-parity.mjsexit 0 (base 0)node scripts/check-closing-keyword-parity.mjs --self-testexit 0 (base 0)node scripts/check-comment-mask-adoption.mjsexit 0 (base 0)node scripts/check-comment-mask-adoption.mjs --self-testexit 0 (base 0)node scripts/check-comment-mask-corpus.mjsexit 0 (base 0)node scripts/check-doc-frontmatter.mjsexit 0 (base 0)node scripts/check-doc-frontmatter.mjs --self-testexit 0 (base 0)node scripts/check-doc-route-spelling.mjs --advisoryexit 0 (base 0)node scripts/check-doc-route-spelling.mjs --self-testexit 0 (base 0)node scripts/check-docs-section-name.mjsexit 0 (base 0)node scripts/check-docs-section-name.mjs --self-testexit 0 (base 0)node scripts/check-dts-emitted.mjs --self-testexit 0 (base 0)node scripts/check-issue-citations.mjsexit 0 (base 0)node scripts/check-keyed-text-bounds.mjsexit 0 (base 0)node scripts/check-keyed-text-bounds.mjs --self-testexit 0 (base 0)node scripts/check-platform-object-tenancy-census.mjsexit 0 (base 0)node scripts/check-platform-object-tenancy-census.mjs --self-testexit 0 (base 0)node scripts/check-plugin-teardown-shape.mjsexit 0 (base 0)node scripts/check-plugin-teardown-shape.mjs --self-testexit 0 (base 0)node scripts/check-position-name-fold-loaders.mjsexit 0 (base 0)node scripts/check-position-name-fold-loaders.mjs --self-testexit 0 (base 0)node scripts/check-registry-log-declared.mjsexit 0 (base 0)node scripts/check-registry-log-declared.mjs --self-testexit 0 (base 0)node scripts/check-rest-log-spy-declared.mjsexit 0 (base 0)node scripts/check-rest-log-spy-declared.mjs --self-testexit 0 (base 0)node scripts/check-section-landing-index.mjsexit 0 (base 0)node scripts/check-section-landing-index.mjs --self-testexit 0 (base 0)node scripts/check-system-context-census.mjsexit 0 (base 0)node scripts/check-system-context-census.mjs --self-testexit 0 (base 0)node scripts/check-tenant-audit-census.mjsexit 0 (base 0)node scripts/check-tenant-audit-census.mjs --self-testexit 0 (base 0)node scripts/check-undeclared-dep-imports.mjsexit 0 (base 0)node scripts/check-undeclared-dep-imports.mjs --self-testexit 0 (base 0)node scripts/docs-audit/check-affected-docs.mjsexit 0 (base 0)node scripts/docs-audit/check-drift-comment.mjsexit 0 (base 0)pnpm --filter @objectstack/lint run check:doc-formula-expressionsexit 0 (base 0)pnpm --filter @objectstack/lint run check:doc-security-postureexit 0 (base 0)pnpm --filter @objectstack/spec run check:docsexit 0 (base 0)pnpm --filter @objectstack/spec run check:duration-unit-keysexit 0 (base 0)pnpm --filter @objectstack/spec run check:empty-stateexit 0 (base 0)pnpm --filter @objectstack/spec run check:livenessexit 0 (base 0)pnpm --filter @objectstack/spec run check:skill-examplesexit 0 (base 0)pnpm --filter @objectstack/spec run check:strictness-ledgerexit 0 (base 0)pnpm --filter @objectstack/spec run check:variant-docsexit 0 (base 0)pnpm --filter @objectstack/spec run check:yaml-examplesexit 0 (base 0)pnpm check:closed-ceremony-textexit 0 (base 0)pnpm check:corpus-claim-driftexit 0 (base 0)pnpm check:cross-package-test-inputsexit 0 (base 0)pnpm check:doc-anchorsexit 0 (base 0)pnpm check:doc-authoringexit 0 (base 0)pnpm check:docs-audit-scopeexit 0 (base 0)pnpm check:docs-redirectsexit 0 (base 0)pnpm check:docs-single-h1exit 0 (base 0)pnpm check:docs-spec-enumerationsexit 0 (base 0)pnpm check:docs-transcript-driftexit 0 (base 0)pnpm check:driver-memory-censusexit 0 (base 0)pnpm check:dts-closureexit 0 (base 0)pnpm check:dual-build-cjs-loadsexit 0 (base 0)pnpm check:engine-double-contractexit 0 (base 0)pnpm check:error-status-conformanceexit 0 (base 0)pnpm check:gitlink-declaredexit 0 (base 0)pnpm check:i18nexit 0 (base 0)pnpm check:i18n-stale-fillexit 0 (base 0)pnpm check:issue-citationsexit 0 (base 0)pnpm check:lean-entry-closureexit 0 (base 0)pnpm check:logger-receiver-detachexit 0 (base 0)pnpm check:merge-driverexit 0 (base 0)pnpm check:nul-bytesexit 0 (base 0)pnpm check:objectql-double-limitexit 0 (base 0)pnpm check:org-identifierexit 0 (base 0)pnpm check:page-declaration-shapeexit 0 (base 0)pnpm check:published-filesexit 0 (base 0)pnpm check:published-readme-linksexit 0 (base 0)pnpm check:query-options-erasureexit 0 (base 0)pnpm check:react-page-adapter-contractexit 0 (base 0)pnpm check:refd-timer-probeexit 0 (base 0)pnpm check:role-wordexit 0 (base 0)pnpm check:skill-identifier-livenessexit 0 (base 0)pnpm check:slot-lookupexit 0 (base 0)pnpm check:sourcemap-no-sources-contentexit 0 (base 0)pnpm check:test-source-aliasexit 0 (base 0)pnpm check:tier-file-adoptionexit 0 (base 0)pnpm check:type-check-coverageexit 0 (base 0)pnpm check:type-check-debtexit 0 (base 0)pnpm check:vendor-version-stampsexit 0 (base 0)pnpm check:watch-hint-literalexit 0 (base 0)pnpm check:where-matcherexit 0 (base 0)node scripts/check-adr-symbol-anchors.mjsexit 0 (base 0)pnpm check:adr-anchorsexit 0 (base 0)node scripts/check-platform-checklist.mjsexit 0 (base 0)pnpm check:platform-checklistexit 0 (base 0)node scripts/check-scripts-symbol-anchors.mjsexit 0 (base 0)node scripts/check-scripts-symbol-anchors.mjs --self-testexit 0 (base 0)node scripts/isystem-census.mjs --jsonexit 0 (base 0)node scripts/tenant-audit-census.mjs --jsonexit 0 (base 0)pnpm --filter @objectstack/spec check:livenessexit 0 (base 0)Acceptance notes
Nothing outside this cut is touched: no other section of the class, no behaviour, no
docs/adr/**, noscripts/adr-anchors/entry, no census ledger and nopackages/spec/**. Outside the package, one file changes: the one anchor path on the system-context page.Serial check, also in the claim: of the 20 open PRs at claim time, one touched
security-plugin.tsandsystem-context.mdx. That was feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483 (Security: a runAs system flow writes identity-assignment rows (sys_user_position, sys_invitation) past the delegated-administration gate #25251's first PR): one import near:29, a call instart()near:2577, and a new page row 1b. None of those lines is in row 8 or in an import line this cut edits. feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483 has since merged (a285ff29a1). Re-checked before opening this PR: of the 13 open PRs now, two more touch the package::47and a block instart()near:4759, ahead of row 8;builtin-positions.ts, whose two functions the everyone-anchor module imports, and keeps both signatures unchanged.A scratch merge of
5188e7f18f, feat(plugin-security): a boot upgrade impact report for the non-access classes, raised to the administrator like the access upgrade report #25512, feat(security): an external organization role — portal users read their own identity rows and reach data only through explicit grants #25515 and this head merges cleanly. Neither PR touches a moved line or a name this cut moves.Since the local runs,
origin/maingained 13 commits, up to5188e7f18f, feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483 among them. They touch two of this cut's files, at the feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483 hunks above.5188e7f18fis clean. On it, these all exit 0:check-adr-symbol-anchors,check:adr-anchors,check-platform-checklist,check-system-context-censusandcheck-tenant-audit-census;isystem-censusread;tsc --noEmitfor the package;check:adr-anchorscounts 82 anchored files and 1970 decision-letter citations, against 82 and 1979 on5188e7f18falone: the same 9 leave. The system-context census counts 149 sites across 76 files, against 75 files on5188e7f18falone: feat(plugin-security)!: a flow that runs as system writes no identity assignment #25483's new read is the 149th site.After this cut, row 8 leaves
everyoneBaselineanddestroywhole insecurity-plugin.ts, plus the ten method shells.security-plugin.tshas 8,406 lines at head and 8,852 at base. Next isstart(). It is Tier H, and it goes to the seat with its scope before any PR.Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa