Repository navigation
refactor(plugin-security): move the rest of the tenant write wall and the context prologue out of security-plugin.ts (#25223 cut 6) - #25396
Conversation
… the context prologue out of security-plugin.ts The bodies of organizationWallRefusal (the ADR-0123 D2 scope pre-test) and writeWallVerdict (the wall's per-row verdict for the write preview) join composeWriteTenantWall in security-plugin-write-tenant-wall.ts, with the two module-level declarations only they read, callerHasOrganizationScope and carriesActiveOrganization. The bodies of resolveOperationPrincipals (the middleware's context prologue: ADR-0096 D5, ADR-0090 D10) and refuseGuestWithoutOrganization (ADR-0138 D4) move unedited to a new module, security-plugin-operation-principals.ts, with the two things only they read: the strict-mode refusal principalLessDenial and the @objectstack/core import of the guest refusal. Each method keeps its name, signature, docblock and position and hands its body one host literal of exactly the members it reads. isPrincipalLessContext, isGuestEnvelope and isTenantWallDenial stay and are exported and imported by value; principalLessDenial is imported back type-only for the docblock link that names it. The system-context census row 6c anchor and the per-file delegator-resolution pin follow the moved bodies. 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): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
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 5cd08369108354731272baf7634167e4dca98309 && git checkout 5cd08369108354731272baf7634167e4dca98309
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b22ab8d6fb3462bff05142efc6b6cc040c95f646 0177b21a24872691eb7853eac63dfa0c6039678f && git checkout -B drift-repro b22ab8d6fb3462bff05142efc6b6cc040c95f646 && git merge --no-ff 0177b21a24872691eb7853eac63dfa0c6039678f
node scripts/docs-audit/affected-docs.mjs --json b22ab8d6fb3462bff05142efc6b6cc040c95f646
|
席位核对 · ACCEPT · 2026-10-23T07:56Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25223
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6353073966
Cut 6 of the
security-plugin.tssplit: the rest of step 3.7's Layer 0 tenant write wall, and the engine middleware's context prologue. The claim is 6358893564, amended by 6359105931 before the push (below). The bodies of four async methods move unedited out of the class:packages/plugins/plugin-security/src/security-plugin-write-tenant-wall.ts, beside cut 5'scomposeWriteTenantWall:organizationWallRefusal, the ADR-0123 D2 scope pre-test. The wall andcanWriteObjectboth ask it.writeWallVerdict, the wall's per-row verdict for the write preview (objectql: thevalidatepreview (import dry run) runs no tenant write wall, so after PR #25048 it reportsorganization_idas dropped for a walled insert that the write stores, and it answers valid for a value the write refuses #25062), which the engine asks throughregisterWriteWallVerdict.packages/plugins/plugin-security/src/security-plugin-operation-principals.ts:resolveOperationPrincipals, the context prologue. It refuses a context the operation may not run under (ADR-0096 D5 strict mode; on a walled posture, an on-behalf-of context whose delegator is not a member of the live organization, ADR-0090 D10 and plugin-security / objectql: an on-behalf-of insert by a crossing platform operator that names no organization may be stamped outside the delegator's organization set (source reading only; measure reach first) #25063). It resolves every other context to the principal's permission sets, and the delegator's on a delegated context.refuseGuestWithoutOrganization, the prologue's ADR-0138 D4 refusal of a guest acting in no organization.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/mainatbdf762c9bd, which carries cut 5's mergebe318db654. It 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. Three thin adapters of the wall stay whole:computeWriteTenantCheckFilter,computeWriteTenantWallandraiseWriteWallRefusal. Each body is shorter than its stub would be. The middleware,canWriteObjectand every other caller still call the methods.Module-level declarations.
callerHasOrganizationScopeandcarriesActiveOrganization. Only the moved bodies read them: the pre-test reads the first, and the first and cut 5's body read the second.carriesActiveOrganizationloses theexportcut 5 gave it, so the wall module no longer imports anything back fromsecurity-plugin.tsfor it.principalLessDenial, the ADR-0096 D5 refusal only the prologue throws, docblock included, and the@objectstack/coreimport ofGuestOrganizationUnresolvedErrorandresolveGuestOrganization, comment included, because only the guest refusal reads it.principalLessDenialgainsexportthere.export. Each module imports what it reads by value, as cuts 3 and 5 import theirs.isPrincipalLessContext: ADR-0138:152 anchors it insecurity-plugin.ts, and the probes and the row scope read it there.isGuestEnvelope: the middleware's owner-write step and the set resolution read it.isTenantWallDenial:tenant-layer.tsnames it as living insecurity-plugin.ts.isPrincipalLessContext's docblock names the refusal with{@link principalLessDenial}.security-plugin.tsimports that name type-only from the prologue module, so the link still resolves. A type-only import emits nothing and has no runtime effect; typecheck and lint pass with it.principalLessDenialstaying insecurity-plugin.tswithexport. That was measured on the first head build: its return type makes the file's declarations importerrors.ts, and the publisheddist/index.d.tsthen declaresPermissionDeniedErrorandisPermissionDeniedErrorat the top instead of after the class. That is a raw diff of 102 lines, one 51-line block moved with its text unchanged, and ruling 6348891160 admits union-order swaps only. With the refusal moved, the head.d.tsis byte-identical to base.index.tsstill exports onlySecurityPlugin, so the published surface does not move (measured below).WriteTenantWall,WriteWallRefusalandWriteWallRowVerdictkeep their one declaration. The method signatures,raiseWriteWallRefusalandstart()read them. The wall module names all three through the class's own signatures, as cuts 2 to 5 name theirs.postureUsesUnionScopename leaves the@objectstack/spec/securityimport ofsecurity-plugin.ts, because no code left there reads it. The wall module already imports it.Two readers outside the moved code follow the bodies.
content/docs/permissions/system-context.mdx, row 6c. Its anchor issecurity-plugin.ts#writeWallVerdict, the census site of the body'scontext.isSystemread.check-system-context-censuskeys each elevation read by file and enclosing symbol, so with the body moved and the anchor unchanged the census finds a file with no row. The anchor now namessecurity-plugin-write-tenant-wall.ts#writeWallVerdict. Only the path changed. The census lists the same sites at base and head, with the one site's file mapped back (below).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. It counts, per source file, the delegator resolutions and the organization-refusal asks, and lists the files that resolve one. The prologue's one resolution and its one ask now sit in the new module. The expected list gainssecurity-plugin-operation-principals.ts, and the title says three files instead of two. The rule it pins, asks equal resolutions in every file, is unchanged, and it holds at head: the new module counts 1 and 1,security-plugin.ts7 and 7.The published
.d.tsdoes not changeUnder the series bar (ruling 6348891160 item 1), measured on base
bdf762c9bdand on the head (the tree of this PR's commit):dist/index.d.tsanddist/index.d.mts, base and head, all four the same:af5dee1bebafa0376a876d1e7b247c878ea42ac35c7336a6d3c12a7953143156. The raw diff is 0 lines, so no union order moved either.431e7f5097ac1c220bc840188aef02105bc0b16e845e976dd562d02cc79be8bd.origin/mainmoved between cut 5's base and this one. Base and head here are both built againstbdf762c9bd's dependencies, rebuilt on that commit first.principalLessDenialkept insecurity-plugin.tsand exported, the head.d.tsdeclaredPermissionDeniedErrorandisPermissionDeniedErrorat the top instead of after the class: a raw diff of 102 lines, one 51-line block moved with its text unchanged. The refusal moved to the prologue module instead, and the head above is the tree after that change.packages/plugins/plugin-security(it sorts the members of each string-literal union and changes nothing else):dist/index.d.ts, line 1813 (private organizationWallRefusal;):organizationWallRefusal;): raw648873d54d4f3adf15cdbdd199ae7fa25bc46a30230249cebfe5e5bb812c29d1, normalised847afa0caa5b4756a02a9e7833a0a878783a41187ac45e2e6aea5600a2b35fcf.private organizationWallRefusalRenamed;): raw7a1e24b3874a47fb0a2a54f368917c6db961f2fcde858a11145e55cf7c9e423d, normalisedf76ae5f05b7ca709d90f94a15277906f2862240f3b923c61707dcd703a632bec.writeWallVerdictis public, so the.d.tscarries its full signature. It is the same line at base and head (line 1874 of both): the four parametersobject,operation,contextandrows, and the promise of one verdict per row ornull.requireandimport()of the built bundles), not parsed. The sorted name list hashes83576430f83cf60cecbcc5c0e80214f16c626a8e66f700294fc6a44361423116on all four. The count differs from cut 5's 63 becauseorigin/mainadded exports between the two bases; it is the same at this base and head. The new exports ofsecurity-plugin.ts(isGuestEnvelope,isPrincipalLessContext,isTenantWallDenial) do not reach the entry.index.jsfe2eebf9to77ef1113,index.mjs254fc57btof604cc07), because code moved within them.No changeset:
skip-changeset, per ruling 6348891160 item 4.What moved
The base line numbers are each method's signature and closing brace at
bdf762c9bd. The last column lists the members the body reads, which is exactly what its host carries.organizationWallRefusal(private, async):8471-:8493computeWriteTenantCheckFilter,orgScopingEnabled,tenancyPosturewriteWallVerdict(public, async):8580-:8612composeWriteTenantWall,resolveOperationPrincipalsresolveOperationPrincipals(private, async):8953-:9012logger,ql,refuseGuestWithoutOrganization,resolvePermissionSetsForContext,tenancyPosturerefuseGuestWithoutOrganization(private, async):9038-:9062guestOrganizationRefusalWarned,logger,ql,tenancyPostureEach stub drops
asyncand returns the moved function's promise, as cuts 1, 3 and 5 did for their async methods. The.d.tsemits noasync, so this does not reach it. That includeswriteWallVerdict, the one public method here, whose full signature the.d.tscarries.The shape (#23050 shape B, as cuts 1 to 5 applied it)
.d.ts. Each body is now one host literal plusfn.call(host, …), marked[#25223 cut 6].WriteTenantWallHostgains the members the two new wall bodies read (composeWriteTenantWall,resolveOperationPrincipals), andOperationPrincipalsHostis 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.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:computeWriteTenantCheckFilter;refuseGuestWithoutOrganizationandresolvePermissionSetsForContext.No moved body calls a function-valued field, so no receiver changes.
guestOrganizationRefusalWarnedis declaredreadonlyand goes by value, so the guest refusal records each reason it warned for in the plugin's own set, once per reason as before.ql,loggerandtenancyPostureare notreadonly(the plugin assigns each after construction), andorgScopingEnabledis the class's own getter. All four go as getters, so each read happens when the body makes it.readonly, so a moved body cannot assign a member through it; the typecheck passes, so none does.Proof of move-only
asyncmatch. Two negative controls fail the check:context.isSystem || !context.userIdchanged tocontext.isSystem && !context.userIdin the preview's first guard.composeWriteTenantWallin the wall module is byte-identical to base, its pointer docblock included.async. Every other class member, 122 named members plus the constructor, is byte-identical to base. That includes the three adapters,canWriteObject,checkControlledByParentWrite,resolvePermissionSetsForContextandstart(). The class has 127 members at base and at head.security-plugin.ts: 96 of the 105 base top-level statements other than the class are byte-identical to base,WriteTenantWall,WriteWallRefusalandWriteWallRowVerdictincluded. The nine that differ:exportbeforefunction:isGuestEnvelope,isPrincipalLessContext,isTenantWallDenial;callerHasOrganizationScope(identical),carriesActiveOrganization(identical less theexportcut 5 added),principalLessDenial(identical plusexport) and the@objectstack/coreimport (identical, comment included);@objectstack/spec/securityimport, which losespostureUsesUnionScope;principalLessDenial.git diff --color-moved=plain --color-moved-ws=allow-indentation-change bdf762c9bd HEADover the three source files): 183 of 240 deleted lines and 211 of 448 added lines are detected as moved.security-plugin.ts(14): cut 5's import of the wall module with its comment (4 lines), thepostureUsesUnionScope,import line, the five declaration lines that come back changed (isGuestEnvelope,isPrincipalLessContextandisTenantWallDenialwithexport;principalLessDenialwithexport, in the prologue module;carriesActiveOrganizationwithoutexport, in the wall module) and the four method signature lines, which come back withoutasync. In the wall module (43): cut 5's module docblock and its import ofcarriesActiveOrganization, rewritten for two cuts, and two lines of the host docblock, which now speak of several functions.OperationPrincipalsHost, the pointer docblocks 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 9,992 to 9,863 lines, and the wall module from 437 to 567. The prologue module is 207 lines.ADR anchors (ruling 6353073966 item 2)
node scripts/check-adr-symbol-anchors.mjs: exit 0 at basebdf762c9bdand 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 same counts as cut 5. 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 at base and at head, 79 anchored files at both (no entry added or changed). One count moves, and it is disclosed here rather than answered with a new anchor entry. The decision-letter citations on the anchored surface go from 2014 to 2010. SevenADR-NNNN Dkcitations move with the prologue's text into the prologue module, which has no anchor entry:ADR-0090 D10four times in the delegation branch,ADR-0138 D4twice (the moved import comment and the guest refusal's warning) andADR-0096 D5once (principalLessDenial's docblock). The two new import comments insecurity-plugin.tsadd three. Every citation still resolves as a citation: the total goes from 52091 to 52100, across 6619 to 6620 files. Writing an invariant paragraph for the prologue module would be new prose about the prologue, outside a move-only cut. [maintainer] refactor(plugin-auth): splitsrc/auth-manager.ts(10,338 lines, +1,867 in 30 days) along itsAuthManagerseams, one move-only slice per PR #25224 cut 5 disclosed the same kind of shift the same way. The wall module is anchored, so the six citations that move into it (the pre-test's comment and the two lifted docblocks) stay on the anchored surface.scripts/adr-anchors/is unchanged. The wall module's entry (cut 5) carries ADR-0123 and the ADR-0123 D2 paragraph, which describes the no-active-organization pre-test;organizationWallRefusalis that pre-test, and it now lives in the module the entry names. No paragraph of thesecurity-plugin.tsentry (ADR-0055, ADR-0106, ADR-0123 D2, ADR-0138 D1) describes the prologue or the guest refusal, so the prologue module gets no entry, as cut 1's module has none. Thesecurity-plugin.tsentry is unchanged.docs/adr/**is not touched.security-plugin.ts: 27 anchors, 18 symbols. It was measured withextractAnchorsandsymbolSegmentResolutionfromscripts/symbol-anchors.mjs, which is measurement, not a new gate. It was read onbdf762c9bdBEFORE the cut, with the four 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:663(top-level):658#SecurityPlugin:1340(class):1290#SecurityPlugin.getMetadataReadableFields:2516(start),:6135(method):2466,:6085#SecurityPlugin.resolvePermissionSetsForContextUnmemoized:7046(method):6996#assertControlledByParentWrite:9249(method shell):9120#assertMasterRowEditable:9375(method shell):9246#collectRLSPolicies:9429(method):9300#composeWriteTenantWall:8513(method shell):8458(method shell), and:8533(see below)#computeControlledByParentFilter:8841(method shell):8766#computeDelegatedReadFilter:6013(method):5963#computeRlsFilter:5265(explainAccessForCaller),:7740(method):5215,:7690#getReadFilter:2275(start),:5891(method):2225,:5841#hasPlatformAdminPosture:7767(method):7717#isPrincipalLessContext:517(top-level):531(top-level, nowexport function)#manage_metadata:266(PLATFORM_ADMIN_ONLY_CAPABILITIES):280#nav_permission_sets:1768(init):1718#rlsFilter:4342(local instart):4292#writeCheckPolicies:1235(top-level):1185No resolving site moves out of the file. One anchor gains a site, and its class does not change. ADR-0123:4
#composeWriteTenantWallstill resolves on the method declaration, which stays as a shell. At head the name also starts a line inwriteWallVerdict's stub, as its host-literal key, the closure that calls the method on the plugin (:8533), and appears quoted in that stub'sPick(:8532). Cut 3's stub added the same kind of site for#assertControlledByParentWrite(checkControlledByParentWrite's stub,:8980here). The anchor is judged by its class, which isdeclarationat base and at head. ADR-0138:152#isPrincipalLessContextresolves on the declaration, which stays and gainsexport.Readers
Two readers of the moved lines exist outside the moved code, the census row and the enumeration pin above, and both are re-pointed in this PR. No other test, script, gate, baseline or docs anchor reads the moved lines by path.
check-system-context-censusexits 0 at base and at head. Both print148 elevation read sites in 22 packages … the page cites 140 symbol(s) against 140 required, over 163 anchors. The only difference is the file count, 70 to 71, because the one read site ofwriteWallVerdictnow sits in the wall module.node scripts/isystem-census.mjs --jsongives the same 148 sites at base and head, compared without line numbers and with the two new modules mapped back tosecurity-plugin.ts: 0 differences. The role counts are the same (read153,declaration38,key438,other9), and only the corpus and scanned file counts move, by one.resolveDelegatorContextandresolveGuestOrganization, which takeqlas an argument.node scripts/tenant-audit-census.mjs --jsongives the same 237 write call sites at base and head, compared the same way: 0 differences, the same totals, 0 unresolved, 0 unledgered. OnlyscannedSourcesmoves, 715 to 716, plus the measured-at ref.check-tenant-audit-censusprints the same line at both.packages/core/src/security/row-serving-door-exposure.pin.test.tsclassifies every caller of the read admission. The moved bodies call no admission, so the new module needs no row. Run on its own: 3 of 3 pass.check-platform-checklistexits 0 at base and head with the same output. No checklist citation names a moved symbol or a local inside a moved body.packages/core/src/security/guest-organization.tsnamesSecurityPlugin.resolveOperationPrincipals, andexplain-engine.tsanddelegated-organization-membership.test.tsnameresolveOperationPrincipals. The method still exists under that name.tenant-layer.tsnamesisTenantWallDenialinsecurity-plugin.ts, andexplain-engine.tsnamesisTenantWallDenial. It stays there.write-wall-verdict-equivalence.test.tscallsplugin.writeWallVerdict(…), andpackages/objectqlnames it in comments. The public method still exists with the same signature.security-plugin.tsname{@link resolveOperationPrincipals},{@link composeWriteTenantWall}and{@link canWriteObject}. The methods still exist.security-plugin.ts:Ncitation in the repo points into the moved ranges (base:8471-:8493,:8580-:8612,:8953-:9012,:9038-:9062) or at the moved module-level declarations. The citations found are the list cuts 2 to 5 read (docs/launch-readiness.md:309,packages/objectql/src/engine.ts:1614,:1690,:1736, spec liveness:3281and the rest). 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 bdf762c9bd(advisory): 1 page,content/docs/permissions/system-context.mdx, throughwriteWallVerdict. That is the census row this PR re-points.bdf762c9bdand head and compared:check-adr-symbol-anchors,check-platform-checklist,check-platform-object-tenancy-census,check-registry-log-declared,check-rest-log-spy-declared,check-tenant-audit-census,check:cross-package-test-inputs,check:driver-memory-censusandcheck:query-options-erasure. The last reports the same counts at both, andsecurity-plugin.tsis not a baseline key.check-system-context-census: 70 to 71 files, as above.check-undeclared-dep-imports: 3301 to 3302 files, and 2843 to 2844 specifiers. The prologue module imports@objectstack/spec/securityand@objectstack/core, andsecurity-plugin.tsdrops the one@objectstack/coreimport that moved. Both are declared dependencies.check:org-identifier: 3643 to 3644 files.check:doc-authoring: 1420 to 1421 parsed sources. Its clean-file count goes from 424 to 427 because the head worktree holds three gitignored generated pages that the base worktree does not (content/docs/protocol-upgrade/17.md,18.mdandindex.md). CI's checkout does not have them.check:logger-receiver-detach: 3617 to 3619 files: the new module, and one local build output, as at cuts 2 to 5.check-position-name-fold-loaders: 12331 to 12335 files, with the same 8 references (6 test, 2 declared instrument, 0 loader). It walks the working tree: the new module plus local build outputs that exist only in the head worktree.check-issue-citationsjudges a change set, so it has nothing to read at base. At head it judged 13 citations across 3 files, and all resolve.Acceptance notes
docs/adr/**, noscripts/adr-anchors/entry. The one file outside the package is the census page, and only its row 6c path changes.security-plugin.ts, the wall module,system-context.mdx, the delegated-organization test or the plugin-security anchor entries.origin/maingained 15 commits (tob22ab8d6fb). None touches this cut's files. On a scratch merge of this head onto that main,check-adr-symbol-anchors,check:adr-anchors,check-platform-checklistandcheck-system-context-censusall exit 0.check:adr-anchorsthere counts 80 anchored files and 2013 decision-letter citations, against 80 and 2017 on that main alone (main added an anchored file), so the shift this cut makes is the same four.security-plugin.tsafter this cut: the three adapters (computeWriteTenantCheckFilter,computeWriteTenantWall,raiseWriteWallRefusal), the four method shells, the three type declarations and the middleware's step 3.7.Gates
All gates ran at the head, the tree of
0177b21a24, on basebdf762c9bd.pnpm --filter @objectstack/plugin-security test(vitest run --maxWorkers=3, under the verify lock): Test Files 214 passed (214), Tests 4465 passed, 45 skipped (4510). Basebdf762c9bdgives the same counts. VERDICT command-exit 0 on both. The first head commit (before the claim amendment) gave the same counts too.pnpm --filter @objectstack/plugin-security typecheck: exit 0. It runstsc --noEmit, the scripts tsconfig, andcheck:test-typecheck.eslint --no-inline-configover the three touched source files and the touched test: exit 0.pnpm --filter @objectstack/plugin-security build: exit 0. It produced the.d.tsand bundles measured above. The package's dependencies were rebuilt onbdf762c9bdfirst.packages/core/src/security/row-serving-door-exposure.pin.test.ts: 3 of 3 pass.node scripts/check-adr-symbol-anchors.mjs,pnpm check:adr-anchors,pnpm check:platform-checklist(and its script),check-scripts-symbol-anchors(and--self-test), and the two census--jsonreads. None of these is derived for this change set; each was run by hand, and each exit is listed below.dispatch-gates --commands: 88 commands. The census page adds the docs families, as it did at cut 3. The--ranreconciliation shows 88 derived, 88 run, 0 NOT-MEASURED, 0 UNRUN. The zero is derived: all 88 recorded an exit code and none is 3.Per command (exit code), the 88 derived first and the 8 hand-run after:
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-doc-frontmatter.mjsexit 0node scripts/check-doc-frontmatter.mjs --self-testexit 0node scripts/check-doc-route-spelling.mjs --advisoryexit 0node scripts/check-doc-route-spelling.mjs --self-testexit 0node scripts/check-docs-section-name.mjsexit 0node scripts/check-docs-section-name.mjs --self-testexit 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-section-landing-index.mjsexit 0node scripts/check-section-landing-index.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/lint run check:doc-formula-expressionsexit 0pnpm --filter @objectstack/lint run check:doc-security-postureexit 0pnpm --filter @objectstack/spec run check:docsexit 0pnpm --filter @objectstack/spec run check:duration-unit-keysexit 0pnpm --filter @objectstack/spec run check:empty-stateexit 0pnpm --filter @objectstack/spec run check:livenessexit 0pnpm --filter @objectstack/spec run check:skill-examplesexit 0pnpm --filter @objectstack/spec run check:strictness-ledgerexit 0pnpm --filter @objectstack/spec run check:variant-docsexit 0pnpm --filter @objectstack/spec run check:yaml-examplesexit 0pnpm check:closed-ceremony-textexit 0pnpm check:corpus-claim-driftexit 0pnpm check:cross-package-test-inputsexit 0pnpm check:doc-anchorsexit 0pnpm check:doc-authoringexit 0pnpm check:docs-audit-scopeexit 0pnpm check:docs-redirectsexit 0pnpm check:docs-single-h1exit 0pnpm check:docs-spec-enumerationsexit 0pnpm check:docs-transcript-driftexit 0pnpm check:driver-memory-censusexit 0pnpm check:dts-closureexit 0pnpm check:dual-build-cjs-loadsexit 0pnpm check:engine-double-contractexit 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:merge-driverexit 0pnpm check:nul-bytesexit 0pnpm check:objectql-double-limitexit 0pnpm check:org-identifierexit 0pnpm check:page-declaration-shapeexit 0pnpm check:published-filesexit 0pnpm check:published-readme-linksexit 0pnpm check:query-options-erasureexit 0pnpm check:react-page-adapter-contractexit 0pnpm check:refd-timer-probeexit 0pnpm check:role-wordexit 0pnpm check:skill-identifier-livenessexit 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:vendor-version-stampsexit 0pnpm check:watch-hint-literalexit 0pnpm check:where-matcherexit 0node scripts/check-adr-symbol-anchors.mjsexit 0node scripts/check-platform-checklist.mjsexit 0node scripts/check-scripts-symbol-anchors.mjsexit 0node scripts/check-scripts-symbol-anchors.mjs --self-testexit 0node scripts/isystem-census.mjs --jsonexit 0node scripts/tenant-audit-census.mjs --jsonexit 0pnpm check:adr-anchorsexit 0pnpm check:platform-checklistexit 0Generated by Claude Code
🤖 Generated with Claude Code
https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa