Skip to content
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 into
mainfrom
feat/kora-KR-P2-I-integration-st3-provider-wire
May 21, 2026
Merged

feat(KR-P2-I-integration ST3): boot-time wire-in of operational-state holder#34
rafe-walker merged 1 commit into
mainfrom
feat/kora-KR-P2-I-integration-st3-provider-wire

Conversation

@rafe-walker

Copy link
Copy Markdown
Owner

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 to main after ST2 merges.

After this PR lands, every Kora boot:

  1. Constructs the singleton OperationalStateHolder with OperationalState(primary_state=BOOTING, claim_permission=NONE).
  2. Registers the chain-event emit listener bound to the live IsoKronMemoryProvider.
  3. Transitions BOOTING → READY with trigger "all §9.2 gates pass" — which fires both kora.operational_state.transitioned (generic) and kora.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 via connection.submit_and_wait(...) — agent_init.py is synchronous, so we can't await from there; the dedicated loop pattern matches how _refresh_capability_matrix_from_mcp already 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 in BOOTING.
  • connection.submit_and_wait raises → warning, holder stays in BOOTING.

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 site

After agent._memory_manager.initialize_all(**_init_kwargs) succeeds, the IsoKron provider is looked up via agent._memory_manager.get_provider("isokron") and wire_operational_state(provider) runs. Outer try/except around the import so a wire-in module-import failure doesn't break boot either.

Honest scope

  • Unconditional BOOTING → READY — KR-P2-H follow-on adds the §9.2 gate-check guard that may instead route to STOPPED based on invariant gate results. KR-P2-H is blocked on substrate-round Bucket C event vocab.
  • No idempotence in the public APIinit_holder is first-wins per ST1, but a second wire_operational_state call will add a SECOND listener and re-attempt the transition. Production callers run it once per boot; tests cover the second-call shape explicitly.
  • No multi-Kora-instance support — singleton holder pattern, Phase 3 per spec §4.

Tests — tests/test_operational_state_wire.py (219 LOC)

4 cases, each with a deliberately small _FakeConnection that runs the coro on the test's event loop and wraps results in a concurrent.futures.Future:

  • Happy path: holder ends in READY with ClaimPermission.NORMAL; exactly two emits with the right event types in order (kora.operational_state.transitioned, then kora.boot.ready); payload trigger matches the canonical TRANSITION_TABLE wording.
  • No-connection branch: holder created in BOOTING, no emits, [kora.operational_state.wire_in] … no _connection warning logged.
  • Submit-raises branch: RuntimeError("Sea MCP unavailable") from submit_and_wait does NOT propagate; holder stuck in BOOTING; [kora.operational_state.wire_in] wire-in raised … warning logged.
  • Idempotence: second wire_operational_state call registers a second listener and triggers the transition again; state stays at READY (same-state bypass via transition_to); generic event emits twice (once per listener); kora.boot.ready does NOT re-fire because from != BOOTING on the second call.

Sub-task chain

ST PR Status
ST1 #32 open
ST2 #33 open (stacked on ST1)
ST3 this PR open (stacked on ST2)
ST4 TBD depends on ST1 + KR-P2-E ST1 merged
ST5 TBD depends on ST1

Test plan

  • CI green on pytest tests/test_operational_state_wire.py
  • Manual smoke: boot a real Kora against a live IsoKron substrate, grep flyctl logs for [kora.operational_state.wire_in] BOOTING → READY transitioned and the two chain events in the substrate event_log
  • If the substrate is unreachable at boot, confirm the boot proceeds and [kora.operational_state.wire_in] wire-in raised shows in the log (don't block boot — provider is functional, just observability is degraded)

🤖 Generated with Claude Code

@rafe-walker rafe-walker force-pushed the feat/kora-KR-P2-I-integration-st2-emit branch from b4ba4d3 to 78e64ea Compare May 21, 2026 20:57
@rafe-walker rafe-walker changed the base branch from feat/kora-KR-P2-I-integration-st2-emit to main May 21, 2026 20:57
…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>
@rafe-walker rafe-walker force-pushed the feat/kora-KR-P2-I-integration-st3-provider-wire branch from 7d571d8 to 244f373 Compare May 21, 2026 20:57
@rafe-walker rafe-walker merged commit 5341f7e into main May 21, 2026
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>
@rafe-walker rafe-walker deleted the feat/kora-KR-P2-I-integration-st3-provider-wire branch May 21, 2026 20:58
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant