fix: release comms-blocking parks when replies arrive and give every role file authoring tools
Project 3333 forensics: env_engineer sent a blocking question to the CTO, got a full reply 96s later, and still deadlocked the whole run — the park half (`_park_for_blocking_comms`) had no wired consumer, so WAITING_FOR_PEER work items could never be released. Unpark path (root fix): - The dispatcher loop now calls `_try_unpark_blocking_comms` each tick for parked, non-in-flight tasks; blocking replies land as durable inbox files, so the check is read-only until all replies are present. - `_try_unpark_blocking_comms` accepts orphaned waits (peer_wait stripped by the legacy resolver while the work item stayed parked) and falls back to the park predicate itself: an empty unresolved blocking outbox releases the task, anything pending keeps it parked. - `resolve_task_peer_wait` no longer touches comms_blocking waits (it flipped task.status without the work-item phase and stripped the peer_wait evidence); `_resume_peer_checkpoint` re-enters the company runtime for comms/orphaned waits and lets the dispatcher converge. File tools (defaults changed at their declaration sites, honoring the "empty tools = everything, explicit list = exactly that" contract): - corporate builtin groups gain file_write/file_edit for coordination, QA, and data-acquisition roles. - all shipped org YAML role tool lists gain the missing file_write/file_edit entries. - coordination turn modes no longer strip file_write/file_edit at runtime — in-context content (briefs, matrices) must be persistable instead of getting trapped in blocking DM hand-offs. Also includes the pending office_ui ws_handler change from the working tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4236,6 +4236,21 @@ class CompanyWorkItemExecutor:
|
||||
)
|
||||
] or list(self._active_tasks)
|
||||
self._active_tasks = tasks
|
||||
# Consumer half of `_park_for_blocking_comms`: blocking
|
||||
# replies arrive as durable inbox files, so each tick checks
|
||||
# parked tasks and releases the ones whose replies are all
|
||||
# present. In-flight tasks are skipped — their coroutine
|
||||
# still owns the Task object and a late save_task would
|
||||
# clobber the transition.
|
||||
in_flight_task_ids = {
|
||||
claimed.id for _member, claimed in active_work_item_tasks.values()
|
||||
}
|
||||
for parked in tasks:
|
||||
if (
|
||||
parked.status == TaskStatus.AWAITING_PEER
|
||||
and parked.id not in in_flight_task_ids
|
||||
):
|
||||
await self._try_unpark_blocking_comms(parked)
|
||||
await self.runtime.refresh_inbox_state(tasks)
|
||||
work_items = await self._load_delegation_work_items(tasks)
|
||||
work_items = await self._refresh_ready_work_items(work_items, tasks=tasks)
|
||||
@@ -9225,7 +9240,13 @@ class CompanyWorkItemExecutor:
|
||||
if task.status != TaskStatus.AWAITING_PEER:
|
||||
return False
|
||||
peer_wait = dict(task.metadata.get("peer_wait", {}) or {})
|
||||
if str(peer_wait.get("kind") or "") != "comms_blocking":
|
||||
wait_kind = str(peer_wait.get("kind") or "")
|
||||
# An empty kind is an orphaned wait (e.g. a legacy resolver popped
|
||||
# `peer_wait` while the work item stayed WAITING_FOR_PEER); those
|
||||
# are recoverable from the durable comms state below. Waits with a
|
||||
# different explicit kind (meeting, message-id) have their own
|
||||
# resolvers.
|
||||
if wait_kind and wait_kind != "comms_blocking":
|
||||
return False
|
||||
try:
|
||||
from opc.layer2_organization import comms as _comms
|
||||
@@ -9240,7 +9261,11 @@ class CompanyWorkItemExecutor:
|
||||
return False
|
||||
blocking_ids = list(peer_wait.get("blocking_message_ids", []) or [])
|
||||
if not blocking_ids:
|
||||
# Nothing to wait for — defensively unpark.
|
||||
# No recorded ids (orphaned or empty wait): fall back to the
|
||||
# park predicate itself — any unanswered blocking outbox
|
||||
# message keeps the task parked, none means release.
|
||||
if _comms.find_unresolved_blocking_outbox(layout, role_id):
|
||||
return False
|
||||
task.metadata = dict(task.metadata)
|
||||
task.metadata.pop("peer_wait", None)
|
||||
await transition_work_item_from_task(
|
||||
|
||||
Reference in New Issue
Block a user