Commit c745e2b
Fixes #20381
Clause-②: no
Item 3 of #20381, under director ruling `5866558247` (letter A,
maintainer 「同意」): registry source 3 is retired. Items 1–2 landed in PR
#20407 (`50e273fd`), so this round completes the card.
## What changes
- The ad-hoc `query` and `sql` doors (`POST /api/v1/analytics/query`,
`POST /api/v1/analytics/sql`) no longer publish the cube `ensureCube`
infers for an ADMITTED request. That cube stays in the call's request
scope, the one PR #20407 gave these doors, and it is dropped with the
call, like a measure appended to a configured cube. The next request for
the same name infers the cube again, through the same existence and
source-field gates, and gets the same answer.
- The shared `CubeRegistry`, and therefore `getMeta()` and `GET
/api/v1/analytics/meta`, is now written by configuration only: manifest
cubes (`AnalyticsServiceConfig.cubes`) and `registerDataset` datasets.
`/analytics/meta` lists the authored vocabulary, whatever traffic the
server has seen since boot.
- `publishInferredCube` had no caller left and is removed, and
`ensureCube` returns `void` again. The `CubeRegistry` class docblock now
lists the two configuration sources and states that no request writes
the registry. The `CubeScope`, `queryIn`, `requestScope`, `ensureCube`
and #5918 comments in `analytics-service.ts` no longer describe the
publication.
- No refusal, code or status changes. There is no `packages/spec`
change, no visibility marker and no caller-aware `getMeta`; options B, C
and D are not taken.
Landing point, as dispatched:
`packages/services/service-analytics/src/analytics-service.ts` (the
producer of the write) and `cube-registry.ts` (docblock only).
## Tests: re-observed, not deleted
On the fix commit, 36 cases in six service-analytics test files went
red; each of those files read an inferred cube back through `getMeta` or
the shared registry. PR #20348, which landed while this round ran, added
a seventh such case. Each case is re-observed through a window that
still exists after A: the cube the request's own strategies are handed.
A probe strategy placed ahead of the built-in ones records
`ctx.getCube(query.cube)` and always declines, so the chain runs as it
would without the probe. Where an assertion's subject was the retired
registration itself, the assertion now pins its absence.
| File | Was | Now |
|---|---|---|
| `infer-cube-where-spelling-parity.test.ts` | dimension keys via
`getMeta('deal')` | the same keys, read from the request's cube |
| `infer-cube-relation-traversal.test.ts` | `run()` members via
`getMeta` | the request's cube |
| `dotted-measure-refusal.test.ts` | `run()` measures via `getMeta`;
block 2 case 1 asserted that the first query warmed the registry | the
request's cube; case 1 now pins that the first query warms nothing and
that the second, cold again, is still refused (the augmentation site
stays covered by the block's authored-cube case) |
| `analytics-service.test.ts` 'auto-infer' | `cubeRegistry.has('case')`
is true | the request was handed a cube named `case` and backed by
`case`; `has('case')` is false |
| `cube-inference-gate.test.ts` KPI case |
`cubeRegistry.get('crm_account')` is truthy | it is undefined; a second
request is served the same way and asks the existence gate again |
| `adhoc-query-request-scope.test.ts` (PR #20407) | the admitted
inference "publishes after admission (source 3)"; the CONTROL case runs
"through the published cube" | the admitted inference is served from its
own cube, and both the registry and the observer's view are exactly
unchanged (the order pin is kept); the CONTROL case is now "a second
same-name request infers again and gets the same answer" |
| `cube-public-visibility.test.ts` (PR #20348) | the ad-hoc KPI path's
inferred cube is registered `public: true` and listed | it is answered
on every request, and never registered or listed |
The route pin is
`packages/qa/dogfood/test/analytics-adhoc-query-isolation.dogfood.test.ts`:
`bootStack` with two sign-ups plus the administrator, on sqlite-wasm and
memory, through both doors.
- Both CONTROL legs are tightened from `arrayContaining` to exact
equality on member B's cube list. B's `meta` is also kept as the raw
response bytes.
- After the administrator's admitted ad-hoc query over the walled
object, B's `meta` is byte-identical and B's query of the configured
cube is unchanged. B's own query of that object is still refused with
the ADR-0112 envelope.
- An admitted scalar metric is re-inferred on a second request and
answers identically. Between the two requests, not even the asker's own
`meta` lists the name.
- A configured cube still serves: the baseline leg, and every
observation.
## Evidence (head `8214a5b6` unless stated)
- `pnpm --filter @objectstack/service-analytics typecheck` is clean.
`vitest run`: 132 files, 3093 passed. `tsc --listFiles` includes every
changed test file.
- `pnpm --filter @objectstack/dogfood typecheck` is clean, and
`--listFiles` includes the route pin. The six analytics dogfood files:
54 passed, on a `dist` rebuilt after merging `main`.
- **Ablation M1** puts the publication back at both ad-hoc sites, after
`callCtx`, as in `50e273fd`. It was applied with
`scripts/ablation-replace.mjs` (anchor 2 → 0) and predicted before
running.
- Unit, 7 files: 10 red / 132 green. The red cases are the
admitted-inference and CONTROL cases (4 + 2), auto-infer, the KPI gate
case, the #20348 KPI case and the dotted warm case.
- Route, after rebuilding `service-analytics`, with
`ablation-dist-preflight` finding the marker in 2 dist files: 8 red / 16
green, both CONTROL legs on all four boots. For example: `expected [
'open_summary', …(3) ] to deeply equal [ 'open_summary', …(2) ]`, with
`+ "admission_walled"`.
- Restore: blob equals `HEAD`, `git diff HEAD` is empty, rebuilt, and
`--absent` preflight is green with a clean tree.
- On the pre-merge head `fccfc3e5` the same ablation gave 9/117 and
8/16.
- **Ablation M2**, on `fccfc3e5`, proves the probe window can fail. It
makes an array `where` seed no dimension, the pre-#5353 shape. Across
parity and traversal: 16 red / 24 green. In parity, the 11 conjunction
table cases, ALONGSIDE and the two dotted array-versus-object cases went
red; both `$or` cases stayed green, as the file predicts. In traversal,
the two array-spelling mint cases went red. The restore was proven the
same way.
- **Gates**: `dispatch-gates --commands` derives 66 commands over the 12
changed paths. `--ran` reconciles 66 derived, 66 run, 0 NOT MEASURED and
0 UNRUN. 65 exit 0; `check:empty-changeset` exits 1 by design (see
Changesets).
- **Lint, narrowed**: eslint `--no-inline-config --format json` over the
10 changed `.ts` files reports 10 files, 0 errors and 0 warnings. The
population is those 10 files, none ignored. Invariance:
`eslint.config.mjs` never enables type-aware linting, so an untouched
file's verdict cannot move. The full `pnpm lint` is left to CI.
## Changesets
- New: `.changeset/20381-retire-inferred-cube-source.md`,
`@objectstack/service-analytics` `patch`, `Clause-②: no`.
- **A deliberate correction of a pending release note, for
confirmation:** `.changeset/20381-adhoc-cube-request-scope.md` was added
by PR #20407 and is not yet released. It said that an admitted request's
inferred cube "still registers the cube it inferred, as before" and that
it "is still listed". This PR makes both sentences false, so they are
removed and replaced by a pointer to the new entry.
`check:empty-changeset` refuses any PR that modifies a changeset it did
not add. For this DELIBERATE CORRECTION class it stays red by design
(ruling D on #17712), and it needs a person's confirmation here.
Restoring the file from `50e273fd` would clear the gate, but the release
would then ship both statements in one CHANGELOG.
## Overlap with PR #20348
PR #20348 landed first (`f2c7eef5`), and `main` is merged here
(`dfd185d7`) with no textual conflict.
- Its `public: true` on the inferred cube is moot under ruling A,
because no visibility verdict ever reads that cube. The literal is
**kept**: the pending note
`.changeset/20282-analytics-cube-public-enforced.md` says the inferred
cube "now writes `true`", and dropping the key would falsify a second
foreign changeset. Only its comment, which said the cube is registered,
is corrected.
- Its `generateSql` gate still asks `this.sharedScope` rather than the
call's scope. The answer is identical, because a fresh request scope
with no dataset reads through to the shared registry, so this is noted,
not changed.
- Its other changes are untouched.
## Acceptance notes
- Log frequency: for a GROUPED ad-hoc query over an object with no
configured cube, `ensureCube`'s `warn` ("No cube registered …;
auto-inferred a minimal cube …") used to fire once per name per process,
because the second request found the published cube. It now fires on
every such request; scalar metrics stay at `debug`. This was not
measured against real dashboard traffic, and it is noted, not changed:
the ruling adds no state.
- `content/docs/api/data-api.mdx`, in its `GET /analytics/meta` section,
says that a cube a query references "is lazily auto-inferred from that
query's shape". It does not claim the cube gets listed, but it could now
say that it does not. That file is outside this card's file surface.
- Per the ruling, the two unmeasured cases (a cross-org boot, and an
FLS-hidden field used as a dimension) cannot leak through `meta` for
inferred cubes once this lands. They stay as notes for the ADR-0106 D5
audit.
- The refusal tests that assert `cubeRegistry.get(...)` is undefined
after a rejected query (the three source-field gate files,
`cube-inference-gate`, `dotted-measure-refusal`) now hold by
construction for every request, not only for refused ones. They are left
as they are.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017B6YKCGu8CTY2KBWgwaHAs)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 95f729a commit c745e2b
12 files changed
Lines changed: 389 additions & 183 deletions
File tree
- .changeset
- packages
- qa/dogfood/test
- services/service-analytics/src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
11 | | - | |
| 10 | + | |
| 11 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 54 additions & 23 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
4 | | - | |
5 | | - | |
6 | | - | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| |||
14 | 15 | | |
15 | 16 | | |
16 | 17 | | |
17 | | - | |
18 | | - | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
19 | 23 | | |
20 | 24 | | |
21 | 25 | | |
22 | 26 | | |
23 | 27 | | |
24 | 28 | | |
25 | | - | |
26 | | - | |
27 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
28 | 33 | | |
29 | 34 | | |
30 | 35 | | |
| |||
41 | 46 | | |
42 | 47 | | |
43 | 48 | | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
50 | 55 | | |
51 | 56 | | |
52 | 57 | | |
| |||
123 | 128 | | |
124 | 129 | | |
125 | 130 | | |
126 | | - | |
| 131 | + | |
127 | 132 | | |
128 | 133 | | |
129 | 134 | | |
| |||
133 | 138 | | |
134 | 139 | | |
135 | 140 | | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
136 | 147 | | |
137 | 148 | | |
138 | 149 | | |
139 | | - | |
| 150 | + | |
140 | 151 | | |
141 | 152 | | |
142 | 153 | | |
| |||
147 | 158 | | |
148 | 159 | | |
149 | 160 | | |
150 | | - | |
| 161 | + | |
151 | 162 | | |
152 | 163 | | |
153 | 164 | | |
154 | 165 | | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
155 | 171 | | |
156 | 172 | | |
157 | 173 | | |
| |||
257 | 273 | | |
258 | 274 | | |
259 | 275 | | |
260 | | - | |
| 276 | + | |
261 | 277 | | |
262 | 278 | | |
263 | 279 | | |
| 280 | + | |
264 | 281 | | |
265 | 282 | | |
266 | 283 | | |
267 | 284 | | |
268 | 285 | | |
269 | 286 | | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
270 | 294 | | |
| 295 | + | |
271 | 296 | | |
272 | 297 | | |
273 | | - | |
274 | | - | |
| 298 | + | |
| 299 | + | |
275 | 300 | | |
276 | 301 | | |
277 | | - | |
| 302 | + | |
278 | 303 | | |
279 | 304 | | |
280 | 305 | | |
281 | 306 | | |
282 | 307 | | |
283 | 308 | | |
284 | | - | |
| 309 | + | |
285 | 310 | | |
286 | 311 | | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
287 | 318 | | |
288 | | - | |
| 319 | + | |
289 | 320 | | |
290 | 321 | | |
291 | 322 | | |
0 commit comments