feat(content): anti-soup guard on the flow verbs' free-text

Extends structured-content enforcement from the content tools to every
flow verb that carries agent free-text, closing the last hole where a
dev/PM could pass word soup: i_am_blocked(reason), i_am_done(notes),
submit_up/submit_root/complete(notes), escalate_up/escalate_to_ceo(reason),
pass_review(notes), fail_review/pr_fail(issues), pr_pass(notes),
i_documented(notes), delegate(title/description). Plans (i_will_plan /
i_will_work_on) keep their existing >=150-char approach + sub_task gates
and are skipped here so recovery re-entry with thin values still works.

Shared helpers on the choreographer: _free_text_soup (bare envelope, list
aware) and _soup_or_decision_env (folds the soup check into a verb's
existing spec-gate return so no verb gains a return or tips the xenon
bound). reject_trivial now also catches all-filler multi-token strings.

base.md documents the broadened rule for agents.
This commit is contained in:
Renn F
2026-06-21 05:11:20 +02:00
parent 96ca53eff2
commit 4c85cc6dfa
13 changed files with 432 additions and 59 deletions
+12 -4
View File
@@ -131,7 +131,9 @@ async def test_board_escalate_to_ceo_blocks_wrong_state() -> None:
deps = _make_deps(task=task_svc)
c = Choreographer(deps)
env = await c.escalate_to_ceo(agent_id, task_id, reason="x")
env = await c.escalate_to_ceo(
agent_id, task_id, reason="escalating to the CEO for sign-off"
)
body = env.as_dict()
assert body["error"] == "invalid_state"
assert "awaiting_pm_review" in body["message"]
@@ -173,7 +175,9 @@ async def test_board_escalate_to_ceo_blocks_disallowed_role() -> None:
deps = _make_deps(task=task_svc)
c = Choreographer(deps)
env = await c.escalate_to_ceo(agent_id, task_id, reason="x")
env = await c.escalate_to_ceo(
agent_id, task_id, reason="escalating to the CEO for sign-off"
)
body = env.as_dict()
assert body["error"] == "not_authorized"
assert "qa" in body["message"]
@@ -194,7 +198,9 @@ async def test_board_escalate_to_ceo_requires_journal_decision() -> None:
deps = _make_deps(task=task_svc, journal=journal_svc)
c = Choreographer(deps)
env = await c.escalate_to_ceo(agent_id, task_id, reason="x")
env = await c.escalate_to_ceo(
agent_id, task_id, reason="escalating to the CEO for sign-off"
)
body = env.as_dict()
assert body["error"] == "tracing_gap"
assert "journal:decision" in body["missing"]
@@ -210,7 +216,9 @@ async def test_board_escalate_to_ceo_returns_not_found_when_task_missing() -> No
deps = _make_deps(task=task_svc)
c = Choreographer(deps)
env = await c.escalate_to_ceo(agent_id, task_id, reason="x")
env = await c.escalate_to_ceo(
agent_id, task_id, reason="escalating to the CEO for sign-off"
)
body = env.as_dict()
assert body["error"] == "not_found"
task_svc.escalate_to_ceo.assert_not_awaited()