# Test: compute_segmentation_urls produces the URL pattern that is the public contract
# of init_segmentation_routers. End-to-end parity (compute_segmentation_urls(prefix) matches
# the URLs populated by init_segmentation_routers(prefix)) is enforced by structural
# verification here: both functions derive URLs from the same {prefix}/card_stack and
# {prefix}/workflow sub-prefixes plus the same route function names. Any divergence in
# either function — sub-prefix change, route name change, new route added without
# corresponding compute_segmentation_urls update — would surface in segment-align's
# host-side integration tests where the wrapper is bound through wrapped_handlers.
test_prefix = "/test_workflow/seg"
test_urls = compute_segmentation_urls(test_prefix)
# Workflow routes
assert test_urls.init == "/test_workflow/seg/workflow/init"
assert test_urls.split == "/test_workflow/seg/workflow/split"
assert test_urls.merge == "/test_workflow/seg/workflow/merge"
assert test_urls.undo == "/test_workflow/seg/workflow/undo"
assert test_urls.reset == "/test_workflow/seg/workflow/reset"
assert test_urls.ai_split == "/test_workflow/seg/workflow/ai_split"
# Split-mode routes (under card_stack prefix)
assert test_urls.enter_split == "/test_workflow/seg/card_stack/enter_split"
assert test_urls.exit_split == "/test_workflow/seg/card_stack/exit_split"
# Card stack routes
assert test_urls.card_stack.nav_up == "/test_workflow/seg/card_stack/nav_up"
assert test_urls.card_stack.nav_down == "/test_workflow/seg/card_stack/nav_down"
assert test_urls.card_stack.nav_first == "/test_workflow/seg/card_stack/nav_first"
assert test_urls.card_stack.nav_last == "/test_workflow/seg/card_stack/nav_last"
assert test_urls.card_stack.nav_page_up == "/test_workflow/seg/card_stack/nav_page_up"
assert test_urls.card_stack.nav_page_down == "/test_workflow/seg/card_stack/nav_page_down"
assert test_urls.card_stack.nav_to_index == "/test_workflow/seg/card_stack/nav_to_index"
assert test_urls.card_stack.update_viewport == "/test_workflow/seg/card_stack/update_viewport"
assert test_urls.card_stack.save_width == "/test_workflow/seg/card_stack/save_width"init
compute_segmentation_urls
def compute_segmentation_urls(
prefix:str, # Base prefix for segmentation routes (e.g., "/workflow/seg")
)->SegmentationUrls: # Fully populated URL bundle (no routes registered)
Compute the URL bundle for segmentation routes from a prefix.
Returns the same SegmentationUrls that init_segmentation_routers(prefix=prefix) would populate, without registering any routes. Lets consumers (e.g., page-centric libraries that wrap segmentation) build wrapped handlers BEFORE calling init_segmentation_routers, resolving the chicken-and-egg dependency between URL bundle and wrapper construction.
The URL conventions encoded here are the public contract of init_segmentation_routers and stay synchronized via the test cell below.
Router Assembly
init_segmentation_routers
def init_segmentation_routers(
state_store:SQLiteWorkflowStateStore, # The workflow state store
workflow_id:str, # The workflow identifier
source_service:SourceService, # Service for fetching source blocks
segmentation_service:SegmentationService, # Service for NLTK sentence splitting
prefix:str, # Base prefix for segmentation routes (e.g., "/workflow/seg")
max_history_depth:int=50, # Maximum history stack depth
wrapped_handlers:Dict=None, # Dict with 'init', 'split', 'merge', 'undo', 'reset', 'ai_split' keys
urls:Optional=None, # Pre-populated URL bundle (e.g., from compute_segmentation_urls); created if None
)->Tuple: # (routers, urls, merged_routes)
Initialize and return all segmentation routers with URL bundle.
The wrapped_handlers dict should contain handlers that already have cross-domain concerns (KB system, alignment status) handled by the combined layer’s wrapper factories.
The urls parameter accepts a pre-populated SegmentationUrls (typically from compute_segmentation_urls(prefix)) so consumers can build wrapped handlers that need URLs before init_segmentation_routers runs. Default behavior (urls=None) is unchanged from before — an empty SegmentationUrls is created internally and populated after routes are registered.