test: bring the v2 smoke to parity with v1's #92

Merged
yuris merged 1 commit from flip-smoke-parity into main 2026-08-21 10:44:57 +00:00
Owner

Gate 3 of #89. The v2 wire asserted strictly less than v1 did, which is not a wire you promote to default. Three gaps closed.

One-off query, both wires. Neither wire tested it before (grep -c one_off was 0). PHASE4 now issues SELECT * FROM message and asserts no error plus at least one row. The per-wire shape is asserted where it genuinely differs: on both wires the row must be a non-empty byte string — a row arriving as a Lua table fails with its type printed — and on v1 it must additionally decode as JSON, done through the un-instrumented raw_decode local so nothing is billed into PHASE6's buckets.

Sliced apply over BSATN, unit level. test_slicing.lua never touched cache.row_decoder, so frame-budget slicing over BSATN had no automatic coverage at all. One case now applies 600 BSATN rows (> SLICE_ROWS) with row_decoder = protocol_v2.decode_typed_bsatn and asserts the slice hook fired, all 600 landed, and both columns of every row decoded correctly. It drives apply_database_update directly rather than going through connect, because the pump owns slice_hook while it runs so a counting hook cannot coexist with a yielding one; the two paired for real is PHASE6's sliced run.

v2 stand-in for the light assertion. v1's PHASE1 asserts light = true still delivers the caller's own full update; v2 has no light. The v2 guarantee is stronger, so it is asserted instead: the ReducerResult::Ok table handed to the callback and the table the transaction event carries must be the SAME table under rawequal — single-frame inline delivery, not merely both arriving. Gated on the existing tx_reducer request_id correlation so a foreign commit is not read as our own frame gone missing.

Each new assertion was proved to bite by breaking it deliberately (decoder removed, values shifted, row count lowered below one slice).

Pre-existing finding, not fixed here. STDB_BULK_BUDGET is in SECONDS, so the sliced run needs 0.008 to mean 8ms. At the default 10000-row sweep, v2 with a real 8ms budget fails the PHASE6 gate — BSATN decode finishes the apply in one frame, so nothing slices. Verified identical on base main with these changes stashed. The corrected proof is STDB_PROTOCOL=v2 STDB_BULK_BUDGET=0.008 STDB_BULK_SWEEP=50000. Whether the default sweep should scale with wire speed is a separate question and deliberately not answered inside a smoke-parity change.

All four engine runs green locally: v1 native, v2 native (12.1ms vs v1's 21.7ms for 10000 rows), v2 sliced (50000 rows over 6 frames at 8.0ms), v2 HTML5 (handshake asserted v2.bsatn.spacetimedb, green first attempt).

Gate 3 of #89. The v2 wire asserted strictly less than v1 did, which is not a wire you promote to default. Three gaps closed. **One-off query, both wires.** Neither wire tested it before (`grep -c one_off` was 0). PHASE4 now issues `SELECT * FROM message` and asserts no error plus at least one row. The per-wire shape is asserted where it genuinely differs: on both wires the row must be a non-empty byte string — a row arriving as a Lua table fails with its type printed — and on v1 it must additionally decode as JSON, done through the un-instrumented `raw_decode` local so nothing is billed into PHASE6's buckets. **Sliced apply over BSATN, unit level.** `test_slicing.lua` never touched `cache.row_decoder`, so frame-budget slicing over BSATN had no automatic coverage at all. One case now applies 600 BSATN rows (> `SLICE_ROWS`) with `row_decoder = protocol_v2.decode_typed_bsatn` and asserts the slice hook fired, all 600 landed, and both columns of every row decoded correctly. It drives `apply_database_update` directly rather than going through `connect`, because the pump owns `slice_hook` while it runs so a counting hook cannot coexist with a yielding one; the two paired for real is PHASE6's sliced run. **v2 stand-in for the light assertion.** v1's PHASE1 asserts `light = true` still delivers the caller's own full update; v2 has no `light`. The v2 guarantee is stronger, so it is asserted instead: the `ReducerResult::Ok` table handed to the callback and the table the `transaction` event carries must be the SAME table under `rawequal` — single-frame inline delivery, not merely both arriving. Gated on the existing `tx_reducer` request_id correlation so a foreign commit is not read as our own frame gone missing. Each new assertion was proved to bite by breaking it deliberately (decoder removed, values shifted, row count lowered below one slice). **Pre-existing finding, not fixed here.** `STDB_BULK_BUDGET` is in SECONDS, so the sliced run needs `0.008` to mean 8ms. At the default 10000-row sweep, v2 with a real 8ms budget fails the PHASE6 gate — BSATN decode finishes the apply in one frame, so nothing slices. Verified identical on base `main` with these changes stashed. The corrected proof is `STDB_PROTOCOL=v2 STDB_BULK_BUDGET=0.008 STDB_BULK_SWEEP=50000`. Whether the default sweep should scale with wire speed is a separate question and deliberately not answered inside a smoke-parity change. All four engine runs green locally: v1 native, v2 native (12.1ms vs v1's 21.7ms for 10000 rows), v2 sliced (50000 rows over 6 frames at 8.0ms), v2 HTML5 (handshake asserted `v2.bsatn.spacetimedb`, green first attempt).
conformance: bring the v2 smoke to parity with the v1 one
All checks were successful
ci / ci (pull_request) Successful in 1m26s
conformance / conformance (pull_request) Successful in 2m35s
ddff5d6838
The v2 smoke asserted strictly less than the v1 smoke, which is not a
position to flip a default from. Three gaps, closed here.

The one-off query was exercised by NEITHER wire: `grep -c one_off
dev/example/main/smoke.script` was 0, and the unit tests reach it only
through a replayed frame. PHASE4 now runs one against the live server and
asserts the part the two wires do not share -- the row encoding. On both
a row is a byte string, because nothing about a one-off answer reaches
the cache; a row arriving as a Lua table would mean stdb decoded a
relation it was never given a type for. On v1 those bytes decode as the
double-encoded JSON a TableUpdate carries; on v2 they stay ENCODED,
because a one-off projects an arbitrary relation the generated schema
need not describe at all.

PHASE1's light-mode assertion had no v2 counterpart, so the v2 run proved
nothing about the frame its own transaction arrives in. v2 has no light
mode to ask for, and the guarantee that replaces it is both stronger and
an identity: the caller's transaction is INLINE in the ReducerResult::Ok
that settles the call, so the table the callback is handed and the table
the transaction event carries are one table. `rawequal` is what tells
that apart from a commit arriving in a frame of its own -- which is
exactly what a light update is. Gated on the request_id correlation that
already exists, so a foreign commit, which correlates to nothing, is not
read as our own frame gone missing.

test_slicing.lua never touched `cache.row_decoder`, so the slice points
of #67 were only ever reached through the two v1 readers. A BSATN decoder
wired into the same loop without one applies a whole batch in a single
frame, and the budget then means nothing on the wire that is about to
become the default. The new case applies 600 encoded rows -- more than
the 256 one slice covers -- with the v2 decoder installed, counts the
hook calls, and checks both columns of every row, because a count alone
passes with the values wrong. Driven at the cache rather than through
`connect`: the pump owns `slice_hook` while it runs, so a counting hook
cannot coexist with a yielding one, and the two paired for real is
PHASE6's sliced run.

No default moves here. `stdb/init.lua` still resolves to v1 and both
harnesses still default to v1.
yuris force-pushed flip-smoke-parity from ddff5d6838
All checks were successful
ci / ci (pull_request) Successful in 1m26s
conformance / conformance (pull_request) Successful in 2m35s
to 26ddc8cbf5
Some checks failed
ci / ci (pull_request) Successful in 1m31s
conformance / conformance (v1) (pull_request) Failing after 2m35s
conformance / conformance (v2) (pull_request) Successful in 2m28s
2026-08-21 10:15:29 +00:00
Compare
yuris force-pushed flip-smoke-parity from 26ddc8cbf5
Some checks failed
ci / ci (pull_request) Successful in 1m31s
conformance / conformance (v1) (pull_request) Failing after 2m35s
conformance / conformance (v2) (pull_request) Successful in 2m28s
to a7432950ca
All checks were successful
ci / ci (pull_request) Successful in 1m27s
conformance / conformance (v1) (pull_request) Successful in 2m38s
conformance / conformance (v2) (pull_request) Successful in 2m47s
2026-08-21 10:33:01 +00:00
Compare
yuris merged commit 8b0be6cc9d into main 2026-08-21 10:44:57 +00:00
yuris deleted branch flip-smoke-parity 2026-08-21 10:44:57 +00:00
Sign in to join this conversation.
No description provided.