Repair the prompt-queue contract test against the queueing refactor - #9026
Conversation
Repo tests (CPU) is red on main. test_composer_only_queues_behind_the _current_chat greps thread.tsx for code that #8952 moved or replaced, so it fails on any branch regardless of what that branch changes. It is currently red on #8935, #8964, #8980 and #8983 for this reason alone. The behaviour it guards is intact, and in two of the three cases the code that replaced it is stronger than what the test still asserted, so the assertions are repointed rather than dropped: - Queueing moved out of handleSubmit into an extracted queueComposerText, so the Cmd/Ctrl+Enter path could share it. Assert the delegation in handleSubmit and the queueing inside queueComposerText, which keeps this a contract on behaviour rather than on where the code sits. - promptQueueStartPendingRef.current.has(reservationKey) became a .get(reservationKey) === identity comparison. A reservation can be replaced between the start and the callback, and acting on the successor would dispatch the wrong prompt; presence alone never caught that. - temporary: useChatRuntimeStore.getState().incognito became temporary: incognitoAtQueueStart, captured when the queue starts instead of read live at dispatch. A chat toggled out of temporary mid-queue must not have its queued prompts persisted. Each rewritten assertion was checked against a deliberately broken tree rather than assumed to discriminate. Removing the delegation, swapping the identity check back to presence, reading temporary live again, and stopping queueComposerText from queueing each fail the file. tests/studio 11 passed for this file, whole directory green.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 941c03c703
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| # Identity, not mere presence: a reservation can be replaced between the | ||
| # start and the callback, and acting on the successor would dispatch the | ||
| # wrong prompt. `.has` only asked whether the key was occupied. | ||
| assert "promptQueueStartPendingRef.current.get(reservationKey) ===" in THREAD |
There was a problem hiding this comment.
Scope the identity assertion to the dispatch guard
If the dispatch condition at startPromptQueue(...) regresses to promptQueueStartPendingRef.current.has(reservationKey), this test still passes because the later abort and cleanup branches retain matching get(reservationKey) === reservation expressions. That allows a superseded reservation to dispatch the replacement's prompt—the exact regression this new assertion is intended to catch—so assert the identity comparison within the dispatch condition or require all relevant comparisons explicitly.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Correct. The assertion searched thread.tsx as a whole, and the abort and cleanup branches beside the dispatch carry the same identity comparison, so the dispatch guard could regress to .has(reservationKey) on its own and the test would pass on its neighbours, which is precisely the regression it exists to catch. It now slices the if condition guarding the startPromptQueue call and asserts the comparison there, with .has excluded from that guard, and still requires all three comparisons by count since the other two are load-bearing as well: abort without it reports the successor start as this one failing, cleanup without it deletes the successor entry. 11 passed, and rewriting the dispatch guard to .has now fails the test where it passed before.
Reported on this PR and correct. The assertion searched thread.tsx for promptQueueStartPendingRef.current.get(reservationKey) === anywhere, and the abort and cleanup branches beside the dispatch carry the same comparison. The dispatch guard could regress to .has(reservationKey) on its own, which is exactly the bug this assertion exists to catch, and the test would still pass on its neighbours. It now slices the if condition that guards the startPromptQueue call and asserts the identity comparison there, with .has excluded from that guard. All three comparisons are still required by count, because the other two are load-bearing too: abort without it reports the successor's start as this one's failure, and cleanup without it deletes the successor's entry. 11 passed. Rewriting the dispatch guard to .has fails it; it passed before.
|
@codex review |
Following up my own reasoning for accepting #8943's reversal. The reseed is correct because the same flag gates the remembered lookup: it does not blank the slot control, it re-reads THIS model's saved config through resolveResidentInitialConfig. Without that the reversal would take the user's saved slot count away for real, so it is asserted rather than left implicit. Also confirmed the attribution exactly: #8943's direct parent is #9026, which is clean, so there is no intervening commit and one introducer. 184 passed, and dropping slotsModelChanged from the remembered gate fails it
* Repair the contract tests #8943 left behind, and correct one that pinned the old behaviour Repo tests (CPU) has been failing on main since #8943 landed: 54 tests across tests/studio/test_new_chat_context_recount.py and tests/studio/test_model_picker_contracts.py. #9026, immediately before it, is clean. Nothing in the batch of test fixes merged after it caused this. Three separate problems, and the third is a behaviour change, not a stale anchor. 1. test_new_chat_context_recount.py sliced loadModel's already-resident branch by a comment #8943 rewrote, so 52 tests died at the slice. That branch is also no longer a short early return: it now IS the residency decision, reconciling GPU pools, speculative type, managed llama flags and per-model config through 17 imported collaborators. Re-anchoring to the top of it would have meant stubbing all 17, and a replay under 17 stubs asserts against a construction rather than the product. The slice is now the adoption TAIL, which is what this file is about: what happens after the model is judged resident. The judgement itself is stubbed as `adoptable` and is covered on its own by the resident-model-match and resident-config-match suites #8943 added. Six stubs, each derived exactly the way the source derives it, and the two enclosing conditions restated so the entry conditions are visible rather than implied. setParams is stubbed to the merge the tail depends on rather than sliced: the real reducer reaches preset policy, per-turn counters and loaded-context bookkeeping, none of which this file measures. 2. test_external_readoption_drops_a_pin_taken_for_another_model sliced on a condition that no longer exists. The contract survives in a better form: the branch used to clear the pin to null, and now adopts this pick's own pin by the rule a completed load writes it, which drops a stale pin the same way and keeps a pinned cached row loadable. Rewritten against that, ordering requirement unchanged. 3. test_hydration_keeps_the_slot_control_when_readopting_the_running_model was asserting behaviour #8943 deliberately reversed, and #8943 is right. Its commit says why: the adopt path rolls the outgoing model's config back into the store before it hydrates, so the slot and batch controls sitting there belong to the model the tab just left, and suppressing the reseed left a resident model running 4 slots showing the outgoing count for the next Apply to save over it. The test now pins the new contract, asserts nothing reintroduces a same-model exemption, and additionally pins the ordering that makes the reseed necessary: the rollback must precede the hydration. tests/studio: 3896 passed, 4 skipped reinstating the readoptingSameModel guard, or moving the rollback after the hydration, fails the rewritten test * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Pin why the reseed is safe, not only that it happens Following up my own reasoning for accepting #8943's reversal. The reseed is correct because the same flag gates the remembered lookup: it does not blank the slot control, it re-reads THIS model's saved config through resolveResidentInitialConfig. Without that the reversal would take the user's saved slot count away for real, so it is asserted rather than left implicit. Also confirmed the attribution exactly: #8943's direct parent is #9026, which is clean, so there is no intervening commit and one introducer. 184 passed, and dropping slotsModelChanged from the remembered gate fails it * Tighten the comments added by this PR Comments and docstrings only. The AST gate reports a difference because the harness is a Python string holding TypeScript, so its // comments are inside a string literal; every changed line is a comment line in that string or a docstring, checked line by line, and the tests still pass. 237 passed --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

Repo tests (CPU)is red onmain.test_composer_only_queues_behind_the_current_chatgrepsthread.tsxfor code that #8952 moved or replaced, so it fails on any branch regardless of what that branch changes.It is currently the only failing check on #8935, #8964, #8980 and #8983. Reproducible on a clean checkout of
mainwith no PR content:The behaviour is intact
Three assertions drifted. In two of the three the code that replaced them is stronger than what the test still asserted, so they are repointed rather than dropped.
Queueing moved behind a helper.
handleSubmitno longer callsstartHydratedPromptQueueinline; #8952 extractedqueueComposerTextso the Cmd/Ctrl+Enter path could share it.handleSubmitnow callsqueueComposerText(liveThreadIsRunning || livePreStreamRunActive), and the queueing, the composer read and the untrimmed guard all live in that helper. The test now asserts the delegation in one and the queueing in the other, which keeps this a contract on behaviour instead of on where the code happens to sit.Presence became identity.
promptQueueStartPendingRef.current.has(reservationKey)becamepromptQueueStartPendingRef.current.get(reservationKey) === reservation. A reservation can be replaced between the queue start and the callback, and acting on the successor would dispatch the wrong prompt. Presence alone never caught that.Temporary is captured, not read live.
temporary: useChatRuntimeStore.getState().incognitobecametemporary: incognitoAtQueueStart, captured when the queue starts rather than read when it dispatches. A chat toggled out of temporary mid-queue must not have its already-queued prompts persisted, which reading the store at dispatch time would do.Testing
Each rewritten assertion was checked against a deliberately broken tree rather than assumed to discriminate:
thread.tsxhandleSubmitno longer delegates toqueueComposerTexttemporaryread live instead of captured at queue startqueueComposerTextstops queueingtests/studiowhole directory: 3,887 passed, 4 skipped.