Orchestrata Ultimate › Plans › volk-comms-heartbeat-design
Volk↔Volk Messaging + Orchestrator↔Volk Heartbeat/Status Awareness — Design
Source: plans/volk-comms-heartbeat-design.md at 8a2cb42ad · last changed 2026-08-19 · first committed 2026-08-05 · 443 lines.
Prose pending.
Plan text
Volk↔Volk Messaging + Orchestrator↔Volk Heartbeat/Status Awareness — Design
Status: DESIGN ONLY (RUL-022 — no implementation in this artifact) Branch: feat/cxp-direct-transport · Date: 2026-08-05 · Author: analyst (design phase) Epic: Communication: volk<->volk messaging + orchestrator<->volk with real-time status/heartbeat awareness
Every as-built claim below was grounded via the AST toolkit
(scripts/serialize_codebase_ast.py {symbols,refs,source}), never by hand-reading
.py files. Specs are authoritative (RUL-013). Rulings consulted: RUL-002 (no
mocks), RUL-003 (env from profile, injected seams, no endpoint defaults),
RUL-011 (ports from env), RUL-020 (async/sync contract), RUL-022 (design before
build), RUL-023 (feature means wired).
1. AS-BUILT INVENTORY
1.1 Volk↔volk messaging path (message_volk → Postman → handler)
| Component | Location | Facts (AST-verified) |
|---|---|---|
message_volk tool |
agents/tool_registry.py:1171-1225 (ToolExecutor._message_volk) |
Builds a real CXPPacket (from_agent/to_agent/subject/payload/thread_id, WorkStatus.IN_PROGRESS), pre-checks recipient health via self._volk_registry.get_volk_health(to_volk) (line 1191 — the ONLY production caller of get_volk_health per refs), appends a [WARNING: … unhealthy] suffix but still attempts delivery, then await self._postman.aroute(packet). A None reply is reported as success=True, "Message delivered … (no reply)" (lines 1213-1219). |
Postman.aroute |
postman/postman.py:331-351 |
In-process handler-table delivery: destination in self._handlers → call handler (await if coroutine), persist packets. Returns None for ALL of: no handler registered, handler raised, handler returned None. @traces(spec="001", story="FR-002", reqs=["REQ-001-US-002-001"]). |
| Handler registration | postman/postman.py:202 (Postman.register) ← orchestrator/pipeline_runner.py:764 (PipelineRunner._register_handlers) |
Stage volks' handlers are registered per-alias; the same handler table serves both orchestrator dispatch and peer message_volk traffic. _register_handlers also calls volk.set_volk_registry(...) at pipeline_runner.py:786 — the ONLY caller of set_volk_registry in the tree (per refs). |
| Orchestrator dispatch | orchestrator/intent_router.py:259-342 (IntentDispatcher.dispatch) |
Postman.route_sync via asyncio.to_thread (postman/sync_router.py:55); None response = "delivery failed: no route" → DispatchResult(success=False) (honest, unlike message_volk). |
| Tool wiring seam | agents/volks/base.py:351+ (BaseAgent._call_llm_with_tools) constructs ToolExecutor(workspace, postman=self._postman, from_alias=…, thread_id=…, volk_registry=self._volk_registry, …) at base.py:~423 |
_volk_registry is set only by BaseAgent.set_volk_registry (base.py:119). |
| CXP transport factory | cxp/transport.py:79-87 (cxp_transport(env)), create_cxp_server:90, create_cxp_client:110 |
Env-injectable seam (`env: Mapping[str,str] |
| Existing tests | tests/agents/test_tool_messaging.py (message_volk routes a real CXP packet between volks); tests/cxp/test_direct_transport.py, tests/cxp/test_transport.py |
Cover happy-path delivery + transport selection. |
1.2 Existing heartbeat (activity-based, dispatch-time)
refs record_heartbeat — exactly two production write sites, both activity-based:
orchestrator/intent_router.py:296-312(insideIntentDispatcher.dispatch): after a successfulroute_syncdelivery,getattr(self._registry, "record_heartbeat", None)→ call if callable, swallow all failures. In-code comment states verbatim: "IVolkRegistry does not declare record_heartbeat, so guard for its presence" and "A periodic push-heartbeat from volks themselves is a future extension."@traces(spec="001", reqs=["REQ-001-018"])+@traces(spec="003", story="US-004", reqs=["REQ-003-US-004-010"]).orchestrator/pipeline_runner.py:1004(insidePipelineRunner._run_stage): directself.volk_registry.record_heartbeat(stage_spec.alias)after a successful stage dispatch/response, in a try/except debug-log guard.
Registry side (kernel/volk_registry.py):
VolkRegistry.record_heartbeat(1021-1053): setslast_heartbeat=now, transitions toVolkStatus.ACTIVEunderself._lock, best-effort Redis persist (@traces reqs=["REQ-001-018","REQ-001-017"]).VolkRegistry.get_volk_health(968-1019): computesheartbeat_age_seconds; when age >heartbeat_timeout_seconds(ctor default 60,volk_registry.py:364) it lazily flips the volk to INACTIVE and reportsis_healthy=False(@traces reqs=["REQ-001-018"]). ReturnsVolkHealth(volk_registry.py:198).- Constructor already exposes an observer seam:
on_registration: Callable[[VolkRegistration], None] | None(364). add_notification_handler(762): zero imports, zero uses in the entire tree (perrefs).
Interface landscape (the duck-typing root cause):
plugin/interfaces.py:21-54—IVolkRegistry(Protocol): onlyregister_volk+unregister. This is the type annotation onIntentRouter.__init__andIntentDispatcher.__init__(intent_router.py:17,74,255).core/protocols/decoupling.py:63-103—VolkRegistryProtocol(Protocol): DOES declarerecord_heartbeat(91) andget_volk_health(94), but is imported only bycore/interfaces,core/protocols/__init__, andkernel/pipeline.py— not by intent_router or tool_registry, which duck-type viagetattr(intent_router.py:83-97, 305-307;tool_registry.py:1242-1245).
Health-aware routing consumer: IntentRouter._is_volk_healthy (intent_router.py:82-97)
_prefer_healthy_candidates(100) deprioritize unhealthy candidates (@traces spec="003" story="US-004" reqs=["REQ-003-US-004-006","REQ-003-US-004-019"]).
Existing test: tests/orchestrator/test_dispatch_heartbeat.py — 3 tests, all
@traces(spec="001", story="US-001", reqs=["REQ-001-018"]):
test_dispatch_records_heartbeat_for_registered_volk,
test_dispatch_to_unhandled_target_fails_without_heartbeat,
test_dispatch_heartbeat_reflects_recent_activity.
1.3 Status surfaces (two DISJOINT ones)
Surface A — heartbeat health (VolkRegistry): VolkHealth via get_volk_health;
consumed by _is_volk_healthy (routing) and the two tools:
get_volk_statustool (agents/tool_registry.py:1227-1276,ToolExecutor._get_volk_status): getattr-probesget_volk_health/is_registeredon the bound registry, formats status/healthy/heartbeat-age/error. Degrades to "no registry bound to this run" when_volk_registry is None.message_volkhealth pre-warning (1189-1198).
Surface B — roster status (kernel PipelineRunner): kernel/pipeline.py:440-461
PipelineRunner.status_report() (@traces spec="000" story="US-002" reqs=["REQ-000-023"])
reads volk.status.value + current_thread-derived busy off the in-process
BaseAgent instances — it never consults VolkRegistry heartbeat health. Flow:
gateway VolkHandler._live_status (gateway/handlers/volk.py:219-232,
@traces spec="014" reqs=["REQ-014-016","REQ-014-017"]) → kernel client
get_status (both transports) → cxp/grpc_server.py:237-276 → kernel packet handler
_status_reply (agents/kernel_handler.py:119-142) → status_report().
Push-side surfaces that exist but are not fed by registry transitions:
VolkHandler.publish_lifecycle_event(gateway/handlers/volk.py:450) — fired only from gateway-initiatedhandle_register/handle_deregister(volk.py:364, 403).- Gateway kernel-events forwarder:
gateway/server.py:637_init_kernel_events_forwarder(tested bytests/gateway/test_kernel_event_forwarder.py,tests/kernel/test_cli_main_kernel_events.py).
Production registry constructors (refs VolkRegistry, non-test): agents/runtime.py:71
(VolkRuntime), kernel/cli_main.py:252, 428 (kernel boot), kernel/router.py:188,
orchestrator/pipeline_runner.py:190/207, plugin/sdk.py:60.
Unrelated heartbeats (do not conflate): kernel/performance/cluster.py:372
_heartbeat_loop (perf-cluster nodes), gateway/session.py:111 update_heartbeat
(WS connection liveness).
2. SPEC CONTRACT REVIEW (specs are authoritative — RUL-013)
SPEC-001 (kernel) — specs/001-kernel/checklists/requirements.md
US-001 "Volk Registration and Discovery":
- REQ-001-009 — Registration notifies other Volks of new agent.
- REQ-001-014 — Volk can subscribe to registry updates.
- REQ-001-015 — Discovery returns Volk metadata.
- REQ-001-017 — Discovery handles partial failures gracefully.
- REQ-001-018 — Discovery provides health status for each Volk. (The anchor requirement of this epic; already traced by the dispatch-heartbeat code+tests.)
- REQ-001-022 — Deregistration notifies dependent Volks.
US-002 "Message Routing and Dispatch" (NOTE: SPEC-001's checklist numbers this
story's requirements REQ-002-NNN; they are defined in
specs/001-kernel/checklists/requirements.md lines 80-130, distinct from the
postman checklist's REQ-002-USn/FRn-NNN series):
- REQ-001-US-002-008 — Handle routing to offline Volks.
- REQ-001-US-002-009 — Handle routing to unknown recipients.
- REQ-001-US-002-019 — Provide delivery acknowledgments.
- REQ-001-US-002-031..035 — Track message status: queued/dispatched/delivered/acknowledged/failed.
- REQ-001-US-002-036 — Provide message status query API.
- REQ-001-US-002-038 — Notify sender of status changes.
SPEC-002 (postman) — specs/002-postman/checklists/requirements.md
- REQ-002-US-001-011 — Support for NOTIFY message type.
- REQ-002-US-001-026 — Notify sender of permanent failures.
- REQ-002-FR9-007 —
/healthendpoint. - (Pub/sub + subscription series REQ-002-US2-, REQ-002-FR6- — existing
publish_topic/subscribe_topictools already serve these; not in scope.)
SPEC-005 (multi-agent) — specs/005-multi-agent/checklists/requirements.md
- REQ-005-US-004-001 — Operator can monitor agent spawning metrics and performance to ensure system health.
- REQ-005-FR8-001 — Performance metrics collection for agent spawn, query, escalation operations.
- REQ-005-AC6-001 — Metrics accessible via monitoring interface.
- REQ-005-FR10-001 — Senior→junior delegation uses standard CXP packets routed through Postman, identical to senior-to-senior communication (constrains this design: one delivery path, no side-channel for status).
Neither spec.md nor the checklists of 001/002/005 mandate a specific heartbeat
mechanism (no "push every N seconds" requirement exists — grep -i heartbeat
over all three specs returns only REQ-001-018's health-status line). The mechanism
is therefore a design choice; the CONTRACT is: health status per volk must be
available (REQ-001-018), registry updates must be subscribable (REQ-001-014),
senders must learn delivery status (REQ-001-US-002-033/035/038, REQ-002-US-001-026), and
unknown/offline recipients must be handled (REQ-001-US-002-008/009).
3. GAP LIST (as-built vs spec, each with evidence)
| # | Gap | Evidence | Spec anchor |
|---|---|---|---|
| G1 | No periodic liveness heartbeat — liveness is conflated with dispatch recency. Only 2 heartbeat writers exist, both fire on successful dispatch (intent_router.py:305-307, pipeline_runner.py:1004). An idle-but-alive volk decays to INACTIVE after heartbeat_timeout_seconds=60 (volk_registry.py:364, 968-1019), is then deprioritized by routing (intent_router.py:100) and flagged "unhealthy" by message_volk (tool_registry.py:1189-1198) — i.e. health status reports FALSE data for healthy idle volks. The in-code comment at intent_router.py:298 explicitly defers the periodic heartbeat. |
refs record_heartbeat = 2 use-sites; symbols --pattern heartbeat shows no volk-side loop |
REQ-001-018 (health status must be true), REQ-001-017 |
| G2 | Interface gap: the routing/tool seams are typed against the wrong (narrow) protocol and duck-type the rest. IntentRouter/IntentDispatcher annotate IVolkRegistry (plugin/interfaces.py:21 — only register_volk/unregister), then getattr-probe record_heartbeat (intent_router.py:305) and get_volk_health (intent_router.py:90); ToolExecutor._get_volk_status getattr-probes get_volk_health/is_registered (tool_registry.py:1242-1245). Meanwhile core/protocols/decoupling.py:91,94 (VolkRegistryProtocol) already declares both — unused at these seams. |
refs IVolkRegistry, refs VolkRegistryProtocol, sources above |
REQ-001-018 (typed health surface), project composition rule |
| G3 | No real-time status propagation: registry transitions are invisible. (a) add_notification_handler has ZERO consumers (refs: 0 imports, 0 uses) — REQ-001-014's subscription surface is unwired. (b) ACTIVE↔INACTIVE transitions in record_heartbeat/get_volk_health only log (volk_registry.py:1041-1043, 999-1004). (c) The status the gateway/UI polls (volk.list → kernel get_status → kernel/pipeline.py:440-461 status_report) reads BaseAgent.status/current_thread and NEVER includes heartbeat health — the two status surfaces are disjoint; heartbeat health never reaches the orchestrator's roster view or the UI. (d) publish_lifecycle_event fires only on gateway-initiated register/deregister (volk.py:364,403), never on kernel-side transitions. |
sources cited; refs add_notification_handler, refs publish_lifecycle_event |
REQ-001-014, REQ-001-009, REQ-001-022, REQ-005-US-004-001/AC6-001 |
| G4 | False-delivered defect in volk↔volk messaging. Postman.aroute returns None indistinguishably for no-handler / handler-raised / handler-returned-None (postman.py:331-351); message_volk maps None to success=True "Message delivered … (no reply)" (tool_registry.py:1213-1219). An unroutable peer message is reported to the sending volk as delivered. Contrast: IntentDispatcher.dispatch treats the same None as "delivery failed: no route" (intent_router.py:283-294). |
sources cited | REQ-001-US-002-009, REQ-001-US-002-033/035, REQ-001-US-002-038, REQ-002-US-001-026 |
| G5 | Peer-status tools are unwired in the kernel-runtime path. set_volk_registry has exactly ONE caller — orchestrator/pipeline_runner.py:786. The kernel-side PipelineRunner (kernel/pipeline.py:98) never binds a registry into its volks (refs _volk_registry: no kernel/ site), so under the kernel packet-handler path (agents/kernel_handler.py) ToolExecutor._volk_registry is None → get_volk_status returns "no registry bound" and message_volk skips the health pre-check. |
refs set_volk_registry, refs _volk_registry |
REQ-001-018, RUL-023 (feature means wired) |
| G6 (minor) | Delivered volk→volk messages do not count as recipient liveness. Only orchestrator dispatch records heartbeats; a successful message_volk delivery (recipient's handler ran) records nothing. |
refs record_heartbeat |
REQ-001-018 |
Candidate (b) from the epic brief is confirmed with a correction: the gap is not
"no protocol declares record_heartbeat" (VolkRegistryProtocol does, decoupling.py:91)
— it is that the seams are typed against the plugin IVolkRegistry and duck-type
around it.
4. CONTRACT DESIGN
4.1 Key decisions
- One narrow Protocol, composition-first (closes G2). Introduce
VolkHealthProbe— a thin<<Protocol>>with exactly the three members the routing/tool seams actually use (record_heartbeat,get_volk_health,is_registered).kernel.volk_registry.VolkRegistryalready satisfies it structurally (methods exist at 1022, 969, 703) — zero implementation change, no inheritance;VolkRegistryProtocolremains the wide boundary and is a structural superset. RetypeIntentDispatcher._registry,IntentRouterhealth path, andToolExecutor._volk_registryasVolkHealthProbe | None; delete everygetattrprobe. (Twin check performed: no existing narrow health protocol exists to reuse; the wideVolkRegistryProtocolstays untouched.) - Roster heartbeat pump, threads not asyncio (closes G1). A
HeartbeatPumpcomposed of (probe:VolkHealthProbe, roster:Callable[[], list[str]], tracer) beats for every alias in the live roster on a fixed interval usingthreading.Thread+threading.Event— matching the codebase's existing thread posture (route_syncviaasyncio.to_thread, free-threaded 3.14t). Rationale: volks are in-processBaseAgentinstances; "the hosting runtime is up AND the handler is registered" IS the correct liveness signal for them — there is no separate volk process to push from. Busy/failed remainsBaseAgent.status's job (surface B). Interval from an injected env mapping,env.get("VOLK_HEARTBEAT_INTERVAL_S", "15")— a tunable, NOT an infra endpoint key, so a literal default is permitted under RUL-003's inverse rule. Remote/plugin volks (gRPC-registered) are OUT of MVP scope (recorded below). - Status-change observer on the registry (closes G3a/b).
VolkRegistrygains anon_status_change: Callable[[str, VolkStatus, VolkStatus], None] | Noneconstructor seam — the exact precedent is the existingon_registrationseam (volk_registry.py:364). Both transition sites (record_heartbeatACTIVE flip,get_volk_healthlazy INACTIVE flip) fire it under the same lock discipline, best-effort (REQ-001-017). No new event framework: the pattern already exists in this class. - Enrich the EXISTING polled roster instead of a new RPC (closes G3c).
kernel/pipeline.py PipelineRunner.status_report()gains an optional injectedhealth_probe: VolkHealthProbe | None; when present each row addshealthy,heartbeat_age_s,last_heartbeat. The enrichment then flows through the entire existing chain with ZERO new wiring:_status_reply→ gRPC/cxptget_status→ gatewayVolkHandler._live_status→volk.list. Both transports already carry the payload (dict-shapedvolkslist). - Real-time push via the EXISTING kernel-events forwarder (closes G3d).
A tiny
VolkStatusEventBridgeadapter subscribes to decision-3's callback and emits avolk.status_changedkernel event through the already-tested kernel-events → gateway broadcast seam (gateway/server.py:637 _init_kernel_events_forwarder; broadcast, not pubsub.publish, is the delivery mechanism that works — session-193 finding). UI work itself is out of scope; the event on the wire satisfies REQ-001-014's subscription contract. - Honest delivery outcome (closes G4).
DeliveryOutcome<<dataclass>>+DeliveryFailure<<enum>>(OK / NO_ROUTE / HANDLER_ERROR); newPostman.aroute_outcome(packet) -> DeliveryOutcome; existingaroutebecomes a thin delegate (outcome.response if outcome.delivered else None) so all current callers keep exact behavior.message_volkswitches toaroute_outcomeand reportssuccess=Falseon NO_ROUTE/HANDLER_ERROR (REQ-001-US-002-009/035/038); on delivered it records the RECIPIENT's heartbeat via the probe (G6, REQ-001-018) — delivery through the recipient's handler is activity evidence, symmetric with the dispatch-time heartbeat. - Wire the kernel path (closes G5). Kernel-side
PipelineRunnerbinds the registry into every volk it rosters (sameset_volk_registryseam the orchestrator runner already uses at pipeline_runner.py:786), at its volk construction/registration site; kernel boot (kernel/cli_main.pyVolkRegistry construction sites at 252/428) owns theHeartbeatPumplifecycle and theVolkStatusEventBridge.
Non-goals (MVP discipline — explicitly deferred): push heartbeats from remote/
plugin volks over CXP; postman offline-queue/health integration (REQ-001-US-002-018
already served by queue manager); VizInt UI rendering of the new event; multicast
status digests; any change to IVolkRegistry (plugin SDK surface, SPEC-015-owned).
4.2 Contract diagram
patternRole map for gen-skeleton: VolkHealthProbe=protocol ·
HeartbeatPump.beat_once=leaf, lifecycle=boilerplate · VolkStatusEventBridge=
observer-adapter (leaf body one method) · DeliveryOutcome/DeliveryFailure=
dataclass/enum (fully generatable) · Postman.aroute_outcome=leaf ·
edits to existing classes = builder tasks, not generated.
4.3 Wiring plan (RUL-023 / Hard Rule 8 — named production seams, found via refs, not guessed)
| New element | Production importer / seam |
|---|---|
VolkHealthProbe |
orchestrator/intent_router.py:17 (replaces IVolkRegistry import for the dispatcher seam), agents/tool_registry.py (ToolExecutor.__init__:1053 param type), agents/volks/base.py:119 (set_volk_registry param type), kernel/pipeline.py (probe param) |
HeartbeatPump |
kernel boot: kernel/cli_main.py at the VolkRegistry construction sites (252 / 428) — constructed with probe=registry, roster=lambda: list(runner.volks), started with kernel lifecycle, stopped on shutdown. Orchestrator path: orchestrator/pipeline_runner.py lifecycle (same class that already owns volk_registry at 190/207 and _register_handlers at 764). |
VolkStatusEventBridge |
kernel/cli_main.py boot: VolkRegistry(..., on_status_change=bridge.on_status_change) with emit= the existing _emit_kernel_event seam (proven end-to-end by tests/kernel/test_cli_main_kernel_events.py); events reach the UI via gateway/server.py:637 _init_kernel_events_forwarder → broadcast. |
DeliveryOutcome / aroute_outcome |
agents/tool_registry.py:1212 (_message_volk switches from aroute to aroute_outcome); postman/postman.py:332 (aroute delegates). |
status_report enrichment |
Already-wired chain: agents/kernel_handler.py:126 → cxp/grpc_server.py:237 + cxp/direct_cxp_adapter.py:497 → gateway/handlers/volk.py:220 — no new importer needed; the kernel boot passes the probe into kernel/pipeline.py PipelineRunner. |
| G5 binding | kernel/pipeline.py volk-roster site calls volk.set_volk_registry(registry) (mirror of orchestrator/pipeline_runner.py:786). |
Transport constraint respected: all kernel↔gateway status traffic rides the
cxp.transport factory (CXP_TRANSPORT=cxpt in the test profile); both
transports already implement get_status, and the enrichment is payload-only —
no proto change, no new RPC.
5. TDD TEST PLAN (tests-first; real infra only — Redis 10.93.53.11, graph 10.93.53.10, kernel 10.93.53.13:50053, env profile test; no mocks/monkeypatch per RUL-002; env via injected mappings per RUL-003)
| Test module | Test | Requirement | Behavior proven |
|---|---|---|---|
tests/kernel/test_heartbeat_pump.py |
test_beat_once_records_all_rostered_volks |
REQ-001-018 | Real VolkRegistry + 2 registered volks; beat_once() returns 2; both last_heartbeat fresh, status ACTIVE. |
test_idle_volk_stays_healthy_under_pump |
REQ-001-018 | Registry with heartbeat_timeout_seconds=2, pump env={"VOLK_HEARTBEAT_INTERVAL_S":"1"}; after 3s idle, get_volk_health(...).is_healthy is True (kills the G1 decay). |
|
test_partial_failure_tolerated |
REQ-001-017 | Roster contains an unregistered alias; beat_once() still records the registered ones and returns their count. |
|
test_stop_joins_thread |
REQ-001-017 | start() then stop(timeout=5); thread not alive; no further beats recorded. |
|
tests/kernel/test_volk_registry_status_events.py |
test_heartbeat_transition_fires_on_status_change |
REQ-001-014 | Registry constructed with a recording callback; INACTIVE volk + record_heartbeat → exactly one (alias, INACTIVE, ACTIVE) event. |
test_lazy_timeout_transition_fires_on_status_change |
REQ-001-014, REQ-001-018 | Stale heartbeat + get_volk_health → (alias, ACTIVE, INACTIVE) event. |
|
test_no_event_when_status_unchanged |
REQ-001-014 | Second consecutive heartbeat on an ACTIVE volk fires nothing. | |
test_callback_error_never_breaks_heartbeat |
REQ-001-017 | Raising callback; record_heartbeat still returns True and updates state. |
|
tests/kernel/test_status_report_health.py |
test_status_report_includes_heartbeat_health |
REQ-001-018, REQ-001-015 | Kernel PipelineRunner + probe: rows carry healthy/heartbeat_age_s/last_heartbeat consistent with the registry. |
test_status_report_without_probe_backcompat |
REQ-001-015 | No probe injected → rows byte-compatible with today's shape. | |
tests/postman/test_aroute_outcome.py |
test_no_route_outcome |
REQ-001-US-002-009 | Unregistered destination → delivered=False, reason=NO_ROUTE. |
test_handler_error_outcome |
REQ-001-US-002-035 | Registered raising handler → delivered=False, reason=HANDLER_ERROR, error text captured. |
|
test_delivered_with_and_without_reply |
REQ-001-US-002-033 | Echo handler → delivered=True, reason=OK, response set; None-returning handler → delivered=True, response=None (distinguishable from NO_ROUTE at last). |
|
test_aroute_backcompat_delegates |
REQ-001-US-002-019 | aroute returns exactly outcome.response if delivered else None for all three cases above. |
|
tests/agents/test_message_volk_delivery_status.py |
test_message_volk_reports_no_route_as_failure |
REQ-001-US-002-038, REQ-001-US-002-009 | Real Postman, no recipient handler → ToolResult.success is False with a no-route message (kills G4). |
test_message_volk_reports_handler_error_as_failure |
REQ-001-US-002-035, REQ-002-US-001-026 | Raising recipient handler → failure result naming the error. | |
test_message_volk_records_recipient_heartbeat |
REQ-001-018 | Echo handler + real VolkRegistry bound as probe → after delivery, recipient last_heartbeat fresh (G6). |
|
test_message_volk_health_warning_preserved |
REQ-001-018 | Unhealthy-but-registered recipient still gets the [WARNING: …] suffix and delivery is attempted (existing behavior locked). |
|
tests/agents/test_get_volk_status_probe_typing.py |
test_get_volk_status_via_typed_probe |
REQ-001-018 | ToolExecutor with a real VolkRegistry typed as VolkHealthProbe; status text includes status/healthy/heartbeat-age; the code path contains no getattr fallback branches (structural: probe methods called directly). |
tests/kernel/test_kernel_pipeline_registry_binding.py |
test_kernel_pipeline_binds_registry_into_volks |
REQ-001-018, RUL-023 | Kernel PipelineRunner built through the production construction path with a registry → every rostered volk's _volk_registry is that registry (kills G5). |
tests/gateway/test_volk_status_event_broadcast.py |
test_status_change_event_reaches_gateway_broadcast |
REQ-001-014, REQ-001-009 | VolkStatusEventBridge wired to the kernel event emitter; a registry transition produces a volk.status_changed broadcast observable through the existing forwarder fixtures (real Redis). |
existing tests/orchestrator/test_dispatch_heartbeat.py |
3 tests unchanged | REQ-001-018 | Regression: probe retyping must keep all three green with zero edits. |
Runner discipline: scripts/run_tests.py --affected / targeted modules only,
live-observed (observe_pytest attach), 5-min cap per run; kernel required.
6. BUILD PACKETS (each <5 min for a builder agent; MVP order; tests-first inside each)
| # | Packet | Files | Depends on |
|---|---|---|---|
| P1 | VolkHealthProbe protocol + export |
core/protocols/volk_health.py (new), core/protocols/__init__.py |
— |
| P2 | Retype seams, delete getattr duck-typing | orchestrator/intent_router.py (83-97, 255, 305-307), agents/tool_registry.py (1053, 1242-1245), agents/volks/base.py:119 + regression run of test_dispatch_heartbeat.py |
P1 |
| P3 | DeliveryOutcome/DeliveryFailure + Postman.aroute_outcome (+aroute delegate) + tests/postman/test_aroute_outcome.py |
postman/delivery_outcome.py (new), postman/postman.py |
— |
| P4 | message_volk honest delivery + recipient heartbeat + tests/agents/test_message_volk_delivery_status.py |
agents/tool_registry.py (1171-1225) |
P2, P3 |
| P5 | VolkRegistry.on_status_change seam + _fire_status_change + tests/kernel/test_volk_registry_status_events.py |
kernel/volk_registry.py (364, 968-1019, 1021-1053) |
— |
| P6 | HeartbeatPump + tests/kernel/test_heartbeat_pump.py |
kernel/heartbeat_pump.py (new) |
P1 |
| P7 | status_report health enrichment + tests/kernel/test_status_report_health.py |
kernel/pipeline.py (98, 440-461) |
P1 |
| P8 | Kernel wiring: pump lifecycle + probe into kernel runner + set_volk_registry binding (G5) + VolkStatusEventBridge + wiring tests |
kernel/cli_main.py (252/428 region), kernel/pipeline.py, kernel/volk_status_bridge.py (new), tests/kernel/test_kernel_pipeline_registry_binding.py, tests/gateway/test_volk_status_event_broadcast.py |
P5, P6, P7 |
P1/P3/P5/P6 are gen-skeleton-friendly (protocol/dataclass/enum/observer shells + traced test stubs); P2/P4/P7/P8 are builder/wirer edits to existing classes.