This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(KR-P2-CLEANUP ST2): SEA-PANEL flip-to-real + active_provider singleton#57
Merged
Merged
Conversation
…gleton
Flips /api/sea-tickets/kora-assigned from the hardcoded stub to a
live read against the gateway-level IsoKronMemoryProvider, with a
graceful fallback to the stub shape (+ ``error`` field) when no
active provider is registered.
Shared infrastructure:
- New ``plugins/memory/isokron/active_provider.py`` —
process-wide singleton accessor for the live, initialized
IsoKronMemoryProvider. Mirrors the operational_state_holder
pattern. Set once at gateway boot
(sea_ticket_poller_lifecycle.build_and_start_sea_ticket_poller);
read by cross-cutting endpoints (this ST + ST3/ST4 will share).
- sea_ticket_poller_lifecycle.py now calls set_active_provider
after provider.initialize() succeeds (fail-soft: a registration
failure logs but doesn't break the poller startup).
SEA-PANEL read:
- New ``plugins/memory/isokron/assigned_sea_tickets.py`` —
queries public.tickets for sea-tickets assigned to Kora's
actor_id, groups in Python into the four panel buckets
(in_progress / queued / recently_resolved / failed_or_blocked).
Shape matches the v1 stub exactly so the cockpit panel renders
without changes.
- get_assigned_sea_tickets_via_provider() resolves the actor_id
lazily via actor_registry JOIN (mirrors the poller's helper),
runs the read on the IsoKron dedicated I/O loop, projects rows
into the panel-shaped buckets. Returns None on any failure;
caller falls back to stub + error.
Endpoint (kora_cli/web_server.py):
- GET /api/sea-tickets/kora-assigned now reads
get_active_provider() + delegates to the helper. Live branch
returns real data WITHOUT ``stub`` flag (cockpit auto-stops
rendering the stub banner). Uninitialized branch returns
stub-shape with ``stub:True`` + ``error`` naming the cause
(cockpit renders a distinct banner so operators can tell
"no provider yet" from cold-stub).
- Stub block extracted into _kora_assigned_sea_tickets_stub
helper so both fallback paths share the same shape.
# Honest scope — panel-side richness deferred
The stub returned a few cockpit-friendly fields the substrate
doesn't directly carry on tickets:
- model_tier_used — requires a kora.sea_ticket.resolved
chain-event JOIN per row; ST2 returns None and the panel
renders "unknown". A future "denormalize last model_tier
onto tickets" can backfill.
- failure_count_by_reason — requires an event_log aggregation;
ST2 returns {}.
Both are non-blocking for cockpit rendering — the absence is
visible but doesn't break the page.
Tests (tests/kora_cli/test_web_server_sea_tickets.py): full rewrite
covering both branches:
- Uninitialized branch: stub-shape + stub:True + error field +
resolution-enum sanity
- Live branch: monkeypatch the helper to return canned data;
assert real data passes through + neither stub nor error
appears
- Live read returning None: falls back to stub with the
"substrate read returned None" error message
- claim_fence_token never appears in either branch (regression
guard from §4 non-scope)
- active_provider singleton set/get/clear round-trip
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
KR-P2-CLEANUP ST2 of 5. Flips
/api/sea-tickets/kora-assignedfrom the hardcoded stub to a live read against the gateway-levelIsoKronMemoryProvider, with a graceful fallback to the stub shape (+errorfield) when no active provider is registered.Shared infrastructure (also used by ST3 + ST4)
plugins/memory/isokron/active_provider.py(new)Process-wide singleton accessor for the live
IsoKronMemoryProvider. Mirrors theoperational_state_holderpattern: one initialized provider per process, accessed by anything that needs to read substrate state without going through an agent session.set_active_provider(provider)— called once at gateway bootget_active_provider() -> Optional[IsoKronMemoryProvider]— read by cross-cutting endpointsclear_active_provider()— test-only resetWhy not lazy
load_memory_provider("isokron")per request? The plugin loader returns a fresh uninitialized instance each call (the plugin'sregister(ctx)constructs anew). Initializing per request would open new asyncpg pools + MCP transports each time. The singleton holds the ALREADY-initialized provider for re-use.sea_ticket_poller_lifecycle.py— set hookAfter
provider.initialize()succeeds,set_active_provider(provider)runs. Fail-soft: a registration failure logs but doesn't break the poller startup.SEA-PANEL read
plugins/memory/isokron/assigned_sea_tickets.py(new, 273 LOC)Queries
public.ticketsfor sea-tickets assigned to Kora's actor_id, groups client-side into the four panel buckets:in_progresssea_active_claim_token IS NOT NULLqueuedrecently_resolvedsea_status IN ('completed', 'released', 'failed_retryable')(capped at 20)failed_or_blockedsea_status IN ('failed_terminal', 'blocked_needs_operator')get_assigned_sea_tickets_via_provider()resolves the actor_id lazily viaactor_registryJOIN (mirrors the poller's helper), runs the read on the IsoKron dedicated I/O loop. ReturnsNoneon any failure; caller falls back to stub + error.Endpoint flip —
kora_cli/web_server.pystubflag (cockpit auto-stops rendering the stub banner).stub: True+errorfield naming the cause (cockpit renders a distinct banner — "no provider yet" vs cold-stub).Stub block extracted into
_kora_assigned_sea_tickets_stub(error=...)so both fallback paths share the same shape.Honest scope — deferred fields
The stub returned a couple of cockpit-friendly fields the substrate doesn't carry on
tickets:model_tier_used— requires akora.sea_ticket.resolvedchain-event JOIN per row. ST2 returnsNone; panel renders "unknown". A future "denormalize last model_tier onto tickets" change can backfill.failure_count_by_reason— requires anevent_logaggregation. ST2 returns{}.Both non-blocking for cockpit rendering — the absence is visible but doesn't break the page.
Tests —
tests/kora_cli/test_web_server_sea_tickets.py(full rewrite, 258 LOC)Both branches:
stub:True+errorfield + resolution-enum sanitystubnorerrorfield appearsNone: falls back to stub with the"substrate read returned None"errorSub-task chain (this bucket — all independent)
active_providershipped here)active_provideronce this landsactive_provideronce this landsTest plan
pytest tests/kora_cli/test_web_server_sea_tickets.py/api/sea-tickets/kora-assignedreturns live data + stub banner stops renderingerrorfield; panel renders the distinct uninitialized-state banner🤖 Generated with Claude Code