Repository navigation
fix(engine): repair the session test #1892 broke, and cover both directions of what it guards - #1964
Conversation
…ctions of what it guards `cargo test -p onnx-genai-engine --features native-backend --lib` fails on a pristine `bb39883e3`: 618 passed, 1 failed. The failure is `native_generate_in_workflow_session_rejects_over_kv_byte_budget`, added by #1900, and it fails at `engine.create_session()` — before it reaches the thing it was written to check. #1892 made `create_session` refuse a package that publishes tokens and declares no `scope: session` state. That refusal is right, and the fixture is genuinely such a package: a bare decoder restarts from its own prompt every turn. But nothing tested the refusal, so it landed by breaking a test written for something else. Rather than relax the assertion, this gives the session test a package that declares a conversation. `test_session_decoder_runtime` is the same canonical decoder with its loop-carried cells promoted to `scope: session` — the shape a real multi-turn decoder declares, and one flag away from the stateless fixture so the two differ in exactly the property under test. The repaired test now reaches admission and is refused there, which is what #1900 wrote it to prove. Two tests are added, each killing a distinct mutant: * `create_session_refuses_a_package_that_declares_no_conversation` — the reject direction for that fixture, and the only unit test of #1892's gate. Disable the gate: 620/1, this test alone. * `a_tensor_bound_request_signals_admission_even_though_it_takes_no_reservation` — the accept direction for the `!prompt_only` branch of `generate_with_pipeline_callbacks`, which had neither direction under test. `on_admitted` is the only thing that resolves the server's admission oneshot with `Ok` on a successful run, so deleting it turns every successful tensor-bound request into a 500 while generation itself succeeds. Delete the call: 620/1, this test alone. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1964 +/- ##
==========================================
+ Coverage 80.64% 80.75% +0.11%
==========================================
Files 408 423 +15
Lines 197607 207429 +9822
Branches 197607 207429 +9822
==========================================
+ Hits 159359 167513 +8154
- Misses 32818 34281 +1463
- Partials 5430 5635 +205
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
justinchuby
left a comment
There was a problem hiding this comment.
Reviewed. This is right, and it is better than the PR I had open for the same defect (#1954, now closed in favour of this one). The fixture repair, the reject-direction test, and the on_admitted accept-direction test are all correct, and the reasoning about why the obvious falsifier is backwards is the part I'd have got wrong.
Three things: one real finding measured from this PR's own CI, one place where a guard is weaker than its comment claims, and one confirmation I ran.
1. test_session_decoder_runtime is dead code under default features — and this PR's CI already says so
Its only caller is runtime.rs:3009, inside interpreted_engine_inner, which is #[cfg(feature = "native-backend")]. The function itself is only #[cfg(test)]. So a default-feature test build compiles it with no reference to it. From this PR's CLI ORT (Linux x86_64) log, in the Test ORT-backed workspace crates step (default features, and onnx-genai-engine is in that list):
2026-08-24T09:29:56Z warning: function `test_session_decoder_runtime` is never used
2026-08-24T09:29:56Z = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default
One line:
#[cfg(all(test, feature = "native-backend"))]
pub(crate) fn test_session_decoder_runtime() -> anyhow::Result<WorkflowRuntime> {The reason I'd fix it rather than shrug is not the warning. It is that the job stayed green with it. onnx-genai-engine has no -D warnings gate at default features anywhere in ci.yml — the only -D warnings clippy that names it is Check the native backend compiles, which turns the feature on. So the default-feature configuration of this crate emits warnings into a log nobody reads and cannot fail a lane. A genuine unused-code defect there has the same signature as this one.
That is the mirror image of #1944: native-backend code that no default lane compiles, and now default-feature code that no lane lints. Both halves of the crate have a hole; #1961 closed one. Worth an issue rather than scope creep here — I'll file it unless you'd rather.
I hit exactly this and only caught it because I ran both configurations, so this is a shared trap and not a criticism.
2. loop_carried_cell_names's _ => {} is a silent-miss arm
match step {
WorkflowStep::Sequence { .. } => ...,
WorkflowStep::Loop { .. } => ...,
WorkflowStep::Branch { .. } => ...,
_ => {}
}A future WorkflowStep variant that nests steps falls into _ and its carries are missed. The three ensure!s catch total failure (carried empty, promoted == 0, predicate false) but not a partial one: if a new nesting variant holds some carried cells and the top-level loop still holds others, the fixture promotes a subset, all three guards pass, and it declares session state that is missing a cell — a package subtly unlike the one it claims. Anti-correlated in the usual direction: it fails only in the case a reader would most want it to catch.
Exhaustive matching makes the compiler point at this function when a variant is added:
WorkflowStep::Invoke { .. } | WorkflowStep::Emit { .. } | ... => {}Not a blocker — today's WorkflowStep has no other nesting variant, so the set is currently complete. It is a future silent miss, and the cost of removing it is listing the leaf variants once.
Credit where due: ensure!(workflow_carries_session_state(&workflow)) — asserting the fixture satisfies the same predicate create_session reads, not a lookalike — is the single best line in this diff, and it is what my version was missing.
3. Independently confirmed the lane this repairs
Before this PR existed I replayed the CLI ORT steps locally against the same fixture repair (Linux x86_64, CARGO_INCREMENTAL=0, taskset -c 16-23, under hostlock.sh with a declared reason — not claiming an idle host):
| lane step | result |
|---|---|
cargo test --locked -p onnx-genai-engine --features native-backend -- --test-threads=1 |
exit 0, 619 passed lib + all integration suites |
... --test onnx_genai_workflow_conformance |
exit 0, 15 passed |
cargo clippy --locked -p onnx-genai-cli --all-targets -- -D warnings |
exit 0 |
cargo clippy ... --features onnx-genai-cli/native-cuda,onnx-genai-server/native-cuda --all-targets -- -D warnings |
exit 0 |
cargo fmt --all -- --check |
exit 0 |
And this PR's own CLI ORT (Linux x86_64) is now green on every step, including both clippy steps — which is the first time that lane has run them at all in this sequence, since they were skipped behind the earlier failure on #1944 and red on #1954.
On the on_admitted asymmetry
Nothing to add except that I want it on record, because I reached the wrong falsifier first and someone else will too. assert!(!admitted) is the natural encoding of "this branch takes no reservation", it is true of the engine, and it passes under the deletion that turns every successful tensor-bound request into a 500. A test can be a correct statement about the code and still be green precisely when the system is broken. The limitation paragraph — that the interpreted fixture cannot complete a generation, so this pins the signal and not the Ok-path — is exactly the right thing to state rather than imply.
Verdict: approve on the substance. Item 1 is a one-line change I'd make before merging; item 2 is optional and worth its two lines. Neither changes the correctness of what is here.
…1891) #1964 added `a_tensor_bound_request_signals_admission_even_though_it_takes_no_reservation` to pin the server-facing half of `on_admitted` on the `!prompt_only` branch. That test is right and it stays. Its name and half its docstring describe the defect this PR removes, so after this change they assert the opposite of the code they sit on. Measured, not read. Shrinking the budget in that test from 20 to 1 -- touching nothing else -- is a two-arm control: pristine main 1 passed (a 1-byte budget does not stop it) main + this PR 1 failed (admissions: left 0, right 1) So "takes no reservation" is true on main and false here. Renamed to `a_tensor_bound_request_signals_admission_exactly_once`, which is true in both worlds, and rewrote the stale paragraph while keeping #1964's asymmetry argument intact -- that argument is still correct and is still the reason `!admitted` is the wrong falsifier. Also records why the budget is load-bearing: after this change a budget that refuses the request never reaches the callback at all. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…oo (#1973) Closes #1969. ## The gap `Check the native backend compiles` (`ci.yml:493`) is the **only** `-D warnings` gate in `ci.yml` that names `onnx-genai-engine` or `onnx-genai-server` — and it turns the feature *on*. | candidate | why it doesn't cover default features | |---|---| | `Run clippy on all offline crates` | cannot list them; both pull in `ort-sys` | | `Clippy onnx-genai-cli` (`CLI ORT`) | names `-p onnx-genai-cli`; `-D warnings` applies to named packages, and dependencies don't build test targets | | `Clippy ... with native CUDA` | `native-cuda` implies `native-backend` | | `Test ORT-backed workspace crates` | compiles them at default features — and gates nothing | So the configuration that **ships** was compiled and linted by nothing. ## Measured, not assumed #1964 merged with this in a **green** `CLI ORT (Linux x86_64)` log, `Test ORT-backed workspace crates` step: ``` 2026-08-24T09:29:56Z warning: function `test_session_decoder_runtime` is never used 2026-08-24T09:29:56Z = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default ``` The warning is harmless — a `#[cfg(test)]` fixture whose only caller is `#[cfg(feature = "native-backend")]`. What is not harmless is that nothing could turn it red. A genuine unused-code defect in either crate at default features has exactly the same signature, lands in the same log, and that log is only read once something else has already failed. ## This PR 1. **The mirror-image step.** Same two crates, no features, `-D warnings`. Separate from the native-backend step rather than a union, because default is what ships and a union stops checking it on its own — same argument the existing `Clippy onnx-genai-cli` / `... with native CUDA` pair already makes two steps below. 2. **The one warning it finds**, gated to match its only caller: ```rust -#[cfg(test)] +#[cfg(all(test, feature = "native-backend"))] pub(crate) fn test_session_decoder_runtime() -> anyhow::Result<WorkflowRuntime> { ``` ## The gate is not vacuous — falsified in both directions The step is worth nothing unless it fails on the defect it was added for, so I ran it against the pre-fix tree: | tree | `cargo clippy --locked --all-targets -p onnx-genai-engine -p onnx-genai-server -- -D warnings` | |---|---| | pre-fix (`#[cfg(test)]`, as merged on `main`) | **exit 101** — ``error: function `test_session_decoder_runtime` is never used`` | | with the fix | **exit 0** | That is the whole argument for the step: it goes red on the exact state `main` is in right now, and green after a two-word change. ## Validation Linux x86_64, `CARGO_INCREMENTAL=0`, `taskset -c 16-23`, under `scripts/hostlock.sh` with a declared reason. Not claiming an idle host — pass/fail results, not timings. | command | result | |---|---| | `cargo clippy --locked --all-targets -p onnx-genai-engine -p onnx-genai-server -- -D warnings` (the new step) | exit 0 | | same, on the pre-fix tree | **exit 101**, one error, the expected one | | `cargo clippy ... --features onnx-genai-engine/native-backend,onnx-genai-server/native-backend -- -D warnings` (existing step, unchanged) | exit 0 | | `cargo test --locked -p onnx-genai-engine --features native-backend --lib` | `621 passed; 0 failed` | | `cargo test --locked -p onnx-genai-engine` (default features) | exit 0, 80 suites ok | | `cargo fmt --all -- --check` | exit 0 | | `ci.yml` parses; new step present in `rust-quality` | yes | Backups restored with `cp` + `touch`, never `mv` — `mv` gives the restored file the backup's older mtime and cargo then silently reuses the artifacts built from the *mutant*. ## Cost One `cargo clippy` over two crates on a job that already builds both. It shares the entire dependency graph with the step above minus one feature, so almost all of it is cache-warm; the incremental run measured **10.9s** here after the native-backend step. ## Scope Deliberately just these two crates — they are the ones with the demonstrated hole and the ones #1961 already pairs. #1969 raises the general question (*for each feature configuration a crate ships or tests, which lane goes red if it breaks?*) for the rest of the workspace; that is a survey, not this change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rries (#1982) ## What this adds A test that a **continuing** turn is charged for the conversation it carries, and a fresh turn is not. `admit_interpreted_generate_request` (`runtime.rs:846`) computes: ```rust let carried = if self.workflow_sessions.contains_key(&session_id) { self.workflow.session_prepended_prompt_len(&session_id.to_string()) } else { 0 }; let prompt_tokens = self.interpreted_prompt_token_count(prompt)? + carried; ``` The scheduler id chosen in `workflow_api.rs:206-216` is the whole input to that lookup. A **fresh** id makes `carried = 0`, so turn N is admitted as though it were turn 1, under-reserving by the entire conversation prefix — verbatim what the function's own comment warns about. ## Why it did not already exist I measured this, I did not infer it. Reviewing #1951 I ran a mutation battery on the merged tree; **M7 — disabling the session-reuse arm entirely — SURVIVED at 622 passed / 0 failed.** The only test reaching that branch (`native_generate_in_workflow_session_rejects_over_kv_byte_budget`) asserts a **refusal**, and a refusal arrives identically whether the id was reused or freshly minted. The observable it needs is on the *admitted* side. This also corrects the review record on #1951, which stated that coverage "returns when that fixture is repaired." The fixture is repaired — I repaired it in #1964 — and the gap is still open. ## Design **Two directions on one engine, path held fixed.** A single `interpreted_conversation_engine_with_byte_budget(30)` builds two sessions: one that has heard nothing, one seeded with 3 tokens. Same budget, same prompt, same call. Only the carry differs, so the fresh arm is the control that stops the test from passing by refusing everything. **Non-vacuity asserted, not hoped for.** The test asserts the seed is visible through `session_prepended_prompt_len` — the exact accessor the accounting reads — *before* exercising either arm. Without that, a seed that silently failed would make both arms cost the same and the test would be pinning the budget rather than the carry. **The refusal is matched specifically** (`scheduler admission failed: KV byte budget`). The fixture's interpreted decoder cannot finish a generation, so the admitted arm still returns `Err`; matching the admission refusal by name is what keeps "was not refused" from being satisfied by any other failure. ## A third fixture shape was required Neither existing fixture can express this. `Stateless` has no session state; `LoopCarriedSession`'s state is threaded by the loop. The shape the scheduler actually charges for is a **session-scoped cell the workflow never reads**, which the request binding prepends to each turn's prompt — and the validator refuses a cell that is both loop-carried and a continuation ("the lease and an SSA carry are two answers about the same value"), so it has to be its own cell. Hence `TestWorkflowShape::PromptPrefixConversation`, built against the constraints enumerated at `validation.rs:2540+`, with five construction-time `ensure!` self-checks. The test-only `seed_session_conversation` seeds through the **declared** continuation cell rather than a name spelled in the test, so a fixture that declares no continuation fails loudly instead of writing an entry nothing will ever read. ## Mutation battery: 3/3 caught | mutation | result | caught by | |---|---|---| | **M7** session-reuse arm unreachable (a continuing turn gets a fresh id) | **CAUGHT** | the continuing arm, both assertions | | **M8** `carried` always 0 | **CAUGHT** | the continuing arm, both assertions | | **M9** vacuity: admission refuses every workflow-driven turn | **CAUGHT** | the **fresh** arm only — opposite polarity | Tree verified byte-identical after the battery (`workflow_api.rs 3976c9c7a664`, `runtime.rs bf4d8e20ff56`), post-battery baseline PASS. **M9 is the arm that earns the fresh session.** It is the direction a one-sided test cannot see, and it is caught by the *opposite* arm from M7/M8 — which is the point of running the guard against the case it must accept as well as the case it must reject. ### The battery's first run reported 0/3, and that is why M9 exists Including M9, which refuses everything and therefore *must* fail. A vacuity arm reading SURVIVED is impossible, so the battery itself was broken: `cargo test --lib <NAME> -- --exact` with a **bare** test name matches nothing, exits 0, and is textually indistinguishable from a survived mutant. Repaired with the full module path plus an assertion that the run actually selected one test. Anyone running mutation testing here: **a filter that selects zero tests reads exactly like SURVIVED.** Assert your run selected something. ## Default-feature dead code, and a convention adopted from #1973 While validating I found `cargo clippy -p onnx-genai-engine --all-targets -- -D warnings` (default features) failing on `origin/main` — measured by checking `origin/main` out and running it, 1 error: `test_session_decoder_runtime` never used, because its only consumer is gated on `native-backend`. A landmine I introduced in #1964. This branch would have added two more. **#1973 landed the same finding while this PR was open**, and cites that exact warning as its motivating evidence. It added `Check the default features compile clean` to `rust-quality` (`ci.yml:517`) — a **required** job — so this is now enforced rather than latent. I had fixed it with `#[cfg_attr(not(feature = "native-backend"), allow(dead_code))]`. Main fixed it with `#[cfg(all(test, feature = "native-backend"))]`. Both work; **main's is now the convention, so I took main's** and applied it to my two new items as well. Consistency here is worth more than my marginal preference for keeping the items type-checked in both configurations. Verified with the exact command the new required lane runs: `cargo clippy --locked --all-targets -p onnx-genai-engine -p onnx-genai-server -- -D warnings` → clean. ## Validation | check | result | |---|---| | `cargo fmt --all -- --check` | clean | | `clippy --locked --all-targets -p onnx-genai-engine -p onnx-genai-server -- -D warnings` (the new required lane from #1973) | clean | | `clippy -p onnx-genai-engine --features native-backend --all-targets` | clean | | `cargo test -p onnx-genai-engine --features native-backend --lib` | **623 passed / 0 failed** | | mutation battery | **3/3 caught**, tree byte-identical, baseline PASS | Run under `hostlock.sh run` with `taskset` **outermost**, on latest `main`. ## Limitation, stated plainly **No required CI lane runs these tests.** `onnx-genai-engine` is not in `workspace_test_packages.py` offline-linux. `rust-quality` is required and, since #1973, does now *compile and lint* this test at both default and `native-backend` features — but clippy does not run tests, so a test that compiles and **fails** is still structurally invisible to it. The engine `native-backend` tests run in `cli-ort` (`ci.yml:889`), which is advisory. So **a required green on this PR is not evidence for the test.** The mutation battery is the evidence. I would rather say that than let a green tick imply something it does not cover. Scope: 3 files, +289/-11, **tests and test fixtures only** — no production behaviour change. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
mainis red, and has been since #1892On a pristine
bb39883e3, cleangit status:Attribution by build, not by
git log:387f840b0(parent of #1892)08bc957f5(#1892)The test was added by #1900 (
cd99e5ceb). #1892 (08bc957f5) merged later and broke it. It runs incli-ort(ci.yml:889), a lane that has been red for long enough that its colour stopped being read — andrust-qualitynever caught it because that job runscargo clippy, so a compiling test that fails is invisible to the required check.What actually broke, and why the fix is not to relax the test
The test fails at
engine.create_session(), before reaching the assertion it exists for. #1892 added a gate:That gate is correct, and the fixture is honestly caught by it:
test_decoder_runtimebuilds a bare decoder whose state is allscope: invocation, which is a package that restarts from its own prompt every turn. Opening a session over it would be meaningless. #1900's test simply predates the rule.So the fix gives the session test a package that declares a conversation.
test_session_decoder_runtimeis the same canonical decoder with its loop-carried cells promoted toscope: session:workflow_carries_session_stateafterwards — the same predicatecreate_sessionreads — so the fixture cannot silently degrade into the stateless one if the promotion ever stops matching.The repaired test now reaches admission and is refused there, which is the thing #1900 wrote it to prove. Its assertions are untouched.
Two tests added, and the mutant each one kills
Everything below is one revision, one command,
--test-threads=1.bb39883e3NoSessionStategate disabledcreate_session_refuses_a_package_that_declares_no_conversationon_admitted()deleted from!prompt_onlya_tensor_bound_request_signals_admission_even_though_it_takes_no_reservation1.
create_session_refuses_a_package_that_declares_no_conversation— the reject direction for the fixture the repaired test accepts. A fixture that grants the property under test is worth nothing until the ungranted case is shown to be refused, and both fixtures come from one builder differing in one flag, so this pins that the flag is what the refusal turns on.It is also the only unit test of #1892's gate. There is a server-side test of the status mapping, but nothing asserted the engine refuses. How the gate was discovered to be load-bearing is that it silently broke a test written for something else — which is a slower way to find out, and the reason this PR exists.
2.
a_tensor_bound_request_signals_admission_even_though_it_takes_no_reservation— the accept direction for the!prompt_onlybranch, which had neither direction under test. This one needs its asymmetry stated, because the obvious falsifier is backwards:on_admittedcarries two contracts at that call site. To the engine it means "the scheduler admitted this" — false there: the branch takes no reservation and calls nocomplete(). To the server it means "you may begin streaming", andrun_generation's success arm never touches the admission sender (only its error arm does), so this callback is the only thing that resolves that oneshot withOkon a request that succeeds. Delete the call and every successful tensor-bound request becomes a 500 at the awaiting end (completions.rs) while the generation itself completes perfectly.So asserting
!admittedhere — the natural reading of "this branch does not admit" — passes under exactly the change that breaks serving. The test asserts the contract a mutation can actually violate.Limitation, stated rather than implied
The engine fixture's interpreted decoder cannot complete a generation, so what test 2 pins is that the branch signals admission, not that it does so on a run returning
Ok. The signal is unconditional and precedes the work, so it is the same line either way — but a genuinely end-to-end version needs a package the interpreter can finish, and no such fixture exists in this crate yet. The server crate has noEnginefixture at all, so it cannot host that test today either.Gate
On this branch, from clean:
Unrelated to the
head_sinkbuild break (#1961, #1958) that was red onmainat the same time; that one is fixed and verified separately, and I deliberately kept the two apart.