Skip to content
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ src/ai_company/
communication/ # Message bus, dispatcher, messenger, channels, delegation, loop prevention, conflict resolution, meeting protocol
config/ # YAML company config loading and validation
core/ # Shared domain models, base classes, and resilience config (RetryConfig, RateLimiterConfig)
engine/ # Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination error classification, and prompt policy validation
engine/ # Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination error classification, prompt policy validation, and AgentEngine-TaskEngine incremental status sync
hr/ # HR engine: hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)
memory/ # Persistent agent memory (Mem0 initial, custom stack future — see Decision Log), retrieval pipeline (ranking, injection, context formatting, non-inferable filtering), shared org memory (org/), consolidation/archival (consolidation/)
persistence/ # Operational data persistence — pluggable PersistenceBackend protocol, SQLite initial (see Memory & Persistence design page)
Expand Down
50 changes: 45 additions & 5 deletions docs/design/engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,42 @@ Agent / API ──submit()──▶ asyncio.Queue ──▶ _processing_loop
- **stop()**: Sets `_running = False`, drains the queue within a configurable
timeout, then cancels. Abandoned futures receive a failure result.

### AgentEngine ↔ TaskEngine Incremental Sync

`AgentEngine` syncs task status transitions to `TaskEngine` incrementally at
each lifecycle point, rather than reporting only the final status. This gives
real-time visibility into execution progress and improves crash recovery
(a crash mid-execution leaves the task at the last-reached stage, not stuck
at `ASSIGNED`).

**Transition sequences** (1–3 `submit()` calls per execution, bounded):

| Path | Synced transitions |
|------|--------------------|
| Happy (COMPLETED) | `IN_PROGRESS` → `IN_REVIEW` → `COMPLETED` |
| Shutdown | `IN_PROGRESS` → `INTERRUPTED` |
| Error | `IN_PROGRESS` → `FAILED` (after recovery) |
Comment on lines +239 to +243
Copy link

Copilot AI Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown table formatting here uses double pipes (|| ... | ... |), which will render as an extra empty first column in most Markdown renderers. Use standard table syntax with single leading pipes on each row (e.g., | Path | Synced transitions |).

Copilot uses AI. Check for mistakes.
| MAX_TURNS / BUDGET | `IN_PROGRESS` only |

**Semantics:**

- **Best-effort**: Sync failures are logged and swallowed — agent execution
is never blocked by a TaskEngine issue. Each sync failure is isolated and
does not prevent subsequent transitions.
- **Critical IN_PROGRESS**: The initial `ASSIGNED → IN_PROGRESS` sync is
logged at `ERROR` on failure (TaskEngine state coherence for all subsequent
transitions depends on it). Other sync failures log at `WARNING`.
- **Direct `submit()`**: Uses `TaskEngine.submit()` with
`TransitionTaskMutation` directly (not the convenience `transition_task()`
method) to inspect `TaskMutationResult` success/failure without exception
propagation, keeping sync best-effort.
- **No concurrency concern**: Each task has exactly one executing agent at
any time. Parallel agents operate on separate tasks.

**Snapshot channel**: TaskEngine publishes `TaskStateChanged` events to the
`"tasks"` channel (matching `CHANNEL_TASKS` in `api.channels`) so events
reach the `MessageBusBridge` and WebSocket consumers.

---

## Agent Execution Loop
Expand All @@ -250,8 +286,8 @@ All loop implementations satisfy the `ExecutionLoop` runtime-checkable protocol:
**Supporting models:**

`TerminationReason`
: Enum: `COMPLETED`, `MAX_TURNS`, `BUDGET_EXHAUSTED`, `SHUTDOWN`, `ERROR`.
`max_turns` defaults to 20.
: Enum: `COMPLETED`, `MAX_TURNS`, `BUDGET_EXHAUSTED`, `SHUTDOWN`, `ERROR`,
`PARKED`. `max_turns` defaults to 20.
Comment on lines +289 to +290
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Document what PARKED does to task state.

The enum now exposes PARKED, but the post-execution section still only documents MAX_TURNS and BUDGET_EXHAUSTED as no-op terminations. Please spell out whether parked runs also leave the task in its current state so the lifecycle description stays complete.

As per coding guidelines, "docs/design/*.md: When approved deviations occur, update the relevant docs/design/ page to reflect the new reality".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/design/engine.md` around lines 289 - 290, Update the post-execution
lifecycle docs to describe the newly exposed enum value PARKED: state explicitly
whether a PARKED termination is a no-op that leaves the task in its current
state (same behavior as MAX_TURNS and BUDGET_EXHAUSTED) or whether it
transitions to a specific state; specifically modify the post-execution section
in docs/design/engine.md to mention PARKED alongside MAX_TURNS and
BUDGET_EXHAUSTED and state the exact semantics (e.g., "PARKED: leaves task in
current state" or the alternative) and keep the note about max_turns defaulting
to 20 intact for context.


`TurnRecord`
: Frozen per-turn stats (tokens, cost, tool calls, finish reason).
Expand Down Expand Up @@ -377,7 +413,7 @@ invocation, and cost tracking into a single `run()` call.
```python
async run(
identity, task, completion_config?, max_turns?,
memory_messages?, timeout_seconds?
memory_messages?, timeout_seconds?, effective_autonomy?
) -> AgentRunResult
```

Expand Down Expand Up @@ -421,8 +457,12 @@ async run(
- `ERROR` termination: recovery strategy is applied (default
`FailAndReassignStrategy` transitions to FAILED;
see [Crash Recovery](#agent-crash-recovery)).
- All other termination reasons (`MAX_TURNS`, `BUDGET_EXHAUSTED`) leave the
task in its current state.
- All other termination reasons (`MAX_TURNS`, `BUDGET_EXHAUSTED`, `PARKED`)
leave the task in its current state. `PARKED` indicates the agent was
suspended by an approval-timeout policy; the task remains at its current
status until explicitly resumed.
- Each transition is synced to TaskEngine incrementally (see
[AgentEngine ↔ TaskEngine Incremental Sync](#agentengine--taskengine-incremental-sync)).
- Transition failures are logged but do not discard the successful execution
result.
11. **Return result** -- wraps `ExecutionResult` in `AgentRunResult` with
Expand Down
Loading
Loading