feat(analytics): BI-P014/016..022/025..026/028..031 — query contract, analyst SQL, execution, cache, cost, dbt, quality, metrics #35

Merged
admin merged 14 commits from feat/bi-p014-p031-query-metric-quality into main 2026-09-07 01:25:38 +00:00
Owner

Extends the analytics core merged in #34 (BI-P002..P012). Nothing there is rebuilt — SiaxAnalyticsEngine, the 19 entities, the forced tenant predicate and the BI-P012 budgets are all composed, not replaced.

Fourteen prompts: BI-P014, P016–P022, P025–P026, P028–P031.

Full write-up: docs/analytics/BI_QUERY_METRIC_QUALITY.md, organised around what is proven and what is not.


Five findings that changed the implementation

Each came from a test written so it could fail; each is pinned so it cannot quietly stop being true.

1. ClickHouse readonly = 1 on a user profile forbids EVERY request setting. Measured on 24.8.14.39: Code: 164. Cannot modify 'max_rows_to_read' setting in readonly mode. So BI-P012's ceilings cannot travel with an analyst statement — they are baked into the user's SETTINGS PROFILE at provisioning. Proven both ways: over-budget scan fails with code 158 from the profile with no setting sent; raising it from the request fails with 164.

2. CREATE ROW POLICY IF NOT EXISTS is a silent no-op on re-provisioning. Dropping a user empties the policy's apply-to list; the next run sees the policy exists and does nothing, so it survives applying to nobody while system.row_policies still shows the right filter. The restricted user read 100 of 100 rows — both tenants — from the base table. Fixed with OR REPLACE plus rowPolicyVerificationSql(), which turns "the policy is applied" into a query. CREATE SETTINGS PROFILE IF NOT EXISTS has the identical shape and stale-budgeted the whole integration suite; same fix.

3. DuckDB's allowed_directories ALONE is a no-op. Measured on 1.5.5: with the allow-list set and nothing else, read_csv('/etc/passwd') returned the file and an https:// URL was fetched over the network. Only enable_external_access = false alongside it binds the list. The obvious spelling appears in duckdb_settings(), looks like a sandbox, and permits everything. Pinned by a test that never skips.

4. pg_constraint.conname is not schema-scoped. sql/009 applied into a second schema silently skipped every guarded CHECK — and the tests that should have proven them passed, because the INSERTs they expected to fail succeeded. Every guard now scopes via conrelid = '<table>'::regclass, proven by applying to public first and then running into a fresh schema.

5. describe.skipIf(flagSetInBeforeAll) skips ALWAYS. Vitest evaluates skipIf at collection time. Ten tests reported green while skipping against a live, reachable ClickHouse.


BI-P017 — the dangerous one

Four layers, and the guard is deliberately called the weakest of them. 61 bypass attempts in one shared corpus, run twice: against the guard, and against a live ClickHouse with layer 2 removed. The live assertion is not "the engine errored" — it is "no canary came back", with BETA_CANARY seeded into the other tenant's rows and SECRET_CANARY into an ungranted table. The counterfactual runs first: as admin the same table returns 100 rows and the canary IS readable.

0 of 61 leaked. Evidence: evidence/bi-p017/analyst-sql-live-bypass.json, pinned by a test that never skips, so adding an attack without re-running breaks the build.

Honest about what the guard does NOT catch: SELECT tenant_id FROM t_alpha_sql.sales is structurally legitimate and IS admitted — it fails at the engine because the view does not project the column. Recorded in its own GUARD_ADMITS_ENGINE_REFUSES list and asserted live, rather than deleted from the corpus.

BI-P019 / BI-P021 — things that actually stop

Cancellation verifies, it does not request: abort, kill, then poll until the query is gone or the deadline passes — and if it passes, verified_stopped: false, not a cheerful true. Live: the cancelled query had read 6,559,476,156 rows and was gone 57 ms later, with system.query_log recording code 394 QUERY_WAS_CANCELLED. Counterfactual: an identical bomb left alone is still running after 2 s.

The budget polls live engine counters and cancels mid-scan. Live: a 1,000,000-row window stopped the query at 130,883,409 rows, 92 ms in, verified. The 130× overshoot is documented, in the module and the evidence — this is a bound of the form "within one poll interval of engine throughput", not a hard cap, and quoting it as one would be wrong. Admission-only mode reports itself as "NOT ENFORCED — this budget is advisory", and an unshared window store reports "with N replicas a tenant can spend up to N times this budget".

The rest

  • BI-P016 — eight fields verbatim; unknown keys rejected, not ignored; tenant/project_id/catalog/table rejected by name (INF0's Wave-2 lesson). The DoD is an equality between two compile targets, with the ClickHouse side reconstructed by parsing the emitted SQL and its bound parameters inside the test.
  • BI-P018 — hash, never the statement, enforced by assertNoRawSql using ClickHouse's own query-echoing error text as the test case. Writes at every transition.
  • BI-P020 — all six key components required; a cross-tenant hit is refused and evicted, not returned as a miss; staleness tested by mutating each component one at a time with a coverage assertion.
  • BI-P022 — contract only; execution stays in n0d#38. Refuses success with zero nodes (a selector matching nothing exits 0), a branch name as revision, a runtime that is not the pin. DBT_RUNTIME_PIN ships with both review flags false and production runs refused until they are true.
  • BI-P025/P026 — an empty dataset FAILS rather than reporting zero violations (proven against a real engine returning 0-over-0); zero verdicts quarantines; the read side is refused, not annotated; a result with no freshness envelope is unreleasable.
  • BI-P028/P030/P031 — certified metrics immutable in TS and by Postgres trigger; replaceability tested with two providers whose naming rules genuinely differ.
  • BI-P029 — providerReadiness() returns verified: false. MetricFlow was never executed (no Python runtime; the pin has had no licence review), and the Superset and AI-Analyst DoD surfaces are open. A test asserts the readiness report says so.

Verification

957 tests pass with ClickHouse 24.8, Postgres 16 and DuckDB 1.5.5 all live. typecheck, eslint, prettier clean. Every live suite fails rather than skipping when its env var is set and the backend is down.

.gitea/workflows/** and .gitignore are untouched. One latent flaw in that workflow is reported in the docs without changing it: the integration guard's Tests [0-9]+ passed pattern never matches because vitest writes ANSI codes between the words, so the guard is stricter than it reads. It is what caught the DuckDB suite's placement, so it earned its keep either way.


🤖 Generated with Claude Code

Extends the analytics core merged in **#34** (BI-P002..P012). Nothing there is rebuilt — `SiaxAnalyticsEngine`, the 19 entities, the forced tenant predicate and the BI-P012 budgets are all composed, not replaced. **Fourteen prompts: BI-P014, P016–P022, P025–P026, P028–P031.** Full write-up: `docs/analytics/BI_QUERY_METRIC_QUALITY.md`, organised around what is proven and what is not. --- ## Five findings that changed the implementation Each came from a test written so it could fail; each is pinned so it cannot quietly stop being true. **1. ClickHouse `readonly = 1` on a user profile forbids EVERY request setting.** Measured on 24.8.14.39: `Code: 164. Cannot modify 'max_rows_to_read' setting in readonly mode`. So BI-P012's ceilings cannot travel with an analyst statement — they are baked into the user's `SETTINGS PROFILE` at provisioning. Proven both ways: over-budget scan fails with code 158 from the profile with no setting sent; raising it from the request fails with 164. **2. `CREATE ROW POLICY IF NOT EXISTS` is a silent no-op on re-provisioning.** Dropping a user empties the policy's apply-to list; the next run sees the policy exists and does nothing, so it survives applying to **nobody** while `system.row_policies` still shows the right filter. **The restricted user read 100 of 100 rows — both tenants — from the base table.** Fixed with `OR REPLACE` plus `rowPolicyVerificationSql()`, which turns "the policy is applied" into a query. `CREATE SETTINGS PROFILE IF NOT EXISTS` has the identical shape and stale-budgeted the whole integration suite; same fix. **3. DuckDB's `allowed_directories` ALONE is a no-op.** Measured on 1.5.5: with the allow-list set and nothing else, `read_csv('/etc/passwd')` **returned the file** and an `https://` URL was fetched over the network. Only `enable_external_access = false` alongside it binds the list. The obvious spelling appears in `duckdb_settings()`, looks like a sandbox, and permits everything. Pinned by a test that never skips. **4. `pg_constraint.conname` is not schema-scoped.** `sql/009` applied into a second schema silently skipped every guarded `CHECK` — and the tests that should have proven them passed, because the INSERTs they expected to fail succeeded. Every guard now scopes via `conrelid = '<table>'::regclass`, proven by applying to `public` first and then running into a fresh schema. **5. `describe.skipIf(flagSetInBeforeAll)` skips ALWAYS.** Vitest evaluates `skipIf` at collection time. Ten tests reported green while skipping against a live, reachable ClickHouse. --- ## BI-P017 — the dangerous one Four layers, and the guard is deliberately called the weakest of them. **61 bypass attempts in one shared corpus, run twice**: against the guard, and against a live ClickHouse with **layer 2 removed**. The live assertion is not "the engine errored" — it is "no canary came back", with `BETA_CANARY` seeded into the other tenant's rows and `SECRET_CANARY` into an ungranted table. The counterfactual runs first: as admin the same table returns 100 rows and the canary IS readable. **0 of 61 leaked.** Evidence: `evidence/bi-p017/analyst-sql-live-bypass.json`, pinned by a test that never skips, so adding an attack without re-running breaks the build. Honest about what the guard does NOT catch: `SELECT tenant_id FROM t_alpha_sql.sales` is structurally legitimate and IS admitted — it fails at the engine because the view does not project the column. Recorded in its own `GUARD_ADMITS_ENGINE_REFUSES` list and asserted live, rather than deleted from the corpus. ## BI-P019 / BI-P021 — things that actually stop Cancellation **verifies**, it does not request: abort, kill, then poll until the query is gone or the deadline passes — and if it passes, `verified_stopped: false`, not a cheerful `true`. Live: the cancelled query had read **6,559,476,156 rows** and was gone **57 ms** later, with `system.query_log` recording code **394 QUERY_WAS_CANCELLED**. Counterfactual: an identical bomb left alone is still running after 2 s. The budget polls live engine counters and cancels mid-scan. Live: a 1,000,000-row window stopped the query at **130,883,409 rows**, 92 ms in, verified. **The 130× overshoot is documented, in the module and the evidence** — this is a bound of the form "within one poll interval of engine throughput", not a hard cap, and quoting it as one would be wrong. Admission-only mode reports itself as `"NOT ENFORCED — this budget is advisory"`, and an unshared window store reports `"with N replicas a tenant can spend up to N times this budget"`. ## The rest - **BI-P016** — eight fields verbatim; unknown keys **rejected**, not ignored; `tenant`/`project_id`/`catalog`/`table` rejected by name (INF0's Wave-2 lesson). The DoD is an equality between two compile targets, with the ClickHouse side reconstructed by **parsing the emitted SQL and its bound parameters inside the test**. - **BI-P018** — hash, never the statement, enforced by `assertNoRawSql` using ClickHouse's own query-echoing error text as the test case. Writes at every transition. - **BI-P020** — all six key components required; a cross-tenant hit is **refused and evicted**, not returned as a miss; staleness tested by mutating each component one at a time with a coverage assertion. - **BI-P022** — contract only; execution stays in `n0d#38`. Refuses `success` with zero nodes (a selector matching nothing exits 0), a branch name as revision, a runtime that is not the pin. `DBT_RUNTIME_PIN` ships with **both review flags false** and production runs refused until they are true. - **BI-P025/P026** — an **empty dataset FAILS** rather than reporting zero violations (proven against a real engine returning 0-over-0); zero verdicts **quarantines**; the read side is **refused**, not annotated; a result with no freshness envelope is unreleasable. - **BI-P028/P030/P031** — certified metrics immutable in TS **and** by Postgres trigger; replaceability tested with two providers whose naming rules genuinely differ. - **BI-P029** — `providerReadiness()` returns **`verified: false`**. MetricFlow was never executed (no Python runtime; the pin has had no licence review), and the Superset and AI-Analyst DoD surfaces are open. A test asserts the readiness report says so. --- ## Verification **957 tests** pass with ClickHouse 24.8, Postgres 16 and DuckDB 1.5.5 all live. `typecheck`, `eslint`, `prettier` clean. Every live suite **fails** rather than skipping when its env var is set and the backend is down. `.gitea/workflows/**` and `.gitignore` are untouched. One latent flaw in that workflow is reported in the docs without changing it: the integration guard's `Tests [0-9]+ passed` pattern never matches because vitest writes ANSI codes between the words, so the guard is stricter than it reads. It is what caught the DuckDB suite's placement, so it earned its keep either way. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
admin added 1 commit 2026-09-07 00:00:22 +00:00
feat(analytics): BI-P016 — canonical query request contract, two compile targets
CI (SIAX Cloud) / security (push) Successful in 25s
Security Scan (SIAX Cloud) / security (push) Successful in 24s
CI (SIAX Cloud) / integration (push) Successful in 39s
CI (SIAX Cloud) / security (pull_request) Successful in 25s
CI (SIAX Cloud) / quality (push) Successful in 57s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 20s
CI (SIAX Cloud) / integration (pull_request) Successful in 1m38s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m59s
b1884a89a5
The eight-field AnalyticsQueryRequest BI-P016 names, verbatim, with the rule
that makes it safe enforced structurally rather than by convention: there is
no field that can carry SQL, and parseAnalyticsQueryRequest REJECTS unknown
keys instead of ignoring them. Ignoring an unsupported field is how it becomes
a supported one after the next refactor.

`tenant`, `tenant_id`, `project_id`, `database`, `catalog` and `table` are
rejected by name. INF0's Wave-2 cross-tenant hole was a tenant read out of a
request body; the fix that sticks is a request type in which the field does
not exist, and a parser that refuses it when a caller sends it anyway.

BI-P016's DoD ("same request compiles to ClickHouse/semantic engine without
consumer rewrite") is tested as an equality, not narrated: both targets are
projected onto a comparable shape and compared. The ClickHouse side of that
projection is obtained by PARSING the emitted SQL and its bound parameters in
the test — not by reading a field the compiler helpfully returned — so a
compiler that silently drops a filter fails the comparison instead of passing
it. The parser throws on any condition it does not recognise, which is what
would catch unbound caller text appearing in the statement.

BI-P011's forced tenant predicate is re-proven against this new path: five
request shapes are enumerated and each must have FORCED_TENANT_PREDICATE as
condition[0]; a blank tenant refuses to compile rather than emitting no
predicate; a hostile filter value appears only in params, never in sql.

45 tests, all failing-capable: every rejection assertion checks the SiaxError
code, and the DoD assertions compare reconstructed meaning rather than strings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CODEOWNERS rules requested review from sax3l 2026-09-07 00:00:22 +00:00
admin added 1 commit 2026-09-07 00:17:09 +00:00
feat(analytics): BI-P017 — analyst SQL contract, proven against live ClickHouse with the guard removed
CI (SIAX Cloud) / quality (push) Successful in 50s
CI (SIAX Cloud) / integration (push) Successful in 35s
CI (SIAX Cloud) / security (push) Successful in 21s
Security Scan (SIAX Cloud) / security (push) Successful in 26s
CI (SIAX Cloud) / quality (pull_request) Successful in 46s
CI (SIAX Cloud) / integration (pull_request) Successful in 35s
CI (SIAX Cloud) / security (pull_request) Successful in 19s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 16s
d9c8f7190b
AnalyticsSQLSession is a SEPARATE class, not a method on SiaxAnalyticsEngine.
executeMetric has no raw-SQL surface at all; on the same object the closed DSL
would be one refactor away from growing one. ClickHouseAnalyticsEngine
.executeQuery stays a documented refusal.

FOUR LAYERS, and the guard is deliberately called the weakest:
  1 role   — the CL0UD analyst role is checked when the SESSION IS OPENED, so
             a caller without it never obtains an object that can run SQL.
  2 guard  — statement admission. Comments and literals removed with
             ClickHouse's own lexical rules, ASCII-only outside literals, one
             statement, SELECT/WITH only, and every table reference POSITIVELY
             resolved against the tenant's catalog. Deny-lists are a second
             opinion, never the mechanism.
  3 grants — a per-tenant ClickHouse user with SELECT on that tenant's catalog
             alone, plus a restrictive row policy on the base tables.
  4 views  — the tenant catalog holds only VIEWS, each baking
             `WHERE tenant_id = '<tenant>'`, and tenant_id is not projected.

61 bypass attempts, in one shared corpus, run TWICE: against the guard, and
against a live ClickHouse 24.8.14.39 with LAYER 2 REMOVED. The live assertion
is not "the engine errored" — it is "no canary came back", with BETA_CANARY
seeded into the other tenant's rows and SECRET_CANARY into an ungranted table.
The counterfactual runs first: as admin the same base table returns 100 rows
and BETA_CANARY IS readable, so the isolation numbers mean something.
Result: 0 of 61 leaked. Evidence: evidence/bi-p017/analyst-sql-live-bypass.json.

FOUR DEFECTS FOUND BY WRITING TESTS THAT CAN FAIL — three of them mine:

  * `CREATE ROW POLICY IF NOT EXISTS` is a SILENT NO-OP on re-provisioning.
    Dropping the user empties the policy's apply-to list; the next run skips
    re-creation, so the policy survives with `apply_to_list = []`, applying to
    nobody, while `system.row_policies` still shows the correct select_filter.
    The restricted user read 100 of 100 rows — BOTH TENANTS — from the base
    table. Fixed with OR REPLACE + `rowPolicyVerificationSql`, which turns
    "the policy is applied" from an assumption into a query, and a test that
    asserts applied_to_user = 1 per object.

  * `CREATE SETTINGS PROFILE IF NOT EXISTS` has the identical shape: a changed
    budget never lands. A stale 60-row ceiling survived re-provisioning and
    starved every legitimate statement, with the DDL reporting success
    throughout. Also OR REPLACE now.

  * `describe.skipIf(!reachable)` where `reachable` is set in `beforeAll`
    skips ALWAYS — vitest evaluates skipIf at collection time. Ten tests
    reported green while skipping against a live, reachable server. Replaced
    with the repo's existing env-var gate, which FAILS when CLICKHOUSE_URL is
    set and the server is down rather than degrading to a skip.

  * MEASURED, and it changes the design: a ClickHouse user whose profile
    carries `readonly = 1` cannot have ANY setting changed from the request
    (Code: 164). The BI-P012 ceilings therefore cannot travel with an analyst
    statement; they are baked into the user's SETTINGS PROFILE at provisioning.
    Proven both ways: over-budget scan fails with code 158 from the profile
    with no setting sent, and an attempt to raise it from the request fails
    with code 164.

Honest about what the guard does NOT catch: `SELECT tenant_id FROM
t_alpha_sql.sales` is structurally legitimate and IS admitted. It fails at the
engine because the view does not project the column. Recorded in its own
GUARD_ADMITS_ENGINE_REFUSES list and asserted live, rather than deleted from
the corpus.

evidence/bi-p017 is pinned by tests/analytics/analyst-sql-evidence.test.ts,
which never skips: adding an attack without re-running the live suite breaks
the build, and the pin also checks the counterfactual numbers so the evidence
cannot be a recording of an empty database.

246 tests pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:19:12 +00:00
fix(analytics): replace the control-character class with a code-point scan (no-control-regex)
CI (SIAX Cloud) / quality (push) Successful in 45s
CI (SIAX Cloud) / integration (push) Successful in 34s
CI (SIAX Cloud) / security (push) Successful in 19s
Security Scan (SIAX Cloud) / security (push) Successful in 17s
CI (SIAX Cloud) / quality (pull_request) Successful in 45s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 27s
CI (SIAX Cloud) / security (pull_request) Successful in 37s
CI (SIAX Cloud) / integration (pull_request) Successful in 45s
adc7a9be31
The ASCII-only check used a regex character class of control characters. It
tripped eslint's no-control-regex, which the repo's quality gate enforces —
and the rule has a point: such a class is unreadable and is exactly the kind
of expression a reviewer nods at without checking. Same behaviour, written as
an explicit scan naming the permitted set.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:26:07 +00:00
feat(analytics): BI-P018/BI-P019/BI-P021 — execution model, verified cancellation, a budget that stops queries
CI (SIAX Cloud) / integration (push) Successful in 43s
CI (SIAX Cloud) / security (push) Successful in 21s
CI (SIAX Cloud) / quality (push) Successful in 1m5s
Security Scan (SIAX Cloud) / security (push) Successful in 23s
CI (SIAX Cloud) / security (pull_request) Successful in 24s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m0s
CI (SIAX Cloud) / integration (pull_request) Successful in 42s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 38s
8f6357044f
BI-P018 — every field the prompt names, plus the one it names by its absence.
The record carries `compiled_query_hash` and never the statement, and that is
ENFORCED: `assertNoRawSql` runs before any record is emitted and refuses one
whose `error` field looks like SQL. The realistic leak is ClickHouse's own
error text, which echoes the query — the test uses exactly that string. The
recorder writes at EVERY transition, because a recorder that only writes at
the end loses precisely the executions worth investigating. Illegal state
transitions throw rather than overwrite a terminal row.

The one place raw SQL is retained stays BI-P017's analyst statement history,
where a privileged human ran unreviewed SQL and "who ran what" must survive.
Different record, different retention posture, stated in both files.

BI-P019 — "stops" is VERIFIED, not requested. Aborting the client stops US
waiting, not the server; KILL QUERY without SYNC tells you nothing; even SYNC
can return while the query unwinds. So cancelAndVerify aborts, kills, then
POLLS the process list until the query is GONE or the deadline passes — and
if the deadline passes it returns `verified_stopped: false` rather than a
cheerful `true`. The return type is an outcome object, not a boolean, so a
caller cannot mistake "we asked" for "it stopped". An unreachable engine is
read as "still running" (fail closed), so the kill is still attempted at the
moment an operator most needs it to be.

Proven live against ClickHouse 24.8.14.39, with the counterfactual first:
  * an identical bomb left alone IS still running after 2s — so "gone after
    we cancelled" is not just "the query was short";
  * the cancelled query had read 6,559,476,156 rows, and was verified gone
    57ms after the request;
  * system.query_log records ExceptionWhileProcessing with code 394,
    QUERY_WAS_CANCELLED — ClickHouse's verdict, not ours.

BI-P021 — the honest version. A budget checked once before execution bounds
nothing, and the module says so in its first paragraph. ClickHouse's
max_rows_to_read bounds ONE query; it cannot express "this tenant may scan X
per hour", which is what a budget is for. So enforcement polls the engine's
live read_rows/read_bytes counters and CANCELS the running query, through
BI-P019 with verification, when window spend would be exceeded.

Measured live: a 1,000,000-row window cancelled the query at 130,883,409 rows,
92ms in, verified stopped, code 394. THE OVERSHOOT IS DOCUMENTED, in the module
and in the evidence: enforcement is granular to the poll interval, so this is
a bound of the form "within roughly one poll interval of engine throughput",
not a hard cap. Quoting it as a hard cap would be wrong.

Two more things stated rather than implied:
  * `enforceInFlight: false` is reported by describeEnforcement() as
    "NOT ENFORCED — this budget is advisory", and the constructor REFUSES to
    accept enforceInFlight without a process control;
  * an unshared window store is reported as non-binding across replicas
    ("with N replicas a tenant can spend up to N times this budget").
  * a query the budget killed is still charged for the work it burned. Not
    charging is how a tenant learns the cheapest query is a cancelled one.

Evidence: evidence/bi-p019-p021/cancellation-and-budget.json
268 unit tests, 7 live tests. typecheck + eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:28:30 +00:00
feat(analytics): BI-P020 — provider-neutral query cache that cannot serve a stale or foreign result
CI (SIAX Cloud) / security (push) Successful in 23s
Security Scan (SIAX Cloud) / security (push) Successful in 19s
CI (SIAX Cloud) / integration (push) Successful in 38s
CI (SIAX Cloud) / quality (push) Successful in 56s
CI (SIAX Cloud) / security (pull_request) Successful in 22s
CI (SIAX Cloud) / integration (pull_request) Successful in 40s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 22s
CI (SIAX Cloud) / quality (pull_request) Successful in 54s
ca0a3292df
All six key components BI-P020 lists are REQUIRED, and the builder refuses to
produce a key when one is missing rather than defaulting it. A defaulted
component is how a cache serves a result computed under a metric definition
that no longer exists — that answer is not stale, it is WRONG, and it looks
current.

"Never share tenant result cache" is enforced structurally, in three layers,
because the obvious one is a naming convention:
  * the key is prefixed with the tenant id, derived here from the verified
    identity and never passed in;
  * the stored ENTRY records its own tenant id, and every read checks the
    entry against the caller — so a prefix collision or a shared backing store
    still cannot serve a foreign result;
  * a cross-tenant hit is REFUSED and the poisoned entry EVICTED, not returned
    as a miss. Returning undefined would hide a security event.

The third point is the one the test corpus exercises directly: an entry stored
under t_alpha's key carrying t_beta's id, and a forged key that a SCAN glob
would happily include. ST0RE was relying on naming conventions when it turned
out to have four cross-tenant paths rather than the one reported.

Staleness is tested by mutating each key component ONE AT A TIME and requiring
a miss for every one — and a coverage assertion compares the mutation list
against the component type, so adding a field to CacheKeyComponents without
adding a mutation fails the suite instead of going untested.

ValkeyResultCache takes the client as an injected interface, so no dependency
and no CVE surface is added — the same reason BI-P005's Arrow writer is
hand-written. It sets a store-side TTL as well as recording one (a recorded
timestamp alone leaves expired results resident forever), pages with SCAN
rather than blocking on KEYS, and removes a corrupt entry instead of letting
it become a permanent silent miss.

32 tests. typecheck + eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:34:54 +00:00
feat(analytics): BI-P028/BI-P029/BI-P030/BI-P031 — metric model, semantic registry, governance, semantic provider
CI (SIAX Cloud) / security (push) Successful in 29s
CI (SIAX Cloud) / integration (push) Successful in 52s
CI (SIAX Cloud) / quality (push) Successful in 1m10s
Security Scan (SIAX Cloud) / security (push) Successful in 25s
CI (SIAX Cloud) / security (pull_request) Successful in 29s
CI (SIAX Cloud) / integration (pull_request) Successful in 53s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m13s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 22s
65cce86144
BI-P028 extends the BI-P002 MetricDefinition rather than defining a second,
parallel metric — a second metric type would be the exact failure the DoD is
about. `MetricSpec` is the fifteen-field shape the prompt names; explicit
bridge functions map it onto the persisted row plus the six columns it adds.

The DoD ("no production metric exists only inside a dashboard tool") is
enforced by something that refuses: a dashboard panel carrying an inline
expression, inline aggregation or inline SQL is rejected, as is one naming an
unregistered or RETIRED metric. So a metric cannot come into existence inside
Superset and quietly become production.

Validation refuses the metrics that render as a plausible number and mean
nothing: no description, no owner, no time dimension, a ratio without a
denominator, a currency format with no currency (which is how SEK gets
rendered as USD), a filter on a dimension the metric does not declare.

BI-P031's DoD is checked on a MEANING HASH rather than a field diff, so a
field this code has never heard of still counts as changing the meaning —
fail-closed rather than fail-open on future additions. Eight semantic fields
are each mutated and each refused; three editorial fields are each mutated and
each allowed, because forcing a version bump to fix a typo is how teams learn
to route around governance. A new version starts DRAFT and EXPERIMENTAL: it
never inherits the certification, or a certified metric's meaning could change
while still wearing the badge.

BI-P030 stores the semantic model separately from any engine, and that
negative is enforced two ways after the first way turned out to be
insufficient:
  * a VOCABULARY scan rejects a model mentioning metricflow / clickhouse /
    trino / cube / superset anywhere in its serialized form;
  * a STRUCTURAL check rejects any top-level key outside BI-P030's field list.
The second exists because a test expected the vocabulary scan to catch a
MetricFlow-shaped manifest and it did not — the manifest never says
"metricflow". The structural check holds regardless of what a document calls
itself. The manifest is now also stamped with its generator so a leaked copy
is detectable.

The replaceability DoD is tested with TWO providers whose naming rules differ
(MetricFlow-shaped and Cube-shaped, `ds_sales.amount_sek`): the canonical id
set must be identical across both AND the provider bindings must differ —
otherwise the equality would hold trivially for a registry that only ever had
one provider.

BI-P029 — WHAT IS NOT PROVEN IS STATED IN THE MODULE AND RETURNED BY THE CODE.
MetricFlow is a Python package and was NOT executed: no Python runtime is
provisioned here, and BI-P029's "pin a currently verified version" requires a
licence/supply-chain review that has not happened. `providerReadiness()`
returns `verified: false` and names all four open items, including that the
Superset path is unimplemented and the AI Analyst path uncompared. The
constant `METRICFLOW_PINNED_VERSION` is labelled a declared intent, not a
certification. A test asserts the readiness report says so, so the honesty
cannot rot silently.

353 tests. typecheck + eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:40:37 +00:00
feat(analytics): BI-P025/BI-P026 — quality providers that fail on empty, freshness that refuses to be silent
CI (SIAX Cloud) / integration (push) Successful in 43s
CI (SIAX Cloud) / security (push) Successful in 23s
CI (SIAX Cloud) / quality (push) Successful in 57s
Security Scan (SIAX Cloud) / security (push) Successful in 21s
CI (SIAX Cloud) / security (pull_request) Successful in 23s
CI (SIAX Cloud) / integration (pull_request) Successful in 40s
CI (SIAX Cloud) / quality (pull_request) Successful in 55s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 25s
6cb00add57
Both prompts are about a FALSE GREEN, so both are tested by constructing the
false green and requiring a failure.

BI-P025. Three provider kinds, and the interface makes the difference visible
instead of implying parity: `providerKind` is "executing" or "ingesting". The
native provider runs checks; the dbt and Great Expectations adapters INGEST
results produced elsewhere (n0d#38 owns dbt execution against BI-P022, and GE
is Python — nothing here runs it). Calling all three "providers" while only
one executes is how "we have Great Expectations coverage" ends up in a status
report.

THE FALSE GREEN, proven against a live ClickHouse 24.8.14.39: a `not_null`
check over an EMPTY table returns violations=0, evaluated=0. The raw counts
are asserted first — the engine really does say zero-over-zero — and then the
provider is required to call it a FAILURE, not a pass. Every rule compiles to
a VIOLATION expression rather than a "rows that are fine" count, so the
distinction cannot be lost at the runner layer.

Same treatment for the ingesting providers: a MISSING dbt result is a failure,
and dbt's `error`, `warn` and `skipped` are each asserted not to be passes.
`skipped` in particular is the exact shape of l0re's 28 silently-skipped
Postgres tests.

The gate: zero verdicts QUARANTINES. "No rules ran" and "everything passed"
produce the same green in most implementations, which is how a quality gate
stops gating without anyone noticing — api0's `trivy --exit-code 0` in another
costume. A later passing run does not auto-release either; release takes a
principal and a reason, or a flapping rule would quarantine and un-quarantine
a dataset between two reads of the same dashboard.

BI-P025's DoD is the READ side: a quarantined projection is REFUSED, not
annotated. An annotation on a dashboard nobody reads is not a quarantine.

Live counts, all exact, from evidence/bi-p025/native-quality-live.json:
not_null 5/118, range 3, distribution 4, unique 11. The unique figure is 11
rather than the 6 the first version of the test asserted — the five
empty-string customers are duplicates of each other, so not_null and unique
overlap on the same defect. The engine was right and the fixture arithmetic
was wrong; the breakdown is written into the test rather than the number
quietly adjusted.

Tenant isolation re-proven on this path specifically, because a quality run
uses an ADMINISTRATIVE credential: with 618 rows in the base table, t_alpha
evaluates 118 and t_beta 500, and t_beta's blocking failure quarantines t_beta
alone. Without the forced predicate one tenant's dirty rows would quarantine
another tenant's projection.

BI-P026. Five states, and the DoD's operative word is "silently". So the gate
sits at the boundary every result crosses: a result with NO freshness envelope
is unreleasable — not defaulted to FRESH, not defaulted to UNKNOWN — because
"we forgot to attach it" and "the data is current" are indistinguishable to a
reader, and any default makes forgetting the silent path. A non-current state
without an explicit acknowledgement is refused, so a consumer that ignores the
envelope cannot render stale numbers as current.

FAILED outranks age: a pipeline that just failed may still hold
recently-timestamped data from the previous run, and reporting that as FRESH
is how a broken pipeline stays invisible for a day. UNKNOWN is never collapsed
into FRESH.

54 unit tests, 11 live tests. typecheck + eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:45:43 +00:00
feat(analytics): BI-P022 — dbt canonical transformation CONTRACT and reference project
CI (SIAX Cloud) / security (push) Successful in 22s
CI (SIAX Cloud) / integration (push) Successful in 38s
CI (SIAX Cloud) / quality (push) Successful in 52s
Security Scan (SIAX Cloud) / security (push) Successful in 20s
CI (SIAX Cloud) / security (pull_request) Successful in 22s
CI (SIAX Cloud) / integration (pull_request) Successful in 37s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 20s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m1s
ec26ddc296
Scope stated in the module's first paragraph: this defines the contract, it
does not run dbt, and nothing in this repo does. Execution is N0D's (n0d#38),
which consumes this from the other side. There is deliberately no DbtExecutor
implementation here — only the interface — so "0pt can run dbt" cannot become
true by accident and the two repos share one versioned shape.

`analytics/dbt/` is a real reference project covering raw -> staging ->
curated -> serving, with the four layers mapped onto BI-P003's existing tiers
rather than inventing a fifth vocabulary. The tenant predicate is applied at
the FIRST modelled layer so every downstream model inherits it and no later
model has to remember — the same forced-predicate rule as the query compiler's,
one layer up.

Two singular tests ship with it, both aimed at false greens:
  * `assert_mart_is_not_empty` — without it every column test passes trivially
    over zero rows and the whole suite reports green while the pipeline is dead;
  * `assert_no_cross_tenant_rows` — which should be impossible to fail, and
    exists precisely for that reason. An isolation guarantee nobody tests is the
    one that turns out to have four holes rather than the one reported.

BI-P022's version rule is a GATE. `DBT_RUNTIME_PIN` records dbt Core 1.10.13
(stable) with dbt-clickhouse 1.9.2 and BOTH review flags FALSE — no licence or
supply-chain review has been performed in this repo and none is claimed.
`assertDbtRuntimeCertified` refuses a production run until they are true, and
refuses an alpha line in production without a separate certification reference.
A version constant on its own would be the documented-but-unenforced pattern;
a test pins the flags at false so flipping them is a visible deliberate act.

The result is treated as UNTRUSTED INPUT, because it arrives from another
repo's executor and this estate has been burned by green statuses that meant
nothing. Refused: `status: success` with ZERO nodes executed (a dbt selector
matching nothing exits 0 — the dbt-shaped false green); an observed runtime
different from the pin (the models mean something else and nothing says so); a
revision that is not an exact 40-char SHA (a branch name is not reproducible,
and reproducibility is the DoD); a missing manifest.json or run_results.json,
without which the run cannot be audited or its tests ingested by BI-P025.

Layer dependencies are checked against the manifest GRAPH, not asserted as a
convention: a mart reading a source directly, or skipping a layer, or a
staging model reading a mart, are each refused.

Selectors are validated rather than passed through — a free-form argument list
is a shell surface, and a shell surface on a job holding warehouse credentials
is the whole problem. The job also cannot name an arbitrary target schema: it
must be scoped to the job's tenant.

39 tests. typecheck + eslint clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 00:54:57 +00:00
feat(analytics): BI-P014 — DuckDB provider, and the measured finding that its sandbox does not work the way it looks
CI (SIAX Cloud) / integration (push) Failing after 42s
CI (SIAX Cloud) / security (push) Successful in 24s
CI (SIAX Cloud) / quality (push) Successful in 1m6s
Security Scan (SIAX Cloud) / security (push) Successful in 22s
CI (SIAX Cloud) / security (pull_request) Successful in 23s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 23s
CI (SIAX Cloud) / integration (pull_request) Failing after 43s
CI (SIAX Cloud) / quality (pull_request) Successful in 57s
a9ae7f25a4
BI-P014's security requirement is the whole prompt: DuckDB is a local engine
with `read_csv('/etc/passwd')` built in. Two layers.

LAYER 1, the mount registry: a caller names ARTIFACT IDS, never paths. The
registry resolves an id, checks the artifact belongs to the caller's tenant,
and realpath()s the result to confirm it is still inside the mount root once
symlinks are followed. A symlink to /etc/passwd, a traversing relative path, an
absolute path, and a sibling directory sharing the root's prefix
(/mnt/artifacts-evil for root /mnt/artifacts) are each refused. A cross-tenant
read is AUTHORIZATION_DENIED, deliberately not NOT_FOUND — an operator reading
the audit trail needs to see an attempted cross-tenant read as one.

LAYER 2, THE FINDING:

    `SET allowed_directories=[...]` ALONE IS A NO-OP.

Measured on DuckDB 1.5.5. With allowed_directories set to a single mount
directory and nothing else, `read_csv('/etc/passwd')` RETURNED THE FILE and an
https:// URL was fetched over the network. Only when `enable_external_access
= false` is ALSO set does the allow-list bind; the same reads then fail with
"Permission Error: Cannot access file". `lock_configuration=true` afterwards
stops a query re-opening either.

So the obvious spelling of this control — set the allow-list and move on —
yields a setting that appears in duckdb_settings(), looks like a sandbox, and
permits everything. That is `trivy --exit-code 0` in a different costume. The
bootstrap is therefore emitted as ONE unit, `assertSessionSandboxed` refuses a
session missing any part of it, and the measurement itself is PINNED by a test
that never skips: if someone simplifies the bootstrap, the pinned evidence
(`read_etc_passwd_succeeded: true` for the naive spelling) keeps the finding
attached to the build rather than to a comment that would rot.

Twelve bypass attempts run against a real DuckDB with LAYER 1 REMOVED — sent
straight to the engine, registry not in the picture. Zero succeeded. The
counterfactual runs in the same session and is asserted: the mounted file IS
readable, so "nothing succeeded" is not a sandbox that blocks everything, which
would be an outage rather than a control. A locked session is then shown unable
to re-open its own sandbox, all three ways.

DoD, against real fixtures: Parquet aggregates correctly (SE 394.5, NO 105.5),
CSV round-trips a value containing a quote, and an ICEBERG SNAPSHOT reads via
iceberg_scan on an exact metadata JSON — which is not a workaround: DuckDB
refuses to glob for the latest version because globbing can read uncommitted
data, and an exact metadata file is what "snapshot" means. Arrow is reported
UNPROVEN by `readiness()` with the reason: DuckDB's arrow extension is a
network install the offline environment could not download.

THREE DEFECTS IN MY OWN TESTS, found and fixed rather than tidied away:
  * a bypass-corpus entry was `SELECT 1 FROM (SELECT 1) t` — not an attack at
    all. It succeeded and was reported as a leak. A corpus entry that cannot
    fail is as useless as a check that cannot. Replaced with real ATTACH and
    COPY attempts.
  * the harness ran the session bootstrap outside its try block, so a statement
    that correctly REFUSED to re-open a locked sandbox killed the subprocess
    and surfaced as an unreadable "Command failed" instead of as the meaningful
    refusal it was.
  * the Iceberg fallback was `expect(evidence.iceberg ?? "unbuilt").toBeDefined()`
    — a check that can never fail. It let a run without the fixture ERASE the
    recorded Iceberg result from the evidence file while both suites stayed
    green. It now rebuilds the fixture when pyiceberg is available and, when it
    is not, asserts against the previously recorded result and marks it as
    carried forward.

The Iceberg fixture is deliberately NOT committed: pyiceberg writes absolute
paths into its metadata and Avro manifests, so a committed table resolves only
on the machine that built it, and a fixture that silently fails everywhere else
is worse than none. tests/fixtures/duckdb/README.md says so and gives the
rebuild command; the recorded result is the artefact instead.

Heavy artifacts are refused with CAPABILITY_UNAVAILABLE and pointed at N0D,
per BI-P014 — silently running a 40 GB scan in the API process is how a preview
engine takes down a control plane.

35 unit tests, 12 live tests. Evidence: evidence/bi-p014/duckdb-sandbox.json.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 01:00:58 +00:00
feat(analytics): sql/009 — execution metadata, metric governance, semantic registry, quarantine, usage ledger
CI (SIAX Cloud) / integration (push) Failing after 38s
CI (SIAX Cloud) / quality (push) Successful in 1m0s
CI (SIAX Cloud) / security (push) Successful in 25s
Security Scan (SIAX Cloud) / security (push) Successful in 28s
CI (SIAX Cloud) / integration (pull_request) Failing after 48s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m16s
CI (SIAX Cloud) / security (pull_request) Successful in 27s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 23s
2bfbd3f159
The migration the previous commits' comments promised. Additive and idempotent
throughout, so the restore drill can replay it onto a live database.

BI-P018 adds principal_id, workload, compiled_query_hash, datasets, metrics,
cache_status and cost_units to the execution record — and deliberately NO
`query_text` column, with a CHECK pinning the hash to 64 hex characters so a
statement smuggled into that field is refused by the database. A test asserts
the absence of `query_text` and `sql` as columns.

BI-P031's DoD gets a Postgres TRIGGER, not just the TypeScript guard: a
certified metric whose meaning_hash changes without a version bump raises. That
is the layer that holds when something writes without going through the
application — a migration script, a psql session, an admin tool. A compiler
opinion does not survive `psql`; a trigger does.

BI-P030 gets a version-history table whose CONTENT is immutable (status and
valid_from may still move) plus a PARTIAL UNIQUE INDEX enforcing at most one
ACTIVE version per model per tenant. Two active versions is a silent
correctness failure in which half the dashboards read a different model, so it
is an index rather than application discipline.

BI-P021's usage ledger is append-only by trigger: update and delete both raise.
BI-P026's freshness table DEFAULTS TO 'UNKNOWN' — a dataset nobody has computed
freshness for must not read as FRESH — and requires a non-empty disclosure,
because a state with no words attached is a state a dashboard renders as a
silent green dot. BI-P025's quarantine record requires a principal and a reason
on release.

A BUG IN THIS MIGRATION, FOUND BY RUNNING IT:

  `SELECT 1 FROM pg_constraint WHERE conname = '...'` is NOT schema-scoped.
  conname is unique per schema, not per database, so on a database that already
  had these tables in `public` every guarded CHECK was silently skipped when
  the file was applied into a second schema. The constraints simply were not
  there, and the tests that should have proven them PASSED — because the
  INSERTs they expected to fail succeeded, and `expect(message).toContain(...)`
  on an undefined message reads as a type error, not as "the constraint is
  missing". Every guard now scopes via `conrelid = '<table>'::regclass`, and
  the fix is proven by applying the file to `public` FIRST and then running the
  suite into a fresh schema: 20/20.

Two flaws in my own test harness, also found by running it:
  * `sql.unsafe("SET search_path ...; SELECT ...")` is a multi-statement simple
    query, and postgres.js returns the LAST statement's result — so a SELECT
    silently returned the connection object and two assertions compared against
    nonsense. search_path is now pinned on the CONNECTION.
  * the fixture INSERT used column names that do not exist in sql/008's dataset
    table, which surfaced as a suite-level failure rather than as 20 skips.

20 tests against a real Postgres 16, gated on ANALYTICS_TEST_DATABASE_URL, and
each of them attempts the write the constraint exists to refuse.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 01:08:15 +00:00
fix(analytics): move the DuckDB live suite out of tests/integration, and add the BI docs
CI (SIAX Cloud) / security (push) Successful in 28s
CI (SIAX Cloud) / integration (push) Successful in 42s
Security Scan (SIAX Cloud) / security (push) Successful in 21s
CI (SIAX Cloud) / quality (push) Successful in 1m2s
CI (SIAX Cloud) / security (pull_request) Successful in 23s
CI (SIAX Cloud) / integration (pull_request) Successful in 40s
CI (SIAX Cloud) / quality (pull_request) Successful in 58s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 20s
3ee023017b
CI's integration job failed, and it failed CORRECTLY. That job provides a live
OpenSearch and a live ClickHouse and then asserts that nothing in
tests/integration/ skipped, on the stated principle that "a skipped suite must
never masquerade as a green integration gate". The DuckDB suite needs a Python
interpreter that job does not provide, so it skipped, and the guard fired — the
first suite in that directory ever to skip there.

Moving the file to tests/duckdb/ respects the guard rather than weakening it.
Loosening it to tolerate skips would have traded a real gate for one green job,
which is the trade this estate keeps regretting. The suite still runs, still
fails rather than skipping when DUCKDB_PYTHON is set and DuckDB is broken, and
is still pinned by the committed evidence.

Also fixed: "does NOT ship the Iceberg table" asserted against the FILESYSTEM
when the claim is about what is COMMITTED. The live suite rebuilds that table
locally, so the assertion failed whenever both suites ran in one pass — it was
testing the wrong thing rather than catching a real problem. It now asserts
against `git ls-files`, and also asserts the parquet/csv fixtures ARE tracked,
so a version that stopped shipping any fixture would fail too.

docs/analytics/BI_QUERY_METRIC_QUALITY.md is organised around what is PROVEN
and what is NOT, because the second list is the one that gets dropped. It
records the five findings and the six open items, including that BI-P029's
MetricFlow was never executed and that BI-P021's budget is a bound rather than
a hard cap (measured 130x overshoot at a 100ms poll).

It also reports, without changing it, a latent flaw in .gitea/workflows/ci.yml
(a protected file this branch does not touch): the integration guard's second
pattern `Tests  [0-9]+ passed` never matches, because vitest writes that
summary with ANSI colour codes between the words. The guard is therefore
STRICTER than it reads — "any skip fails" rather than "skipped and nothing
passed" — so nothing is currently unguarded, but the escape hatch does not
work and someone will eventually be puzzled by it.

957 tests pass with ClickHouse, Postgres and DuckDB all live.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin changed title from feat(analytics): BI-P014/016..022/025..031 — query contract, analyst SQL, execution, cache, cost, dbt, quality, metrics to feat(analytics): BI-P014/016..022/025..026/028..031 — query contract, analyst SQL, execution, cache, cost, dbt, quality, metrics 2026-09-07 01:08:53 +00:00
admin added 1 commit 2026-09-07 01:11:22 +00:00
docs(analytics): anchor two security claims to the measurements behind them
CI (SIAX Cloud) / security (push) Successful in 30s
CI (SIAX Cloud) / integration (push) Successful in 56s
CI (SIAX Cloud) / quality (push) Successful in 1m23s
CI (SIAX Cloud) / integration (pull_request) Successful in 49s
CI (SIAX Cloud) / security (pull_request) Successful in 24s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m18s
Security Scan (SIAX Cloud) / security (push) Successful in 23s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 23s
ba5c796b36
Neither changes behaviour; both replace an assertion with a measurement.

BI-P014's escaping strategy rests on DuckDB treating a backslash as a literal
character, so that doubling `'` is sufficient. Verified on 1.5.5 rather than
assumed: `SELECT length('a\\b')` returns 4, and `SELECT 'a\''` is a parser
error ("unterminated quoted string") — which is exactly what a NON-escaping
backslash produces. In a dialect where `\'` closed the literal, doubling would
not be enough, so the claim is worth pinning to a run.

BI-P017's FROM/JOIN scan is conservative where it is unsure, and that has a
cost worth naming rather than discovering: ClickHouse's `ARRAY JOIN arr` takes
a COLUMN, not a table, and this guard refuses it. A false refusal an analyst
can see and report is the right side of that trade; a false ADMISSION is not.
Supporting it means teaching the scan which JOIN flavours take a non-table
target, which is the direction that grows holes — so it stays refused until
someone needs it and can add it WITH a corpus entry proving the narrowing did
not open anything else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 01:16:33 +00:00
fix(analytics): revert 18 files reformatted by mistake, and actually apply the Iceberg fix a previous message claimed
CI (SIAX Cloud) / quality (push) Successful in 55s
CI (SIAX Cloud) / integration (push) Successful in 40s
CI (SIAX Cloud) / security (push) Successful in 21s
Security Scan (SIAX Cloud) / security (push) Successful in 22s
CI (SIAX Cloud) / integration (pull_request) Successful in 39s
CI (SIAX Cloud) / quality (pull_request) Successful in 54s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 21s
CI (SIAX Cloud) / security (pull_request) Successful in 25s
c864c8c417
Two corrections, both of my own errors.

1. SCOPE CREEP, NOW REVERTED. Running `prettier --write` on whole directories
   reformatted 18 files this branch had no business touching — the entire
   merged BI-P002..P012 core (arrow/, domain/, engine/, repositories/, five
   analytics integration suites) and, absurdly,
   search-opensearch.integration.test.ts, which has nothing to do with this
   work. The changes were purely stylistic quote churn, but they added ~1,400
   lines of noise to the diff, made the PR harder to review honestly, and
   invited conflicts with the four other lanes working in this estate. All 18
   are now byte-identical to origin/main again (verified with `git diff
   origin/main --name-only`, which reports nothing for them). Formatting is now
   applied only to the files this branch actually owns.

2. A COMMIT MESSAGE THAT DESCRIBED A FIX THAT WAS NOT IN THE CODE. Commit
   3ee0230 said the "does NOT ship the Iceberg table" assertion had been moved
   from a filesystem check to `git ls-files`. It had not. The patch anchor used
   double quotes, prettier had already converted that file to single quotes, so
   the replacement silently matched nothing and the script printed its success
   line anyway. The suite then passed — but only because I had just deleted the
   fixture directory, so the OLD filesystem assertion happened to be true.

   That is precisely the failure mode this branch keeps writing tests against:
   a green run that means something different from what it appears to mean. The
   fix is now genuinely applied and verified the way it should have been the
   first time — by running the suite WITH the Iceberg fixture present on disk,
   which is the case that previously failed. It passes, and it now also asserts
   that the parquet/csv fixtures ARE tracked, so a version shipping no fixture
   at all would fail rather than pass vacuously.

957 tests pass with ClickHouse, Postgres and DuckDB live. typecheck, eslint and
prettier clean on the files this branch owns.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin added 1 commit 2026-09-07 01:18:14 +00:00
docs(analytics): quote the numbers the COMMITTED evidence actually contains
CI (SIAX Cloud) / quality (push) Successful in 48s
CI (SIAX Cloud) / integration (push) Successful in 37s
CI (SIAX Cloud) / security (push) Successful in 17s
Security Scan (SIAX Cloud) / security (push) Successful in 17s
CI (SIAX Cloud) / quality (pull_request) Successful in 1m16s
CI (SIAX Cloud) / integration (pull_request) Successful in 55s
CI (SIAX Cloud) / security (pull_request) Successful in 35s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 29s
c4862fb7a7
The live suites regenerate their evidence on every run, so the BI-P021
overshoot figures moved when they were last re-run — and the doc and module
comment still quoted the earlier ones (130,883,409 rows / 130x / 92ms) while
the committed file said 153,318,696 / 153x / 55ms. Prose that disagrees with
the evidence it cites is worse than prose with no number in it.

Both now match the committed file, and both say what the earlier version
implied but did not state: the multiple is MACHINE-DEPENDENT and moves between
runs on the same box. That is the actual finding. The bound is "roughly one
poll interval of engine throughput", not a number, and quoting any specific
multiple as if it were a property of the system is the mistake this correction
exists to prevent.

The unwatched-baseline figure is corrected the same way: 4,227,318,261 rows
read before cancellation in the current BI-P019 record, not the 6.5 billion of
the earlier run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
admin merged commit into main 2026-09-07 01:25:38 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: admin/0pt#35