Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 3 additions & 21 deletions src/agent_comms/commander_heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
from dataclasses import dataclass, field
from typing import TYPE_CHECKING

from agent_comms.commander_service import CommanderService
from agent_comms.heartbeat_frame import HeartbeatFrame
from agent_comms.message_bus import WatchPass
from agent_comms.registration import Registration
from agent_comms.service_sender import ServiceSender
from agent_comms.standing_rules import StandingRule
from agent_comms.thread_identity import ThreadIncarnation
from agent_comms.threads import Thread
Expand All @@ -17,8 +16,7 @@

if TYPE_CHECKING:
from agent_comms.comms import Comms
from agent_comms.goal_management import Goals
from agent_comms.todos import OwnerRequest, Todo
from agent_comms.todos import Todo


@dataclass
Expand Down Expand Up @@ -81,25 +79,9 @@ def read(cls, comms: Comms, thread: Thread,


@dataclass
class CommanderHeartbeat(ServiceSender, WatchPass[tuple[CommanderObservation, ...]]):
sender = "commander-heartbeat"
class CommanderHeartbeat(CommanderService, WatchPass[tuple[CommanderObservation, ...]]):
reviews: dict[ThreadIncarnation, CommanderReview] = field(default_factory=dict)

@classmethod
def requested_by(cls, thread: Thread, goals: Goals) -> tuple[OwnerRequest, ...]:
"""The oldest open request makes a live holder owed service, even before its first beat."""
return (goals.todos.open_requests(thread.incarnation, limit=1, descending=False)
if thread.process_alive else ())

@classmethod
def commanders(cls, registry: Registration, goals: Goals) -> tuple[Thread, ...]:
"""The live request holders whose current incarnation owns commander work."""
return tuple(
thread
for thread in registry.all_threads().values()
if cls.requested_by(thread, goals)
)

def observe(self, comms: Comms) -> tuple[CommanderObservation, ...]:
return tuple(
CommanderObservation.read(comms, thread)
Expand Down
32 changes: 32 additions & 0 deletions src/agent_comms/commander_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""The commander service's sender and live request holders."""

from __future__ import annotations

from typing import TYPE_CHECKING

from .service_sender import ServiceSender
from .threads import Thread

if TYPE_CHECKING:
from .goal_management import Goals
from .registration import Registration
from .todos import OwnerRequest


class CommanderService(ServiceSender):
sender = "commander-heartbeat"

@classmethod
def requested_by(cls, thread: Thread, goals: Goals) -> tuple[OwnerRequest, ...]:
"""The oldest open request makes a live holder owed service, even before its first beat."""
return (goals.todos.open_requests(thread.incarnation, limit=1, descending=False)
if thread.process_alive else ())

@classmethod
def commanders(cls, registry: Registration, goals: Goals) -> tuple[Thread, ...]:
"""The live request holders whose current incarnation owns commander work."""
return tuple(
thread
for thread in registry.all_threads().values()
if cls.requested_by(thread, goals)
)
8 changes: 4 additions & 4 deletions src/agent_comms/owner_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
SynchronousProcess,
)
from .cli_result import CliResult
from .commander_heartbeat import CommanderHeartbeat
from .commander_service import CommanderService
from .diagnostics import OwnerProcessOutput
from .errors import RelationViolationError
from .launch_root import LaunchRoot
Expand Down Expand Up @@ -335,7 +335,7 @@ def _register_owner(
if previous.process_identity.offline():
orders = tuple(
todo
for commander in CommanderHeartbeat.commanders(self.registry, self.goals)
for commander in CommanderService.commanders(self.registry, self.goals)
for todo in self.goals.todos.held_by(commander.incarnation, owned.incarnation)
if todo.pending and todo.held().owner_identity == owned.incarnation
)
Expand All @@ -348,8 +348,8 @@ def _register_owner(

def _announce_restart(self, name: str, identities: str) -> None:
"""Publish once after the registry's process reservation commits."""
CommanderHeartbeat.register(self.registry)
self.messaging.send(CommanderHeartbeat.sender, name,
CommanderService.register(self.registry)
self.messaging.send(CommanderService.sender, name,
f"Restarted holding {identities}; continue them from your branch")

def ensure_owner(
Expand Down
Loading