3131 "SCAN stream" ,
3232 "MATERIALIZE model_calls_visible" ,
3333 "AUTOMATIC COVERING INDEX" ,
34- "USE TEMP B-TREE" ,
34+ )
35+ _TEMP_SORT_MARKER = "USE TEMP B-TREE FOR ORDER BY"
36+ _PORTABLE_SESSION_SORT_VIEWS = frozenset ({"timeline" , "allowance_interval" })
37+ _SESSION_LOOKUP = "SEARCH s USING PRIMARY KEY (session_id=?)"
38+ _OCCURRENCE_LOOKUP = "SEARCH o USING PRIMARY KEY (occurrence_id=?) LEFT-JOIN"
39+ _MANIFESTATION_LOOKUP = (
40+ "SEARCH sm USING INDEX source_manifestations_by_occurrence_key "
41+ "(manifestation_key=?) LEFT-JOIN"
42+ )
43+ _LIFECYCLE_LOOKUP = (
44+ "SEARCH lt USING INDEX evidence_lifecycle_by_session_order (session_id=?)"
45+ )
46+ _BOUND_HEAD_LOOKUP = "SEARCH bound_head USING PRIMARY KEY (singleton=?)"
47+ _BOUND_HEAD_EXISTS_LOOKUP = (
48+ "SEARCH bound_head EXISTS USING PRIMARY KEY (singleton=?)"
3549)
3650
3751
@@ -310,13 +324,13 @@ def _add_unrelated_lifecycle_history(
310324 connection .execute ("PRAGMA query_only = ON" )
311325
312326
313- def _explain_details (
327+ def _explain_rows (
314328 connection : sqlite3 .Connection ,
315329 view : str ,
316330 direction : str ,
317331 scope : Mapping [str , Any ],
318332 cursor_order : tuple [Any , ...] | None ,
319- ) -> tuple [str , ...]:
333+ ) -> tuple [tuple [ int , int , int , str ] , ...]:
320334 sql , parameters = evidence_service ._page_statement (
321335 view ,
322336 direction ,
@@ -326,11 +340,170 @@ def _explain_details(
326340 7 ,
327341 )
328342 return tuple (
329- str ( row [- 1 ] )
343+ ( int ( row [0 ]), int ( row [ 1 ]), int ( row [ 2 ]), str ( row [ 3 ]) )
330344 for row in connection .execute ("EXPLAIN QUERY PLAN " + sql , parameters )
331345 )
332346
333347
348+ def _assert_unique_manifestation_lookup (connection : sqlite3 .Connection ) -> None :
349+ indexes = connection .execute ("PRAGMA index_list(source_manifestations)" ).fetchall ()
350+ matching = [
351+ row
352+ for row in indexes
353+ if str (row [1 ]) == "source_manifestations_by_occurrence_key"
354+ ]
355+ assert len (matching ) == 1
356+ assert int (matching [0 ][2 ]) == 1
357+ columns = connection .execute (
358+ "PRAGMA index_info(source_manifestations_by_occurrence_key)"
359+ ).fetchall ()
360+ assert [str (row [2 ]) for row in columns ] == ["manifestation_key" ]
361+
362+
363+ def _is_session_branch_metadata (detail : str ) -> bool :
364+ return detail == _BOUND_HEAD_EXISTS_LOOKUP or (
365+ detail .startswith ("SCALAR SUBQUERY " )
366+ and detail .removeprefix ("SCALAR SUBQUERY " ).isdigit ()
367+ )
368+
369+
370+ def _descendants (
371+ rows : tuple [tuple [int , int , int , str ], ...],
372+ node_id : int ,
373+ ) -> tuple [tuple [int , int , int , str ], ...]:
374+ children_by_parent : dict [int , list [tuple [int , int , int , str ]]] = {}
375+ for row in rows :
376+ children_by_parent .setdefault (row [1 ], []).append (row )
377+ descendants : list [tuple [int , int , int , str ]] = []
378+ pending = [node_id ]
379+ while pending :
380+ parent = pending .pop ()
381+ children = sorted (children_by_parent .get (parent , ()), key = lambda row : row [0 ])
382+ descendants .extend (children )
383+ pending .extend (row [0 ] for row in children )
384+ return tuple (descendants )
385+
386+
387+ def _leftmost_branch (
388+ rows : tuple [tuple [int , int , int , str ], ...],
389+ node_id : int ,
390+ ) -> bool :
391+ rows_by_id = {row [0 ]: row for row in rows }
392+ children_by_parent : dict [int , list [tuple [int , int , int , str ]]] = {}
393+ for row in rows :
394+ children_by_parent .setdefault (row [1 ], []).append (row )
395+ current = node_id
396+ while current in rows_by_id :
397+ parent = rows_by_id [current ][1 ]
398+ siblings = sorted (children_by_parent .get (parent , ()), key = lambda row : row [0 ])
399+ if not siblings or siblings [0 ][0 ] != current :
400+ return False
401+ current = parent
402+ return True
403+
404+
405+ def _session_branch_parent_candidates (
406+ rows : tuple [tuple [int , int , int , str ], ...],
407+ ) -> tuple [int , ...]:
408+ children_by_parent : dict [int , list [tuple [int , int , int , str ]]] = {}
409+ for row in rows :
410+ children_by_parent .setdefault (row [1 ], []).append (row )
411+ candidates : list [int ] = []
412+ for parent , children in children_by_parent .items ():
413+ ordered = sorted (children , key = lambda row : row [0 ])
414+ details = tuple (row [3 ] for row in ordered )
415+ required = (
416+ _SESSION_LOOKUP ,
417+ _OCCURRENCE_LOOKUP ,
418+ _MANIFESTATION_LOOKUP ,
419+ )
420+ if any (details .count (detail ) != 1 for detail in required ):
421+ continue
422+ if details .count (_TEMP_SORT_MARKER ) > 1 :
423+ continue
424+ if any (
425+ detail not in required
426+ and detail != _TEMP_SORT_MARKER
427+ and not _is_session_branch_metadata (detail )
428+ for detail in details
429+ ):
430+ continue
431+ positions = {detail : details .index (detail ) for detail in required }
432+ if not (
433+ positions [_SESSION_LOOKUP ]
434+ < positions [_OCCURRENCE_LOOKUP ]
435+ < positions [_MANIFESTATION_LOOKUP ]
436+ ):
437+ continue
438+ marker_position = (
439+ details .index (_TEMP_SORT_MARKER )
440+ if _TEMP_SORT_MARKER in details
441+ else None
442+ )
443+ if marker_position is not None and marker_position <= positions [
444+ _MANIFESTATION_LOOKUP
445+ ]:
446+ continue
447+ metadata = [
448+ row
449+ for row in ordered
450+ if _is_session_branch_metadata (row [3 ])
451+ ]
452+ if len (metadata ) > 1 :
453+ continue
454+ for row in metadata :
455+ if row [3 ].startswith ("SCALAR SUBQUERY " ):
456+ descendants = _descendants (rows , row [0 ])
457+ assert len (descendants ) == 1
458+ assert descendants [0 ][3 ] == _BOUND_HEAD_LOOKUP
459+ candidates .append (parent )
460+ return tuple (candidates )
461+
462+
463+ def _assert_session_marker_branch_ownership (
464+ rows : tuple [tuple [int , int , int , str ], ...],
465+ ) -> None :
466+ candidates = _session_branch_parent_candidates (rows )
467+ assert len (candidates ) == 1 , (candidates , rows )
468+ session_parent = candidates [0 ]
469+ assert _leftmost_branch (rows , session_parent ), (session_parent , rows )
470+ markers = [row for row in rows if row [3 ] == _TEMP_SORT_MARKER ]
471+ if markers :
472+ assert markers [0 ][1 ] == session_parent , (session_parent , markers , rows )
473+
474+
475+ def _assert_page_plan_contract (
476+ connection : sqlite3 .Connection ,
477+ rows : tuple [tuple [int , int , int , str ], ...],
478+ * ,
479+ view : str ,
480+ cursor_order : tuple [Any , ...] | None ,
481+ ) -> None :
482+ details = tuple (row [3 ] for row in rows )
483+ assert not any (
484+ marker in "\n " .join (details ) for marker in _FORBIDDEN_PLAN_MARKERS
485+ ), details
486+ temp_markers = [detail for detail in details if "USE TEMP B-TREE" in detail ]
487+ markers = [row for row in rows if row [3 ] == _TEMP_SORT_MARKER ]
488+ allows_session_sort = (
489+ cursor_order is not None and view in _PORTABLE_SESSION_SORT_VIEWS
490+ )
491+ if view in _PORTABLE_SESSION_SORT_VIEWS :
492+ assert _LIFECYCLE_LOOKUP in details , details
493+ if not allows_session_sort :
494+ assert temp_markers == [], (view , cursor_order , details )
495+ return
496+
497+ assert temp_markers in ([], [_TEMP_SORT_MARKER ]), (view , cursor_order , details )
498+ assert len (markers ) <= 1 , (view , cursor_order , details )
499+ if not markers :
500+ _assert_session_marker_branch_ownership (rows )
501+ return
502+
503+ _assert_session_marker_branch_ownership (rows )
504+ _assert_unique_manifestation_lookup (connection )
505+
506+
334507def test_evidence_contract_is_closed_and_bounded () -> None :
335508 with pytest .raises (EvidenceContractError , match = "exactly one" ):
336509 EvidenceRequest ()
@@ -595,13 +768,103 @@ def test_first_and_deep_physical_plans_prune_unbounded_shapes(
595768 ):
596769 for direction in ("forward" , "backward" ):
597770 for cursor_order in (None , deep_order ):
598- details = _explain_details (connection , view , direction , scope , cursor_order )
599- assert not any (
600- marker in "\n " .join (details ) for marker in _FORBIDDEN_PLAN_MARKERS
601- ), (view , direction , cursor_order , details )
771+ rows = _explain_rows (connection , view , direction , scope , cursor_order )
772+ _assert_page_plan_contract (
773+ connection ,
774+ rows ,
775+ view = view ,
776+ cursor_order = cursor_order ,
777+ )
602778 connection .close ()
603779
604780
781+ def test_session_marker_branch_ownership_rejects_structural_mutations (
782+ tmp_path : Path ,
783+ ) -> None :
784+ connection , _case , selector = _published (tmp_path )
785+ try :
786+ scope = {
787+ "kind" : "session" ,
788+ "logical_id" : selector .partition (":" )[2 ],
789+ "start_us" : None ,
790+ "end_us" : None ,
791+ }
792+ deep_order = (0 , 0 , 0 , 0 , 0 , "activity:physical:00000" , 0 )
793+ rows = _explain_rows (
794+ connection ,
795+ "timeline" ,
796+ "forward" ,
797+ scope ,
798+ deep_order ,
799+ )
800+ markers = [row for row in rows if row [3 ] == _TEMP_SORT_MARKER ]
801+ if not markers :
802+ pytest .skip ("SQLite build emitted the portable plan marker-free" )
803+ marker = markers [0 ]
804+
805+ def assert_rejected (
806+ mutated : tuple [tuple [int , int , int , str ], ...],
807+ ) -> None :
808+ with pytest .raises (AssertionError ):
809+ _assert_page_plan_contract (
810+ connection ,
811+ mutated ,
812+ view = "timeline" ,
813+ cursor_order = deep_order ,
814+ )
815+
816+ calls_parent = next (
817+ row [1 ]
818+ for row in rows
819+ if row [3 ].startswith ("SEARCH mc USING INDEX" )
820+ )
821+ tools_parent = next (
822+ row [1 ]
823+ for row in rows
824+ if row [3 ].startswith ("SEARCH ti USING INDEX" )
825+ )
826+ lifecycle_parent = next (
827+ row [1 ] for row in rows if row [3 ] == _LIFECYCLE_LOOKUP
828+ )
829+ for foreign_parent in (calls_parent , tools_parent , lifecycle_parent , 0 ):
830+ assert_rejected (
831+ tuple (
832+ (row [0 ], foreign_parent , row [2 ], row [3 ])
833+ if row [0 ] == marker [0 ]
834+ else row
835+ for row in rows
836+ )
837+ )
838+
839+ assert_rejected (
840+ rows
841+ + ((max (row [0 ] for row in rows ) + 1 , marker [1 ], 0 , _TEMP_SORT_MARKER ),)
842+ )
843+ assert_rejected (
844+ tuple (
845+ (row [0 ], row [1 ], row [2 ], "SCAN o" )
846+ if row [3 ] == _OCCURRENCE_LOOKUP
847+ else row
848+ for row in rows
849+ )
850+ )
851+ ambiguous_rows = tuple (
852+ row
853+ for row in rows
854+ if row [1 ] != calls_parent
855+ or row [3 ]
856+ in {
857+ _SESSION_LOOKUP ,
858+ _OCCURRENCE_LOOKUP ,
859+ _MANIFESTATION_LOOKUP ,
860+ }
861+ or _is_session_branch_metadata (row [3 ])
862+ )
863+ assert_rejected (ambiguous_rows )
864+ finally :
865+ connection .close ()
866+
867+
605868def test_activity_scale_keeps_decode_and_work_bounded (
606869 tmp_path : Path ,
607870 monkeypatch : pytest .MonkeyPatch ,
@@ -618,7 +881,7 @@ def count_decodes(row: Mapping[str, Any]) -> dict[str, Any]:
618881 for count in (2_000 , 10_000 ):
619882 connection , case , selector = _published (tmp_path / f"activities-{ count } " )
620883 _add_synthetic_activities (connection , selector , count )
621- details = _explain_details (
884+ rows = _explain_rows (
622885 connection ,
623886 "timeline" ,
624887 "forward" ,
@@ -630,7 +893,12 @@ def count_decodes(row: Mapping[str, Any]) -> dict[str, Any]:
630893 },
631894 None ,
632895 )
633- assert not any (marker in "\n " .join (details ) for marker in _FORBIDDEN_PLAN_MARKERS )
896+ _assert_page_plan_contract (
897+ connection ,
898+ rows ,
899+ view = "timeline" ,
900+ cursor_order = None ,
901+ )
634902 callback_count = 0
635903
636904 def progress () -> int :
@@ -675,16 +943,19 @@ def test_foreign_lifecycle_history_does_not_change_session_page_work(
675943 }
676944 for direction in ("forward" , "backward" ):
677945 for cursor_order in (None , (0 , 0 , 0 , 0 , 0 , "activity:physical:00000" , 0 )):
678- details = _explain_details (
946+ rows = _explain_rows (
679947 connection ,
680948 "timeline" ,
681949 direction ,
682950 scope ,
683951 cursor_order ,
684952 )
685- assert not any (
686- marker in "\n " .join (details ) for marker in _FORBIDDEN_PLAN_MARKERS
687- ), (count , direction , cursor_order , details )
953+ _assert_page_plan_contract (
954+ connection ,
955+ rows ,
956+ view = "timeline" ,
957+ cursor_order = cursor_order ,
958+ )
688959 callback_count = 0
689960
690961 def progress () -> int :
@@ -709,8 +980,7 @@ def progress() -> int:
709980 connection .close ()
710981
711982 assert callback_counts [0 ] > 0
712- assert callback_counts [1 ] <= callback_counts [0 ] * 2
713- assert callback_counts [2 ] <= callback_counts [0 ] * 2
983+ assert callback_counts [1 :] == [callback_counts [0 ], callback_counts [0 ]]
714984
715985
716986def test_rate_card_pages_remain_valid_empty_and_query_only (
0 commit comments