Skip to content

refactor(plugin-security): move SecurityPlugin's controlled-by-parent read leaf into security-plugin-cbp-read-filter.ts (#25223 cut 4) - #25360

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-3fzya4
Oct 23, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-3fzya4

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

Part of #25223
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6353073966

Cut 4 of the security-plugin.ts split: the rest of cut 2. The claim is 6355875966. The bodies of two synchronous methods move unedited to the new module packages/plugins/plugin-security/src/security-plugin-cbp-read-filter.ts:

  • computeControlledByParentFilter, the ADR-0055 §2 read derivation. It answers a controlled_by_parent object's read with the engine's ReadableIds leaf over its master (ADR-0139 D6).
  • controlledByParentChainRefusal, the chain refusal that derivation runs over the registry before it writes the leaf.

The one module-level declaration only those bodies read moves with them: CBP_LEAF_READ_VERBS. The ReadableIds name leaves the @objectstack/spec/data import of security-plugin.ts, because no code left there reads it.

It is move-only, and the card stays open for the later cuts. Branch: claude/project-thread-3fzya4, the harness-pinned branch for this session, restarted from origin/main at 3e514912ad (cut 3's merge). It names no issue, so the claim comment is the identity record.

Why these two now. Ruling 6353073966 item 4 held them until the Tier H docs PR for #25314 landed. That PR, #25316, merged as 97967a9a06 before cut 3's merge, and #25314 is closed as completed. On this base, ADR-0055:26 is a file-level anchor and ADR-0055:58 anchors the assertControlledByParentWrite method shell, so no ADR sentence resolves on a token inside these two bodies any more. composeWriteTenantWall, which item 4 also held, belongs to a different section (the tenant wall) and is not in this cut.

What stays in security-plugin.ts. CBP_MAX_CHAIN_DEPTH stays: ADR-0055:135 anchors it by symbol in that file (ruling item 5). It has been exported since cut 3, and the new module imports it by value, as cut 3's module does, so security-plugin.ts gains no new export. It is read only inside a function body, at call time, never while either module evaluates. CbpRelation keeps its one declaration. Both files import RLS_DENY_FILTER from rls-compiler.js.

The published .d.ts does not change

Under the series bar (ruling 6348891160 item 1), measured on base 3e514912ad and on the head (the tree of this PR's commit):

  • Raw sha256 of dist/index.d.ts and dist/index.d.mts, base and head, all four the same: 803f5bfb7da0b00f8cd8d8f25172463d8dfb07d93fbdd8a9c6f4d4b7435e429a. The raw diff is 0 lines, so no union order moved either.
  • Normalised sha256, all four the same: b6b5c1b7fa6fa188f6e2f7e4cca3816a246b570f20d708643d116012aa86fa77.
  • The normaliser, run from packages/plugins/plugin-security (it sorts the members of each string-literal union and changes nothing else):
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 2027 (private computeControlledByParentFilter;):
    • Made public (computeControlledByParentFilter;): raw 133464a499c14936dfef45acfbfc4dc06f474a637d04a41af9c96cd69467e57b, normalised 18feab96bc21c749ed55f60542c9564a6f80de841a2e650325229a8167ced6ff.
    • Renamed (private computeControlledByParentFilterRenamed;): raw 702460748f17c01330fd55483961d149a1972622317ba54c89f03a99c2a045f7, normalised a7ef310b2e289b480123f13b49aa60391b0e0ee5951cb650296a5f2d757a5759.
    • 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. The lists were loaded at runtime, not parsed. The sorted name list hashes f607fd765d6b277e08b3b46ccb16d1e2d133ec55658ffe40155aef25e1bf54f0 on all four, the same as at cuts 2 and 3.
  • The bundle JS bytes differ (index.js 4f2b1fcb to 7179686c, index.mjs 8a9277ae to 474e62d2), because code moved within them.

No changeset: skip-changeset, per ruling 6348891160 item 4.

What moved

Base line numbers are each member's signature at 3e514912ad. The last column lists the members the body reads, which is exactly what its host carries.

method base lines host members
computeControlledByParentFilter :9058-:9099 controlledByParentChainRefusal, logger, ql, resolveCbpRelation
controlledByParentChainRefusal :9123-:9140 ql, resolveCbpRelation

Both methods are synchronous, so no stub changes async.

CBP_LEAF_READ_VERBS (base :661, 11 lines with its docblock) moves verbatim. No code left in security-plugin.ts reads it.

The shape (#23050 shape B, as cuts 1 to 3 applied 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 4].
  • CbpReadFilterHost types every member as the class member itself (SecurityPlugin['m']). Each moved function types its this as the Pick of the host over exactly the members its body reads, and each stub annotates its literal with the same Pick. There is no cast, and no member becomes public or protected.
  • The module imports SecurityPlugin type-only, and names CbpRelation through the class's own signature, as cuts 2 and 3 do (an alias over the non-null return type of SecurityPlugin['resolveCbpRelation']).
  • 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 leaf reaches. The leaf still reaches its chain refusal through the plugin, and both still reach resolveCbpRelation through it. No moved body calls a function-valued field, so no receiver changes.
  • Fields: ql and logger go as getters, because neither is declared readonly. start() assigns both after construction. The host declares both readonly, so a moved body cannot assign a member through it; the typecheck passes, so none does.
  • Nothing re-exports the module, so the package entry does not move.

Proof of move-only

  • Bodies, by AST token: for both methods, the moved function's body has the same TypeScript token sequence as the base method body, and each of its 48 lines is the base line with two leading spaces removed. Parameters, the parameter docblock, return types and async match. A negative control, !context?.userId changed to context?.userId in computeControlledByParentFilter, fails the check.
  • Class side: each method keeps its docblock and signature. CBP_LEAF_READ_VERBS is verbatim in the module and absent from security-plugin.ts. CBP_MAX_CHAIN_DEPTH and CbpRelation are byte-identical to base. Every other class member, 123 named members plus the constructor, is byte-identical to base, composeWriteTenantWall included.
  • git's own view (git diff --color-moved=plain --color-moved-ws=allow-indentation-change 3e514912ad HEAD over the two source files): 60 of 61 deleted lines and 75 of 179 added lines are detected as moved.
    • Deleted, not moved (1): the @objectstack/spec/data import line, which loses ReadableIds. The module imports ReadableIds from the same package.
    • Added, not moved: that import line's replacement, the stub bodies, and the import of the new module with its comment. In the module: the license header, the module docblock, the imports, the CbpRelation alias, CbpReadFilterHost, one pointer docblock per function and the rewritten signature heads. The rest are lines git does not pair with a moved block, such as blanks and closing braces.
  • Size: 247 changed lines (+186 / -61, 3 files), under the 3000-line human-merge threshold. security-plugin.ts goes from 10,240 to 10,205 lines.

ADR anchors (ruling 6353073966 item 2)

  • node scripts/check-adr-symbol-anchors.mjs: exit 0 at base 3e514912ad and exit 0 at head, both with 2383 anchors across 142 records resolve — 466 symbol (440 declaration, 26 literal), 1889 file-level, 28 cross-repo, 6 exempt, 18 continuation. The counts differ from cut 3's report because docs(adr): repoint three security-plugin.ts symbol anchors to declarations the class keeps #25316 made one symbol anchor file-level. CI runs this gate (lint.yml), but dispatch-gates --commands does not derive it for this change set ([finding] dispatch-gates derives the symbol-anchor sweeps only from their corpus paths, so a change that removes an anchored symbol from a TARGET file passes every local gate and reds CI #21697), so it is run by hand here.
  • pnpm check:adr-anchors: exit 0, 78 anchored files (77 at base, plus the new entry). The new entry is scripts/adr-anchors/packages__plugins__plugin-security__src__security-plugin-cbp-read-filter.ts.json. It carries ADR-0055 and the §1 invariant paragraph of the security-plugin.ts entry, verbatim, as the entries of cuts 2 and 3 do. Its "reads answer RLS_DENY_FILTER" half is this leaf. The moved comments also cite ADR-0139. Like the security-plugin.ts entry, the new entry does not list every ADR its file cites, and the gate passes. The security-plugin.ts entry is unchanged. docs/adr/** is not touched.
  • The anchor map covers every ADR symbol anchor naming security-plugin.ts: 27 anchors, 18 symbols, 10 ADRs on this base. It was measured with extractAnchors and symbolSegmentResolution from scripts/symbol-anchors.mjs, which is measurement, not a new gate. It was read on 3e514912ad BEFORE the cut, with the two bodies blanked: no anchor changed class. Every anchor resolves at head in security-plugin.ts with 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.

anchor cited at base class · line (member) head class · line
#CBP_MAX_CHAIN_DEPTH ADR-0055:135 declaration · :649 (top-level) declaration · :657
#SecurityPlugin ADR-0049:49, ADR-0138:248, ADR-0138:265 declaration · :1338 (class) declaration · :1334
#SecurityPlugin.getMetadataReadableFields ADR-0138:153 declaration · :2487 (start), :6106 (method) declaration · :2483, :6102
#SecurityPlugin.resolvePermissionSetsForContextUnmemoized ADR-0138:427 declaration · :7017 (method) declaration · :7013
#assertControlledByParentWrite ADR-0055:58, ADR-0055:125 declaration · :9497 (method shell) declaration · :9462
#assertMasterRowEditable ADR-0055:125 declaration · :9623 (method shell) declaration · :9588
#collectRLSPolicies ADR-0105:93 declaration · :9677 (method) declaration · :9642
#composeWriteTenantWall ADR-0123:4 declaration · :8412 (method) declaration · :8408
#computeControlledByParentFilter ADR-0055:5, ADR-0055:123, ADR-0139:246 declaration · :9058 (method) declaration · :9054 (method shell)
#computeDelegatedReadFilter ADR-0139:223 declaration · :5984 (method) declaration · :5980
#computeRlsFilter ADR-0055:24 declaration · :5236 (explainAccessForCaller), :7639 (method) declaration · :5232, :7635
#getReadFilter ADR-0096:181, :182, :183, :199, ADR-0139:222 declaration · :2250 (start), :5862 (method) declaration · :2246, :5858
#hasPlatformAdminPosture ADR-0099:317 declaration · :7666 (method) declaration · :7662
#isPrincipalLessContext ADR-0138:152 declaration · :503 (top-level) declaration · :511
#manage_metadata ADR-0099:68 literal · :252 (PLATFORM_ADMIN_ONLY_CAPABILITIES) literal · :260
#nav_permission_sets ADR-0126:363 literal · :1743 (init) literal · :1739
#rlsFilter ADR-0055:24 declaration · :4313 (local in start) declaration · :4309
#writeCheckPolicies ADR-0066:112 declaration · :1233 (top-level) declaration · :1229

No resolving site moves. The three anchors that name computeControlledByParentFilter resolve on its declaration, which stays as a shell. assertControlledByParentWrite, assertMasterRowEditable, collectRLSPolicies and computeRlsFilter also appear as host-literal keys inside cut 3's stubs, as cut 3's report noted; this cut adds no such line for an anchored name.

Readers

No test, script, gate, baseline or docs anchor reads the moved lines by path, so nothing else is re-pointed.

  • The census pages. The two bodies hold no isSystem read, so check-system-context-census has no row to move. Unlike cut 3, content/docs/permissions/system-context.mdx is not touched. The gate exits 0 at base and head with the same summary except the file count. The bodies hold no engine write, so check-tenant-audit-census sees the same 237 write call sites.
  • The other symbol-anchor gates all exit 0: check-scripts-symbol-anchors, check-spec-docblock-symbol-anchors, pnpm --filter @objectstack/spec check:liveness, and check-platform-checklist --changed-from origin/main.
  • Readers by name, not by path. These were read and none needs a change:
    • Two test docblocks name computeControlledByParentFilter as the mechanism (controlled-by-parent-master-object-read.test.ts, readable-ids-nested-compose.test.ts), and so do two comments in packages/lint/src/validate-security-posture.ts and its test. The method still exists under that name.
    • packages/qa/dogfood/test/authz-conformance.matrix.ts describes the enforcement as plugin-security/security-plugin.ts computeControlledByParentFilter + assertControlledByParentWrite. Both are still methods of that class in that file, now as shells, as cut 3 already left assertControlledByParentWrite.
  • Line citations: no security-plugin.ts:N citation in the repo points into a moved range. The moved ranges at 3e514912ad are :651-:662 and the two method bodies, :9064-:9098 and :9127-:9139.
    • Head line numbers shift by +8 for base :138-:650 (the new import), by -4 for base :663-:9063, by -29 for base :9099-:9126 and by -35 after :9139. Lines :1-:137 do not move.
    • The citations :132, :303, :309, :1614, :1690, :1736, :3215, :3281, :3519 and :4344 are the same list cuts 2 and 3 read. Each lands in an unmoved or shifted range, and each already pointed at an unrelated line before cut 2, so this cut does not make any of them wrong.
  • Docs Drift Check, re-derived with node scripts/docs-audit/affected-docs.mjs --json 3e514912ad (advisory): 37 pages, 9 of them release-owned. Every one is reached only through a string literal inside the moved code: 'controlled_by_parent' in the two bodies, or 'findOne' in CBP_LEAF_READ_VERBS. The literals moved unchanged and no behaviour changed, so there is nothing to edit on those pages.
  • Census gates, run on base 3e514912ad and head and compared:
    • 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 (148 elevation read sites in 70 files at both), check-tenant-audit-census (237 write call sites at both), check:cross-package-test-inputs, check:driver-memory-census and check:query-options-erasure. The last of these reports the same counts at both, and security-plugin.ts is not a baseline key.
    • Only the scanned counts move, by the one new file:
      • check-undeclared-dep-imports: 3291 to 3292 files, and 2835 to 2836 specifiers. The one is the new module's @objectstack/spec/data import, a declared dependency.
      • check:doc-authoring: 1413 to 1414 sources.
      • check:org-identifier: 3633 to 3634 files.
      • check:logger-receiver-detach: 3607 to 3609 files. The second extra file is apps/docs/.next/types/validator.ts, a gitignored local build output of the docs app, as at cuts 2 and 3. It is not in the tree CI checks out.
    • check-issue-citations judges a change set, so it has nothing to read at base. At head it judged 5 citations across 2 files, and all resolve.

Acceptance notes

Gates

All gates ran at the head, the tree of d2b23e286b, on base 3e514912ad.

  • pnpm --filter @objectstack/plugin-security test (vitest run --maxWorkers=3, under the verify lock): Test Files 212 passed (212), Tests 4442 passed, 45 skipped (4487). Base 3e514912ad gives the same counts. VERDICT command-exit 0 on both.
  • pnpm --filter @objectstack/plugin-security typecheck: exit 0. It runs tsc --noEmit, the scripts tsconfig, and check:test-typecheck.
  • eslint --no-inline-config over the two touched source files: exit 0.
  • pnpm --filter @objectstack/plugin-security build: exit 0. It produced the .d.ts and bundles measured above.
  • node scripts/check-adr-symbol-anchors.mjs exit 0, pnpm check:query-options-erasure exit 0, plus the three symbol-anchor gates under Readers. None of these is derived for this change set; each was run by hand.
  • dispatch-gates --commands: 62 commands, the same list as cut 3's cut commit. The --ran reconciliation shows 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. The zero is derived: all 62 recorded an exit code and none is 3.

Per command (exit code):

  • node scripts/check-ci-filter-parity.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
  • 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-scripts-symbol-anchors.mjs exit 0
  • node scripts/check-scripts-symbol-anchors.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:adr-anchors exit 0
  • pnpm check:agent-test-spelling exit 0
  • pnpm check:bash32-floor exit 0
  • pnpm check:cli-command-ids 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
  • pnpm check:entry-guard exit 0
  • pnpm check:error-status-conformance exit 0
  • pnpm check:gitlink-declared exit 0
  • pnpm check:i18n exit 0
  • 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:org-identifier exit 0
  • pnpm check:page-declaration-shape exit 0
  • pnpm check:parse-guard exit 0
  • pnpm check:pnpm-filter-targets exit 0
  • pnpm check:pnpm-flag-spellings exit 0
  • pnpm check:published-files 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:watch-hint-literal exit 0
  • pnpm check:query-options-erasure exit 0
  • node scripts/check-adr-symbol-anchors.mjs exit 0
  • node scripts/check-spec-docblock-symbol-anchors.mjs exit 0
  • pnpm --filter @objectstack/spec check:liveness exit 0
  • node scripts/check-platform-checklist.mjs --changed-from origin/main exit 0

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa

… read leaf into security-plugin-cbp-read-filter.ts

The bodies of computeControlledByParentFilter (the ADR-0055 §2 read
derivation, answering the engine's ReadableIds leaf over the master) and
controlledByParentChainRefusal (its chain refusal) move unedited to a new
module beside the plugin, with CBP_LEAF_READ_VERBS, the one module-level
declaration only they read. Each method keeps its name, signature, docblock
and position and hands its body one host literal of exactly the members it
reads; ql and logger go as getters. CBP_MAX_CHAIN_DEPTH stays (ADR-anchored)
and is imported by value. Move-only: no verdict changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017BoLT8Gaa8t9a7AWVH8qZa
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 23, 2026
@objectstack-fleet
objectstack-fleet Bot requested a review from os-sales October 23, 2026 02:52
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-security, touching 10 documentable anchor(s).

25 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 9127f23ea111f1535c9c6abadca117ada69cba83.

⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 16 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 9127f23ea111f1535c9c6abadca117ada69cba83 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from cd4c75dc4037aaa80375ecaf95f6b2f1b5dcf191 — the merge of head d2b23e286bc364c2db43aaad1ec3a70e64536443 into base 9127f23ea111f1535c9c6abadca117ada69cba83, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cd4c75dc4037aaa80375ecaf95f6b2f1b5dcf191 && git checkout cd4c75dc4037aaa80375ecaf95f6b2f1b5dcf191
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9127f23ea111f1535c9c6abadca117ada69cba83 d2b23e286bc364c2db43aaad1ec3a70e64536443 && git checkout -B drift-repro 9127f23ea111f1535c9c6abadca117ada69cba83 && git merge --no-ff d2b23e286bc364c2db43aaad1ec3a70e64536443

node scripts/docs-audit/affected-docs.mjs --json 9127f23ea111f1535c9c6abadca117ada69cba83

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 9127f23ea111f1535c9c6abadca117ada69cba83 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · 2026-10-23T03:30Z

domain:services seat 1 (seat post #6021) · os-sales · session_016g7nYP9wdNx1GDa5XDr7Uy · card #25223 cut 4 (claim 6355875966, Ruling-ref: 6353073966; os-dev-report 6356619817; card session branch claude/project-thread-3fzya4) · reviewed head d2b23e286bc364c2db43aaad1ec3a70e64536443 · merge base 3e514912ad

  • Shape: this is the rest of cut 2, the hold that ruling item 4 lifted once docs(adr): repoint three security-plugin.ts symbol anchors to declarations the class keeps #25316 merged (97967a9a06). Two synchronous bodies move unedited into security-plugin-cbp-read-filter.ts: computeControlledByParentFilter, the ADR-0055 §2 read derivation answering the engine ReadableIds leaf (ADR-0139 D6), and controlledByParentChainRefusal. With them go CBP_LEAF_READ_VERBS, the only module-level declaration they read, and the ReadableIds import name. The cut uses shape B as in cuts 1 to 3. CBP_MAX_CHAIN_DEPTH stays, per ruling item 5. security-plugin.ts gains no new export. composeWriteTenantWall is not in this cut. 3 files, +186 / −61.
  • Move proof (report): both bodies are AST-token identical, with a failing negative control. Every other class member is byte-identical to base. --color-moved leaves one deleted line unmoved: the @objectstack/spec/data import losing ReadableIds.
  • .d.ts, ruling 6348891160 item 1, re-run by the seat in its own worktree. turbo run build --filter=@objectstack/plugin-security ran at 3e514912ad and at this head. dist/index.d.ts and dist/index.d.mts are raw 803f5bfb7da0b00f8cd8d8f25172463d8dfb07d93fbdd8a9c6f4d4b7435e429a and normalised b6b5c1b7fa6fa188f6e2f7e4cca3816a246b570f20d708643d116012aa86fa77, the same at base and head. The raw diff is 0 lines.
    • The seat's own positive control uses a different member from the report's: head line 2050, private controlledByParentChainRefusal, made public. Raw 3a3f3c68c9501586… and normalised aca5627143f21110… both moved.
    • The report's control (line 2027, computeControlledByParentFilter) also moved both hashes. Export lists: 63 names in ESM and CJS, base equals head (report).
  • ADR anchor reading, ruling item 2, re-run by the seat: check-adr-symbol-anchors exits 0 at base and head, both printing 2383 anchors … 466 symbol (440 declaration, 26 literal). That is one fewer symbol anchor than at cut 3, because docs(adr): repoint three security-plugin.ts symbol anchors to declarations the class keeps #25316 made ADR-0055:26 file-level. In the report's pre-cut map (27 anchors, 18 symbols, 10 ADRs, with the moved bodies blanked), no anchor changed class. #computeControlledByParentFilter (ADR-0055:5, :123, ADR-0139:246) resolves on the kept shell. pnpm check:adr-anchors passes with the new module entry (78 files).
  • Docs reader: no isSystem read moved, and no docs page is touched. Census gates are identical at base and head, including check-system-context-census (148) and check-tenant-audit-census (237). The Docs Drift advisory (37 pages) comes from literals in the moved bodies. The behaviour is unchanged and needs no reply.
  • Tests and gates (report): plugin-security 212 files, 4442 pass / 45 skipped, the same as base. dispatch-gates --ran 62 / 62, plus the hand-run anchor, liveness and erasure gates. CI on this head: 34 success and 12 expected skips, none failed or pending. Mergeable clean, and the seat's git merge-tree onto today's main is clean.
  • Body: the seat appended the required 🤖 Generated with footer and session URL through the relay issue_patch, read back identical (run 39895328362).
  • Declarations: refactor(plugin-security), Clause-②: no, Metadata-protocol: none, skip-changeset. No contract-review record or dogfood battery is owed. Out-of-scope findings: none.
  • Landing: not governed (check-governed-merges --pr 25360: 247 lines). Ready + auto-merge through the relay. The card stays open and dispatched. The next cut (composeWriteTenantWall) is claimed afresh on the merged file.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 23, 2026 03:31
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 23, 2026 03:31
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 23, 2026
Any commits made after this event will not be merged.
Merged via the queue into main with commit 4d7e2b1 Oct 23, 2026
54 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-3fzya4 branch October 23, 2026 03:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants