feat(engine): the manager — chat orders, team, triage, verdicts, bootstrap

Phase 3 of the AI-workforce engine (TODO-agent-2):

- engine/manager.py: ManagerTurns.chat (CREATE_TASK/PAUSE/RESUME/CANCEL,
  HIRE/UPDATE_EMPLOYEE/FIRE, KNOWLEDGE), triage (ASSIGN/REJECT/ASK_USER),
  final_review (COMPLETE/REVISE +cycle), error_review (RESOLVE_ERROR/CANCEL),
  bootstrap_team; post_user_message now delegates to it.
- One billing path: call_model/BillingScope in runner.py is the single model
  entry point (fixes the agybot chat-billing bug).
- Team changes capped at 3/turn, employee cap, last manager cannot be fired,
  hires pick a granted model of the requested tier strictly.
- container_engine.py wires the real WorkforceBootstrapPort.
- Fixes: REVISE is a body tag (prompt and parser now agree); ASSIGN
  employee="" means pick-for-me; the runner's protocol-error retry is capped by
  engine_max_protocol_retries (was unbounded until the turn timeout).
- 20 new manager tests + regression tests; report in
  docs/plan/reports/agent-2-phase-3.md.
This commit is contained in:
mjalmoudy94
2026-10-14 18:12:56 +03:30
parent c6488bfadf
commit 581f703910
15 changed files with 2385 additions and 99 deletions
+18
View File
@@ -37,6 +37,7 @@ from app.domain.workforce.models import RoleKey
from app.domain.workforce.pipeline import Mode
MANAGER_TRIAGE = {"role": RoleKey.MANAGER, "mode": Mode.TRIAGE}
MANAGER_FINAL = {"role": RoleKey.MANAGER, "mode": Mode.FINAL}
MANAGER_CHAT = {"role": RoleKey.MANAGER, "mode": Mode.CHAT}
DEVELOPER = {"role": RoleKey.DEVELOPER, "mode": Mode.WORK}
PLANNER = {"role": RoleKey.PLANNER, "mode": Mode.WORK}
@@ -249,6 +250,23 @@ def test_assign_requires_an_employee_or_a_task():
assign_of(tag)
def test_an_empty_employee_means_pick_for_me():
"""``employee=""`` is a *deliberate* value, unlike an absent attribute."""
tag = parse_reply('[[ASSIGN employee="" pipeline="plan_code_test"/]]', **MANAGER_TRIAGE).first(
TagName.ASSIGN
)
assign = assign_of(tag)
assert assign.employee == ""
assert assign.pipeline == "plan_code_test"
def test_revise_carries_a_body():
"""The prompt asks for ``[[REVISE]]reason[[/REVISE]]``; so does the parser."""
reply = parse_reply("[[REVISE]]تست‌ها قرمز است[[/REVISE]]", **MANAGER_FINAL)
assert reply.errors == ()
assert reply.first(TagName.REVISE).body == "تست‌ها قرمز است"
def test_assign_refuses_an_unknown_pipeline():
reply = parse_reply('[[ASSIGN employee="سارا" pipeline="plan_ship_test"/]]', **MANAGER_TRIAGE)
with pytest.raises(ProtocolError):