Repository navigation
Commit bdfad1f
<!-- claude-projects-attribution: {"github_login":"os-project-manager"}
-->
_Requested by **Leehom** · [project
thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XN7mA8KDABioDDHv3PkYJVj)_
Part of #25522
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
Cut 10 of the `packages/objectql/src/engine.ts` split (#24586 ruling A,
recorded in `6270097840`). The claim is `6423785652`. Two bodies of the
"Referential integrity and master-detail resolution" section move
unedited into a new module,
`packages/objectql/src/engine-referential-integrity.ts`:
`assertReferencesResolve` (the write path's #4441 reference check) and
`masterDetailHeadersCallerReads` (#22519, which master-detail headers
the caller's read door returns). The module helper
`isEmptyReferenceValue`, which only the first calls, moves with them. No
behaviour changes. #25522 stays open for the later cuts.
Branch `claude/project-thread-oed4lq` (the card thread's harness-pinned
branch), cut from `origin/main` at `366e4aa69`, which is cut 9's merge
(#26263). Nothing was edited before that merge landed.
## Why this area, and why not Autonumber
Measured on `366e4aa69` with cut 9's script plus two screens it lacked.
`engine.ts` is 23,040 lines, 7,384 of them from the last 30 days (`git
blame -s --since=2026-09-26T19:30Z`). `scripts/pm/git-history.mjs
count`: 128 commits in 30 days, 34 in 7. `class ObjectQL` holds 318
members, 255 methods and accessors.
| range | section | first … last member | lines | 30-day lines | methods
| inferred public | U | private statics |
|---|---|---|---|---|---|---|---|---|
| 4764-4989 | Instance state and constructor | `txStore` … `constructor`
| 226 | 50 | 0 (0 public) | - | 0 | - |
| 4990-5012 | Status and registry accessor | `getStatus` … `registry` |
23 | 0 | 2 (2 public) | getStatus | 0 | - |
| 5013-5356 | Hook and function registry | `registerHook` …
`triggerHooks` | 344 | 6 | 16 (16 public) | registerHook, triggerHooks |
1 | - |
| 5357-5712 | Hook dispatch internals | `hasHooksFor` …
`assertBulkPerRowHookBudget` | 356 | 61 | 7 (0 public) | - | 0 | - |
| 5713-5873 | Action registry and activation | `registerAction` …
`setActionActive` | 161 | 19 | 10 (10 public) | - | 1 | - |
| 5874-6155 | Registered resolvers and write-gate probes |
`_heldFileResolver` … `registerInsertRecomputeListener` | 282 | 116 | 6
(6 public) | - | 0 | - |
| 6156-6909 | Readable-ids security and permission/formula resolution |
`readableIdsSecurityReported` … `resolveDefaultPermissions` | 754 | 546
| 15 (2 public) | - | 0 | - |
| 6910-7016 | Authz invalidation and middleware registration |
`writeEpoch` … `hasObjectMiddleware` | 107 | 18 | 4 (4 public) | - | 0 |
- |
| 7017-7194 | Execution plumbing (middleware, session, provenance,
users) | `executeWithMiddleware` … `buildUser` | 178 | 45 | 7 (0 public)
| - | 0 | - |
| 7195-7525 | Driver options and engine posture | `buildDriverOptions` …
`resolveEnginePosture` | 331 | 209 | 6 (0 public) | - | 0 | - |
| 7526-7735 | Organization probe and system-insert organization |
`probeInstallOrganizations` … `transactionCoversDriverFor` | 210 | 45 |
4 (0 public) | - | 0 | - |
| 7736-8101 | Field defaults | `resolveNowDefault` …
`defaultFieldEntries` | 366 | 4 | 6 (0 public) | - | 0 | - |
| 8102-8714 | Autonumber | `applyAutonumbers` … `seedAutonumber` | 613 |
80 | 6 (0 public) | - | 1 | - |
| 8715-9379 | Package, app, plugin and driver registration |
`registrationConversionWarned` … `registerDriver` | 665 | 170 | 6 (3
public) | registerApp, registerDriver | 0 | - |
| 9380-9588 | Datasource registry | `supplyFileColumnsMovedResolver` …
`listUnavailableDatasources` | 209 | 1 | 8 (7 public) | - | 3 | - |
| 9589-9740 | Write guard and realtime publish | `assertWriteAllowed` …
`publishBulkDataEvent` | 152 | 18 | 4 (1 public) | - | 0 | - |
| 9741-9839 | Validation-message i18n | `setI18nService` …
`objectDisplayLabel` | 99 | 14 | 4 (1 public) | - | 0 | - |
| 9840-10932 | Referential integrity and master-detail resolution |
`assertReferencesResolve` … `masterDeclaredFields` | 1093 | 429 | 12 (0
public) | - | 0 | - |
| 10933-11048 | Dangling-reference and cascade inspection |
`inspectDanglingReferences` … `inspectCascadeDelete` | 116 | 48 | 2 (2
public) | - | 0 | - |
| 11049-11143 | Crypto and tenancy providers | `setCryptoProvider` …
`setTenancyPostureProvider` | 95 | 20 | 4 (4 public) | - | 1 | - |
| 11144-11563 | Secret and internal-field handling |
`encryptSecretFields` … `resolveInternalField` | 420 | 61 | 9 (3 public)
| - | 0 | - |
| 11564-11632 | Schema lookup and filter judging | `getSchema` …
`judgeFilter` | 69 | 48 | 3 (2 public) | - | 0 | - |
| 11633-11898 | Datasource routing | `LIFECYCLE_DATASOURCE` …
`setDatasourceMapping` | 266 | 20 | 8 (3 public) | setDatasourceMapping
| 0 | - |
| 11899-12066 | Lifecycle init and health | `init` …
`resolvePrimaryDatasource` | 168 | 16 | 3 (3 public) | init | 0 | - |
| 12067-12140 | Media and value-shape presence | `objectHasMediaField` …
`valueShapeStrictFor` | 74 | 13 | 4 (0 public) | - | 0 |
mediaFieldPresence, coveredValueFieldPresence |
| 12141-12823 | Migration flags and attestations |
`isFileReferencesMigrationVerified` … `wasDatastoreCreatedFromEmpty` |
683 | 81 | 17 (8 public) | - | 0 | - |
| 12824-12846 | destroy | `destroy` … `destroy` | 23 | 0 | 1 (1 public)
| destroy | 0 | - |
| 12847-12865 | Expand/cascade limits (statics) | `MAX_EXPAND_DEPTH` …
`MAX_BULK_PER_ROW_HOOK_ROWS` | 19 | 0 | 0 (0 public) | - | 0 |
MAX_EXPAND_DEPTH, MAX_CASCADE_DEPTH |
| 12866-13316 | Roll-up summaries | `autonumberCounters` …
`recomputeSummaries` | 451 | 0 | 7 (1 public) | - | 0 | - |
| 13317-13570 | Related-record expansion | `expandRelatedRecords` …
`expandRelatedRecords` | 254 | 10 | 1 (0 public) | - | 0 | - |
| 13571-14014 | File-reference hydration | `resolveFileReferences` …
`readParentDerivedFiles` | 444 | 310 | 5 (0 public) | - | 0 | - |
| 14015-14493 | Query lowering | `resolveWhereTokens` …
`requireFindOnePredicate` | 479 | 272 | 9 (0 public) | - | 0 | - |
| 14494-15050 | find / findOne | `find` … `staticReadonlyCreateStrip` |
557 | 142 | 4 (2 public) | - | 0 | - |
| 15051-15708 | validate | `validate` … `validate` | 658 | 447 | 1 (1
public) | - | 0 | - |
| 15709-17055 | insert / insertMany | `readUpdatePriorRow` …
`insertMany` | 1347 | 439 | 3 (2 public) | - | 1 | - |
| 17056-18989 | update | `update` … `update` | 1934 | 275 | 1 (1 public)
| - | 0 | - |
| 18990-20530 | Delete cascade and reference walking |
`planCascadeAtomicity` … `organizationOwnedRowCounts` | 1541 | 733 | 22
(1 public) | - | 0 | storedReferenceIncludes,
remainderAfterMemberRemoval, referenceCheckContext,
outlivesOrganization, keepsOrganizationReference,
organizationCountContext |
| 20531-21100 | delete | `delete` … `delete` | 570 | 47 | 1 (1 public) |
- | 0 | - |
| 21101-21720 | count / aggregate | `count` … `aggregate` | 620 | 236 |
3 (2 public) | - | 0 | - |
| 21721-22029 | execute and transactions | `execute` …
`enforceTransactionOrigin` | 309 | 74 | 8 (2 public) | - | 0 | - |
| 22030-22322 | Compatibility API | `registerObject` … `create` | 293 |
52 | 16 (16 public) | - | 0 | - |
The full per-method screen and both price lists are in the project file
`pm-orders/server/25522-cut10-seam-table.md`. The stay rules are cut 9's
(inferred-return public, a U site, a private-static read, an
`objectql/src/engine.ts#NAME` pointer under `packages/spec/**` or
`docs/adr/**`, an engine self-call, a static), plus two new ones:
- **Erasure.** A body holding a `query-options/no-any-erasure` site
stays. `scripts/query-options-erasure-baseline.json` is shrink-only in
its key set ("adding a file here is not a sanctioned move"), so a new
module cannot carry one. Linting `engine.ts` under an ungrandfathered
file name finds the baseline's 8 sites, in `seedAutonumber`,
`referenceExists`, `resolveMasterDetailParent`,
`resolveMasterDetailParents`, `inspectDanglingReferences`, `find`,
`findOne` and `probeReferencingRows`.
- **Module-helper closure.** A body calling a non-exported top-level
helper of `engine.ts` moves only when every user of that helper (and of
that helper's own helpers) moves too. Otherwise the module would
value-import `engine.ts`, a cycle at run time.
| area | paying bodies under both screens | 30-day lines in them | net |
verdict |
|---|---|---|---|---|
| **Referential integrity** (this PR) | `assertReferencesResolve` 79,
`masterDetailHeadersCallerReads` 23, helper `isEmptyReferenceValue` |
**29 of 102** body lines | **-98 measured** | the coldest area that pays
|
| Autonumber (cut 9's proposal, about -75) |
`isIssuedAutonumberCollision` 5, `warnAutonumberReadersUnserved` 13 | 15
of 18 | about -5 | does not pay. `seedAutonumber` holds an erasure site;
`adoptExplicitAutonumber` shares `readStoredAutonumberCounter` with it,
and spec prose (`packages/spec/src/data/autonumber-format.ts`) names
that helper in `engine.ts`, which this claim may not edit |
| File-reference hydration | 5 bodies, 288 lines, helpers local | 176 of
288 | about -243 | pays most, but 61% of its lines are from the last 30
days |
| Driver options and engine posture | 6 bodies with
`accessibleOrganizationsOf` | about 84 of 146 | about -120 | hot |
| Migration flags and attestations | 8 bodies | 50 of 154 | about -91 |
the section writes fields, so its host needs setters (a seat ruling
first) |
| Readable-ids, Package registration, count, Delete cascade, Query
lowering and the rest | - | - | -87 to 0 | hot, or blocked by a shared
helper, or pays little |
In Referential integrity the rest stays: `referenceExists`,
`resolveMasterDetailParent` and `resolveMasterDetailParents` read the
private static `referenceCheckContext` and hold erasure sites;
`settleMasterDetailLanding` calls `staticReadonlyStripTakes`, whose
`staticReadonlyStoredView` `update` also uses; `resolvePreviewParents`
(`masterIdOf`, `servedAsStored`) and `resolvePredicateRelated` /
`resolveOneHopRelated` (`undeclaredRelatedFields`) share helpers with
bodies that stay; `masterDetailHeaderExists`, `materializeParentHeader`
and `masterDeclaredFields` are no longer than their stubs would be.
## The shape
- The class keeps both methods with name, modifiers (`private async`),
signature, docblock and position. Each body is now one host literal of
exactly the `ReferentialIntegrityHost` members it reads, plus a `.call`,
marked `[#25522 cut 10]`, as cut 8's stubs are:
`assertReferencesResolve` gets `masterDetailHeaderExists` and
`referenceExists` as late-bound closures;
`masterDetailHeadersCallerReads` gets `find` as a closure and `logger`
as a getter.
- **One named host.** `ReferentialIntegrityHost` types each member as
the class member (`ObjectQL['find']` and so on); each function's `this`
is the `Pick` of what it reads. The module imports the engine only as a
type.
- **The helper.** `isEmptyReferenceValue` moves with its docblock byte
for byte (508 bytes, 12 lines) and is not exported; its only user was
`assertReferencesResolve`. One copy, no re-derivation.
- **Imports.** The module imports `referenceTargetOf` and
`EngineQueryOptions` (`@objectstack/spec/data`), `ExecutionContext`
(type), and `ValidationError` and `buildFieldError`
(`./validation/record-validator.js`), the bindings `engine.ts` used.
`engine.ts` drops `buildFieldError` from its import (its last reader
moved; `tsc` reported TS6133) and gains the new module import with a
three-line comment.
- The module header names what moved, what stays and why. It cites no
ADR.
## Net lines (the seat's rule)
`engine.ts` goes from 23,040 to 22,942 lines, **net -98** (18 added, 116
removed). The module is 200 lines. With the two re-keyed census rows and
the one re-keyed readers row, the diff is 340 changed lines over 4 files
(+221 / -119).
## The published `.d.ts` does not change (ruling `6348891160`)
Base: `@objectstack/objectql` built with `engine.ts` restored from
`366e4aa69` (blob `c9773e3a` checked) and the new module removed;
restored with `git checkout HEAD --`, blob hashes compared, `git diff
HEAD` empty. Head: built at `b1f3252c8`.
`masterDetailHeadersCallerReads(master` occurs 2 times in the base
`dist/index.mjs` and 3 times in the head's, so each leg built the source
it names. All six declaration files are **byte-identical**, and equal to
the hashes cuts 7 to 9 recorded:
| file | sha256, base = head |
|---|---|
| `core.d.mts` |
`ef178bf0725eccc2f358f082ce13dc5122a6d5a53c19ef630017256f67de03b4` |
| `core.d.ts` |
`1d20303a455a909e4b55fd0802fb0b0bcb59ec51784daf27d5ef4c0659410953` |
| `index.d.mts` |
`ccf900240aa137abe72f82b8abfc872d42c5645467400134c580285f2a3b3090` |
| `index.d.ts` |
`0454d5e70b156d8effbf27b8915587c7cbee241dcd7d698013c0a4c369a02aa5` |
| `util-CVzOGxAn.d.mts` |
`85ef1cbec2b016d5f7afabaa21689c86c6dffc573076fdfdeb8ea0f1a1b1ad53` |
| `util-CVzOGxAn.d.ts` |
`85ef1cbec2b016d5f7afabaa21689c86c6dffc573076fdfdeb8ea0f1a1b1ad53` |
**Positive control:** dropping `private` from the
`masterDetailHeadersCallerReads` stub (anchor landed once, 0 private
hits after) and rebuilding changed all six hashes and the chunk name
(`util-Cz_Kcdws.d.ts` `e762bbf9…`, `index.d.ts` `5f1e19b4…`), and the
chunk now prints `masterDetailHeadersCallerReads(master: string, ids:
ReadonlyArray of unknown, context: ExecutionContext | undefined)`.
Restored with `git checkout HEAD --` (blob hash compared, `git diff
HEAD` empty, the private spelling back to 1 hit), rebuilt, and all six
files compared equal to the head build again.
## Proof of move-only
A TypeScript-AST leaf-token compare
(`pm-orders/server/evidence/25522/astcmp.mjs`, unchanged from cut 9) of
base `engine.ts` (`366e4aa69`) against the head module:
- Both bodies are **SAME** in parameters, return type, type parameters,
body tokens and `async`, and every body line equals the base line less
two leading spaces, comments included: `assertReferencesResolve` 285
tokens / 79 lines, `masterDetailHeadersCallerReads` 239 / 23.
- `isEmptyReferenceValue`: the base text from its docblock to its
closing brace is a substring of the module, and `engine.ts` no longer
declares it.
- Class: 318 members at both ends, in the same order. 316 are
byte-identical, and the 2 stubs keep docblock, modifiers and signature
byte-identical up to the body.
- Top level: 229 of 233 base statements are byte-identical in head. The
other four: the class; the `record-validator.js` import, less
`buildFieldError`; `isEmptyReferenceValue`, moved; and `masterIdOf`,
whose own text and docblock are identical (`getText` equal) but whose
leading trivia now starts with the `implements IObjectQLEngine` comment
that used to sit above `isEmptyReferenceValue`'s docblock. That comment
describes the class, so it stays in `engine.ts` unedited. Head adds the
new module import with its comment.
## Readers of the moved lines
- **The system-context census page (migrated in this PR).** Both bodies
read `isSystem`, so rows 23 (`assertReferencesResolve`) and 29c
(`masterDetailHeadersCallerReads`) of
`content/docs/permissions/system-context.mdx` now anchor
`packages/objectql/src/engine-referential-integrity.ts#NAME`; their
wording is unchanged. `check-system-context-census` at base: 153 read
sites across 81 files, 145 of 145 symbols cited, OK; at head: 153 across
82 files, 145 of 145, OK. **Control:** with the base page over this head
the gate exits 1 with exactly three findings: `file-without-a-row` for
the new module and `anchor-is-not-a-read-site` for each old anchor.
Restored from `HEAD`, blob hash compared, green again.
- **The federated injected-column readers table (migrated in this PR).**
`federated-injected-column-readers.test.ts` keys rows by `FILE#FUNCTION
:: SEAM`, and `assertReferencesResolve` calls `referenceTargetOf`, so
its row is re-keyed
`engine-referential-integrity.ts#assertReferencesResolve ::
referenceTargetOf()`; disposition and reason unchanged. Head: 5 of 5
pass. **Control:** the base file over this head fails 1 of 5 and names
the new key; restored, blob hash compared.
- **ADR anchors.** No `scripts/adr-anchors/` entry describes a moved
body (the engine entry's one paragraph is the transaction seam). The
moved bodies cite no decision letter, and the docblocks that cite
ADR-0131 D1 and ADR-0055 stay on the methods. `check-adr-anchors` at
base `366e4aa69` (clean detached worktree): 90 anchored files, **1,880**
decision-letter citations, 59,261 resolving citations; at head: 90,
**1,880**, 59,261.
- No `objectql/src/engine.ts#NAME` pointer under `packages/spec/**` or
`docs/adr/**` names the two or the helper.
`docs/qa/platform-checklist/areas/records-forms.json` cites
`packages/objectql/src/engine.ts#assertReferencesResolve` twice as
evidence; the method is still declared there with the docblock the
citation quotes, and `check-platform-checklist --changed-from
origin/main` judges 13 anchors in the change set, 0 lost. Prose that
names the methods (`service-messaging` and `service-settings`
`actor-reference.ts`, `plugin-security` `position-catalog-refusal.ts`,
test comments) still resolves, because both methods stay on the class.
- No gate baseline keys a moved body
(`durability-read-invention.baseline.json` keys only `referenceExists`,
which stays; `query-options-erasure-baseline.json` keeps `engine.ts` at
8, since `masterDetailHeadersCallerReads` spells `as
EngineQueryOptions`).
## Acceptance notes
- Serial check, re-read before `pr_create`: no open PR touches
`packages/objectql/src/engine.ts`, `system-context.mdx` or
`scripts/adr-anchors/`. #26138 edits `validation/rule-validator.test.ts`
only. `origin/main` moved 10 commits after the base, none on
`packages/objectql/**`, `content/docs/permissions/**`,
`scripts/adr-anchors/**` or the readers test, so the branch was not
merged again.
- **Cut 9's proposal does not pay.** Autonumber, priced about -75
without the erasure and helper screens, pays about -5 under them (table
above). The seam script now in the project files carries both screens.
- Next cut from this screen: File-reference hydration (about -243, all
five bodies and their three helpers local, 61% recent lines), or Driver
options (about -120, `driverWriteTarget` moves with
`engineLayer0Verdict` so `accessibleOrganizationsOf` stays
module-local). Migration flags still needs a setter ruling.
## Verification
At `b1f3252c8`:
- `pnpm --filter @objectstack/objectql typecheck` (`tsc --noEmit` +
`check:test-typecheck`): VERDICT command-exit 0. `pnpm exec eslint
--no-inline-config packages/objectql`: exit 0.
- `pnpm --filter @objectstack/objectql exec vitest run --project local
--maxWorkers=2`: 472 files, 8,789 tests passed (VERDICT command-exit 0).
- Dependants: both changed members keep their declarations and all six
published `.d.ts` files are byte-identical, so no dependant can see a
difference. CI's `TypeScript Type Check` covers them.
- Gates: `dispatch-gates --commands` derived 90 families on the real
diff. Every one was run, each exit code captured before any pipe, then
reconciled with `--ran`: 90 accounted for, 88 run green, 2 **NOT
MEASURED** (exit 3, PREREQUISITE NOT MET; CI runs both):
`check:dual-build-cjs-loads` (needs every package's `dist/`) and
`@objectstack/spec check:skill-examples` (needs the full build). Three
first-run exit codes were prerequisites, re-run green after meeting
them: `check-engine-split-ratio --days 90` (shallow clone; deepened to
2026-07-21, 97.6%), `check-plugin-teardown-shape --self-test` and
`check:lean-entry-closure` (needed the objectql `dist/`). Named
readings: `check-system-context-census` OK,
`check:query-options-erasure`, `check:durability-log-level`,
`check:org-identifier`, `check:logger-receiver-detach`,
`check:doc-anchors`, `check-affected-docs` exit 0.
- Hand gates outside the derivation, all exit 0 at `b1f3252c8`:
`check-adr-anchors` at base and head (above),
`check-adr-symbol-anchors`, `check-tenant-audit-census`,
`check-platform-checklist --changed-from origin/main` and
`check-commit-card-trailers --range origin/main..HEAD`.
- No ablation, because no tested behaviour changed. `test:repo` was not
run locally; CI's `Test Core` runs it.
`skip-changeset`: move-only. The published `.d.ts` files 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. The docs edit changes only the anchor path in two rows of the
census table.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01KiAsjPtReu1tmXvCEd8f7R)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 77dc706 commit bdfad1f
5 files changed
Lines changed: 229 additions & 120 deletions
File tree
- content/docs/permissions
- packages
- core/src/security
- objectql/src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
131 | | - | |
| 131 | + | |
132 | 132 | | |
133 | 133 | | |
134 | 134 | | |
| |||
140 | 140 | | |
141 | 141 | | |
142 | 142 | | |
143 | | - | |
| 143 | + | |
144 | 144 | | |
145 | 145 | | |
146 | 146 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
138 | 138 | | |
139 | 139 | | |
140 | 140 | | |
141 | | - | |
142 | 141 | | |
143 | 142 | | |
144 | 143 | | |
| |||
210 | 209 | | |
211 | 210 | | |
212 | 211 | | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
213 | 220 | | |
214 | 221 | | |
215 | 222 | | |
| |||
0 commit comments