Skip to content

dispatcher: retire Tier-4 legacy resolve_slot + add public SlotManager.state()#649

Draft
thinmintdev wants to merge 2 commits into
mainfrom
afk/624-retire-tier4
Draft

dispatcher: retire Tier-4 legacy resolve_slot + add public SlotManager.state()#649
thinmintdev wants to merge 2 commits into
mainfrom
afk/624-retire-tier4

Conversation

@thinmintdev

Copy link
Copy Markdown
Contributor

Closes #624

Summary

  • Delete dispatcher/proxy.py and the legacy Tier-4 resolution block from Dispatcher.dispatch(). Tiers 1-3 (registry → passthrough → cold-cache prefetch) now raise NoRouteFound directly when all miss. Image-gen and embed models must have explicit registry bindings (the seed haloai_models.json already has them).
  • Add SlotManager.state(name) -> SlotState as a public method; switch both Dispatcher call sites off the private _current_state() leak (router.py:669,704).
  • Document the two orthogonal routing axes in ARCHITECTURE.md: Dispatcher = model-id→URL transport; route_for_request = capability→slot selection. Clarify that omni_router/route_for_request are untouched and load-bearing.
  • Update all SlotManager stubs in tests to expose .state() matching the new public API.

Files changed

File Change
src/hal0/dispatcher/proxy.py Deleted — 132 lines of legacy heuristics
src/hal0/dispatcher/router.py Remove Tier-4 block + import; update docstring; switch _current_statestate at both call sites
src/hal0/slots/manager.py Add public state() method (delegates to _current_state)
ARCHITECTURE.md Two-axes routing note; fix stale MoonshineProvider/KokoroProvider refs
tests/dispatcher/test_image_routing.py Deleted — tested resolve_slot directly, now gone
tests/dispatcher/test_router.py Rewrite 3 legacy-fallback tests → NoRouteFound assertions; add state() to _FakeSlotManager
tests/dispatcher/test_serving_integration.py Add state() to _RecordingSlotManager
tests/dispatcher/test_composite_dispatch.py Add state() to _OfflineSlotManager
tests/slots/test_manager.py Add 3 targeted tests for SlotManager.state()
tests/api/test_v1_chat_slot_alias.py Remove test that called the now-deleted resolve_slot

Test results

tests/dispatcher/ + tests/slots/ → 233 passed, 0 failed
tests/capabilities/             → 34 passed

Rebase note

This branch is independent of #647 (voice provider retirement) in code — both touch ARCHITECTURE.md, so a rebase conflict will land only there if #647 merges first. No dispatcher or manager.py overlap between the two branches.

🤖 Generated with Claude Code

thinmintdev and others added 2 commits June 7, 2026 17:41
…r.state() (closes #624)

Remove the legacy path/name heuristics (proxy.py) from the Dispatcher's
four-tier resolution order. Tiers 1-3 (registry → passthrough → cold-cache
prefetch) now raise NoRouteFound directly on a miss; image-gen and embed
models must have explicit registry bindings. Delete proxy.py and
test_image_routing.py; update all SlotManager stubs in tests to expose
.state() alongside the private ._current_state().

Add SlotManager.state(name) -> SlotState as a public API; switch both
Dispatcher sites off the private _current_state() leak. Document the two
orthogonal routing axes in ARCHITECTURE.md: Dispatcher = model-id→URL
transport; route_for_request = capability→slot selection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@thinmintdev thinmintdev left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REVIEWER VERDICT: APPROVE — conditional on green CI (python checks pending; the rewritten dispatcher/integration tests are what validate this — certify on green). Sharp correctness read below; this is a clean Tier-1 refactor.

SPEC (#624): Deletes dispatcher/proxy.py (Tier-4 resolve_slot + LegacyResolutionFailed); adds public SlotManager.state(); 3-tier docstring rewrite; ARCHITECTURE two-axes note (transport vs capability). The proxy.py "do not delete until post-v0.2" caveat is now satisfied (v0.3 shipped; #624 sanctions it).

(a) No live importers — VERIFIED. Zero references to dispatcher.proxy/resolve_slot/LegacyResolutionFailed in src/ except the explanatory comment at router.py:548. proxy.py fully deleted, import removed from router.py.

(b) NoRouteFound preserves behavior — no silent route-drop. VERIFIED, and it is STRICTLY LOUDER than before. Old Tier-4 rule-7 silently routed any unmatched model → primary (a silent misroute: unknown/embed/image requests got misdelivered to the chat slot). New code raises NoRouteFound when Tiers 1-3 miss. So the deleted path WAS the silent drop; removing it eliminates the misroute. Two load-bearing confirmations:

  • Capability axis intact: omni_router/route_for_request untouched by this PR (only documented); live wiring in api/__init__.py:1086-1115 unchanged.
  • Image-gen unaffected: /v1/images/generations (v1.py:995) calls get_provider("comfyui").infer() directly — it does NOT pass through Dispatcher.dispatch() tier resolution, so the deleted /images/* path-pins were redundant for the live flow. No image regression.
  • New operator-facing contract (note for merge-gate): bare-slot-name + path-pin addressing is gone — image/embed models must have explicit registry bindings. The rewritten test_router.py asserts exactly this (/embeddings no-binding → NoRouteFound). The integration tests (test_serving_integration, test_composite_dispatch) confirm the runtime binding path — hence the on-green condition.

(c) SlotManager.state() delegation + coverage — VERIFIED. state() is a correct one-line delegate to _current_state (manager.py:317). Dispatcher now calls .state() EXCLUSIVELY (router.py:630, 665); every remaining _current_state caller is internal to manager.py — decoupling is complete. 3 new tests: offline-unknown, post-transition, agrees-with-private.

(d) Rewritten legacy-fallback tests assert correct new behavior — VERIFIED. The 3 router tests flip from resolution_path == "legacy_slot:*" to pytest.raises(NoRouteFound) + dispatch.no_route; the __cause__ is LegacyResolutionFailed assertion is removed; the legacy-only test_image_routing.py + the proxy non-regression test in test_v1_chat_slot_alias.py are deleted. Mock SlotManagers across 3 test files gained state() alongside _current_state. Coherent.

Merge-ready on green. ARCHITECTURE.md overlaps #647 — see merge-order flag in report.

@thinmintdev thinmintdev marked this pull request as draft June 7, 2026 22:11
@thinmintdev

Copy link
Copy Markdown
Contributor Author

Converted to draft — blocked: the audit premise (#624 “Tier-4 is legacy”) does not survive the test suite.

Removing Tier-4 resolve_slot turns audio/image happy-paths red: tests/api/test_v1_audio.py + test_v1_images.py now return dispatch.no_route for moonshine-small-streaming-en / sdxl-turbo (main was green). That proves Tier-4 was the capability-model→slot resolver for audio/image dispatch — load-bearing, not legacy.

Retiring it requires migrating that resolution into tiers 1–3 first (a routing feature), not deleting it. The happy-path assertions are the spec and must stay 200 — do not weaken them. Holding for a maintainer decision on the migration path.

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.

dispatcher: retire Tier-4 legacy resolve_slot + add public SlotManager.state(); document the two routing axes

1 participant