The lead currently leaves teams alive after the task list drains because
none of the prompt surfaces tell it WHEN to close or HOW. omx-style
'self-closing' behavior was missing for four reasons (diagnosed via
prompt-engineering A/B/C: wrong / misframed / missing):
1. builtin team-mode skill 'Lifecycle' (B+C): 'phase ends / shape
outgrown' is qualitative, so the model maps it to 'wait for user'.
Step 6 jumped to team_delete without the request/approve pair the
tool contract requires. Replaced with a 'Closure Contract' (a
computable predicate over team_task_list + team_status) and an
explicit 'Closure Sequence' (request -> approve -> delete, with
force=true reserved for unrecoverable paths only).
2. TEAM_MESSAGE keyword injection (C): spent 100%% of its one-shot
budget on routing ('do not substitute delegate_task'), 0%% on
closure. Added the same closure rule in compressed form. Kept the
'NEVER substitute with delegate_task' literal that
keyword-detector/index.test.ts depends on.
3. team-mode-status-injector body (C): the only per-session injection
for team mode had no closure obligation. Replaced the optional
'load the team-mode skill ... otherwise use the team_* tools'
sentence with a 'Closure invariant' clause that ties the check to
every team_task_update.
4. member-guidance Wrap-up (A+B): step 3 said 'so the lead can decide
whether to request shutdown', but team_shutdown_request is
lead-only - members cannot initiate it. Step ordering also placed
the completion message before team_task_update, so the lead's
closable check would see stale data. Reordered to
task_update -> check task_list for new work -> if nothing left,
send a single 'closure-ready' message and idle. Test assertion
updated to match the new accurate contract.
Also: stripped Korean alternation from TEAM_PATTERN per directive
('절대로 코드 내에 한국어 적지 마라'). Pattern is now
/\\bteam[\\s_-]?mode\\b/i. Removed 4 Korean test cases
(2 positive triggers + 2 false-positive guards) that the pattern no
longer needs to defend, and updated the keyword-detector AGENTS.md
row.
Net: -71 lines across prompt surfaces. The Closure Contract is the
only addition; everything else tightened.
Tests: 428/428 pass across src/features/team-mode/,
src/features/builtin-skills/, src/hooks/keyword-detector/,
src/hooks/team-mode-status-injector/, src/hooks/team-mailbox-injector/,
src/hooks/team-tool-gating/, src/hooks/team-session-events/.
LSP: no errors introduced (one pre-existing error in
keyword-detector/index.test.ts confirmed pre-existing on dev).
Bring the team member addendum and the team-mode skill in line with the
Claude Code experimental Agent Teams reference (../free-code) on the
behavioral framing the LLM needs, while keeping every omo-specific
constraint (TeamRunId param, lead-only tool blocklist, eligibility
registry, sisyphus-junior routing) intact.
Member addendum (src/features/team-mode/member-guidance.ts) — adopt
free-code teammatePromptAddendum.ts wisdom that was missing:
- "User interacts primarily with the team lead" — members no longer try
to address the user directly; they coordinate via task system + DMs.
- Stronger emphasis that plain assistant text is invisible to the team;
team_send_message is mandatory, not optional.
- Broadcast (`to: "*"`) called out explicitly with "sparingly" guidance
and the lead-only constraint.
- "Idle is normal" section: idle ≠ done, idle teammates can still
receive messages, the lead must not treat idle as an error.
- "Communication rules" section forbidding structured JSON status
messages and forbidding terminal tools as a peer-inspection backdoor;
always reference teammates by NAME, not session ID.
- Auto-delivery framing: messages arrive as new turns, no manual inbox
polling, no need to quote a teammate's message back to the lead.
Team-mode skill (src/features/builtin-skills/skills/team-mode.ts) —
adopt free-code TeamCreateTool/prompt.ts sections that were missing:
- Task ownership: any agent can set/change `owner` via team_task_update.
- Automatic message delivery: <peer_message ...> envelope flow.
- Teammate idle state: idle is the expected post-turn state; peer DM
summaries surface in the lead's idle notification.
- Discovering team members: team_status + ~/.omo/teams/ config; refer by
name, never raw session IDs.
- Task list coordination: claim by ID order (lowest first), check after
every completion, escalate blocking deadlocks to the lead.
- Communication rules: same anti-patterns as the member addendum, scoped
for the lead's awareness (no JSON status, no terminal peer-inspection,
no delegate-task from inside members).
Test (create.test.ts) updated to track the renamed section header
("Lead-only tools you must NOT call") and to lock the new free-code
parity assertions ("user interacts primarily with the team lead",
"Idle is normal", "structured JSON status messages").
All other contract tests (team-mode skill keyword/section tests,
member parser, runtime tests) untouched and still passing.
Generated with assistance of [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode).