|
|
@@ -70,8 +70,8 @@ from .validation_policy import (
|
|
|
ValidationContext,
|
|
|
ValidationPolicy,
|
|
|
)
|
|
|
-from ._goal_projection import GoalProjection
|
|
|
from ._decision_engine import DecisionEngine
|
|
|
+from ._task_context import render_task_context
|
|
|
from ._task_graph import (
|
|
|
TERMINAL_TASK_STATUSES,
|
|
|
TaskGraph,
|
|
|
@@ -118,11 +118,6 @@ class TaskCoordinator:
|
|
|
self._task_graph,
|
|
|
max_repair_continuations=lambda: self.config.max_repair_continuations,
|
|
|
)
|
|
|
- self._goal_projection = GoalProjection(
|
|
|
- task_store=self.task_store,
|
|
|
- get_trace_store=lambda: self.trace_store,
|
|
|
- mutate=self._mutate,
|
|
|
- )
|
|
|
self._operations = OperationController(
|
|
|
task_store=self.task_store,
|
|
|
mutate=self._mutate,
|
|
|
@@ -186,7 +181,6 @@ class TaskCoordinator:
|
|
|
root_task_id = new_id()
|
|
|
root = TaskRecord(
|
|
|
task_id=root_task_id,
|
|
|
- goal_id=None,
|
|
|
parent_task_id=None,
|
|
|
display_path="0",
|
|
|
specs=[spec],
|
|
|
@@ -278,6 +272,13 @@ class TaskCoordinator:
|
|
|
],
|
|
|
}
|
|
|
|
|
|
+ async def task_context(self, root_trace_id: str) -> str:
|
|
|
+ """Return a compact read model of the authoritative TaskLedger."""
|
|
|
+
|
|
|
+ ledger = await self.task_store.load(root_trace_id)
|
|
|
+ self._root_task(ledger)
|
|
|
+ return render_task_context(ledger)
|
|
|
+
|
|
|
async def _mutate(
|
|
|
self,
|
|
|
root_trace_id: str,
|
|
|
@@ -462,15 +463,6 @@ class TaskCoordinator:
|
|
|
"placement": placement,
|
|
|
},
|
|
|
)
|
|
|
- after_task_id = result.get("after_task_id")
|
|
|
- for task_id in result["task_ids"]:
|
|
|
- await self._project_goal_compatibility(
|
|
|
- root_trace_id,
|
|
|
- task_id,
|
|
|
- ensure=True,
|
|
|
- after_task_id=after_task_id,
|
|
|
- )
|
|
|
- after_task_id = task_id
|
|
|
return await self._tasks_result(root_trace_id, result["task_ids"])
|
|
|
|
|
|
async def focus_task(self, root_trace_id: str, task_id: str) -> Dict[str, Any]:
|
|
|
@@ -479,19 +471,7 @@ class TaskCoordinator:
|
|
|
ledger.focused_task_id = task_id
|
|
|
return {"task_id": task_id, "status": task.status.value}
|
|
|
|
|
|
- result = await self._mutate(root_trace_id, "task_focused", mutate)
|
|
|
- if self.trace_store:
|
|
|
- try:
|
|
|
- ledger = await self.task_store.load(root_trace_id)
|
|
|
- task = ledger.tasks[task_id]
|
|
|
- if task.goal_id:
|
|
|
- tree = await self.trace_store.get_goal_tree(root_trace_id)
|
|
|
- if tree and tree.find(task.goal_id):
|
|
|
- tree.focus(task.goal_id)
|
|
|
- await self.trace_store.update_goal_tree(root_trace_id, tree)
|
|
|
- except Exception as exc:
|
|
|
- self._log_projection_failure(root_trace_id, task_id, exc)
|
|
|
- return result
|
|
|
+ return await self._mutate(root_trace_id, "task_focused", mutate)
|
|
|
|
|
|
async def dispatch_tasks(
|
|
|
self,
|
|
|
@@ -981,7 +961,6 @@ class TaskCoordinator:
|
|
|
# persistent Store outage cannot be repaired in-process (V2), but
|
|
|
# must not cancel sibling Tasks in this batch.
|
|
|
return
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
|
|
|
async def _cycle_error_result(
|
|
|
self,
|
|
|
@@ -1101,7 +1080,6 @@ class TaskCoordinator:
|
|
|
},
|
|
|
operation_id=operation_id,
|
|
|
)
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
return result
|
|
|
|
|
|
async def advance_cycle(
|
|
|
@@ -1756,7 +1734,6 @@ class TaskCoordinator:
|
|
|
idempotency_payload=submission_payload,
|
|
|
operation_id=attempt.operation_id,
|
|
|
)
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
return result
|
|
|
|
|
|
async def _start_validation(
|
|
|
@@ -1818,7 +1795,6 @@ class TaskCoordinator:
|
|
|
mutate,
|
|
|
operation_id=operation_id,
|
|
|
)
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
return result["validation_id"]
|
|
|
|
|
|
async def _plan_validation(
|
|
|
@@ -1957,7 +1933,6 @@ class TaskCoordinator:
|
|
|
idempotency_payload=report_payload,
|
|
|
operation_id=validation.operation_id,
|
|
|
)
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
return result
|
|
|
|
|
|
async def query_evidence(
|
|
|
@@ -2332,24 +2307,6 @@ class TaskCoordinator:
|
|
|
"payload": payload,
|
|
|
},
|
|
|
)
|
|
|
- affected = [task_id]
|
|
|
- affected.extend(result.get("payload", {}).get("child_task_ids", []))
|
|
|
- replacement = result.get("payload", {}).get("replacement_task_id")
|
|
|
- if replacement:
|
|
|
- affected.append(replacement)
|
|
|
- try:
|
|
|
- ledger = await self.task_store.load(root_trace_id)
|
|
|
- parent_id = ledger.tasks[task_id].parent_task_id
|
|
|
- if parent_id:
|
|
|
- affected.append(parent_id)
|
|
|
- except Exception as exc:
|
|
|
- self._log_projection_failure(root_trace_id, task_id, exc)
|
|
|
- for affected_task in dict.fromkeys(affected):
|
|
|
- await self._project_goal_compatibility(
|
|
|
- root_trace_id,
|
|
|
- affected_task,
|
|
|
- ensure=True,
|
|
|
- )
|
|
|
return result
|
|
|
|
|
|
@staticmethod
|
|
|
@@ -2455,7 +2412,6 @@ class TaskCoordinator:
|
|
|
operation_id=operation_id,
|
|
|
)
|
|
|
validation_id = result["validation_id"]
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
return await self.advance_validation(
|
|
|
root_trace_id,
|
|
|
task_id,
|
|
|
@@ -2634,7 +2590,6 @@ class TaskCoordinator:
|
|
|
return {"task_id": task_id, "attempt_id": attempt_id, "error": error}
|
|
|
|
|
|
await self._mutate(root_trace_id, "worker_failed", mutate)
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
|
|
|
async def _mark_validation_error(
|
|
|
self,
|
|
|
@@ -2676,7 +2631,6 @@ class TaskCoordinator:
|
|
|
return {"task_id": task_id, "validation_id": validation_id, "error": error}
|
|
|
|
|
|
await self._mutate(root_trace_id, "validation_error", mutate)
|
|
|
- await self._project_goal_compatibility(root_trace_id, task_id)
|
|
|
|
|
|
@staticmethod
|
|
|
def _repair_feedback(ledger: TaskLedger, task: TaskRecord) -> Optional[Dict[str, Any]]:
|
|
|
@@ -2780,54 +2734,13 @@ class TaskCoordinator:
|
|
|
def _update_parent_after_child(ledger: TaskLedger, child: TaskRecord) -> None:
|
|
|
TaskGraph.update_parent_after_child(ledger, child)
|
|
|
|
|
|
- async def project_goal_state(self, root_trace_id: str, task_id: str) -> None:
|
|
|
- await self._goal_projection.project_state(root_trace_id, task_id)
|
|
|
-
|
|
|
- async def _project_goal_compatibility(
|
|
|
- self,
|
|
|
- root_trace_id: str,
|
|
|
- task_id: str,
|
|
|
- *,
|
|
|
- ensure: bool = False,
|
|
|
- after_task_id: Optional[str] = None,
|
|
|
- ) -> None:
|
|
|
- await self._goal_projection.project_compatibility(
|
|
|
- root_trace_id,
|
|
|
- task_id,
|
|
|
- ensure=ensure,
|
|
|
- after_task_id=after_task_id,
|
|
|
- )
|
|
|
-
|
|
|
- @staticmethod
|
|
|
- def _log_projection_failure(
|
|
|
- root_trace_id: str,
|
|
|
- task_id: str,
|
|
|
- error: Exception,
|
|
|
- ) -> None:
|
|
|
- GoalProjection.log_failure(root_trace_id, task_id, error)
|
|
|
-
|
|
|
- async def _ensure_goal_projection(
|
|
|
- self,
|
|
|
- root_trace_id: str,
|
|
|
- task_id: str,
|
|
|
- after_task_id: Optional[str] = None,
|
|
|
- ) -> None:
|
|
|
- await self._goal_projection.ensure(
|
|
|
- root_trace_id,
|
|
|
- task_id,
|
|
|
- after_task_id=after_task_id,
|
|
|
- )
|
|
|
-
|
|
|
- async def reconcile_goal_tree(self, root_trace_id: str) -> Dict[str, Any]:
|
|
|
- return await self._goal_projection.reconcile(root_trace_id)
|
|
|
-
|
|
|
async def _tasks_result(self, root_trace_id: str, task_ids: Iterable[str]) -> Dict[str, Any]:
|
|
|
ledger = await self.task_store.load(root_trace_id)
|
|
|
return {
|
|
|
"tasks": [
|
|
|
{
|
|
|
"task_id": task_id,
|
|
|
- "goal_id": ledger.tasks[task_id].goal_id,
|
|
|
+ "goal_id": None,
|
|
|
"display_path": ledger.tasks[task_id].display_path,
|
|
|
"status": ledger.tasks[task_id].status.value,
|
|
|
"spec_version": ledger.tasks[task_id].current_spec_version,
|