Skip to content

test(ingest): prove the Rust classifier and the SQL compiler agree - #435

Closed
JeremyFunk wants to merge 1 commit into
ai/05-ingest-classification-write-pathfrom
ai/06-rust-sql-equivalence
Closed

test(ingest): prove the Rust classifier and the SQL compiler agree#435
JeremyFunk wants to merge 1 commit into
ai/05-ingest-classification-write-pathfrom
ai/06-rust-sql-equivalence

Conversation

@JeremyFunk

@JeremyFunk JeremyFunk commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

What this layer contains

The differential proof that the two evaluators of one registry agree — no production behaviour changes.

  • apps/ingest/src/ai_equivalence_fixtures.rs — drives the real row writer (encode_traces) over a branch-covering span set and emits the fixture corpus, plus telemetry.rs's #[cfg(test)] hookup for it.
  • packages/domain/src/ai-registry/__fixtures__/equivalence-spans.jsonl (+ README) — the emitted rows.
  • equivalence-support.ts, equivalence.clickhouse.e2e.test.ts, hash-alignment.clickhouse.e2e.test.ts, corpus-equivalence.clickhouse.e2e.test.ts — replay those rows through a real ClickHouse and compare the compiled SQL's verdict to the Rust writer's, span for span.

Why it is shaped this way

The fixtures are generated from the row writer rather than hand-written because the thing under test is the writer's canonicalization — first-occurrence-wins duplicate-key resolution, present-but-empty versus absent, AnyValue → String coercion. A hand-written fixture would encode our belief about that behaviour instead of the behaviour itself, and would keep agreeing with the SQL after the writer drifted. fixture_is_reproducible asserts the emitted bytes are stable, so a drifted classifier fails in Rust before the SQL leg runs at all.

The remaining known divergences are enumerated in PINNED_DIVERGENCES, and the suite fails if that set changes in either direction — a silently-fixed divergence is as much a signal as a new one. hash-alignment separately pins that ClickHouse's cityHash64(value) equals the Rust hash, including adversarial non-ASCII shapes and the negative case that multi-argument cityHash64 is a different function. The fixtures module hooks into telemetry.rs rather than its tests child because encode_traces is private there.

Gates run at this level

  • cargo test --locked (+ TRACE_CAPTURE_DIR) — 136 lib + 75 bin, 0 failures, including fixture_is_reproducible and fixture_covers_every_branch.
  • packages/domain vitest under CLICKHOUSE_E2E=1equivalence 6 passed, hash-alignment 5 passed.
  • bun typecheck — 37/37 tasks pass.

corpus-equivalence.clickhouse.e2e.test.ts skips without a local trace-capture corpus, by the same on-demand design as the Rust corpus replay.

Dependencies

Requires #434 — the fixtures drive the wired row writer, and the replay inserts into the traces schema that PR adds. Requires #433 for the SQL compiler and #432 for the classifier.

🤖 Generated with Claude Code


Stack created with GitHub Stacks CLIGive Feedback 💬


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Adds the differential layer that makes the two evaluators of one registry
checkable against each other: `ai_equivalence_fixtures.rs` drives the real
row writer (`encode_traces`) over a branch-covering span set and emits
`equivalence-spans.jsonl`, and the domain-side e2e suites replay those exact
rows through a real ClickHouse and compare the compiled SQL's verdict to the
Rust writer's, span for span.

The fixtures are generated from the row writer rather than hand-written
because the thing under test is the writer's canonicalization — including
first-occurrence-wins duplicate-key resolution and present-but-empty versus
absent — and a hand-written fixture would encode our belief about that
rather than its behaviour. `fixture_is_reproducible` asserts the emitted
bytes are stable, so a drifted classifier fails in Rust before the SQL leg
ever runs. `hash-alignment` separately pins that ClickHouse's
`cityHash64(value)` equals the Rust hash, including adversarial non-ASCII
shapes, and keeps the multi-argument negative pin: `cityHash64(a, b)`
combines per-argument hashes rather than hashing a concatenation, so any
SQL recomputing this column must pass exactly one argument. The divergences
that remain are enumerated in `PINNED_DIVERGENCES` and the suite fails if
the set changes in either direction. The fixtures module hooks into
`telemetry.rs` rather than its `tests` child because `encode_traces` is
private there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JeremyFunk
JeremyFunk force-pushed the ai/06-rust-sql-equivalence branch from 1d3ab76 to 5fa7589 Compare August 12, 2026 21:05
@JeremyFunk

Copy link
Copy Markdown
Collaborator Author

Superseded by the v2 stack: #451#452#453. The registry-artifact + SQL-equivalence design this stack implemented was dropped in the write-side plan's v2 revision; vendor rules are now plain Rust with the vendored classification fixture as the CI gate.

@github-actions

Copy link
Copy Markdown

🍁 Maple PR preview

Note

Preview resources were removed when this pull request closed.

Final commit 5fa7589 · View workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant