refactor(service-analytics): set the drill sidecars on the declared AnalyticsResult - #20762
Conversation
…yte for byte One dataset answer carries all four drill-through sidecars at once: a stage grouping beside a month-bucketed date dimension, with a per-stage subtotal and the grand total. The answer is read as the declared AnalyticsResult with no cast, and the serialised sidecars are pinned byte for byte, so the retirement of the local augmentation that follows can be shown to move nothing. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
…nalyticsResult AnalyticsResult declares dimensionFields, drillRawRows, drillRawTotals and drillRanges with the shapes the service emits. The module-private AnalyticsResultWithDrill augmentation that shadowed them is deleted, and the four sets in answerDataset write the declared members with no cast. Two of the deleted doc comments disagreed with the emission (drillRanges bounds and omission rule; dimensionFields values can be relationship paths); the contract's own descriptions follow the emission and now stand alone. Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
…entation Claude-Session: https://claude.ai/code/session_01XY5uCwTjZj7884yYtyur4H Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 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 fdae9e904e3b53c149c7d7356b2829da263121ee && git checkout fdae9e904e3b53c149c7d7356b2829da263121ee
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a51920f5fb1059ae6e8c7b1a96aa785f1da5d248 39d49081b094815b5048a4535eee5ed637fc3c74 && git checkout -B drift-repro a51920f5fb1059ae6e8c7b1a96aa785f1da5d248 && git merge --no-ff 39d49081b094815b5048a4535eee5ed637fc3c74
node scripts/docs-audit/affected-docs.mjs --json a51920f5fb1059ae6e8c7b1a96aa785f1da5d248
|
Contract reviewServed-tier: PR #20762 for card #20727. The head is the merge of the refactor commit ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #20727
Clause-②: no
AnalyticsResult(@objectstack/spec/contracts) has declareddimensionFields,drillRawRows,drillRawTotalsanddrillRangessince #20700 (PR #20720). This retires the service's module-privateAnalyticsResultWithDrillaugmentation and sets the four members on the declared type, with no cast. No answer changes, and nothing published changes.What changed
packages/services/service-analytics/src/analytics-service.ts: deletedtype AnalyticsResultWithDrill(at:100on the base) and its doc comments. The four sets inanswerDatasetnow writeresult.dimensionFields,result.drillRawRows,result.drillRawTotalsandresult.drillRangesonresult: AnalyticsResult. Each was(result as AnalyticsResultWithDrill).…(:1970,:1973,:1985,:2034on the base). No new local type, no other cast, no contract change.drillRangesbounds and omission rule, anddimensionFieldsvalues, which can be relationship paths. The contract's descriptions follow the emission and are now the only text.src/__tests__/drill-sidecars-emission.test.ts(1 test).previewDraftsearly return (triage ruled it out of this card), andpackages/spec.Premises measured (dispatch D1 to D4)
pnpm --filter @objectstack/service-analytics run typecheckexits 0 with the casts gone.tsc --listFilesholds 138 of 138src/__tests__files, the new pin included. Two reverse checks ran throughscripts/ablation-replace.mjs, each with the restore proven (blob equals HEAD,git diff HEADempty):dimensionFieldsset givesTS2322at(1923,7): the type{ [k: string]: number; }is not assignable to the declaredRecordof string to string. (The message's angle-bracket spelling is written out here in words.)drillRangesset givesTS2551: Property 'drillRangez' does not exist on type 'AnalyticsResult'. Did you mean 'drillRanges'?at(1987,14).AnalyticsResultdeclares. The emission and the contract agree.git grep AnalyticsResultWithDrillover the whole tracked tree at the base finds 5 hits, all inanalytics-service.ts: the type and the four casts. After the edit it finds 0 (exit 1). After a full build, none of the 68packages/**/distdirectories names it, against a positive control (drillRawTotalshits the spec and service-analytics dists).skip-changeset.@objectstack/service-analyticswas built at the base and after the edit. A determinism control (the base rebuilt unchanged) gave the same six hashes.index.js,index.cjs,index.d.ts,index.d.cts: byte-identical (cmpexit 0 for each).index.js.map,index.cjs.map: differ in position data only. Decoded,version,sourcesandnamesare identical, and neither map embeds source text. Every segment matches once the original lines ofanalytics-service.tsafter the deleted block move up by 47, except on the four set lines, whose columns moved with the removed cast. A comment-only edit makes the same kind of delta.dist, before and after. The positive controldrillRawTotalshitsindex.jsandindex.cjs.totalsfixtures inquery-dataset.test.tsgroup by no date dimension. So the pin's fixture joins two existing ones: the matrix "X by time" dataset (stagebeside a month-bucketedclose_date), and the per-grouping driver of the subtotal test, withtotals: { groupings: [['stage'], []] }.06130c86…3ec980both times, andcmpexits 0. The same bytes came back on the final head.AnalyticsResultwith no cast, and holds the four keys, their order and their serialised bytes.Pin and ablation
285a39f6e) and was green on the unedited source. The retirement follows in725819485.result.drillRawTotals = …becomesvoid …(anchor 1 to 0, blobd22f648eto9be86803).expected [ 'dimensionFields', …(2) ] to deeply equal [ 'dimensionFields', …(3) ].d22f648e) andgit diff HEADis empty.src/and nodistleg applies.Verification, at
39d49081bThis is the head after merging
origin/mainata6866da0c, which touched two otherservice-analyticsfiles.pnpm --filter @objectstack/service-analytics run typecheck: exit 0.pnpm --filter @objectstack/service-analytics test: 141 files and 3267 tests passed.dimensionFields,drillRawRows,drillRawTotals,drillRanges) are emitted by service-analytics and read by objectui, butAnalyticsResultdeclares none of them #20700,packages/specsrc/api/analytics.test.tsandsrc/contracts/analytics-service.test.ts: 2 files and 40 tests passed.dispatch-gates --commandsderived 55. All 55 ran with exit 0, and--ranreads "55 derived, 55 run, 0 NOT-MEASURED, 0 UNRUN", a derived zero with every line carrying its exit code.check-changeset-fixed,check:authz-resolver,check:error-code-casing,check:filter-alias-parity).check:dual-build-cjs-loadsandcheck:type-check-debtfirst answered exit 3 (unbuilt packages). Both were green after a full build.pnpm lintis CI's to run..tsfiles in scope (isPathIgnoredis false).eslint --no-inline-config --format jsonreads 2 files, 0 errors, 0 warnings.eslint.config.mjs:327-328, andcalculateConfigForFileshows noparserOptions.projectorprojectServicefor either file). It reads only baseline JSONs this diff does not touch, so no untouched file's verdict can move.Acceptance notes
previewDraftsquestion the card carried is answered by triage for this card: a preview answer carries no sidecars, and the early return and the contract sentence stay. A consumer that needs preview drill is a card of its own..mapfiles move by position only (above). By the package's policy that is the same class as a comment edit, so this PR carriesskip-changesetand no changeset. If the seat reads a position-only map delta as published, apatchchangeset on@objectstack/service-analyticsis the whole remedy.Generated by Claude Code