Skip to content

refactor(objectql): move the secret and internal-field bodies out of engine.ts into engine-secret-internal-fields.ts (#25522 cut 5) - #26028

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-npj50o
Oct 26, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-npj50o

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread
Part of #25522
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160

Cut 5 of the packages/objectql/src/engine.ts split (#24586 ruling A, recorded in 6270097840). The claim is 6412019236, which amends 6411795983 after the dev report 6411990837. The nine bodies of the "Secret and internal-field handling" area move unedited into a new module, packages/objectql/src/engine-secret-internal-fields.ts: encryptSecretFields, refuseEmptyPasswordFields, maskSecretFields, omitInternalFields, stripSearchCompanionFromRead, privilegedReadDriverOptions, resolveSecret, resolveSecretField and resolveInternalField. No secret-handling behaviour changes. #25522 stays open for the later cuts.

Branch claude/project-thread-npj50o (the card thread's harness-pinned branch), cut from origin/main at c8cb510a5. main has since moved to c16c00f93; nothing in that range touches packages/objectql/**, the two docs files or the anchor directory entries this PR edits, so the branch is not merged forward and CI builds the merge ref. #25980 (the last commit before the base) changed engine.ts only in its preamble and trailer; the nine bodies are byte-identical from a4eae0649 to c8cb510a5.

Why this area (the fresh seam table)

Measured on origin/main a4eae06499: engine.ts is 23,458 lines, 7,214 of them (30.8%) from the last 30 days (git blame -s --since=2026-09-26T08:45Z), 121 commits in 30 days and 31 in 7. The full 41-section table in #23050's shape, with a per-method screen of every section, is in the dev report 6411990837 and its project file. The three areas left by cut 4 do not pay a move-only cut without a packages/spec edit:

area 30-day lines movable inside the claim why not
Roll-up summaries 0 46 body lines, about -15 net buildSummaryIndex is cited from packages/spec (functional-completeness.ts, ADR-anchored; liveness/field.json summaryOperations, and liveness/ ships in the spec tarball); recomputeSummaries hands this to aggregateSummaryValue
Datasource registry 1 27 body lines, about -5 net unregisterDriver and clearDatasourceUnavailable are this.FIELD.delete( sites (U = 3)
Related-record expansion 10 0 expandRelatedRecords reads the private static MAX_EXPAND_DEPTH and is cited from liveness/query.json and liveness/field.json
Secret and internal-field handling (this PR) 32 of 566 all 9 bodies, 208 body lines none: no inferred-return public method, no this.FIELD.delete/insert/update( site, no private-static read (screened by AST), no packages/spec or docs/adr pointer

The shape (#23050 shape B, as cuts 1 to 4 apply it)

  • The class keeps all nine methods with name, modifiers, signature, docblock and position, because tsup emits members with their JSDoc into the .d.ts. Each body is now one host literal plus fn.call(host, …), marked [#25522 cut 5].
  • SecretInternalFieldsHost is new. Each member is typed as the class member (ObjectQL['cryptoProvider'], ObjectQL['getDriver']), and each moved function types its this as the Pick of exactly the members its body reads; each stub annotates its literal as ThisParameterType of that function. No cast, and no member changes visibility.
  • The three fields (_registry, cryptoProvider, txStore) go as GETTERS, so setCryptoProvider between calls is seen exactly as before. The five methods the bodies call (getDriver, transactionCoversDriverFor, omitInternalFields, privilegedReadDriverOptions, resolveSecret) go as late-bound closures over the instance, so a double set on the engine still intercepts. Tests do replace resolveInternalField (4 sites) and resolveSecretField (2) on the instance; those methods stay on the class, so the doubles still take effect.
  • stripSearchCompanionFromRead reads no member and is called directly, as cut 1 calls objectDisplayLabel.
  • Imports. The module imports ObjectQL as a TYPE only (no runtime cycle) and nothing re-exports it. engine.ts drops the names whose last code reader moved: CryptoHandle, collectSecretFields, collectMaskedReadFields, collectMaskedPasswordFields, makeSecretRef, parseSecretRef, isSecretRef, EmptyCredentialWriteError, SECRET_MASK and isSearchCompanionRequested. collectInternalReadFields, collectCredentialFields, stripSearchCompanion, redactPropagatedDriverFault and ICryptoProvider still have readers there and stay.

Net lines (the seat's rule)

engine.ts 23,492 → 23,351, net -141. The module is 360 lines: the header and host interface (about 75), nine one-line docblocks, and the 208 moved body lines with their signatures. 667 changed lines over 6 files (+447 / -220), well under the 3,000 human-merge line.

The published .d.ts does not change (ruling 6348891160)

Base: @objectstack/objectql built with engine.ts restored from c8cb510a5 and the new module removed (tree only, restored with git checkout HEAD --, git status clean afterwards). Head: built at bd40de2733. The head dist/index.mjs carries the stub's readCryptoProvider, which the base build lacks, so each leg built the source it names. All six declaration files are raw byte-identical, so the ruled normaliser has nothing to do:

file sha256, base = head
index.d.ts 3d0e556fe6cf12c69d3b9516d9f414d235bd7279b3f9cb4bbd10f8fb3f098726
index.d.mts 9d741b723c1c6697d9b2ccd98cac69075dc5f87328d50936c5d73496c45d4626
core.d.ts a024599701ba68ebfa6d0c3781d3c8d755ab1c7c1fc2db5ab89cb30020091a7e
core.d.mts 9acb9b18b3220f17a45abc36ef86fb54f53244e634b71400ba45c12895dc5b2b
util-CR5tVhqM.d.ts and .d.mts 2696708b9ee625ddcdf62709ea19e6d92b39d2d82cdd803652be1b17f625b059

Positive control: making line 8014 of the head util-CR5tVhqM.d.ts (private maskSecretFields;) public gives 7f91b0b1062f7356115bd25365df7bc3baaae7678e3d06dc92552ffdb87df35f, so the comparison can fail. Export lists, ESM = CJS at both ends: . 182 names (sorted sha256 881616cc…0adaa), ./core 54 (393e8069…90f49), identical at base and head; no moved name is exported.

Proof of move-only

A TypeScript-AST leaf-token compare (recursive getChildren, JSDoc skipped), base engine.ts against the head module:

  • Each of the nine: parameters (less this) + return type + body tokens SAME, async the same, and every body line equals the base line less two leading spaces. encryptSecretFields 354 tokens / 73 lines, resolveInternalField 324 / 41, resolveSecret 197 / 31, resolveSecretField 169 / 24, maskSecretFields 128 / 17, omitInternalFields 96 / 9, refuseEmptyPasswordFields 76 / 7, privilegedReadDriverOptions 57 / 4, stripSearchCompanionFromRead 37 / 2.
  • Negative controls: one return rewritten in the moved resolveInternalField or maskSecretFields reports DIFFER.
  • Class: 318 members at both ends, same order; 309 byte-identical (full text, docblocks included); the 9 stubs keep docblock, modifiers and signature byte-identical up to the body.
  • Top level: 224 of 228 base statements byte-identical in head. The others are the class, the three narrowed imports above, and the new module import with its comment.

Readers of the moved lines, migrated in this PR

  • docs/qa/platform-checklist/areas/records-forms.json: the secret-field item's source pointer, engine.ts#encryptSecretFields → engine-secret-internal-fields.ts#encryptSecretFields. check:platform-checklist green.
  • content/docs/permissions/system-context.mdx row 27: engine.ts#stripSearchCompanionFromRead → engine-secret-internal-fields.ts#stripSearchCompanionFromRead, the isSystem read's new home. check-system-context-census green: 151 read sites in 22 packages across 79 files, the page cites 144 symbols against 144 required.
  • packages/objectql/src/federated-injected-column-readers.test.ts: the ledger key engine.ts#encryptSecretFields :: created_at → engine-secret-internal-fields.ts#encryptSecretFields :: created_at (the scan keys by file).
  • ADR anchors. The engine anchor entry's one paragraph is the transaction seam (buildDriverOptions, enforceTransactionOrigin) and describes no moved body. But the moved encrypt and decrypt bodies carry two ADR-0128 D1 citations, and moving them dropped the anchored surface's decision-letter count from 1,844 to 1,842. Cut 4 resolved the same drop with an entry for its new module, so this PR adds scripts/adr-anchors/packages__objectql__src__engine-secret-internal-fields.ts.json (ADR-0100, ADR-0128). check:adr-anchors: base 86 anchored files, 1,844 letter citations; head 87, 1,845.
  • Census and receivers. check-tenant-audit-census green (243 write call sites, every UNTYPED_RECEIVERS row matched; the moved secretDriver.create( is a local receiver, not a this.FIELD one). check:logger-receiver-detach green (the moved bodies read no logger). check:durability-log-level green over the objectql read seams.

Acceptance notes

Verification

At head bd40de2733:

  • pnpm --filter @objectstack/objectql typecheck (tsc --noEmit + check:test-typecheck, 39 files / 233 errors / 64 pinned signatures held): exit 0. eslint --no-inline-config packages/objectql: exit 0.
  • pnpm --filter @objectstack/objectql exec vitest run --project local: 470 files, 8,742 tests, 8,742 passed, 0 failed, 0 skipped. This includes read-internal-column.test.ts, the secret and password suites, and federated-injected-column-readers.test.ts.
  • Dependants: every changed member keeps its declaration and all six published .d.ts files are byte-identical, so no dependant can see a difference; CI's TypeScript Type Check covers them.
  • dispatch-gates --commands derived 103 families on the real diff. All were run at head, each exit code recorded before any pipe, and reconciled with --ran (103 accounted for: 102 run, 1 NOT MEASURED). check:skill-examples exited 3 until client-react's closure was built, then 0. NOT MEASURED (CI runs it): check:dual-build-cjs-loads exit 3, PREREQUISITE NOT MET (it needs every package's dist/; the export lists above answer it for objectql). check-engine-split-ratio --days 90: 98.1%, exit 0.
  • Hand gates, all exit 0: check-adr-anchors (base and head, above), check-adr-symbol-anchors (2,658 anchors resolve), check-tenant-audit-census, check-nul-bytes, check-platform-checklist --changed-from origin/main (2 anchored files, 14 anchors, 0 lost), check-commit-card-trailers --range origin/main..HEAD.

skip-changeset: move-only. The published .d.ts files and export lists are byte-identical, liveness/ and every other published path are untouched, and the module's runtime is the same code under a different file name.


Generated by Claude Code

…engine.ts behind one host (#25522 cut 5)

The nine ObjectQL bodies of the "Secret and internal-field handling" area
(encryptSecretFields through resolveInternalField) move unedited, one level
less indented, into engine-secret-internal-fields.ts. Each method keeps its
name, signature, docblock and position and hands its body the members it
reads through SecretInternalFieldsHost: _registry, cryptoProvider and
txStore as getters, five methods as late-bound closures.
stripSearchCompanionFromRead reads no member and is called directly.

The three path readers move with them: the federated injected-column
ledger key, the records-forms checklist source pointer, and row 27 of the
system-context census page.

Claude-Session: https://claude.ai/code/session_01FCw8ckDE3VXVavCoqsMQ3u
Co-authored-by: Claude <noreply@anthropic.com>
…00 and ADR-0128

The two ADR-0128 D1 citations in the moved encrypt and decrypt bodies left
engine.ts's anchored surface with them (decision-letter citations 1,844 at
base, 1,842 after the move). This entry anchors the new module, so the
letters are verified where the code now lives (1,845 at head).

Claude-Session: https://claude.ai/code/session_01FCw8ckDE3VXVavCoqsMQ3u
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 26, 2026
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests labels Oct 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql, touching 25 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 39a78b151d691a538856c0f0d57304f8faa69f96.

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

What this run could not see
  • 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 — 19 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 39a78b151d691a538856c0f0d57304f8faa69f96 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 6b6167b0ccd5c68886e9abb5c30a956605f53656 — the merge of head bd40de27336b8976d75e1e9a1995d8c2fe993e89 into base 39a78b151d691a538856c0f0d57304f8faa69f96, 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 6b6167b0ccd5c68886e9abb5c30a956605f53656 && git checkout 6b6167b0ccd5c68886e9abb5c30a956605f53656
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 39a78b151d691a538856c0f0d57304f8faa69f96 bd40de27336b8976d75e1e9a1995d8c2fe993e89 && git checkout -B drift-repro 39a78b151d691a538856c0f0d57304f8faa69f96 && git merge --no-ff bd40de27336b8976d75e1e9a1995d8c2fe993e89

node scripts/docs-audit/affected-docs.mjs --json 39a78b151d691a538856c0f0d57304f8faa69f96

⚠️ 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 39a78b151d691a538856c0f0d57304f8faa69f96 → 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 · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-10-26T09:40Z · head bd40de27336b8976d75e1e9a1995d8c2fe993e89 · #25522 cut 5

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.

  1. Shape: a draft against main. The body opens with Part of #25522 (⛔ not Fixes; the card stays open for later cuts), and no closing keyword stands next to any other open card. Clause-②: no and Metadata-protocol: none are line-initial. skip-changeset applies: the change is move-only with no product behaviour change. check-governed-merges --pr 26028: NOT governed, 667 changed lines (≤ 3000).
  2. Premise measured: the claim amendment 6412019236 moved the area to "Secret and internal-field handling" after the dev's seam table showed that none of the three named areas pays. The seat read the diff: the nine methods stay in engine.ts as thin delegates that build a ThisParameterType<…> host (getters for _registry / cryptoProvider / txStore, late-bound closures for getDriver, privilegedReadDriverOptions, transactionCoversDriverFor, omitInternalFields, resolveSecret). stripSearchCompanionFromRead reads no member and is called directly. The dev reports AST equality 9/9 with negative controls, and six .d.ts byte-identical between base and head with a positive control. This review did not re-run them.
  3. Gates: at this stamp the head's check-runs read 14 success, 15 skipped and 21 in progress, with none failing. Landing waits for every check to finish success or an expected skip. check:dual-build-cjs-loads was NOT MEASURED locally, so Build Core in CI decides it.
  4. Accepted deviations: the new scripts/adr-anchors/packages__objectql__src__engine-secret-internal-fields.ts.json (ADR-0100, ADR-0128). It restores the decision-letter count that the two moved ADR-0128 D1 citations took with them (1,844 at base, 1,842 after the bare move, 1,845 at head). Cut 4 resolved the same drop the same way. The seat read its invariant paragraph against the moved bodies: it describes them and makes no new claim. The docs row 27 in content/docs/permissions/system-context.mdx and the docs/qa reader are repoints only, and were checked sentence by sentence (only the path changed). The harness branch name and the model-free trailers are as the claim records.
  5. Out of scope: none filed. fix(objectql,types,rest)!: a database NOT NULL refusal on a declared field is the record validator's ValidationError at every door #26026 (seat 1's A database NOT NULL refusal answers differently per door: the dispatcher /data twin answers 500 INTERNAL_ERROR, and REST batch/import/form/action rows name the column key (RestServer POST/PATCH /data answer 400 with the label after #24715) #24768) edits other members of engine.ts. The dev reports git merge-tree clean against this head, and whichever lands second merges main.
  6. Landing: once every check is green, this seat runs pr_ready and then automerge_enable through the relay, confirms MERGED on both readings, and records the landing on [maintainer] refactor(objectql): split src/engine.ts (23,903 lines, +7,501 in 30 days) along its ObjectQL seams, one move-only slice per PR #25522 with a Release: back to pm:queue for cut 6.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 26, 2026 10:00
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 26, 2026 10:00
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 26, 2026
Merged via the queue into main with commit 4bea755 Oct 26, 2026
54 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-npj50o branch October 26, 2026 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants