Skip to content

test(turn): keep the missing-Todo Turn a hold under the scoped binding - #7622

Closed
xiaoan17 wants to merge 1 commit into
loopx-project:mainfrom
xiaoan17:codex/fix-turn-plan-missing-todo-test
Closed

xiaoan17 wants to merge 1 commit into
loopx-project:mainfrom
xiaoan17:codex/fix-turn-plan-missing-todo-test

Conversation

@xiaoan17

Copy link
Copy Markdown
Contributor

Scope

One stale test expectation, and the main-branch breakage it was hiding.

tests/test_loopx_turn_driver.py::test_turn_plan_missing_todo_does_not_bypass_contract_errors
asserted that a Turn whose envelope has no selected Todo is a contract error.
That was the contract before the settlement identity could be resolved by
scope: the driver had no identity for the route, the session binding rejected
the plan, and the route became contract_error.

The native Turn settlement binding changed that deliberately. The driver now
resolves the settlement identity by scope, so an ordinary request no longer
needs a pre-selected Todo, and the missing-Todo branch covers ordinary routes
as well as replans:

if route is LoopXTurnRoute.REPLAN_REQUIRED or (route is LoopXTurnRoute.READY_FOR_HOST and not selected_todo):

A missing Todo with a valid signature now resolves no scoped binding and the
plan reports blocked. The test still expected contract_error, so it failed
on main — and it fails the pytest lane of every open pull request, including
this repository's own.

Fix

The test now states the current contract on both sides of the signature:

  • a broken signature stays a contract error (ok false, contract_error);
  • a missing Todo with a valid signature is a hold (ok true, blocked).

The invariants the test was written to protect are asserted in full and are
unchanged: the Host is never invoked, no state is written and no quota is
spent, in either case. The test is renamed accordingly, because
does_not_bypass_contract_errors no longer describes the contract it checks.

Validation

  • tests/test_loopx_turn_driver.py — 148 passed
  • loopx canary premerge --from-git-diff --git-diff-base origin/main — passed

No production code, schema, wire or default change.

Still red on main, resolved elsewhere

tests/architecture/test_control_plane_import_boundaries.py::test_lark_projection_sinks_are_owned_by_the_extension_layer
is also red on main: loopx/capabilities/manager_context/roundtrip.py imports
loopx.extensions.lark.private_conversations, which the import-boundary policy
reserves for the extension layer. #5677 already removes that import, so this
PR does not touch it.

Signed-off-by: xiaoan17 anbc1224@gmail.com

The Turn driver now resolves the settlement identity by scope, so an
ordinary request no longer needs a pre-selected Todo: a missing Todo with
a valid signature resolves no scoped binding and the plan reports
'blocked' instead of a contract error. That change came with the native
Turn settlement binding, and this test still encoded the previous
contract, so it failed on main.

The test now states the current contract on both sides of the signature:
a broken signature stays a contract error, a missing Todo holds the Turn,
and neither reaches the Host, writes state or spends quota. The
invariants the test was written to protect are asserted in full.

No production code, schema, wire or default change.

Validation: tests/test_loopx_turn_driver.py 148 passed; loopx canary
premerge --from-git-diff green.

Signed-off-by: xiaoan17 <anbc1224@gmail.com>
@xiaoan17

Copy link
Copy Markdown
Contributor Author

Closing — superseded by #7618

I read the regression the wrong way round, and this PR would have cemented it.

I concluded that the native Turn settlement binding deliberately replaced the
missing-Todo contract error with a hold, and updated the test to expect
blocked. #7618 shows the opposite: the scoped identity change (3ebad9d) is
what broke the typed missing_turn_lineage contract error, and #7618 restores
it in the driver.

-            route = LoopXTurnRoute.BLOCKED
+            # Keep a normal host route with no identity ready long enough for
+            # _session_plan to return its typed missing-lineage contract error.
+            if route is not LoopXTurnRoute.READY_FOR_HOST or settlement_identity is not None:
+                route = LoopXTurnRoute.BLOCKED

So the existing expectation was right, and this PR's change to it was wrong.
Closing rather than merging: the correct repair is #7618's, and the test as
written belongs with it.

The investigation was still useful — it pinned that the failure came from
3ebad9d, that the missing-Todo branch now covers ordinary routes, and that
the failure is on main for every open pull request.


Closing — superseded by #7618

I read this regression backwards: the scoped identity change (3ebad9d) broke
the typed missing_turn_lineage contract error, and #7618 restores it in the
driver. The existing test expectation was right, so this PR's change to it was
wrong and would have cemented the regression. Closing in favour of #7618.

@xiaoan17

Copy link
Copy Markdown
Contributor Author

Superseded by #7618, which restores the typed missing-lineage contract error in the driver. The existing test expectation was correct; this PR would have cemented the regression.

@xiaoan17 xiaoan17 closed this Oct 29, 2026
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