Skip to content

refactor(objectql): move count and rejectCredentialAggregation out of engine.ts into engine-count-aggregate.ts (#25522 cut 18) - #26932

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-axdp1m
Oct 27, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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

维护者速读

Cut 18 of the packages/objectql/src/engine.ts split (#24586 ruling A, recorded in 6270097840), under the claim 6453372157. Two bodies of the "count / aggregate" section move unedited into a new module, packages/objectql/src/engine-count-aggregate.ts, behind CountAggregateHost: count (the counting door: alias fold, option refusal, filter-array lowering, search expansion, the operation context, token resolution, the middleware chain, the readable-ids boundary and the find() fallback) and rejectCredentialAggregation (aggregate()'s fail-closed guard against grouping or aggregating a secret / password / internal: true field). No behaviour changes. Every published declaration file is byte-identical to the base. #25522 stays open for the later cuts.

Branch claude/project-thread-axdp1m (the card thread's harness-pinned branch), cut from origin/main at 1708115e2f, which contains cut 17's merge (cea0871ff2, #26881) and #26864.

What changes

Why this area

Re-measured on 1708115e2f with cut 11's measure script and cut 14's price step (price3.mjs), both unchanged. engine.ts is 21,835 lines, 6,991 of them from the last 30 days (git blame --since=2026-09-27T21:00Z, clone deepened to 2026-09-21 first); class ObjectQL holds 326 members, 261 methods and accessors. Query-options erasure sites: 8 at base (lines 8408, 9648, 9894, 9958, 10290, 13570, 13869, 18499), none in this area.

price (net lines) section taken?
-248 Delete cascade and reference walking no: the order excludes it (#26864 landed there; #26871, branch claude/project-thread-vdrn59, is developing the predicate-delete walk across it)
-71 priced, -73 landed count / aggregate yes (count 58 body lines, rejectCredentialAggregation 38; aggregate stays, named engine.ts#aggregate in packages/spec/liveness/query.json)
-40 Query lowering runner-up: pays less
-27 Hook dispatch internals; resolveFormulaRelated pays less; resolveFormulaRelated is a later cut, as the order says
-21 or less find / findOne, the rest pays less

36 of the 96 moved body lines are from the last 30 days. The two other excluded sections ("Dangling-reference and cascade inspection", "Referential integrity and master-detail resolution") price below -30 anyway. registerMetadataCollections was not considered (the order excludes it).

Seam table on 1708115e2f (40 sections of class ObjectQL; U = untyped receivers)
range section first … last member lines 30-day lines methods inferred public U private statics
4974-4996 Status and registry accessor getStatus … registry 23 0 2 (2 public) getStatus 0 -
4997-5340 Hook and function registry registerHook … triggerHooks 344 6 16 (16 public) registerHook, triggerHooks 1 -
5341-5696 Hook dispatch internals hasHooksFor … assertBulkPerRowHookBudget 356 61 7 (0 public) - 0 -
5697-5859 Action registry and activation registerAction … setActionActive 163 24 10 (10 public) - 1 -
5860-6141 Registered resolvers and write-gate probes _heldFileResolver … registerInsertRecomputeListener 282 116 6 (6 public) - 0 -
6142-6783 Readable-ids security and permission/formula resolution readableIdsSecurityReported … resolveDefaultPermissions 642 509 16 (2 public) - 0 -
6784-6890 Authz invalidation and middleware registration writeEpoch … hasObjectMiddleware 107 18 4 (4 public) - 0 -
6891-7068 Execution plumbing (middleware, session, provenance, users) executeWithMiddleware … buildUser 178 45 7 (0 public) - 0 -
7069-7277 Driver options and engine posture buildDriverOptions … resolveEnginePosture 209 150 6 (0 public) - 0 -
7278-7487 Organization probe and system-insert organization probeInstallOrganizations … transactionCoversDriverFor 210 45 4 (0 public) - 0 -
7488-7853 Field defaults resolveNowDefault … defaultFieldEntries 366 4 6 (0 public) - 0 -
7854-8466 Autonumber applyAutonumbers … seedAutonumber 613 80 6 (0 public) - 1 -
8467-9053 Package, app, plugin and driver registration registrationConversionWarned … registerDriver 587 157 6 (3 public) registerApp, registerDriver 0 -
9054-9262 Datasource registry supplyFileColumnsMovedResolver … listUnavailableDatasources 209 1 8 (7 public) - 3 -
9263-9414 Write guard and realtime publish assertWriteAllowed … publishBulkDataEvent 152 18 4 (1 public) - 0 -
9415-9522 Validation-message i18n setI18nService … objectDisplayLabel 108 24 4 (1 public) - 0 -
9523-10231 Referential integrity and master-detail resolution assertReferencesResolve … masterDeclaredFields 709 308 12 (0 public) - 0 -
10232-10347 Dangling-reference and cascade inspection inspectDanglingReferences … inspectCascadeDelete 116 48 2 (2 public) - 0 -
10348-10442 Crypto and tenancy providers setCryptoProvider … setTenancyPostureProvider 95 20 4 (4 public) - 1 -
10443-10862 Secret and internal-field handling encryptSecretFields … resolveInternalField 420 61 9 (3 public) - 0 -
10863-10931 Schema lookup and filter judging getSchema … judgeFilter 69 25 3 (2 public) - 0 -
10932-11197 Datasource routing LIFECYCLE_DATASOURCE … setDatasourceMapping 266 20 8 (3 public) setDatasourceMapping 0 -
11198-11365 Lifecycle init and health init … resolvePrimaryDatasource 168 16 3 (3 public) init 0 -
11366-11439 Media and value-shape presence objectHasMediaField … valueShapeStrictFor 74 13 4 (0 public) - 0 mediaFieldPresence, coveredValueFieldPresence
11440-12029 Migration flags and attestations isFileReferencesMigrationVerified … wasDatastoreCreatedFromEmpty 590 96 17 (8 public) - 0 -
12030-12052 destroy destroy … destroy 23 0 1 (1 public) destroy 0 -
12053-12071 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
12072-12522 Roll-up summaries autonumberCounters … recomputeSummaries 451 0 7 (1 public) - 0 -
12523-12776 Related-record expansion expandRelatedRecords … expandRelatedRecords 254 10 1 (0 public) - 0 -
12777-12974 File-reference hydration resolveFileReferences … readParentDerivedFiles 198 183 5 (0 public) - 0 -
12975-13453 Query lowering resolveWhereTokens … requireFindOnePredicate 479 271 9 (0 public) - 0 -
13454-14010 find / findOne find … staticReadonlyCreateStrip 557 142 4 (2 public) - 0 -
14011-14322 validate validate … assignment 312 224 4 (1 public) - 0 -
14323-15692 insert / insertMany readUpdatePriorRow … insertMany 1370 462 3 (2 public) - 1 -
15693-17640 update update … update 1948 290 1 (1 public) - 0 -
17641-19325 Delete cascade and reference walking planCascadeAtomicity … organizationOwnedRowCounts 1685 955 24 (1 public) - 0 storedReferenceIncludes, remainderAfterMemberRemoval, referenceCheckContext, outlivesOrganization, keepsOrganizationReference, organizationCountContext
19326-19895 delete delete … delete 570 47 1 (1 public) - 0 -
19896-20515 count / aggregate count … aggregate 620 192 3 (2 public) - 0 -
20516-20824 execute and transactions execute … enforceTransactionOrigin 309 74 8 (2 public) - 0 -
20825-21117 Compatibility API registerObject … create 293 52 16 (16 public) - 0 -

The per-member screen and the full price output are in the project files, pm-orders/server/25522-cut18-seam-table.md.

The shape

  • The class keeps both methods with name, modifiers, signature, docblock and position. A body becomes one host literal of exactly the CountAggregateHost members it reads, plus a .call, marked [#25522 cut 18], as cuts 8 and 10 to 17 did. Fields go as getters over a readX closure (_registry, relatedSchemaOf); methods go as late-bound closures (resolveObjectName, expandSearchOnOptions, getDriver, resolveWhereTokens, executeWithMiddleware, buildDriverOptions, composeReadableIdsAtBoundary, find), so a spy or double set on the engine is still the one a call reaches. No body assigns a field, so there is no setter (the interface docblock says only "getters" this time, not the cut-17 wording the seat flagged).
  • Series limits. count is public with a declared return type (Promise<number>); rejectCredentialAggregation is private, so its .d.ts line is private rejectCredentialAggregation; either way. No moved body has a this.FIELD.delete/insert/update( site, a private-static read, a self-call to insert / update / delete, an engine.ts#NAME pointer or an ADR anchor paragraph of its own. count calls composeReadableIdsAtBoundary and executeWithMiddleware, which ADR-0139 names by path; they stay in the class and are reached through the host.
  • Helpers, under the cut-14 seat ruling. count reads seven top-level helpers, all also read by bodies that stay: foldEngineOptionAliases, rejectUnknownEngineOptions, lowerWhereFilterArray, declaredDatetimeLowering (find, findOne, update, delete, aggregate, and judgeWhereAdmission for the last two), ENGINE_WHERE_SLOTS (update, delete, aggregate), ENGINE_COUNT_OPTION_KEYS (ENGINE_OPTION_KEY_SETS) and mergeReadContext (expandSearchOnOptions, find, findOne, aggregate). Each gains export in place, byte for byte otherwise (leading docblocks included), and the module imports them as values from ./engine.js; nothing is copied. foldEngineOptionAliases is also named by packages/spec/liveness/query.json as engine.ts#foldEngineOptionAliases, which still holds: it stays in engine.ts. Measured alone before the move (dts-export-variant-cut18.sh, base engine.ts plus the export, no move): each of the seven, and all seven together, leave all published declaration files byte-identical. index.ts and core.ts name their exports singly, so nothing new is published.
  • The run-time cycle and the load order (measured). The value import makes engine.ts and the module import each other. The module runs nothing at evaluation (declarations only). rejectCredentialAggregation was engine.ts's only reader of StandardErrorCode, so that import line (@objectstack/spec/api, with its [#22646] comment) moves to the module verbatim. Two positions were built and compared (cut18-place-builds.sh, bundlecmp.py): importing the module where the StandardErrorCode line was (line 31) splits an extra engine.ts region and prints the secret-fields.ts region ahead of engine-secret-internal-fields.ts and the regions between, so the module evaluation order changes; importing it right after ./secret-fields.js (the one other module it imports), as this PR does, leaves the order intact. Measured on the final head's bundles: with the // src/engine.ts markers set aside (10 at base and head), the // src/... region order equals the base's with one line added, // src/engine-count-aggregate.ts (index.mjs 99 to 100 regions, core.mjs 88 to 89, index.js 100 to 101, core.js 89 to 90). The external modules load in the same first-occurrence order, with the same statement counts: 113 and 96 import statements in the ESM bundles, 148 and 130 require( calls in the CJS bundles. The late-binding probe answers the same on base and head (Tests).
  • Imports. The module imports EngineAggregateOptions, EngineCountOptions, QueryAST (types, @objectstack/spec/data), StandardErrorCode (@objectstack/spec/api), collectInternalReadFields and collectCredentialFields (./secret-fields.js), the seven helpers (values) and EngineReadOptions, ObjectQL, OperationContext (types) from ./engine.js. engine.ts loses the StandardErrorCode line; its ./secret-fields.js import stays (collectInternalReadFields is still read there, and collectCredentialFields by the staying docblock's {@link}).
  • The module header names what moved, how the bodies reach the class, why aggregate stays, and how the helpers and the import position were handled.

Net lines (the seat's rule)

engine.ts goes from 21,835 to 21,762 lines, net -73 (33 added, 106 removed). The module is 202 lines. The diff against origin/main is 341 changed lines over 2 files (+235 / -106).

Readers of the moved lines

  • No census, ADR anchor, docs row or test key names a moved member by engine.ts path. Searched: engine.ts#count, engine.ts#rejectCredentialAggregation and engine.ts … count spellings, plus every scripts/** file that reads engine.ts (durability baselines, engine-double contract, logger-receiver detach, single-authz resolver, startup-registry verdict, widget-option census, type-check coverage, erasure baseline, tenant-audit and isSystem censuses): none names them. The prose mentions found (CHANGELOG.md entries, a metadata-protocol/src/protocol.ts docblock, service-analytics/src/api-exposure-door.ts) name rejectCredentialAggregation by behaviour, not by path.
  • packages/spec/liveness/query.json names engine.ts#aggregate, engine.ts#foldEngineOptionAliases and engine.ts#ENGINE_QUERY_SLOTS: all three stay in engine.ts, so nothing under packages/spec changes.
  • engine-middleware-operation-vocabulary.test.ts reads the OperationContext['operation'] union out of engine.ts, where it stays; the test passes. The bodies call no canReadObject( (core's row-serving-door-exposure.pin.test.ts is unchanged), no referenceTargetOf(, no isFederatedObject / resolveTenantFieldName, and read no isSystem (system-context.mdx is unchanged; its census is green). They hold no query-options/no-any-erasure site and no tenant-audit untyped receiver.
  • Read-seam census (the cut-21 trap of [maintainer] refactor(metadata-protocol): split src/protocol.ts (33,065 lines, +10,169 in 30 days) along its ObjectStackProtocolImplementation seams, one move-only slice per PR #25467). check-durability-degradation-log-level --list at base and head: identical once line numbers are masked (164 lines: 48 durability seams and 88 read seams); count's driver read sits outside any catch, so it is no seam.
  • No ADR anchor paragraph describes a moved body (the engine.ts entry is the transaction-seam paragraph), so the module needs no anchor entry of its own.
  • Decision-letter count: check-adr-anchors at base 1708115e2f (clean detached worktree): 1,897 decision-letter citations on the anchored surface, 60,009 resolving citations across 8,416 files. At head: 1,896, 60,009 across 8,417. The one letter that leaves the anchored surface is [ADR-0139 D2] in count's body (the executor-boundary comment), which moved verbatim into the unanchored module; cut 15 disclosed the same kind of drop ([ADR-0139 D6], 1,881 to 1,880). No anchor paragraph was written for it: none of the engine.ts entry's text describes count, and docs/adr/** is outside this claim.

Why

#25522 splits engine.ts one seam at a time so that a change to one area reads, reviews and conflicts as that area alone. After cut 17, the highest-paying section no open PR touches is "count / aggregate". Nothing about what the engine does changes.

Tests

No test is added and none is edited: the move is proved by identity, not by new behaviour, and no test reads a moved line by path.

Public .d.ts: byte-identical (ruling 6348891160 item 1), with the 6441032442 report block and a positive control

Built @objectstack/objectql at the base (1708115e2f: engine.ts blob 1878bcbf6512, no engine-count-aggregate.ts) and at head (engine.ts blob c16b33857e02), each from a clean src/ (dts-cut18.sh: the base leg restores all of src/ from the base and deletes the module; both legs check the blob). The builds are the moved code, not a stale cache: count.call(host occurs 0 times in the base dist/index.mjs and once in the head's.

Every declaration file is raw byte-identical base to head, so 6348891160 item 1 holds without any normalisation, and no chunk is renamed (util-BaJGwxSm at both). The 6441032442 block, for the record (normaliser pm-orders/server/evidence/25522/dtsnorm.mjs, sha256 25226e0677bdad24726e9eff76a7e247bf7319440d2430d28cbaab0db6add94b, run as node dtsnorm.mjs --diff BASE_FILE HEAD_FILE; output in cut18-dtsnorm.txt):

file raw sha256 (base = head) raw-diff lines statements base / head normalised sha256 (base = head) declared-surface digest (base = head) import / export specifier sets
core.d.ts 9f22dc90973142623116d4f119dcfbbe37289c83a4b28734425014727212376d 0 14 / 14 40e2fde86719d3ccd1cf492314c426c977ab836a5ab54580800cd1f41352247a 4450aaf478092516… (1 decl) EQUAL 0 / 84, EQUAL
core.d.mts e9a0b5acd001130a4a231c60aa44fbd3b59bcecd0c7d7b032e57e9d4d45d2e51 0 14 / 14 171eab8743fd8dfabc014a162c40c3e034be29f262b26a2bcc24cb3e06be0459 4450aaf478092516… EQUAL 0 / 84, EQUAL
index.d.ts 6a16c278b5ac5e3ccafa7eff4f71e5f848bd77431442b455f87bb7fbfe455621 0 122 / 122 2bb6a21a929b41c8dbb85c72c600f87ed3a3af12ca731d1cace61f5d4aa3d57e 9136189ce4d0aba3… (103 decls) EQUAL 18 / 284, EQUAL
index.d.mts 284396b272347adb8ee7226e5798f152bf76aa4878c821e3628541de7060b0c8 0 122 / 122 c7d7bab765f3b0fb35241fa03bd0b716a93139c98e5638d209379dad96528f98 9136189ce4d0aba3… EQUAL 18 / 284, EQUAL
util-BaJGwxSm.d.ts cb1fddd4136122b5a0cb983b05eca8e06233614eda7e33156ccc3437c199c90c 0 185 / 185 c43cb8b103cc68a1077b69fcbf78fb71ee5de0ace6651f351cb8e90c7a89cc6f b264bade9e7d8157… (173 decls) EQUAL 55 / 147, EQUAL
util-BaJGwxSm.d.mts cb1fddd4136122b5a0cb983b05eca8e06233614eda7e33156ccc3437c199c90c 0 185 / 185 c43cb8b103cc68a1077b69fcbf78fb71ee5de0ace6651f351cb8e90c7a89cc6f b264bade9e7d8157… EQUAL 55 / 147, EQUAL
  • (a) Every file: statements in the same order, statements only in base: 0; only in head: 0, declared-surface digest EQUAL. (b) Every normalised sha256 EQUAL. (c) Specifier sets EQUAL; the run-time export lists are too (index 182 names and core 54, ESM and CJS, the same sorted list at base and head). (d) Raw diff: 0 lines in every file. (e) After the head build, the full turbo run build (116 of 116 tasks, the dependents' DTS builds included) and pnpm --filter @objectstack/core typecheck ran on the head dist/: exit 0 each.
  • Premises re-checked at this head: (P2) packages/objectql/package.json exports names ./dist/index.* and ./dist/core.* only; (P3) git grep -n -E 'objectql/dist|driver-sql/dist' -- scripts package.json: 0 hits.
  • Each in-place export alone, before the move (dts-export-variant-cut18.sh, base engine.ts with only export added): none, each of the seven alone, and all seven together print the same util-BaJGwxSm.d.ts, index.d.ts 6a16c278b5ac… and core.d.ts 9f22dc909731….

Positive control. On this head, one sed dropped private from private rejectCredentialAggregation( (a moved member's stub; anchor count 1 to 0, replacement count 0 to 1; engine.ts blob c16b33857e02 to b97293c87022) and rebuilt (posctl18.sh, build exit 0). The chunk is renamed util-CKT4sa10 and now publishes rejectCredentialAggregation(object: string, query: EngineAggregateOptions): void; (line 11266, where the head prints private rejectCredentialAggregation;); its normalised sha256 is 32d9098f1fd316a6b8ffb7e5a40a0da99017775007bf862aed09cf9ed0c83ae5 ≠ c43cb8b1…, its declared-surface digest 4c6ca61214efff63… ≠ b264bade9e7d8157… (the declare class ObjectQL statement named as the one changed), and the raw core.d.ts (6548856dfe13…) and index.d.ts (7bff7db9b23e…) change with the chunk name. index.d.ts normalises EQUAL under the control (an entry only re-exports), which is why every declaration file is read. The restore leg put the blob back to c16b33857e02 (git diff HEAD empty), and the full build that followed produced an @objectstack/objectql dist/ whose six declaration files and two ESM bundles compare equal to the head build.

AST compare (move-only)

astcmp.mjs (TypeScript compiler API, comments and whitespace ignored, tokens compared) on base engine.ts against head engine.ts plus the module:

  • The two moved bodies are SAME against base 1708115e2f (parameters, return type, async, body tokens, and every body line equal after one level of indent): count 296 tokens / 58 lines, rejectCredentialAggregation 296 / 38.
  • class ObjectQL: 326 of 326 members kept, in the same order; 324 byte-identical, and the 2 stubs keep docblock, modifiers and signature identical.
  • Top level: 227 of 236 base statements byte-identical. The other 9 (exportcmp.mjs): the @objectstack/spec/api StandardErrorCode import, moved verbatim to the module; the seven helpers, exported in place with statement and leading docblock otherwise identical; and the class. The head adds one statement: the module import.
Late-binding probe (base and head dist)

zz-probe-cut18.mjs (a one-off, not committed; in the project files) loads the built ObjectQL and drives both moved bodies with doubles set on the instance after construction. count: with every host member replaced (_registry, relatedSchemaOf, and the eight methods), recording each call and its arguments in order; the filter alias folding into where; a malformed filter array refused (INVALID_FILTER / 400) and a well-formed one lowered; a search expanded through the host; no query; an unknown option refused; a token resolved by resolveWhereTokens and the caller's where captured before the middleware scopes it, on a driver without count (the find() fallback, 3 rows); where and filter given together (refused); and a real engine with a registered driver and a registered object (the driver's count reached twice, the second through a filter array). rejectCredentialAggregation: group by a secret; min over a password with an internal: true object groupBy (both named, envelope INVALID_FIELD / 400 / field / fields / object); safe fields; an object with no protected field; _registry replaced twice after construction; aggregate() reaching the stub; and aggregate() reaching a guard double set on the instance. The 35 output lines are identical on base 1708115e2f and head (the 17 construction log lines, which carry timestamps, set aside); stderr is identical (empty) too.

Suites

Under scripts/pm/os-verify-lock.sh on head b0142c4a2:

  • Full turbo run build (the root build filters): 116 of 116 tasks, exit 0.
  • typecheck of @objectstack/objectql and @objectstack/core: exit 0 each.
  • @objectstack/objectql vitest --project local: 475 files, 8,849 tests passed, exit 0 (engine-aggregate-filter.test.ts and engine-middleware-operation-vocabulary.test.ts, which drive these bodies, among them).
  • Every packages/*/vitest.repo-tests.json project: core 6 files / 83 tests, objectql 1 / 5, rest 5 / 280 passed and 18 skipped, runtime 3 / 948, spec 80 / 1,295, types 1 / 11. Exit 0 each.

Verification

Head b0142c4a27f4b95b789f22f24e7b198a8a449980. The gate list is node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on this diff at that head (48 commands, merge base 1708115e2), each run with its exit code captured before any pipe, then reconciled with --ran.

  • Derived gates: 48 of 48 run, exit 0 each; --ran: 48 run, 0 NOT-MEASURED, 0 UNRUN. check:dual-build-cjs-loads and check:dts-closure ran after the full build; check-plugin-teardown-shape --self-test after fetching 621a4876 and deepening the clone. The exit codes are in pm-orders/server/evidence/25522/cut18-ran-final.txt.
  • Hand gates, exit 0 each: node scripts/check-adr-anchors.mjs (counts above), node scripts/check-tenant-audit-census.mjs, node scripts/check-adr-symbol-anchors.mjs, node scripts/check-platform-checklist.mjs --changed-from origin/main, node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD, node scripts/check-system-context-census.mjs, node scripts/check-durability-degradation-log-level.mjs --list (base and head identical, masked), and eslint as pnpm lint runs it (--no-inline-config) on packages/objectql.

Acceptance notes

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (Not touched.)
  • 2. Every name I added uses the casing for its kind (CountAggregateHost, engine-count-aggregate.ts).
  • 3. No formatter ran; pnpm lint passes on the touched package.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. No publish-visible change, so skip-changeset; both declaration lines are filled in above.
  • 6. packages/spec not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English, apart from the maintainer quick-read block the project asks for.
  • The Before Submitting list in CONTRIBUTING.md is done.

skip-changeset: move-only inside @objectstack/objectql, with every published declaration file byte-identical and no behaviour change, so there is nothing for a release note to say.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5n1LHPdPmHoXe1BGDa

… engine.ts into engine-count-aggregate.ts (#25522 cut 18)

Move-only. The bodies of ObjectQL#count and #rejectCredentialAggregation
move unedited into engine-count-aggregate.ts behind CountAggregateHost
(getters for _registry and relatedSchemaOf, late-bound closures for the
eight methods count calls). The class keeps both methods with name,
modifiers, signature, docblock and position.

The seven top-level helpers count shares with find, findOne, update,
delete and aggregate gain `export` in place and are imported back, never
copied. aggregate stays: packages/spec/liveness/query.json names
engine.ts#aggregate as a producer.

The module is imported right after ./secret-fields.js, so the bundles'
region order gains one line and every external import stays in order.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5n1LHPdPmHoXe1BGDa
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/objectql, touching 27 documentable anchor(s).

8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/data-api.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))
  • content/docs/api/error-catalog.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))
  • content/docs/data-modeling/queries.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))
  • content/docs/kernel/contracts/data-engine.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))
  • content/docs/permissions/system-context.mdx (via buildDriverOptions (symbol, a field of interface CountAggregateHost), buildDriverOptions (literal, a string literal in buildDriverOptions; a string literal on a changed line))
  • content/docs/permissions/tenant-audit-census.mdx (via buildDriverOptions (symbol, a field of interface CountAggregateHost), buildDriverOptions (literal, a string literal in buildDriverOptions; a string literal on a changed line))
  • content/docs/protocol/kernel/error-handling.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))
  • content/docs/protocol/objectql/query-syntax.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))

⛔ 1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-0.mdx (via searchFields (literal, a string literal in ENGINE_COUNT_OPTION_KEYS))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 anchor(s) matched too much of the corpus to be a work list: ObjectQL (symbol, 72 pages)
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 55 of 207 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 e1ab32ed0b68a58096dd9d1c5e2e510e6bbb3a79 → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json e1ab32ed0b68a58096dd9d1c5e2e510e6bbb3a79

⚠️ 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 e1ab32ed0b68a58096dd9d1c5e2e510e6bbb3a79 → 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-27T22:19Z · head b0142c4a27 · #25522 cut 18

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

  1. Shape:
    • A draft against main, assignee os-project-manager, labelled skip-changeset because this is a move.
    • The body opens with Part of #25522. Clause-②: no and Metadata-protocol: none are line-initial.
    • check-governed-merges --pr 26932 reads NOT governed, 341 changed lines.
    • The claim (6453372157) names this PR's branch.
  2. Area against the order: "count / aggregate", priced at −71 on a table re-measured after cut 17; the runner-up was query lowering at −40.
    • It stays clear of every section the order excluded: delete cascade, dangling references, referential integrity, and registerMetadataCollections.
    • aggregate stays in engine.ts, because packages/spec/liveness/query.json names engine.ts#aggregate three times. Only its credential guard rejectCredentialAggregation moves, with count.
  3. Move check (the seat's own): every non-blank line removed from engine.ts against origin/main was compared with the lines added to engine-count-aggregate.ts, indentation stripped. Every one is present there except seven top-level helpers, which gain export in place under cut 14's ruling and are imported, ⛔ not copied: ENGINE_WHERE_SLOTS, ENGINE_COUNT_OPTION_KEYS, rejectUnknownEngineOptions, foldEngineOptionAliases, lowerWhereFilterArray, declaredDatetimeLowering and mergeReadContext. Bodies that stay read them, and foldEngineOptionAliases is also a liveness producer, so it stays put.
    • What engine.ts gains is two delegating bodies. Each is one host literal of exactly the CountAggregateHost members it reads: getters for _registry and relatedSchemaOf, and late-bound closures for the eight methods. No moved body assigns a field.
    • The StandardErrorCode import line moves verbatim, since its only reader moved.
  4. Load order (measured by the dev): the module is imported right after ./secret-fields.js. The bundle region order equals the base's plus one region. The external modules' first-occurrence order and the import and require counts are equal. The late-binding probe answers the same on base and head.
  5. Published .d.ts: raw byte-identical, under ruling 6348891160 item 1, with the positive control caught.
  6. ADR decision letters: 1,897 → 1,896, disclosed in the PR body. The one letter that leaves the anchored surface is [ADR-0139 D2] in count's executor-boundary comment, which moved verbatim into the unanchored module. This is the same kind of drop cut 15 had ([ADR-0139 D6]), accepted on the same grounds: no anchor paragraph describes count, and docs/adr/** is outside the claim.
  7. Overlap: git merge-tree exits 0 against main 7a2818c5 and against fix(objectql)!: a predicate delete honours deleteBehavior for every matched row: restrict refuses, master_detail cascades, set_null clears (#26871) #26930, which is open in the delete section of engine.ts.
  8. Gates:
    • At this stamp every check-run on the head reads success or skipped: 34 success, 16 skipped, none failed or in progress.
    • Locally 48 of 48 derived gates plus the hand gates exit 0, with 0 NOT-MEASURED.
  9. Size: engine.ts goes from 21,835 to 21,762 lines (net −73).

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 22:21
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 27, 2026 22:21
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit beb09fb Oct 27, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-axdp1m branch October 27, 2026 22:41
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