This repository was archived by the owner on May 26, 2026. It is now read-only.
feat(KR-P2-I-integration ST3): boot-time wire-in of operational-state holder#34
Merged
rafe-walker merged 1 commit intoMay 21, 2026
Merged
Conversation
Merged
3 tasks
b4ba4d3 to
78e64ea
Compare
…tate holder
agent/operational_state_wire.py (new): wire_operational_state(provider)
is the one boot-time call that:
1. init_holder(OperationalState(primary_state=BOOTING,
claim_permission=NONE)) — module-level singleton, idempotent.
2. holder.add_listener(make_emit_listener(provider)) — registers
the chain-event listener from ST2.
3. Triggers BOOTING → READY via the connection's submit_and_wait
(agent_init.py is synchronous; we run the coro on the IsoKron
dedicated IO loop and block boot until the emit listener returns).
Per spec §3 ST3: the transition is UNCONDITIONAL in v1. KR-P2-H
follow-on adds the §9.2 invariant-gate-check guard that decides
whether to transition READY or fall through to STOPPED — that
bucket is blocked on substrate-round Bucket C event vocab and
ships later.
Fail-soft posture: every error here is caught + logged with the
greppable [kora.operational_state.wire_in] tag. Operator boot
proceeds even if holder construction or emit raises — only the
observability surface is degraded. KR-P2-H will revisit which
failures should block boot.
agent/agent_init.py: after agent._memory_manager.initialize_all
succeeds, look up the IsoKron provider and call
wire_operational_state(provider). Wrapped in a defensive
try/except so a wire-in module-import failure also doesn't break
boot.
Tests (tests/test_operational_state_wire.py): 4 cases covering
the happy path (holder in READY + generic + boot.ready emits with
the canonical "all §9.2 gates pass" trigger), the no-connection
branch (holder created in BOOTING + greppable WARNING), the
submit-raises branch (wire-in stays fail-soft + WARNING), and
idempotence (second call with second listener doubles the per-
transition emit but state stays at READY).
Stacked on top of ST1 (#32) + ST2 (#33). ST4 wires SeaTicketPoller
claim/release (depends on KR-P2-E ST1 merged), ST5 flips the
/api/operational-state endpoint from stub to read get_holder().
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7d571d8 to
244f373
Compare
rafe-walker
pushed a commit
that referenced
this pull request
May 21, 2026
…l + add history ring
OperationalStateHolder (ST1 module) now retains the last 10
transitions in an in-memory deque ring buffer:
- TransitionRecord dataclass carries (timestamp, from_state,
to_state, trigger).
- Appended under the holder lock at the end of transition_to
(after the held-state swap) so ordering matches the swap.
- holder.history(limit=N) returns a list of dicts ready for the
/api/operational-state JSON payload.
- Bounded at 10 — durable history lives in the chain-event log
(every transition writes kora.operational_state.transitioned
via ST2's emit listener); this ring is for the admin panel's
immediate-history view and dies on process restart.
kora_cli/web_server.py — endpoint flipped:
- Now reads agent.operational_state_holder.get_holder().
- When holder is initialized: returns the live state with
transition_history sourced from holder.history(limit=10),
valid_next_states from transitions_from(state.primary_state),
and the ``stub`` flag DROPPED — CC#2's admin panel auto-stops
rendering its stub banner.
- When holder is None (boot incomplete or IsoKron provider
failed): returns the stub-shape with stub:True PLUS an
``error`` field naming the cause, so the panel can distinguish
"no wire-in yet" from "real runtime is up".
Tests (tests/test_operational_state_endpoint.py): 8 cases covering
the ring mechanics (append, bound at 10, limit clamp, ISO-8601 UTC
timestamp, TransitionRecord shape), the uninitialized-holder
endpoint branch (stub + error), the live endpoint branch (no stub
key, payload keys match pre-flip shape, transition_history and
valid_next_states populated from the holder + table), and the
sorted-degradation_reasons cockpit-diff stability invariant.
Stacked on ST3 (#34); base is feat/kora-KR-P2-I-integration-st3-provider-wire.
Retarget to main once ST3 merges. ST4 (SeaTicketPoller wire-in)
is independently blocked on KR-P2-E ST1 having landed — STOP gate
filed in chat, not in this PR's scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 tasks
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-I-integration ST3 of 5. Wires the holder (ST1) + emit listener (ST2) into the agent boot path. Stacked on top of #33 (ST2) — base is
feat/kora-KR-P2-I-integration-st2-emit. Retarget tomainafter ST2 merges.After this PR lands, every Kora boot:
OperationalStateHolderwithOperationalState(primary_state=BOOTING, claim_permission=NONE).IsoKronMemoryProvider.BOOTING → READYwith trigger"all §9.2 gates pass"— which fires bothkora.operational_state.transitioned(generic) andkora.boot.ready(informational) on the substrate.agent/operational_state_wire.py(new, 121 LOC)wire_operational_state(provider)is the one synchronous call agent_init.py needs. It runs the async transition on the IsoKron dedicated I/O loop viaconnection.submit_and_wait(...)— agent_init.py is synchronous, so we can'tawaitfrom there; the dedicated loop pattern matches how_refresh_capability_matrix_from_mcpalready works.Fail-soft. Every error inside the wire-in is caught and logged with the greppable
[kora.operational_state.wire_in]tag. Two failure modes:provider._connection is None→ warning, holder stays inBOOTING.connection.submit_and_waitraises → warning, holder stays inBOOTING.Boot proceeds in both cases — only operational-state observability is degraded. KR-P2-H follow-on revisits which failures should block boot (e.g. invariant-gate failures must block per R4.1 §9.2).
agent/agent_init.py— call siteAfter
agent._memory_manager.initialize_all(**_init_kwargs)succeeds, the IsoKron provider is looked up viaagent._memory_manager.get_provider("isokron")andwire_operational_state(provider)runs. Outer try/except around the import so a wire-in module-import failure doesn't break boot either.Honest scope
BOOTING → READY— KR-P2-H follow-on adds the §9.2 gate-check guard that may instead route toSTOPPEDbased on invariant gate results. KR-P2-H is blocked on substrate-round Bucket C event vocab.init_holderis first-wins per ST1, but a secondwire_operational_statecall will add a SECOND listener and re-attempt the transition. Production callers run it once per boot; tests cover the second-call shape explicitly.Tests —
tests/test_operational_state_wire.py(219 LOC)4 cases, each with a deliberately small
_FakeConnectionthat runs the coro on the test's event loop and wraps results in aconcurrent.futures.Future:READYwithClaimPermission.NORMAL; exactly two emits with the right event types in order (kora.operational_state.transitioned, thenkora.boot.ready); payload trigger matches the canonicalTRANSITION_TABLEwording.BOOTING, no emits,[kora.operational_state.wire_in] … no _connectionwarning logged.RuntimeError("Sea MCP unavailable")fromsubmit_and_waitdoes NOT propagate; holder stuck inBOOTING;[kora.operational_state.wire_in] wire-in raised …warning logged.wire_operational_statecall registers a second listener and triggers the transition again; state stays atREADY(same-state bypass viatransition_to); generic event emits twice (once per listener);kora.boot.readydoes NOT re-fire becausefrom != BOOTINGon the second call.Sub-task chain
Test plan
pytest tests/test_operational_state_wire.pyflyctl logsfor[kora.operational_state.wire_in] BOOTING → READY transitionedand the two chain events in the substrate event_log[kora.operational_state.wire_in] wire-in raisedshows in the log (don't block boot — provider is functional, just observability is degraded)🤖 Generated with Claude Code