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
14 changes: 13 additions & 1 deletion src/agent_comms/maintenance_barrier.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ def hold_restore(cls, barrier: MaintenanceBarrier, operator: MaintenanceOperator
drained()


# The holding operation hands its nonce to its own child processes through this variable.
HOLDER_NONCE = "AGENT_COMMS_MAINTENANCE_HOLDER"


class MaintenanceStatus:
"""Absent witness is READY; stored phases derive from their record."""

Expand All @@ -154,6 +158,10 @@ def turn_pending(self) -> bool:
def require_open(self) -> None:
self.phase.new_sends.require_open()

def require_open_to(self, holder: str) -> None:
"""Open, or held by ``holder``: the operation holding maintenance still runs its own reads."""
self.require_open()


@dataclass(frozen=True)
class MaintenanceOperator:
Expand Down Expand Up @@ -216,6 +224,10 @@ def successor(self, phase: type[MaintenancePhase], nonce: str) -> MaintenanceSta
generation=self.generation + 1, operator=self.operator,
nonce=nonce, phase=phase)

def require_open_to(self, holder: str) -> None:
if holder != self.nonce:
self.require_open()

def require_draining(self) -> None:
if self.phase is not DrainingPhase:
raise ValueError('Only an acquired draining phase can be cleared')
Expand Down Expand Up @@ -300,7 +312,7 @@ def current_unlocked(self) -> MaintenanceStatus:

def assert_open_unlocked(self) -> MaintenanceStatus:
hold = self.current_unlocked()
hold.require_open()
hold.require_open_to(os.environ.get(HOLDER_NONCE, ""))
return hold

def read(self) -> MaintenanceStatus:
Expand Down
11 changes: 10 additions & 1 deletion src/agent_comms/todos.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import re
import subprocess
import sqlite3
import sys
import time
import asyncio
from abc import abstractmethod
Expand Down Expand Up @@ -758,7 +759,8 @@ class OrderWords(WireValue):
# for long ordinary words with no slash or filename suffix.
CITED: ClassVar[re.Pattern] = re.compile(
r'(?m)^ {4}.*$|`[^`\n]*`|"[^"\n]*"|“[^”\n]*”|'
r'(?:(?<!\S)|(?<=[`"”]))(?:\S*/\S*|\S+\.(?:py|md|jsonl?|txt|png|toml|ts|log|patch)\b)')
r'(?:(?<!\S)|(?<=[`"”]))(?:\S*/\S*|\S+\.(?:py|md|jsonl?|txt|png|toml|ts|log|patch)\b)|'
r'\b\w+_\w+\b|\b[A-Z][a-z0-9]+[A-Z]\w*\b') # identifiers are code, not prose
REFUSED: ClassVar[re.Pattern]
refusal: ClassVar[str]
least_words: ClassVar[int] = 1
Expand Down Expand Up @@ -804,6 +806,13 @@ class PlainText(OrderWords):
REFUSED = re.compile(r'\b(custody|admission|admit\w*|receipt\w*|checkpoint\w*|provenance|authority|authorities|witness\w*|lattice|surface|disposition\w*|invariant\w*|semantic\w*|ownership|lifecycle|acceptance|canonical|retained|borrow(?:ed|er|ers)?|bounded|certif\w*|recertif\w*|candidate|fence\w*|sidecar\w*|substrate|installed-path|source[- ]paired|exact[- ]head|integration owner|separately owned|follow-?up|remaining scope|not claimed|remains held|load[- ]bearing|cutover|carr(?:y|ies) across|declared dependency|qualifying)\b', re.I)
refusal = "Jargon {1} hides what happened; say it plainly."

@staticmethod
def refuses(found: list[str]) -> bool:
"""Flags, never blocks: a wording slip costs a note, not a resend."""
if found:
print(f"Flag: jargon {', '.join(found)} hides what happened; say it plainly.", file=sys.stderr)
return False


class StrikeReason(PlainText):
"""What a unit did, plainly, in enough words to stand on the public record. A unit's
Expand Down
6 changes: 5 additions & 1 deletion tools/upgrade_bus.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
from agent_comms.field_codec import FieldCodec
from agent_comms.history_result import EverythingHistoryPageResult
from agent_comms.host_services import HostService
from agent_comms.maintenance_barrier import MaintenanceBarrier, MaintenanceOperator
from agent_comms.maintenance_barrier import HOLDER_NONCE, MaintenanceBarrier, MaintenanceOperator
from agent_comms.registry_store import RegistryStore
from agent_comms.store_files import _store_lock
from agent_comms.todos import PlannedInstallDeadline
Expand Down Expand Up @@ -265,6 +265,9 @@ def rehearse(root: Path, pending: tuple[Step, ...], stack: Stack, revisions: Rev
copy_bus(root, copy)
stack = replace(stack, home=rehearsal_home(Path(scratch), root, copy))
stack.prepare_native()
# Hold the copy before reading it, exactly as the live run holds the bus.
os.environ[HOLDER_NONCE] = MaintenanceBarrier(copy / "registry.json").begin(
MaintenanceOperator("upgrade-bus")).nonce
with transcripts_held(copy):
before = bus_files(copy)
stopping = before_stop(copy, pending, stack, revisions)
Expand Down Expand Up @@ -374,6 +377,7 @@ def live(checkout: Path, root: Path, current: str, target: str, pending: tuple[S
before_stop(root, pending, staged, revisions)
maintenance = MaintenanceBarrier(RegistryStore.at(root).path)
held = maintenance.begin(MaintenanceOperator("upgrade-bus"))
os.environ[HOLDER_NONCE] = held.nonce # this run's own CLI calls read under its hold
lock = installed.lock
try:
installed.owners(root, "stop", running)
Expand Down
Loading