docs(spec): the element-number D3 entries name the route hop — arrays are lowered by parseFilterAST before the analytics wire (#15828) - #15941
Conversation
…are lowered by parseFilterAST before the analytics wire (#15828) The `element-number-filter-rule-array` semantic migration entry recorded its runtime prerequisite at the wrong hop: "authored array → adapter lowering → filter AST → accepted by `lowerAnalyticsWhere`". Per the `FilterArray` docblock (#5158 ruling C) a `FilterArray` is input-only sugar lowered at the single sink `parseFilterAST`; the wire's door is `POST /analytics/query`, which parses `where` with `AnalyticsQueryRequestSchema` (a `FilterCondition` only) before any service code runs. `lowerAnalyticsWhere` is the in-process door (#5334) for direct `analyticsService.query` callers, not the wire's. Prose only: the entry's reason clause, one sentence on `AnalyticsQuerySchema.where`'s `.describe()`, the regenerated migration registry and the two regenerated docs reference pages. No schema, accept-set, runtime or test-behaviour change. The adapter-side fix is objectui#7752. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TezFG8ZMrNH6n5VTNpPpdH
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 129 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin bb41334210b9ed8afd268276eb26c90f7bca2308 && git checkout bb41334210b9ed8afd268276eb26c90f7bca2308
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f1e91595f706b7eefb73457754f4eb05fa13e362 9b431caf47eb09b77fc5969253251086eeebab58 && git checkout -B drift-repro f1e91595f706b7eefb73457754f4eb05fa13e362 && git merge --no-ff 9b431caf47eb09b77fc5969253251086eeebab58
node scripts/docs-audit/affected-docs.mjs --json f1e91595f706b7eefb73457754f4eb05fa13e362 |
Fixes #15828
The narrowed, spec-side DOCUMENTATION half of #15828, per the director ruling comment (maintainer, 2026-09-05, verbatim 「15828 按前端缺陷处理」). The user-visible adapter fix is objectui#7752 and is not in this PR.
Prose only.
FilterArraySchema,FilterConditionSchemaandAnalyticsQuerySchema's type are untouched; no accept-set, runtime or test-behaviour change.AnalyticsQuerySchema.whereis stillFilterConditionSchemaand still refuses an array — which is the protocol working, not a defect.Why the old text was wrong
packages/spec/src/data/filter.zod.ts, theFilterArraydocblock (#5158 ruling C): aFilterArrayis input-only authoring sugar, "lowered to aFilterConditionat the single sinkparseFilterAST(@objectstack/spec/data) the moment it arrives, and only the loweredFilterConditiontravels any further". A POST body'swhereis transport, not a door.The
element-number-filter-rule-arrayD3 entry recorded its runtime prerequisite one hop too late — "authored array → adapter lowering → filter AST → accepted bylowerAnalyticsWhere".lowerAnalyticsWhere(service-analytics) is the in-process door (#5334) for callers reachinganalyticsService.querydirectly. The wire's door is the runtime routePOST /analytics/query, which parseswherewithAnalyticsQueryRequestSchemabefore any service code runs, so an un-lowered array is refused there. objectui#6302's gate measured the service function, not the route, and the entry inherited that hop.Changes
packages/spec/src/migrations/entries/semantic/18.element-number-filter-rule-array.tstranslateFilterArrayyields AST tuples that are still aFilterArray, so the adapter must lower withparseFilterASTBEFORE the wire; the route parseswhereas aFilterConditiononly;lowerAnalyticsWhereis the in-process door. Cites #15828 and objectui#7752. Every other sentence and the wholeacceptanceCriteriaare byte-identical.packages/spec/src/data/analytics.zod.tswhere.describe()gains one sentence pointing array authors at theparseFilterASTlowering. Type unchanged.packages/spec/src/migrations/registry.tspnpm --filter @objectstack/spec gen:migration-registry, never hand-edited. Diff is exactly the entry clause above.content/docs/references/api/analytics.mdx,content/docs/references/data/analytics.mdxpnpm --filter @objectstack/spec gen:docs, the one artifactcheck:generatedproved stale. Diff is exactly the new.describe()sentence in the two prop tables..changeset/analytics-where-names-the-route-hop.md@objectstack/specpatch — a published package's describe text changed.18.element-record-picker-filter-rule-array.tswas read for the same claim and does not make it: its measured path is the record picker'sfind()/convertQueryParamshop, and it names neither the analytics wire norlowerAnalyticsWhere(grep -i 'analytics|lowerAnalytics|wire'returns nothing in that file). Left unchanged, as the dispatch directed.Verification
pnpm --filter @objectstack/spec check:generated— 15 artifacts, 1 stale (content/docs/references/**), regenerated withgen:docs; re-run green. The migration-registry gate was already green aftergen:migration-registry. Noskills/**file moved, so no landing path changed.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat9b431caf: 93 derived commands, all 93 run, each exit code captured by redirect before any pipe. Reconciled with--ran:93 derived famil(ies) accounted for — 93 run, 0 NOT-MEASURED. Six first answered PREREQUISITE NOT MET rather than a finding (fiveexit 3for unbuilt@objectstack/lint/@objectstack/formula/@objectstack/client-react, one shallow-clone pinned fixture); all cleared and re-run green except the one below.pnpm --filter @objectstack/spec typecheckgreen;pnpm --filter @objectstack/spec test— 476 test files, 12787 tests, all passed (272s).pnpm check:dual-build-cjs-loadsstill reports PREREQUISITE NOT MET — it reads emitted bytes for 86 packages and needs a whole-monorepopnpm build, which is a repo-level scan CI owns. Nothing was measured there; it is not a green. This diff changes nopackage.json, noexportsand no build config, so it cannot move that gate.turbo ls --affectedlists 75 packages, because nearly everything depends on@objectstack/spec; the changed package's own suite is run above and the remaining 74 are CI's full run.origin/mainadvanced toea03c7c16while this branch was being deepened, anddispatch-gatesflags the tree as one commit stale onscripts/engine-double-contract.pinned.json. That roster belongs tocheck:engine-double-contract, which is outside this card's derived 93, and this branch touches none of its inputs.Generated by Claude Code