docs: update AGENTS guidance

This commit is contained in:
YeonGyu-Kim
2026-05-18 11:49:56 +09:00
parent c4dd21e1f3
commit 34e6af1ae6
16 changed files with 1648 additions and 24 deletions
+16 -9
View File
@@ -1,10 +1,10 @@
# oh-my-opencode — OpenCode Plugin
**Generated:** 2026-05-17 | **Commit:** 4d417a33b | **Branch:** dev | **Release:** v4.2.0
**Generated:** 2026-05-18 | **Commit:** 4d417a33b | **Branch:** dev | **Release:** v4.2.0
## OVERVIEW
OpenCode plugin (npm: `oh-my-opencode`, dual-published as `oh-my-openagent` during the rename transition) extending OpenCode with 11 agents, 5461 lifecycle hooks (base / +team-mode) across 59 dirs, 2039 tools (gated by config flags including team-mode), 3-tier MCP system (built-in + .mcp.json + skill-embedded), Hashline LINE#ID edit tool, IntentGate keyword detector, Team Mode (parallel multi-agent coordination, OFF by default), Boulder feature (boulder-state work tracking + cli/boulder subcommand), configurable agent ordering, and Claude Code compatibility. **`src/` contains 2070 TypeScript files (1350 source + 720 test), ~300k LOC, 122 barrel `index.ts` files.** Entry: `src/index.ts` is now an 18-line wrapper that delegates to `src/testing/create-plugin-module.ts` `createPluginModule()` → 7-step init.
OpenCode plugin (npm: `oh-my-opencode`, dual-published as `oh-my-openagent` during the rename transition) extending OpenCode with 11 agents, 5461 lifecycle hooks (base / +team-mode) across 59 dirs, 2039 tools (gated by config flags including team-mode), 3-tier MCP system (built-in + .mcp.json + skill-embedded), Hashline LINE#ID edit tool, IntentGate keyword detector, Team Mode (parallel multi-agent coordination, OFF by default), Boulder feature (boulder-state work tracking + cli/boulder subcommand), configurable agent ordering, and Claude Code compatibility. **Repository contains 2165 TypeScript files across `src/`, `script/`, `test-support/`, and `web/`, ~314k LOC; `src/` itself has 122 barrel `index.ts` files.** Entry: `src/index.ts` is now an 18-line wrapper that delegates to `src/testing/create-plugin-module.ts` `createPluginModule()` → 7-step init.
## STRUCTURE
@@ -63,7 +63,9 @@ pluginModule.server(input, options)
└─→ createPluginInterface() # 10 OpenCode hook handlers → PluginInterface
```
## 10 OPENCODE HOOK HANDLERS
## 13 OPENCODE HOOK HANDLERS
11 wired in [`src/plugin-interface.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin-interface.ts) + 2 wired directly in [`src/testing/create-plugin-module.ts`](file:///Users/yeongyu/local-workspaces/omo/src/testing/create-plugin-module.ts) (`experimental.session.compacting` + `experimental.compaction.autocontinue`).
| Handler | OpenCode Hook | Purpose |
|---------|---------------|---------|
@@ -72,11 +74,14 @@ pluginModule.server(input, options)
| `chat.message` | `chat.message` | First-message variant, session setup, keyword detection (ultrawork/search/analyze/team) |
| `chat.params` | `chat.params` | Anthropic effort, think mode, runtime fallback override |
| `chat.headers` | `chat.headers` | Copilot `x-initiator` header injection |
| `command.execute.before` | `command.execute.before` | Pre-command guards (slash-command interception, etc.) |
| `event` | `event` | Session lifecycle (created/deleted/idle/error), openclaw dispatch, runtime fallback |
| `tool.execute.before` | `tool.execute.before` | Pre-tool guards (write-existing-guard, label-truncator, rules-injector, prometheus-md-only, …) |
| `tool.execute.after` | `tool.execute.after` | Post-tool hooks (output truncator, comment-checker, hashline read-enhancer, json-error-recovery, …) |
| `experimental.chat.messages.transform` | `experimental.chat.messages.transform` | Context injection, thinking-block validation, tool-pair validation, keyword detection |
| `experimental.chat.system.transform` | `experimental.chat.system.transform` | System-message-level transforms |
| `experimental.session.compacting` | `experimental.session.compacting` | Context + todo preservation across compaction |
| `experimental.compaction.autocontinue` | `experimental.compaction.autocontinue` | Auto-resume after compaction completes |
## TOOL CATALOG (config-gated)
@@ -233,15 +238,15 @@ bunx oh-my-opencode mcp-oauth login <server-url> # Tier-3 MCP OAuth (PKCE + DCR
| Workflow | Trigger | Purpose |
|----------|---------|---------|
| `ci.yml` | push/PR to master/dev | Tests, typecheck, build, schema auto-commit |
| `publish.yml` | manual dispatch | Version bump, dual npm publish (`oh-my-opencode` + `oh-my-openagent`), platform binaries, GitHub release |
| `ci.yml` | push/PR to master/dev | Tests, typecheck, build, auto-commit schema on master push, draft "next" release on dev push (blocks master-targeting PRs) |
| `publish.yml` | manual dispatch | Test, typecheck, preflight-trust (OIDC verify 24 packages), dual npm publish (`oh-my-opencode` + `oh-my-openagent`), platform binaries, GitHub release, merge to master |
| `publish-platform.yml` | called by publish.yml | 11 platform binaries via `bun compile` (darwin/linux/windows) |
| `sisyphus-agent.yml` | @mention or manual dispatch | AI agent handles issues/PRs |
| `refresh-model-capabilities.yml` | weekly cron / dispatch | Refresh model capabilities from models.dev API |
| `cla.yml` | issue_comment / PR | CLA assistant for contributors |
| `lint-workflows.yml` | push to .github/ | actionlint + shellcheck on workflow files |
| `web-ci.yml` | push/PR touching `web/**` | format-check, lint, type-check, next build, opennextjs-cloudflare build |
| `web-deploy.yml` | push to master touching `web/**` OR manual dispatch | Cloudflare Workers deploy via `cloudflare/wrangler-action@v3` (requires `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` secrets) |
| `lint-workflows.yml` | push/PR touching `.github/workflows/**` | actionlint only (`shellcheck=""` disables shellcheck) |
| `web-ci.yml` | push/PR to master/dev touching `web/**`, `docs/**`, or the workflow file itself | format-check, lint, type-check, next build, opennextjs-cloudflare build |
| `web-deploy.yml` | push to master/dev touching `web/**`, `docs/**`, or the workflow file itself, OR manual dispatch | Cloudflare Workers deploy via `cloudflare/wrangler-action@v3` (requires `CLOUDFLARE_API_TOKEN` + `CLOUDFLARE_ACCOUNT_ID` secrets) |
## NOTES
@@ -254,11 +259,13 @@ bunx oh-my-opencode mcp-oauth login <server-url> # Tier-3 MCP OAuth (PKCE + DCR
- **Build:** `bun build` (ESM) + `tsc --emitDeclarationOnly`, externals: `@ast-grep/napi`, `zod`.
- **CI tests:** root tests run through plain `bun test`; `web/**` has its own package-level CI workflow.
- **122 barrel `index.ts` files** establish module boundaries.
- **Architecture rules** enforced via `.omo/rules/modular-code-enforcement.md` (when present in workspace).
- **Architecture rules** enforced via the `rules-injector` hook reading `.omo/rules/*.md`. As of v4.2.0 only `test-discipline.md` ships; legacy `modular-code-enforcement.md` was retired.
- **Windows builds:** run on `windows-latest` (not cross-compiled) to avoid Bun segfaults.
- **Platform binaries:** detect AVX2 + libc family at runtime, fallback to baseline if needed.
- **IntentGate (`keyword-detector`):** classifies user intent (`ultrawork`/`ulw`, `search`, `analyze`, `team`) and injects mode-specific prompts.
- **Hashline edit:** every `Read` output tagged with `LINE#ID` content hashes (chars from `ZPMQVRWSNKTXJBYH`); edits reject on hash mismatch.
- **zauc-mocks pattern:** 9 directories named `zauc-mocks-*` (5 in `src/hooks/`, 2 in `src/tools/`, 1 each in `src/mcp/` and `src/shared/`) hold `mock.module()` setup that must load alphabetically before the tests that consume those mocked modules. The `zauc-` prefix is purely a sort-order hack for `bun:test` discovery; these are NOT hooks/tools.
- **Test discipline meta-audits:** two files (`src/shared/mock-module-lifecycle-audit.test.ts` and `src/shared/prompt-async-route-audit.test.ts`) parse the entire codebase via the TS compiler API and FAIL the suite when an architectural invariant is violated (`mock.module()` without restore, raw `session.promptAsync` outside the gate).
- **Docs:** see [`docs/guide/`](file:///Users/yeongyu/local-workspaces/omo/docs/guide/) for user-facing guides (overview, installation, orchestration, agent-model-matching, team-mode), [`docs/reference/`](file:///Users/yeongyu/local-workspaces/omo/docs/reference/) for CLI/configuration/features reference. v4.2.0+ adds [`CHANGELOG.md`](file:///Users/yeongyu/local-workspaces/omo/CHANGELOG.md), [`docs/reference/known-issues.md`](file:///Users/yeongyu/local-workspaces/omo/docs/reference/known-issues.md), [`docs/reference/prompt-async-gate-rfc.md`](file:///Users/yeongyu/local-workspaces/omo/docs/reference/prompt-async-gate-rfc.md), and [`docs/reference/release-process.md`](file:///Users/yeongyu/local-workspaces/omo/docs/reference/release-process.md).
- **Rules files** (auto-injected by `rules-injector` hook): [`.omo/rules/modular-code-enforcement.md`](file:///Users/yeongyu/local-workspaces/omo/.omo/rules/modular-code-enforcement.md) + [`.omo/rules/test-discipline.md`](file:///Users/yeongyu/local-workspaces/omo/.omo/rules/test-discipline.md) (forbids `setTimeout(resolve, N)` / `await sleep(N)` in tests unless time IS the SUT). Scans `.omo/rules/`, `.sisyphus/rules/`, `.claude/rules/`, `.cursor/rules/`, `.github/instructions/`, plus `.github/copilot-instructions.md` and `.mdc` files.
- **Process cleanup:** Background-agent error handlers are now log-only — no force-exit on transient errors. Opt out entirely via `OMO_DISABLE_PROCESS_CLEANUP=1` env var.
@@ -0,0 +1,405 @@
# Fix Delegate-Task Subagent Permissions
## TL;DR
> Summary: Fix OMO `task` so delegated subagents can use repo exploration tools in their child sessions without permission prompts or denials. Match OpenCode native Task semantics by deriving child-session permissions and prompt tools from the delegated agent instead of only denying `question`.
> Deliverables:
> - Shared subagent permission/tool builder with unit coverage
> - Sync delegate-task child-session permission fix
> - Background and unstable delegate-task permission fix
> - Regression coverage for `read`/`bash`/search access and write/tool delegation denial
> - Module QA plus real tmux/non-interactive QA evidence
> Effort: Medium
> Risk: Medium — permission rules affect delegated child-session execution and can accidentally over-allow tools if not scoped.
## Scope
### Must have
- Subagents launched through OMO `task` can call repo exploration tools such as `read`, `bash`, `grep`, and `glob` in child sessions.
- Sync child sessions are created with `parentID`, OpenCode-compatible title, model fields, directory route, and a derived permission ruleset.
- Background child sessions receive the same derived permission ruleset through `LaunchInput.sessionPermission`.
- Prompt bodies for sync, background launch, background resume, and fallback-agent retry use the same derived prompt tool map so `session.prompt` does not overwrite useful child-session permissions with deny-only rules.
- `question` remains denied for delegated child sessions.
- Read-only subagents still cannot write or recursively delegate: `write`, `edit`, `apply_patch`, `task`, and `call_omo_agent` remain denied where the delegated agent restrictions deny them.
- Existing task metadata contract remains intact: `sessionId` is published and visible task metadata still includes `session_id`.
- Regression tests prove both sync and background paths include explicit `allow` rules for exploration tools and explicit `deny` rules for restricted tools.
- Real tmux/manual QA proves an `explore` subagent launched by `task` can inspect files and run a harmless shell command without `"Permission required"` output.
### Must NOT have (guardrails, anti-slop, scope boundaries)
- Do not disable OpenCode permission checks globally.
- Do not add `permission: "*", action: "allow"` or any broad wildcard allow.
- Do not grant write/edit/apply_patch to `explore`, `librarian`, or `oracle`.
- Do not refactor background polling, concurrency, wake gating, model fallback, or tmux layout.
- Do not change agent prompts, category model selection, metadata formatting, or task output text except where tests require permission metadata.
- Do not remove `getAgentToolRestrictions`; centralize the new derived session permission behavior around it or a closely related shared helper.
- Do not change the native OpenCode source under `../opencode`.
## Verification strategy
> Zero human intervention — all verification is agent-executed.
- Test decision: TDD + Bun test
- QA policy: every task has agent-executed scenarios
- Evidence: `evidence/task-<N>-<slug>.<ext>`
## Execution strategy
### Parallel execution waves
> Target 5-8 tasks per wave. <3 per wave (except final) = under-splitting.
> Extract shared dependencies as Wave-1 tasks to maximize parallelism.
Wave 1 (no dependencies):
- Task 1: Add shared subagent permission/tool builder and focused unit tests
- Task 2: Add native-reference contract tests around existing permission fixtures
Wave 2 (after Wave 1):
- Task 3: depends [1, 2] - Wire sync delegate-task session creation and prompt body
- Task 4: depends [1, 2] - Wire background and unstable delegate-task launch path
- Task 5: depends [1] - Wire background resume and fallback-agent prompt bodies
Wave 3 (after Wave 2):
- Task 6: depends [3, 4, 5] - Run module QA and lock regression output
- Task 7: depends [3, 4, 5] - Run real tmux/manual QA for delegated exploration
Critical path: Task 1 -> Task 3 -> Task 6
### Dependency matrix
| Task | Depends on | Blocks | Can parallelize with |
|------|------------|--------|----------------------|
| 1 | none | 3, 4, 5, 6, 7 | 2 |
| 2 | none | 3, 4, 6, 7 | 1 |
| 3 | 1, 2 | 6, 7 | 4, 5 |
| 4 | 1, 2 | 6, 7 | 3, 5 |
| 5 | 1 | 6, 7 | 3, 4 |
| 6 | 3, 4, 5 | none | 7 |
| 7 | 3, 4, 5 | none | 6 |
## Todos
> Implementation + Test = ONE task. Never separate.
> Every task MUST have: References + Acceptance Criteria + QA Scenarios + Commit.
- [ ] 1. Add shared subagent permission/tool builder
What to do: Create one focused helper, preferably `src/shared/subagent-session-permission.ts`, plus `src/shared/subagent-session-permission.test.ts`. The helper must produce both:
- `buildSubagentSessionPermission(agentName, options): SessionPermissionRule[]`
- `buildSubagentPromptTools(agentName, options): Record<string, boolean>`
Required behavior:
- Convert OMO permission-map values (`"allow" | "deny" | "ask"`) into OpenCode session rules (`{ permission, action, pattern: "*" }`).
- Always include `{ permission: "question", action: "deny", pattern: "*" }`.
- Add explicit `allow` rules for repo exploration tools when not denied by the agent: `read`, `bash`, `grep`, `glob`, `lsp_symbols`, `lsp_goto_definition`, `lsp_find_references`, `lsp_diagnostics`, `ast_grep_search`.
- Apply agent/tool deny rules last so read-only restrictions win over defaults.
- Preserve team-tool denylist behavior when `includeTeamToolDenylist` is true.
- Do not add wildcard allow rules.
Must NOT do: Do not change any call sites yet. Do not change agent definitions. Do not add a generic `utils.ts` or `helpers.ts`.
Parallelization: Can parallel: YES | Wave 1 | Blocks: [3, 4, 5, 6, 7] | Blocked by: []
References (executor has NO interview context — be exhaustive):
- Pattern: `src/shared/agent-tool-restrictions.ts:24` — current read-only denylist omits explicit `read`/`bash` allows, which leaves child sessions in ask/permission-required state.
- Pattern: `src/shared/agent-tool-restrictions.ts:66` — current `getAgentToolRestrictions()` is the source of prompt-body deny rules and team-tool deny rules.
- Pattern: `src/shared/question-denied-session-permission.ts:1` — current ruleset shape and `QUESTION_DENIED_SESSION_PERMISSION`.
- Pattern: `src/shared/permission-compat.ts:6` — OMO agent permission map value type.
- Pattern: `src/agents/explore.ts:27``explore` denies write/edit/apply_patch/task/call_omo_agent and explicitly allows some LSP/AST tools.
- Pattern: `src/agents/librarian.ts:24``librarian` denies write/edit/apply_patch/task/call_omo_agent.
- External: `../opencode/packages/opencode/src/agent/subagent-permissions.ts:17` — native Task derives child-session permissions from parent and subagent rules.
- External: `../opencode/packages/opencode/src/permission/evaluate.ts:9` — missing rule defaults to `ask`, which is the source of permission prompts/denials.
- Test: `src/agents/tool-restrictions.test.ts` — existing assertions around agent permission maps.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/shared/subagent-session-permission.test.ts --bail` passes.
- [ ] Test asserts `buildSubagentSessionPermission("explore")` contains `allow` for `read`, `bash`, `grep`, and `glob`.
- [ ] Test asserts `buildSubagentSessionPermission("explore")` contains `deny` for `write`, `edit`, `apply_patch`, `task`, `call_omo_agent`, and `question`.
- [ ] Test asserts no generated rule is `{ permission: "*", action: "allow", pattern: "*" }`.
- [ ] Test asserts prompt tools mirror the permission intent: exploration tools `true`, restricted tools `false`.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: explore permission builder allows read-only repo exploration
Tool: bash
Steps: mkdir -p evidence && bun test src/shared/subagent-session-permission.test.ts --bail > evidence/task-1-permission-builder.txt
Expected: Exit 0, and test names mention read/bash exploration allow plus write/edit denial.
Evidence: evidence/task-1-permission-builder.txt
Scenario: builder rejects overbroad wildcard allow
Tool: bash
Steps: rg -n 'permission: "\\*", action: "allow"|\\["\\*", true\\]' src/shared/subagent-session-permission.ts src/shared/subagent-session-permission.test.ts > evidence/task-1-wildcard-scan.txt; test ! -s evidence/task-1-wildcard-scan.txt
Expected: Exit 0 and evidence file is empty.
Evidence: evidence/task-1-wildcard-scan.txt
```
Commit: YES | Message: `fix(task): derive subagent exploration permissions` | Files: [`src/shared/subagent-session-permission.ts`, `src/shared/subagent-session-permission.test.ts`]
- [ ] 2. Add native-reference contract tests around current fixtures
What to do: Update current permission tests so they describe the native-compatible contract before wiring code:
- `src/tools/delegate-task/sync-session-creator.test.ts`
- `src/tools/delegate-task/background-task.test.ts`
- `src/features/background-agent/manager-session-permission.test.ts`
The tests must assert that child session creation receives a permission ruleset with explicit exploration allows and restricted-tool denies. Keep existing title, `parentID`, and directory assertions.
Must NOT do: Do not make broad behavior changes in this task except the minimal helper import needed if Task 1 already exists. Do not delete the old `question` denial assertion; update it into the larger ruleset.
Parallelization: Can parallel: YES | Wave 1 | Blocks: [3, 4, 6, 7] | Blocked by: []
References (executor has NO interview context — be exhaustive):
- Pattern: `src/tools/delegate-task/sync-session-creator.test.ts:5` — current sync child-session test only expects `question` deny.
- Pattern: `src/tools/delegate-task/background-task.test.ts:209` — current delegate background launch test only expects `question` deny in `sessionPermission`.
- Pattern: `src/features/background-agent/manager-session-permission.test.ts:84` — manager-level test asserts explicit session permission rules are passed into `session.create`.
- External: `../opencode/packages/opencode/src/tool/task.ts:152` — native Task creates child session with `parentID`, title, and derived permission.
- External: `../opencode/packages/opencode/src/tool/task.ts:178` — native Task metadata includes child `sessionId`.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/background-task.test.ts src/features/background-agent/manager-session-permission.test.ts --bail` initially fails before Tasks 3 and 4 if run after only test edits.
- [ ] Tests assert `parentID` and title stay unchanged.
- [ ] Tests assert permission arrays contain `allow` for `read` and `bash`.
- [ ] Tests assert permission arrays contain `deny` for `question`, `write`, `edit`, `apply_patch`, and `task`.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: contract tests capture the regression
Tool: bash
Steps: mkdir -p evidence && bun test src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/background-task.test.ts src/features/background-agent/manager-session-permission.test.ts --bail > evidence/task-2-contract-tests.txt || true
Expected: Evidence shows the new expectations before implementation, or passes if Task 3/4 are already applied by parallel execution.
Evidence: evidence/task-2-contract-tests.txt
Scenario: existing metadata contract remains present
Tool: bash
Steps: rg -n 'session_id|sessionId|parentID|@\\$\\{.*subagent|@explore subagent' src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/background-task.test.ts src/features/background-agent/manager-session-permission.test.ts > evidence/task-2-metadata-contract.txt
Expected: Evidence includes session lineage/title assertions, not only permission assertions.
Evidence: evidence/task-2-metadata-contract.txt
```
Commit: YES | Message: `test(task): pin subagent session permission contract` | Files: [`src/tools/delegate-task/sync-session-creator.test.ts`, `src/tools/delegate-task/background-task.test.ts`, `src/features/background-agent/manager-session-permission.test.ts`]
- [ ] 3. Wire sync delegate-task session creation and prompt body
What to do:
- Update `createSyncSession()` to accept an optional `sessionPermission` argument and use it instead of hardcoded `QUESTION_DENIED_SESSION_PERMISSION`.
- Update `executeSyncTask()` to pass `buildSubagentSessionPermission(agentToUse, ...)` on initial child session creation and retry child session creation.
- Update `sendSyncPrompt()` to use `buildSubagentPromptTools(agentToUse, { allowTask })` rather than assembling a deny-only map inline.
- Keep `setSessionTools()` and `applySessionPromptParams()` behavior.
- Keep `routePromptRetry()` and `routePromptSyncRetry()` behavior unchanged.
Must NOT do: Do not alter polling, fetch result, fallback selection, metadata formatting, or `promptAsync` gate behavior.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [6, 7] | Blocked by: [1, 2]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/tools/delegate-task/sync-session-creator.ts:20` — current sync child session creation hardcodes `QUESTION_DENIED_SESSION_PERMISSION`.
- Pattern: `src/tools/delegate-task/sync-task.ts:88` — initial sync child session is created here.
- Pattern: `src/tools/delegate-task/sync-task.ts:264` — fallback retry creates another sync child session and must receive the same derived permission rules.
- Pattern: `src/tools/delegate-task/sync-prompt-sender.ts:70` — prompt body currently builds tools from `getAgentToolRestrictions()` only.
- Pattern: `src/tools/delegate-task/sync-prompt-route.test.ts:16` — existing route tests must keep passing.
- External: `../opencode/packages/opencode/src/tool/task.ts:194` — native Task prompts `nextSession.id`, not parent session.
- External: `../opencode/packages/opencode/src/session/prompt.ts:1622` — prompt `tools` are converted into session permission rules, so prompt body must not erase exploration allows.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts src/tools/delegate-task/sync-prompt-route.test.ts src/tools/delegate-task/sync-task.test.ts --bail` passes.
- [ ] `createSyncSession()` tests prove sync child session body includes `allow` rules for `read` and `bash`.
- [ ] `sendSyncPrompt()` tests prove prompt body tools include `read: true`, `bash: true`, `grep: true`, `glob: true`, `question: false`, and `write/edit/apply_patch/task` false for `explore`.
- [ ] Sync route tests still prove prompts are routed to the child session directory.
- [ ] Retry path tests prove fallback child sessions also receive derived permission rules.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: sync task creates an exploration-capable child session
Tool: bash
Steps: mkdir -p evidence && bun test src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts src/tools/delegate-task/sync-prompt-route.test.ts src/tools/delegate-task/sync-task.test.ts --bail > evidence/task-3-sync-permissions.txt
Expected: Exit 0; evidence contains passing sync permission and prompt-body tests.
Evidence: evidence/task-3-sync-permissions.txt
Scenario: sync prompt still denies recursive delegation and writes
Tool: bash
Steps: bun test src/tools/delegate-task/sync-prompt-sender.test.ts --bail > evidence/task-3-sync-denies.txt
Expected: Exit 0; tests assert `task`, `call_omo_agent`, `write`, `edit`, and `apply_patch` are false for read-only subagents.
Evidence: evidence/task-3-sync-denies.txt
```
Commit: YES | Message: `fix(task): pass derived permissions to sync subagents` | Files: [`src/tools/delegate-task/sync-session-creator.ts`, `src/tools/delegate-task/sync-task.ts`, `src/tools/delegate-task/sync-prompt-sender.ts`, related tests]
- [ ] 4. Wire background and unstable delegate-task launch path
What to do:
- Update `executeBackgroundTask()` to pass `buildSubagentSessionPermission(normalizedAgent, ...)` into `manager.launch()`.
- Update `executeUnstableAgentTask()` to pass the same derived permission builder into `manager.launch()`.
- Update `BackgroundManager`/`startTask()` permission tests to expect derived session permissions.
- Keep `LaunchInput.sessionPermission` optional for other callers.
- Keep background metadata and `buildTaskMetadataBlock()` output unchanged.
Must NOT do: Do not change background task concurrency, polling, idle detection, parent wake notification, or cancellation behavior.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [6, 7] | Blocked by: [1, 2]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/tools/delegate-task/background-task.ts:116` — background delegate-task launch currently passes only `QUESTION_DENIED_SESSION_PERMISSION`.
- Pattern: `src/tools/delegate-task/unstable-agent-task.ts:32` — unstable-agent path also passes only `QUESTION_DENIED_SESSION_PERMISSION`.
- Pattern: `src/features/background-agent/types.ts:102` — `LaunchInput.sessionPermission` type is already the correct extension point.
- Pattern: `src/features/background-agent/spawner.ts:100` — `startTask()` passes `input.sessionPermission` into `client.session.create`.
- Test: `src/tools/delegate-task/background-task.test.ts:209` — update this existing background permission assertion.
- Test: `src/tools/delegate-task/unstable-agent-permission.test.ts` — update unstable path assertion.
- Test: `src/features/background-agent/manager-session-permission.test.ts:84` — manager-level permission passthrough coverage.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/tools/delegate-task/background-task.test.ts src/tools/delegate-task/unstable-agent-permission.test.ts src/features/background-agent/manager-session-permission.test.ts --bail` passes.
- [ ] Background launch tests assert `manager.launch().sessionPermission` includes `read`/`bash` allows for `explore`.
- [ ] Background launch tests assert write/edit/apply_patch/task/call_omo_agent/question denies remain.
- [ ] Manager passthrough test proves `client.session.create().body.permission` receives the derived ruleset exactly.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: background launch passes derived permissions through manager
Tool: bash
Steps: mkdir -p evidence && bun test src/tools/delegate-task/background-task.test.ts src/tools/delegate-task/unstable-agent-permission.test.ts src/features/background-agent/manager-session-permission.test.ts --bail > evidence/task-4-background-permissions.txt
Expected: Exit 0; evidence shows background and unstable permission tests pass.
Evidence: evidence/task-4-background-permissions.txt
Scenario: background metadata remains OpenCode-compatible
Tool: bash
Steps: bun test src/tools/delegate-task/background-task.test.ts --bail > evidence/task-4-background-metadata.txt
Expected: Exit 0; existing metadata tests still pass and visible output contains `session_id`.
Evidence: evidence/task-4-background-metadata.txt
```
Commit: YES | Message: `fix(task): pass derived permissions to background subagents` | Files: [`src/tools/delegate-task/background-task.ts`, `src/tools/delegate-task/unstable-agent-task.ts`, `src/features/background-agent/manager-session-permission.test.ts`, related tests]
- [ ] 5. Wire background resume and fallback prompt bodies
What to do:
- Update `src/features/background-agent/spawner.ts` initial prompt body, resume prompt body, and fallback-agent prompt body to use `buildSubagentPromptTools()`.
- Keep `includeTeamToolDenylist: input.teamRunId === undefined` behavior.
- Keep fallback agent behavior, `releasePromptAsyncReservation()`, and `task.agent = FALLBACK_AGENT` unchanged.
- Add tests that `promptAsync` body for launch and resume includes `read: true`, `bash: true`, and restricted-tool denies for `explore`.
Must NOT do: Do not change session creation, concurrency release, tmux callback timing, or fallback-agent selection.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [6, 7] | Blocked by: [1]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/features/background-agent/spawner.ts:158` — initial background prompt body currently builds tool map inline.
- Pattern: `src/features/background-agent/spawner.ts:29` — fallback prompt body currently builds a second inline tool map.
- Pattern: `src/features/background-agent/spawner.ts:299` — resume prompt body currently builds another inline tool map.
- Test: `src/features/background-agent/manager-session-permission.test.ts:9` — already captures prompt route and can be extended or paired with a new focused test.
- External: `../opencode/packages/opencode/src/session/prompt.ts:1622` — prompt body tools become session permission rules.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/features/background-agent/manager-session-permission.test.ts src/features/background-agent/manager.test.ts src/features/background-agent/spawner.test.ts --bail` passes, or if `manager.test.ts` is too broad/slow, record the narrower replacement command in evidence.
- [ ] A launch prompt test asserts exploration tools are explicitly true and restricted tools false.
- [ ] A resume prompt test asserts the same tool map contract.
- [ ] Existing fallback-agent retry tests still pass.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: background launch prompt keeps exploration tools enabled
Tool: bash
Steps: mkdir -p evidence && bun test src/features/background-agent/manager-session-permission.test.ts --bail > evidence/task-5-background-prompt-tools.txt
Expected: Exit 0; evidence includes prompt body assertions for read/bash and restricted-tool denies.
Evidence: evidence/task-5-background-prompt-tools.txt
Scenario: no prompt gate or fallback routing regression
Tool: bash
Steps: bun test src/features/background-agent/manager.test.ts src/features/background-agent/spawner.test.ts --bail > evidence/task-5-background-manager.txt
Expected: Exit 0, or if pre-existing unrelated failures occur, evidence includes exact failing test names and a narrower passing command that covers spawner prompt behavior.
Evidence: evidence/task-5-background-manager.txt
```
Commit: YES | Message: `fix(background-agent): keep exploration tools enabled in subagent prompts` | Files: [`src/features/background-agent/spawner.ts`, `src/features/background-agent/manager-session-permission.test.ts`, related tests]
- [ ] 6. Run module QA and lock regression evidence
What to do:
- Run focused delegate-task/background-agent tests.
- Run typecheck.
- Run the full root test suite if focused tests and typecheck pass.
- Capture evidence files and summarize failures only if unrelated/pre-existing.
Must NOT do: Do not fix unrelated failures. Do not weaken tests to pass. Do not skip typecheck.
Parallelization: Can parallel: YES | Wave 3 | Blocks: [] | Blocked by: [3, 4, 5]
References (executor has NO interview context — be exhaustive):
- Command: `bun test src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts src/tools/delegate-task/sync-prompt-route.test.ts src/tools/delegate-task/sync-task.test.ts src/tools/delegate-task/background-task.test.ts src/tools/delegate-task/unstable-agent-permission.test.ts src/features/background-agent/manager-session-permission.test.ts --bail`
- Command: `bun run typecheck`
- Command: `bun test`
- Pattern: `package.json` — scripts use Bun only; no npm/yarn/pnpm.
Acceptance criteria (agent-executable only):
- [ ] Focused delegate-task/background-agent test command exits 0.
- [ ] `bun run typecheck` exits 0.
- [ ] `bun test` exits 0, or evidence documents exact unrelated pre-existing failures with focused command still green.
- [ ] No `as any`, `@ts-ignore`, or `@ts-expect-error` introduced.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: focused module regression suite
Tool: bash
Steps: mkdir -p evidence && bun test src/tools/delegate-task/sync-session-creator.test.ts src/tools/delegate-task/sync-prompt-sender.test.ts src/tools/delegate-task/sync-prompt-route.test.ts src/tools/delegate-task/sync-task.test.ts src/tools/delegate-task/background-task.test.ts src/tools/delegate-task/unstable-agent-permission.test.ts src/features/background-agent/manager-session-permission.test.ts --bail > evidence/task-6-focused-tests.txt
Expected: Exit 0.
Evidence: evidence/task-6-focused-tests.txt
Scenario: typecheck and anti-suppression scan
Tool: bash
Steps: bun run typecheck > evidence/task-6-typecheck.txt && rg -n 'as any|@ts-ignore|@ts-expect-error' src/shared/subagent-session-permission.ts src/tools/delegate-task src/features/background-agent > evidence/task-6-suppression-scan.txt || true
Expected: Typecheck exits 0; suppression scan contains no new suppressions in changed files.
Evidence: evidence/task-6-typecheck.txt
```
Commit: NO | Message: `test(task): verify delegate-task permission regression` | Files: [`evidence/task-6-focused-tests.txt`, `evidence/task-6-typecheck.txt`, `evidence/task-6-suppression-scan.txt`]
- [ ] 7. Run real tmux/manual QA for delegated exploration
What to do:
- Build the local plugin.
- Run a real OMO non-interactive session inside tmux from this repo that forces `task(subagent_type="explore", run_in_background=false or true)` to inspect a known file and run a harmless shell command.
- Confirm output contains the expected file fact and does not contain `"Permission required"`, `"missing permission"`, or `"permission denied"` for `read`/`bash`.
- Repeat with background mode and collect `background_output` after system completion.
- Capture tmux pane output and `/tmp/oh-my-opencode.log` excerpts.
Must NOT do: Do not run destructive shell commands. Do not use `sleep`; use tmux capture/polling loops with bounded attempts.
Parallelization: Can parallel: YES | Wave 3 | Blocks: [] | Blocked by: [3, 4, 5]
References (executor has NO interview context — be exhaustive):
- Command: `bun run build` — local plugin build.
- Command: `bun src/cli/index.ts run --agent Sisyphus --directory /Users/yeongyu/local-workspaces/omo --json "<message>"`
- Pattern: `src/cli/cli-program.ts:74` — local CLI supports `run <message>`.
- Pattern: `src/cli/run/AGENTS.md` — run command waits for todos/background tasks.
- Pattern: `src/tools/delegate-task/tools.ts:21` — `run_in_background` controls sync/background delegation.
- Pattern: `/tmp/oh-my-opencode.log` — project logger destination.
Acceptance criteria (agent-executable only):
- [ ] `bun run build` exits 0.
- [ ] tmux sync QA output includes an `explore` result referencing `src/tools/delegate-task/sync-session-creator.ts`.
- [ ] tmux sync QA output has no case-insensitive match for `Permission required|missing permission|permission denied`.
- [ ] tmux background QA output includes a background task ID and collected result.
- [ ] `/tmp/oh-my-opencode.log` has no child-session permission rejection for the QA session.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: sync delegated explore can read and run harmless bash
Tool: tmux
Steps: mkdir -p evidence && bun run build > evidence/task-7-build.txt && tmux new-session -d -s omo-delegate-sync-qa 'cd /Users/yeongyu/local-workspaces/omo && bun src/cli/index.ts run --agent Sisyphus --directory /Users/yeongyu/local-workspaces/omo --json "Use task with subagent_type=explore, run_in_background=false, load_skills=[] to inspect src/tools/delegate-task/sync-session-creator.ts and run pwd. Report the permission field behavior and the cwd."' ; poll `tmux capture-pane -pt omo-delegate-sync-qa` until the command exits or the pane shows JSON; save final capture.
Expected: Capture includes a useful exploration result and no permission-required text.
Evidence: evidence/task-7-sync-tmux.txt
Scenario: background delegated explore can read and run harmless bash
Tool: tmux
Steps: tmux new-session -d -s omo-delegate-bg-qa 'cd /Users/yeongyu/local-workspaces/omo && bun src/cli/index.ts run --agent Sisyphus --directory /Users/yeongyu/local-workspaces/omo --json "Launch task subagent_type=explore with run_in_background=true and load_skills=[] to inspect src/shared/agent-tool-restrictions.ts. Wait for completion notification, collect background_output, and report whether read/bash were usable."' ; poll `tmux capture-pane -pt omo-delegate-bg-qa` until JSON or completion; save final capture and relevant log excerpt.
Expected: Capture includes background result and no permission-required text.
Evidence: evidence/task-7-background-tmux.txt
```
Commit: NO | Message: `test(task): capture real delegate-task permission QA` | Files: [`evidence/task-7-build.txt`, `evidence/task-7-sync-tmux.txt`, `evidence/task-7-background-tmux.txt`]
## Final verification wave (MANDATORY — after all implementation tasks)
> Runs in PARALLEL. ALL must APPROVE. Surface results to the caller and wait for an explicit "okay" before declaring complete.
- [ ] F1. Plan compliance audit — every task done, every acceptance criterion met
- [ ] F2. Code quality review — diagnostics clean, idioms match, no dead code
- [ ] F3. Real manual QA — every QA scenario executed with evidence captured
- [ ] F4. Scope fidelity — nothing extra shipped beyond Must-Have, nothing Must-NOT-Have introduced
## Commit strategy
- One logical change per commit. Conventional Commits (`<type>(<scope>): <subject>` body + footer).
- Atomic: every commit builds and passes tests on its own.
- No "WIP" / "fix typo squash later" commits on the final branch — clean up before merge.
- Reference the plan file path in the final commit footer: `Plan: plans/fix-delegate-task-subagent-permissions.md`.
## Success criteria
- All Must-Have shipped; all QA scenarios pass with captured evidence; F1F4 approved; commit history clean.
+632
View File
@@ -0,0 +1,632 @@
# Fix Prompt Hang Race
## TL;DR
> Summary: Fix OMO's internal prompt gate so failed or timed-out prompt dispatches do not leave a stale reservation that blocks sibling recovery prompts after OpenCode `promptAsync` returns before durable prompt completion. Keep behavior unchanged except the bug fix, prove it with failing-first Bun tests, actual tmux-backed QA, CI, review-work, and Cubic before merge.
> Deliverables:
> - Shared prompt gate regression tests and minimal gate cleanup fix
> - Route-level regressions for model fallback, runtime fallback, background wakes, and team live delivery
> - Updated `.debugging` journal with root-cause evidence and cleanup ledger
> - PR against `dev`, green CI, review-work pass, Cubic pass, merged branch, removed worktree
> Effort: Medium
> Risk: High — async prompt acceptance is fire-and-forget upstream, so duplicate suppression and recovery retry timing are easy to regress.
## Scope
### Must have
- Work only in `/Users/yeongyu/local-workspaces/gpt 5.5 xhigh` on branch `code-yeongyu/fix-prompt-hang-race`.
- Preserve the sibling OpenCode repo at `/Users/yeongyu/local-workspaces/opencode`; read and run it only as evidence unless an explicit later request changes scope.
- Keep `.debugging` current with hypotheses, red/green evidence, manual QA evidence, artifacts, and cleanup status.
- Add failing-first tests before the fix for the stale prompt reservation behavior.
- Fix the smallest mechanism that makes failed or timed-out prompt dispatches release their reservation promptly while successful dispatches still dedupe immediate duplicate prompts.
- Cover main-session internal prompt routes: model fallback, runtime fallback, session recovery, background-agent parent wakes, and team-mode live delivery/wake hints.
- Run actual manual QA through local commands and tmux sessions owned by this task.
- Commit atomically, create a PR, iterate until CI, review-work, and Cubic are all passing, merge, then remove the worktree.
### Must NOT have (guardrails, anti-slop, scope boundaries)
- Do not modify sibling OpenCode product code.
- Do not kill the tmux server; only kill tmux sessions created by this task.
- Do not add compatibility layers, config switches, broad retry frameworks, or unrelated refactors.
- Do not bypass `src/shared/prompt-async-gate.ts` for any production internal prompt route.
- Do not delete failing tests, suppress type errors, use `as any`, `@ts-ignore`, or `@ts-expect-error`.
- Do not use `git reset --hard`, `git checkout --`, `rm -rf`, `--no-verify`, or direct `bun publish`.
- Do not treat a passing unit suite as enough; actual QA must exercise the local OpenCode/OMO prompt path.
## Verification strategy
> Zero human intervention — all verification is agent-executed.
- Test decision: TDD + Bun test (`bun:test`)
- QA policy: every task has agent-executed scenarios
- Evidence: `evidence/task-<N>-<slug>.<ext>`
## Execution strategy
### Parallel execution waves
> Target 58 tasks per wave. <3 per wave (except final) = under-splitting.
> Extract shared dependencies as Wave-1 tasks to maximize parallelism.
Wave 1 (no dependencies):
- Task 1: Confirm root cause and journal evidence
- Task 2: Fix shared prompt gate reservation semantics
- Task 3: Strengthen static prompt-route audit
Wave 2 (after Wave 1):
- Task 4: depends [2, 3]
- Task 5: depends [2, 3]
- Task 6: depends [2, 3]
- Task 7: depends [2, 3]
Wave 3 (after Wave 2):
- Task 8: depends [4, 5]
- Task 9: depends [4, 5, 6]
- Task 10: depends [6, 7]
- Task 11: depends [1, 2, 3, 4, 5, 6, 7]
Wave 4 (after Wave 3):
- Task 12: depends [8, 9, 10, 11]
Critical path: Task 2 → Task 4 → Task 8 → Task 11 → Task 12
### Dependency matrix
| Task | Depends on | Blocks | Can parallelize with |
|------|------------|--------|----------------------|
| 1 | none | 11 | 2, 3 |
| 2 | none | 4, 5, 6, 7, 11 | 1, 3 |
| 3 | none | 4, 5, 6, 7, 11 | 1, 2 |
| 4 | 2, 3 | 8, 9, 11 | 5, 6, 7 |
| 5 | 2, 3 | 8, 9, 11 | 4, 6, 7 |
| 6 | 2, 3 | 9, 10, 11 | 4, 5, 7 |
| 7 | 2, 3 | 10, 11 | 4, 5, 6 |
| 8 | 4, 5 | 12 | 9, 10, 11 |
| 9 | 4, 5, 6 | 12 | 8, 10, 11 |
| 10 | 6, 7 | 12 | 8, 9, 11 |
| 11 | 1, 2, 3, 4, 5, 6, 7 | 12 | 8, 9, 10 |
| 12 | 8, 9, 10, 11 | none | none |
## Todos
> Implementation + Test = ONE task. Never separate.
> Every task MUST have: References + Acceptance Criteria + QA Scenarios + Commit.
- [ ] 1. Confirm root cause and journal evidence
What to do: Update `.debugging` with the confirmed causal chain and the exact evidence already found: OpenCode `promptAsync` returns 204 after forking `SessionPrompt.prompt`, the fork later publishes `session.error` on failure, and OMO's gate can leave a short-lived reservation that overlaps recovery routes. Add the red/green/QA evidence ledger sections before creating more artifacts.
Must NOT do: Do not alter source code in this task. Do not remove existing `.debugging` entries.
Parallelization: Can parallel: YES | Wave 1 | Blocks: [11] | Blocked by: []
References (executor has NO interview context — be exhaustive):
- Pattern: `.debugging:58` — existing 2026-05-17 journal run starts here and must be preserved.
- Pattern: `.debugging:81` — existing hypothesis says OpenCode `promptAsync` can resolve before durable acceptance.
- Pattern: `.debugging:86` — existing artifact ledger includes the worktree and branch cleanup obligations.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:295``promptAsync` handler starts.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:300` — handler calls `promptSvc.prompt`.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:306` — fork failure publishes `Session.Event.Error`.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:312` — prompt effect is forked with `startImmediately: true`.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:314` — handler returns `NoContent`.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/session/prompt.ts:1092``createUserMessage` starts.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/session/prompt.ts:1098` — invalid agent creates `NamedError.Unknown`.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/session/prompt.ts:1099` — invalid agent publishes `Session.Event.Error`.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/session/prompt.ts:1614``SessionPrompt.prompt` returns a completed `MessageV2.WithParts` effect.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/session/prompt.ts:1631``noReply` returns after user message creation.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/sdk/js/src/gen/sdk.gen.ts:637` — SDK says `promptAsync` returns immediately.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/sdk/js/src/gen/types.gen.ts:2723``SessionPromptAsyncResponses` type starts.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/sdk/js/src/gen/types.gen.ts:2727``promptAsync` success is `204: void`.
- External: `https://github.com/anomalyco/opencode/issues/11616` — public docs issue describes `/prompt_async` as returning immediately and lists `session.error`.
- External: `https://github.com/anomalyco/opencode/issues/12860` — public issue reports `/prompt_async` status can stay unknown after submission.
Acceptance criteria (agent-executable only):
- [ ] `rg -n "Root cause|promptAsync|204|session.error|NoContent|fork" .debugging` prints the updated root-cause section.
- [ ] `rg -n "Artifacts To Revert|worktree|code-yeongyu/fix-prompt-hang-race|tmux" .debugging` confirms the cleanup ledger mentions the branch, worktree, and tmux constraints.
- [ ] `git diff -- .debugging > evidence/task-1-journal.diff` captures only journal changes for this task.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: root-cause journal is complete
Tool: bash
Steps: mkdir -p evidence; rg -n "Root cause|promptAsync|204|session.error|forkIn|NoContent" .debugging | tee evidence/task-1-journal.txt
Expected: output contains .debugging lines for OpenCode 204, forked prompt completion, later session.error, and OMO reservation overlap.
Evidence: evidence/task-1-journal.txt
Scenario: cleanup ledger is preserved
Tool: bash
Steps: rg -n "remove after merged PR|Never kill tmux server|worktree|branch" .debugging | tee evidence/task-1-cleanup-ledger.txt
Expected: output contains cleanup instructions for the PR branch/worktree and tmux server guardrail.
Evidence: evidence/task-1-cleanup-ledger.txt
```
Commit: YES | Message: `test(debugging): document prompt async race evidence` | Files: [`.debugging`]
- [ ] 2. Fix shared prompt gate reservation semantics
What to do: Add failing-first tests in the existing gate test file proving that timed-out and rejected dispatches release their reservation even when `postDispatchHoldMs` is the default, while successful dispatches keep the short post-dispatch hold. Then minimally change `src/shared/prompt-async-gate.ts` so post-dispatch hold is applied only after a real `dispatched` result, not merely after `dispatchAttempted = true`. If the executor confirms that OpenCode's 204 is still too early for recovery routes, keep the gate change minimal and leave route-triggered release to Tasks 4 and 5.
Must NOT do: Do not remove the successful-dispatch hold. Do not add a queue, debounce framework, global lock, or new config option.
Parallelization: Can parallel: YES | Wave 1 | Blocks: [4, 5, 6, 7, 11] | Blocked by: []
References (executor has NO interview context — be exhaustive):
- Pattern: `src/shared/prompt-async-gate.ts:97` — timeout wrapper starts.
- Pattern: `src/shared/prompt-async-gate.ts:122` — `dispatchAfterSessionIdle` starts.
- Pattern: `src/shared/prompt-async-gate.ts:158` — reservation object is created.
- Pattern: `src/shared/prompt-async-gate.ts:163` — reservation is stored before dispatch.
- Pattern: `src/shared/prompt-async-gate.ts:164` — current `dispatchAttempted` flag starts the risky state.
- Pattern: `src/shared/prompt-async-gate.ts:190` — dispatch is marked attempted before awaiting the SDK call.
- Pattern: `src/shared/prompt-async-gate.ts:191` — dispatch is wrapped with timeout.
- Pattern: `src/shared/prompt-async-gate.ts:197` — only this path returns `dispatched`.
- Pattern: `src/shared/prompt-async-gate.ts:198` — failure path returns `failed`.
- Pattern: `src/shared/prompt-async-gate.ts:201` — cleanup starts.
- Pattern: `src/shared/prompt-async-gate.ts:204` — current hold applies after any attempted dispatch.
- Pattern: `src/hooks/shared/prompt-async-gate.test.ts:60` — existing successful-dispatch hold test.
- Pattern: `src/hooks/shared/prompt-async-gate.test.ts:282` — existing timeout test uses `postDispatchHoldMs: 0`; add the default-hold regression beside it.
- Pattern: `src/hooks/shared/prompt-async-gate.test.ts:321` — existing rejected-dispatch test currently expects duplicate blocking; update or supersede with the corrected failing-first behavior.
- Test: `src/hooks/shared/prompt-async-gate.test.ts` — co-located Bun tests import from the shared gate re-export.
Acceptance criteria (agent-executable only):
- [ ] Before changing `src/shared/prompt-async-gate.ts`, `bun test src/hooks/shared/prompt-async-gate.test.ts --bail` fails on the new timeout/rejection reservation test; save output to `evidence/task-2-red.txt`.
- [ ] After the minimal fix, `bun test src/hooks/shared/prompt-async-gate.test.ts --bail` passes; save output to `evidence/task-2-green.txt`.
- [ ] `bun test src/shared/prompt-async-route-audit.test.ts src/hooks/shared/prompt-async-gate.test.ts --bail` passes.
- [ ] `git diff -- src/shared/prompt-async-gate.ts src/hooks/shared/prompt-async-gate.test.ts` shows no unrelated refactor.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: failed promptAsync releases reservation
Tool: bash
Steps: mkdir -p evidence; bun test src/hooks/shared/prompt-async-gate.test.ts --bail 2>&1 | tee evidence/task-2-green.txt
Expected: test output exits 0 and includes the new case where an immediate rejected dispatch lets the next caller attempt dispatch instead of returning reserved.
Evidence: evidence/task-2-green.txt
Scenario: successful promptAsync still dedupes immediate duplicate
Tool: bash
Steps: bun test src/hooks/shared/prompt-async-gate.test.ts --bail 2>&1 | tee evidence/task-2-success-hold.txt
Expected: existing successful hold tests still pass and assert prompt call count remains 1 for immediate duplicate after dispatch.
Evidence: evidence/task-2-success-hold.txt
```
Commit: YES | Message: `fix(prompt-gate): release failed dispatch reservations` | Files: [`src/shared/prompt-async-gate.ts`, `src/hooks/shared/prompt-async-gate.test.ts`]
- [ ] 3. Strengthen static prompt-route audit
What to do: Extend `src/shared/prompt-async-route-audit.test.ts` only if needed so the production invariant remains pinned: raw `session.prompt`/`session.promptAsync` calls stay inside the shared gate or documented wrappers, production callers cannot set `postDispatchHoldMs: 0`, and new route wrappers must throw or requeue on `failed` instead of silently dropping prompt failures. Keep the allowlist small and documented.
Must NOT do: Do not add a broad allowlist for convenience. Do not weaken the existing raw-prompt scanner.
Parallelization: Can parallel: YES | Wave 1 | Blocks: [4, 5, 6, 7, 11] | Blocked by: []
References (executor has NO interview context — be exhaustive):
- Pattern: `src/shared/prompt-async-route-audit.test.ts:6` — `SOURCE_ROOT` points at production `src`.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:8` — current raw prompt allowlist begins.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:48` — AST helper extracts property names.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:106` — raw prompt property detection starts.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:139` — destructured prompt binding detection starts.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:249` — production raw prompt audit test starts.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:270` — production `postDispatchHoldMs: 0` audit starts.
- Pattern: `src/plugin/unstable-agent-babysitter.ts:29` — wrapper currently ignores non-failed statuses and should be assessed by audit or route tests.
- Pattern: `src/features/background-agent/parent-wake-notifier.ts:153` — failed result is thrown and requeued in catch.
- Pattern: `src/features/team-mode/tools/messaging.ts:212` — non-dispatched live delivery falls back to inbox.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/shared/prompt-async-route-audit.test.ts --bail` passes.
- [ ] If the audit is changed, first save a red run in `evidence/task-3-red.txt` proving the audit catches the intended bad pattern.
- [ ] `rg -n "postDispatchHoldMs\\s*:\\s*0" src --glob '*.ts' --glob '!*.test.ts'` returns no production offenders.
- [ ] `bun test src/shared/prompt-async-route-audit.test.ts src/hooks/shared/prompt-async-gate.test.ts --bail` passes.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: raw prompt audit remains strict
Tool: bash
Steps: mkdir -p evidence; bun test src/shared/prompt-async-route-audit.test.ts --bail 2>&1 | tee evidence/task-3-audit.txt
Expected: command exits 0 and reports the production prompt route audit passing with the existing narrow allowlist.
Evidence: evidence/task-3-audit.txt
Scenario: production callers do not disable the hold
Tool: bash
Steps: rg -n "postDispatchHoldMs\\s*:\\s*0" src --glob '*.ts' --glob '!*.test.ts' 2>&1 | tee evidence/task-3-hold-audit.txt; test "${PIPESTATUS[0]}" -eq 1
Expected: no production TypeScript file sets postDispatchHoldMs to 0.
Evidence: evidence/task-3-hold-audit.txt
```
Commit: YES | Message: `test(prompt-gate): audit internal prompt routes` | Files: [`src/shared/prompt-async-route-audit.test.ts`]
- [ ] 4. Cover model-fallback promptAsync overlap
What to do: Add or update model-fallback tests so an OpenCode-style sequence is pinned: first internal `promptAsync` returns/appears accepted, then a `session.error` arrives before the post-dispatch hold expires. Same-model duplicate events must remain deduped, but a legitimate next fallback/recovery route must not be skipped solely because of a stale reservation. Make the smallest route fix in `src/plugin/event.ts` only if the shared gate fix does not satisfy the tests.
Must NOT do: Do not merge model-fallback and runtime-fallback state machines. Do not broaden fallback eligibility.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [8, 9, 11] | Blocked by: [2, 3]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/plugin/event.ts:451` — `autoContinueAfterFallback` starts.
- Pattern: `src/plugin/event.ts:462` — `modelFallbackContinuationsInFlight` is set.
- Pattern: `src/plugin/event.ts:465` — route aborts the active session before retry.
- Pattern: `src/plugin/event.ts:468` — route releases model-fallback reservations by exact source/prefix.
- Pattern: `src/plugin/event.ts:499` — async prompt path starts.
- Pattern: `src/plugin/event.ts:500` — route dispatches through `promptAsyncAfterSessionIdle`.
- Pattern: `src/plugin/event.ts:506` — `dispatched` sets `dispatched = true`.
- Pattern: `src/plugin/event.ts:508` — failed prompt is logged.
- Pattern: `src/plugin/event.ts:530` — cleanup/fallback dedupe state starts.
- Pattern: `src/plugin/event.model-fallback.test.ts:169` — existing overlapping error-events regression.
- Pattern: `src/plugin/event.model-fallback.test.ts:248` — existing providerless duplicate regression.
- Pattern: `src/plugin/event.model-fallback.test.ts:308` — existing distinct-provider regression expects two retries.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:295` — upstream `promptAsync` handler returns before prompt completion.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:306` — upstream later failure publishes `session.error`.
Acceptance criteria (agent-executable only):
- [ ] Before route or shared-gate fix is applied, save a red run for the new model-fallback overlap test in `evidence/task-4-red.txt`.
- [ ] `bun test src/plugin/event.model-fallback.test.ts --bail` passes after the fix.
- [ ] Existing duplicate suppression tests still assert one dispatch for same failed model and two dispatches for distinct providers.
- [ ] `git diff -- src/plugin/event.ts src/plugin/event.model-fallback.test.ts` shows only fallback prompt reservation/recovery changes and tests.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: duplicate same-model fallback remains suppressed
Tool: bash
Steps: mkdir -p evidence; bun test src/plugin/event.model-fallback.test.ts --bail 2>&1 | tee evidence/task-4-model-fallback.txt
Expected: command exits 0; assertions around overlapping same-model events still show one prompt dispatch.
Evidence: evidence/task-4-model-fallback.txt
Scenario: distinct provider fallback is not blocked by stale reservation
Tool: bash
Steps: bun test src/plugin/event.model-fallback.test.ts --bail 2>&1 | tee evidence/task-4-distinct-provider.txt
Expected: command exits 0; distinct-provider regression asserts two prompt dispatches rather than a reserved skip.
Evidence: evidence/task-4-distinct-provider.txt
```
Commit: YES | Message: `test(model-fallback): cover prompt async error overlap` | Files: [`src/plugin/event.ts`, `src/plugin/event.model-fallback.test.ts`]
- [ ] 5. Cover runtime-fallback retry cleanup
What to do: Add runtime-fallback tests proving a failed, timed-out, or OpenCode-style async error retry clears `sessionRetryInFlight`, `sessionAwaitingFallbackResult`, fallback timeout, and pending model state so the next eligible fallback attempt can dispatch. Make the smallest fix in `src/hooks/runtime-fallback/auto-retry.ts` if state cleanup or reservation release is incomplete.
Must NOT do: Do not change fallback model selection order, cooldown policy, or visible-response detection.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [8, 9, 11] | Blocked by: [2, 3]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:38` — `abortSessionRequest` starts.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:50` — abort call is issued.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:51` — runtime-fallback reservation release happens after abort.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:72` — fallback timeout scheduling starts.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:113` — `autoRetryWithFallback` starts.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:140` — retry-in-flight is set.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:157` — awaiting fallback result is set.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:160` — route dispatches through `promptAsyncAfterSessionIdle`.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:177` — failed gate result throws.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:193` — cleanup begins.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:195` — non-dispatched retries clear awaiting state and timeout.
- Pattern: `src/hooks/runtime-fallback/index.test.ts:1274` — existing in-flight race test starts.
- Pattern: `src/hooks/runtime-fallback/index.test.ts:1352` — existing force-advance retry signal test starts.
- Test: `src/hooks/runtime-fallback/index.test.ts` — broad runtime fallback integration tests.
- Test: `src/hooks/runtime-fallback/success-retry-key-cleanup.test.ts` — cleanup-specific test style.
Acceptance criteria (agent-executable only):
- [ ] Red evidence saved to `evidence/task-5-red.txt` for the new retry cleanup regression before implementation.
- [ ] `bun test src/hooks/runtime-fallback/index.test.ts src/hooks/runtime-fallback/success-retry-key-cleanup.test.ts --bail` passes.
- [ ] New assertions prove a second fallback attempt reaches `promptAsync` after the first failed or timed out attempt.
- [ ] No existing runtime-fallback duplicate suppression test starts dispatching duplicate same-source retries.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: failed runtime fallback retry clears state
Tool: bash
Steps: mkdir -p evidence; bun test src/hooks/runtime-fallback/index.test.ts src/hooks/runtime-fallback/success-retry-key-cleanup.test.ts --bail 2>&1 | tee evidence/task-5-runtime-fallback.txt
Expected: command exits 0 and includes the new cleanup regression.
Evidence: evidence/task-5-runtime-fallback.txt
Scenario: in-flight duplicate suppression still works
Tool: bash
Steps: bun test src/hooks/runtime-fallback/index.test.ts --bail 2>&1 | tee evidence/task-5-inflight.txt
Expected: command exits 0 and the existing in-flight race test still expects a single fallback preparation while the retry is pending.
Evidence: evidence/task-5-inflight.txt
```
Commit: YES | Message: `fix(runtime-fallback): clear failed retry prompt state` | Files: [`src/hooks/runtime-fallback/auto-retry.ts`, `src/hooks/runtime-fallback/index.test.ts`, `src/hooks/runtime-fallback/success-retry-key-cleanup.test.ts`]
- [ ] 6. Cover background-agent prompt wake and resume paths
What to do: Add tests around background-agent launch/resume/parent-wake prompt failures so a failed or timed-out gated prompt does not leave the task in a hanging in-between state and does not block the retry/restore path behind a stale reservation. Fix only the affected background-agent path if the shared gate is not enough.
Must NOT do: Do not change background concurrency limits, polling stability thresholds, task state schema, or tmux behavior.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [9, 10, 11] | Blocked by: [2, 3]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/features/background-agent/manager.ts:831` — task history records launch state before prompt dispatch.
- Pattern: `src/features/background-agent/manager.ts:892` — launch prompt uses `promptWithRetryInDirectory`.
- Pattern: `src/features/background-agent/manager.ts:895` — launch prompt failure is caught.
- Pattern: `src/features/background-agent/manager.ts:947` — launch prompt failure can try fallback retry.
- Pattern: `src/features/background-agent/manager.ts:1279` — resume path comments fire-and-forget prompt.
- Pattern: `src/features/background-agent/manager.ts:1293` — resume dispatches through `promptAsyncAfterSessionIdle`.
- Pattern: `src/features/background-agent/manager.ts:1321` — failed resume prompt throws into catch.
- Pattern: `src/features/background-agent/manager.ts:1339` — resume failure can try fallback retry.
- Pattern: `src/features/background-agent/parent-wake-notifier.ts:137` — parent wake dispatches through `promptAsyncAfterSessionIdle`.
- Pattern: `src/features/background-agent/parent-wake-notifier.ts:153` — failed parent wake throws and requeues.
- Pattern: `src/features/background-agent/manager.test.ts:7548` — existing stale launch prompt error regression.
- Pattern: `src/features/background-agent/manager.test.ts:7556` — existing launch prompt can remain pending until rejected.
- Test: `src/features/background-agent/manager.test.ts` — large integration-style manager test file.
- Test: `src/features/background-agent/parent-wake-notifier.test.ts` — use if present; otherwise add focused coverage next to the notifier.
Acceptance criteria (agent-executable only):
- [ ] Red evidence saved to `evidence/task-6-red.txt` before background-agent fix.
- [ ] `bun test src/features/background-agent/manager.test.ts --bail` passes.
- [ ] If a notifier test file exists or is added, `bun test src/features/background-agent/parent-wake-notifier.test.ts --bail` passes.
- [ ] Assertions prove failed parent wake is requeued and failed resume restores or transitions task state instead of leaving an in-flight prompt hang.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: launch/resume failure does not hang task state
Tool: bash
Steps: mkdir -p evidence; bun test src/features/background-agent/manager.test.ts --bail 2>&1 | tee evidence/task-6-background-manager.txt
Expected: command exits 0 and includes the new failed prompt launch/resume regression.
Evidence: evidence/task-6-background-manager.txt
Scenario: parent wake is requeued after failed prompt
Tool: bash
Steps: if [ -f src/features/background-agent/parent-wake-notifier.test.ts ]; then bun test src/features/background-agent/parent-wake-notifier.test.ts --bail; else bun test src/features/background-agent/manager.test.ts --bail; fi 2>&1 | tee evidence/task-6-parent-wake.txt
Expected: command exits 0 and the tested path asserts a wake is requeued after a failed gated prompt.
Evidence: evidence/task-6-parent-wake.txt
```
Commit: YES | Message: `test(background-agent): cover failed prompt wake recovery` | Files: [`src/features/background-agent/manager.ts`, `src/features/background-agent/manager.test.ts`, `src/features/background-agent/parent-wake-notifier.ts`, `src/features/background-agent/parent-wake-notifier.test.ts`]
- [ ] 7. Cover team-mode live delivery and wake hint paths
What to do: Add tests proving team live delivery and idle wake hints recover from failed or gated prompt dispatch by leaving mailbox fallback paths available and not permanently reserving the recipient session. Fix only the affected team route if the shared gate is not enough.
Must NOT do: Do not change team storage schema, member eligibility, worktree creation, or tmux layout behavior.
Parallelization: Can parallel: YES | Wave 2 | Blocks: [10, 11] | Blocked by: [2, 3]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/features/team-mode/tools/messaging.ts:155` — `deliverLive` starts.
- Pattern: `src/features/team-mode/tools/messaging.ts:168` — mailbox reservation is taken before live delivery.
- Pattern: `src/features/team-mode/tools/messaging.ts:202` — live delivery dispatches through `promptAsyncAfterSessionIdle`.
- Pattern: `src/features/team-mode/tools/messaging.ts:212` — non-dispatched live delivery falls back to inbox injection.
- Pattern: `src/features/team-mode/tools/messaging.ts:234` — live delivery catch releases mailbox reservation.
- Pattern: `src/hooks/team-session-events/team-idle-wake-hint.ts:102` — wake hint skips if promptAsync unavailable.
- Pattern: `src/hooks/team-session-events/team-idle-wake-hint.ts:114` — wake hint dispatches through `promptAsyncAfterSessionIdle`.
- Pattern: `src/hooks/team-session-events/team-idle-wake-hint.ts:125` — non-dispatched wake hint is logged.
- Test: `src/features/team-mode/tools/messaging.test.ts` — live delivery tests include network-down cases around line 615.
- Test: `src/hooks/team-session-events/team-idle-wake-hint.test.ts` — wake hint tests cover dispatch and skip behavior.
Acceptance criteria (agent-executable only):
- [ ] Red evidence saved to `evidence/task-7-red.txt` before team-mode fix.
- [ ] `bun test src/features/team-mode/tools/messaging.test.ts src/hooks/team-session-events/team-idle-wake-hint.test.ts --bail` passes.
- [ ] Live delivery failure releases mailbox reservation and leaves the message available for inbox fallback.
- [ ] Wake hint failure does not leave recipient session reserved for the next legitimate prompt.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: team live delivery fallback remains available
Tool: bash
Steps: mkdir -p evidence; bun test src/features/team-mode/tools/messaging.test.ts --bail 2>&1 | tee evidence/task-7-team-messaging.txt
Expected: command exits 0 and includes failed live-delivery fallback coverage.
Evidence: evidence/task-7-team-messaging.txt
Scenario: team wake hint does not poison prompt gate
Tool: bash
Steps: bun test src/hooks/team-session-events/team-idle-wake-hint.test.ts --bail 2>&1 | tee evidence/task-7-team-wake.txt
Expected: command exits 0 and includes the wake-hint failure or gated retry regression.
Evidence: evidence/task-7-team-wake.txt
```
Commit: YES | Message: `test(team-mode): cover failed live prompt delivery` | Files: [`src/features/team-mode/tools/messaging.ts`, `src/features/team-mode/tools/messaging.test.ts`, `src/hooks/team-session-events/team-idle-wake-hint.ts`, `src/hooks/team-session-events/team-idle-wake-hint.test.ts`]
- [ ] 8. Manual QA direct upstream promptAsync failure path
What to do: Exercise the sibling OpenCode promptAsync behavior against a local session so the evidence shows `prompt_async` returns before the later failure event. Use a task-owned tmux session or direct command with bounded polling. Capture request, status, emitted error, and cleanup commands.
Must NOT do: Do not modify sibling OpenCode code. Do not kill the tmux server. Do not rely on a simulated unit test for this task.
Parallelization: Can parallel: YES | Wave 3 | Blocks: [12] | Blocked by: [4, 5]
References (executor has NO interview context — be exhaustive):
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:295` — promptAsync handler.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:306` — later failure event publication.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/server/routes/instance/httpapi/handlers/session.ts:314` — NoContent return.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/opencode/src/session/prompt.ts:1098` — invalid agent error.
- API/Type: `/Users/yeongyu/local-workspaces/opencode/packages/sdk/js/src/gen/types.gen.ts:2727` — SDK success response is 204 void.
- Pattern: `.debugging:86` — cleanup ledger must track tmux sessions and worktree cleanup.
- Test: `packages/opencode/test/server/httpapi-promptasync-context.test.ts` in sibling repo — upstream already has promptAsync context coverage and can be used as a reference pattern.
Acceptance criteria (agent-executable only):
- [ ] `evidence/task-8-opencode-promptasync.txt` contains a 204/NoContent observation and a later `session.error` observation for the same session.
- [ ] `.debugging` records the manual QA command, evidence path, and owned tmux session name if tmux is used.
- [ ] Any tmux session created by this task is killed by name after evidence capture; `tmux ls` still works and no `kill-server` command is used.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: upstream promptAsync returns before durable completion
Tool: tmux
Steps: mkdir -p evidence; create a task-owned tmux session named omo-promptasync-upstream-qa that starts or connects to local OpenCode, creates a session, posts /prompt_async with an invalid agent, subscribes to events, writes the 204 response and subsequent session.error to evidence/task-8-opencode-promptasync.txt, signals completion with tmux wait-for; then kill only tmux session omo-promptasync-upstream-qa.
Expected: evidence file contains the same session id, HTTP 204 or NoContent for prompt_async, and later session.error with Agent not found or equivalent prompt failure.
Evidence: evidence/task-8-opencode-promptasync.txt
Scenario: tmux server is preserved
Tool: bash
Steps: tmux ls 2>&1 | tee evidence/task-8-tmux-ls.txt
Expected: command succeeds or reports no sessions; no task command used tmux kill-server.
Evidence: evidence/task-8-tmux-ls.txt
```
Commit: NO | Message: `n/a` | Files: [`evidence/task-8-opencode-promptasync.txt`, `.debugging`]
- [ ] 9. Manual QA OMO main-session no-hang path
What to do: Run an actual OMO/OpenCode session in a task-owned tmux session and reproduce the original main-session internal prompt race as closely as possible: trigger an internal fallback/recovery prompt, force a prompt failure or timeout, and verify the next recovery/fallback prompt is dispatched or requeued instead of hanging behind `reserved`. Capture `/tmp/oh-my-opencode.log`, session output, and final status.
Must NOT do: Do not declare success from tests alone. Do not kill global tmux server. Do not leave an OpenCode server or task-owned tmux session running.
Parallelization: Can parallel: YES | Wave 3 | Blocks: [12] | Blocked by: [4, 5, 6]
References (executor has NO interview context — be exhaustive):
- Pattern: `package.json:24` — scripts section starts.
- Pattern: `package.json:25` — `bun run build` command.
- Pattern: `package.json:36` — `bun run typecheck` command.
- Pattern: `package.json:38` — `bun test` command.
- Pattern: `src/plugin/event.ts:499` — model-fallback async prompt path.
- Pattern: `src/hooks/runtime-fallback/auto-retry.ts:160` — runtime-fallback async retry path.
- Pattern: `src/features/background-agent/parent-wake-notifier.ts:137` — background parent wake prompt path.
- Pattern: `src/shared/prompt-async-gate.ts:189` — prompt gate logs dispatching.
- Pattern: `src/shared/prompt-async-gate.ts:199` — prompt gate logs failed.
- Pattern: `/tmp/oh-my-opencode.log` — project logger target from AGENTS.md.
Acceptance criteria (agent-executable only):
- [ ] `evidence/task-9-omo-main-session.txt` contains the actual OMO run transcript and exits without indefinite wait.
- [ ] `evidence/task-9-omo-log.txt` contains prompt gate dispatch/failure/retry evidence and no final stale `reserved` skip for the target session.
- [ ] The owned tmux session is removed by name and no tmux server kill is used.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: OMO main-session recovery does not hang after prompt failure
Tool: tmux
Steps: mkdir -p evidence; truncate or mark /tmp/oh-my-opencode.log with a QA delimiter; start a task-owned tmux session named omo-main-prompt-race-qa from /Users/yeongyu/local-workspaces/gpt 5.5 xhigh; run the built local CLI or local plugin against sibling OpenCode with a prompt that triggers fallback/recovery; wait via tmux wait-for; capture pane to evidence/task-9-omo-main-session.txt and log slice to evidence/task-9-omo-log.txt; kill only omo-main-prompt-race-qa.
Expected: transcript reaches a terminal success or expected handled failure, and log shows any failed/reserved prompt is followed by cleanup/retry/requeue rather than permanent hang.
Evidence: evidence/task-9-omo-main-session.txt
Scenario: no stale prompt reservation after QA
Tool: bash
Steps: rg -n "prompt-async-gate.*(reserved|failed|dispatched)|model-fallback|runtime-fallback" evidence/task-9-omo-log.txt | tee evidence/task-9-prompt-gate-log.txt
Expected: output shows the target session's failed prompt path and a subsequent dispatch/requeue; it does not end with only a reserved skip.
Evidence: evidence/task-9-prompt-gate-log.txt
```
Commit: NO | Message: `n/a` | Files: [`evidence/task-9-omo-main-session.txt`, `evidence/task-9-omo-log.txt`, `.debugging`]
- [ ] 10. Manual QA background and team routes
What to do: Exercise at least one background-agent parent wake/resume path and one team live-delivery/wake path in real local execution or, if team-mode live execution is blocked by credentials/config, use the closest agent-executed CLI/tool invocation plus captured logs and explain the limitation in `.debugging`. The key pass condition is no permanent prompt reservation after a failed prompt delivery; background wakes requeue and team messages remain available through inbox fallback.
Must NOT do: Do not create nested teams. Do not leave team worktrees, mailbox files, or tmux panes unmanaged. Do not remove user team config.
Parallelization: Can parallel: YES | Wave 3 | Blocks: [12] | Blocked by: [6, 7]
References (executor has NO interview context — be exhaustive):
- Pattern: `src/features/background-agent/parent-wake-notifier.ts:137` — background parent wake prompt dispatch.
- Pattern: `src/features/background-agent/parent-wake-notifier.ts:153` — failed wake throws into requeue path.
- Pattern: `src/features/background-agent/manager.ts:1293` — background resume prompt dispatch.
- Pattern: `src/features/team-mode/tools/messaging.ts:168` — live delivery reserves mailbox entry.
- Pattern: `src/features/team-mode/tools/messaging.ts:202` — live delivery prompt dispatch.
- Pattern: `src/features/team-mode/tools/messaging.ts:241` — catch releases mailbox reservation.
- Pattern: `src/hooks/team-session-events/team-idle-wake-hint.ts:114` — wake hint prompt dispatch.
- Pattern: `src/features/team-mode/AGENTS.md:1` — team-mode overview and guardrails.
Acceptance criteria (agent-executable only):
- [ ] `evidence/task-10-background-route.txt` contains real background route evidence showing no hang and correct requeue/handled failure.
- [ ] `evidence/task-10-team-route.txt` contains real team route evidence or a documented blocked-run fallback with the exact command and reason.
- [ ] `.debugging` records any created team run, worktree, tmux session, mailbox path, and cleanup command.
- [ ] Any created team/task artifacts are cleaned up or explicitly preserved only if they are required evidence.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: background failed prompt route is handled
Tool: tmux
Steps: run a task-owned local OMO session that launches or resumes a background task with a controlled prompt failure; capture task output and /tmp/oh-my-opencode.log to evidence/task-10-background-route.txt; clean up only task-owned tmux sessions.
Expected: background task reaches completed, error, interrupt, or requeued state; it does not remain indefinitely running due to a reserved prompt gate.
Evidence: evidence/task-10-background-route.txt
Scenario: team live delivery failure falls back without stale reservation
Tool: bash
Steps: execute the smallest team-mode command sequence available in this worktree/config to send a member message or wake hint; if full team-mode is unavailable, run the team-mode focused test plus log why real execution is blocked; capture output to evidence/task-10-team-route.txt.
Expected: real route or documented blocked fallback shows failed prompt delivery releases mailbox reservation or leaves inbox fallback available.
Evidence: evidence/task-10-team-route.txt
```
Commit: NO | Message: `n/a` | Files: [`evidence/task-10-background-route.txt`, `evidence/task-10-team-route.txt`, `.debugging`]
- [ ] 11. Local full verification and atomic commits
What to do: Run focused tests, full Bun tests, typecheck, and build locally. Group commits by logical unit if earlier tasks have not already committed. Preserve `.debugging` updates and keep evidence files uncommitted unless the user explicitly wants evidence committed.
Must NOT do: Do not commit unrelated dirty files. Do not modify `package.json` version. Do not use `--no-verify`.
Parallelization: Can parallel: YES | Wave 3 | Blocks: [12] | Blocked by: [1, 2, 3, 4, 5, 6, 7]
References (executor has NO interview context — be exhaustive):
- Pattern: `package.json:25` — build script.
- Pattern: `package.json:36` — typecheck script.
- Pattern: `package.json:38` — test script.
- Pattern: `src/hooks/shared/prompt-async-gate.test.ts:282` — gate timeout regression area.
- Pattern: `src/plugin/event.model-fallback.test.ts:169` — model-fallback race regression area.
- Pattern: `src/hooks/runtime-fallback/index.test.ts:1274` — runtime-fallback in-flight regression area.
- Pattern: `src/features/background-agent/manager.test.ts:7548` — background stale prompt error regression area.
- Pattern: `src/features/team-mode/tools/messaging.test.ts` — team messaging coverage.
- Pattern: `src/shared/prompt-async-route-audit.test.ts:249` — raw prompt audit.
- External: `/Users/yeongyu/.agents/skills/git-master/SKILL.md` — atomic commit guidance.
Acceptance criteria (agent-executable only):
- [ ] `bun test src/hooks/shared/prompt-async-gate.test.ts src/shared/prompt-async-route-audit.test.ts src/plugin/event.model-fallback.test.ts src/hooks/runtime-fallback/index.test.ts --bail` passes.
- [ ] `bun test src/features/background-agent/manager.test.ts src/features/team-mode/tools/messaging.test.ts src/hooks/team-session-events/team-idle-wake-hint.test.ts --bail` passes.
- [ ] `bun test` passes.
- [ ] `bun run typecheck` passes.
- [ ] `bun run build` passes.
- [ ] `git status --short` shows only intended committed changes plus untracked evidence if evidence is intentionally not committed.
- [ ] `git log --oneline origin/dev..HEAD` shows atomic conventional commits and no WIP commits.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: full local verification passes
Tool: bash
Steps: mkdir -p evidence; bun test 2>&1 | tee evidence/task-11-bun-test.txt; bun run typecheck 2>&1 | tee evidence/task-11-typecheck.txt; bun run build 2>&1 | tee evidence/task-11-build.txt
Expected: all three commands exit 0.
Evidence: evidence/task-11-bun-test.txt
Scenario: commit history is clean
Tool: bash
Steps: git status --short | tee evidence/task-11-git-status.txt; git log --oneline origin/dev..HEAD | tee evidence/task-11-git-log.txt
Expected: no unrelated tracked changes remain unstaged/uncommitted; commits are conventional and logically atomic.
Evidence: evidence/task-11-git-log.txt
```
Commit: YES | Message: `chore(prompt-race): verify local prompt recovery fix` | Files: [`.debugging`, `src/shared/prompt-async-gate.ts`, `src/hooks/shared/prompt-async-gate.test.ts`, `src/shared/prompt-async-route-audit.test.ts`, `src/plugin/event.ts`, `src/plugin/event.model-fallback.test.ts`, `src/hooks/runtime-fallback/auto-retry.ts`, `src/hooks/runtime-fallback/*.test.ts`, `src/features/background-agent/*.ts`, `src/features/background-agent/*.test.ts`, `src/features/team-mode/**/*.ts`, `src/features/team-mode/**/*.test.ts`, `src/hooks/team-session-events/*.ts`, `src/hooks/team-session-events/*.test.ts`]
- [ ] 12. Create PR, pass CI/reviews, merge, and clean up worktree
What to do: Push branch, create a PR against `dev`, run the verification loop until CI, review-work, and Cubic all pass. Use a PR body file under `/tmp/pull-request-prompt-hang-race-<timestamp>.md` and get user confirmation before `gh pr create` per project instruction. After all gates pass, merge as requested by the PR workflow, then remove `/Users/yeongyu/local-workspaces/gpt 5.5 xhigh` worktree after merge.
Must NOT do: Do not create the PR body inline. Do not merge before CI, review-work, and Cubic all pass. Do not remove the worktree before merge. Do not delete user data or unrelated worktrees.
Parallelization: Can parallel: NO | Wave 4 | Blocks: [] | Blocked by: [8, 9, 10, 11]
References (executor has NO interview context — be exhaustive):
- External: `/Users/yeongyu/local-workspaces/gpt 5.5 xhigh/.agents/skills/work-with-pr/SKILL.md` — PR lifecycle requires CI, review-work, Cubic, merge, and worktree cleanup.
- External: `/Users/yeongyu/.agents/skills/git-master/SKILL.md` — commit history must be atomic before push.
- Pattern: `package.json:25` — build command mirrors CI build.
- Pattern: `package.json:36` — typecheck command mirrors CI typecheck.
- Pattern: `package.json:38` — test command mirrors CI root test.
- Pattern: `.debugging:88` — worktree removal is already tracked as a cleanup artifact.
- Pattern: `.debugging:89` — branch cleanup is already tracked as a cleanup artifact.
Acceptance criteria (agent-executable only):
- [ ] PR exists and targets `dev`; `gh pr view --json number,baseRefName,headRefName,url` saved to `evidence/task-12-pr.json`.
- [ ] `gh pr checks --watch --fail-fast` passes for the PR head.
- [ ] review-work final report has no blocking issues and is saved to `evidence/task-12-review-work.txt`.
- [ ] Cubic comment says no issues found, or equivalent pass status, saved to `evidence/task-12-cubic.txt`.
- [ ] PR is merged.
- [ ] `git worktree list` no longer contains `/Users/yeongyu/local-workspaces/gpt 5.5 xhigh` after merge cleanup.
QA scenarios (MANDATORY — task incomplete without these):
```
Scenario: PR gates all pass
Tool: bash
Steps: mkdir -p evidence; gh pr view --json number,baseRefName,headRefName,url > evidence/task-12-pr.json; gh pr checks --watch --fail-fast 2>&1 | tee evidence/task-12-ci.txt; run review-work and save its final report to evidence/task-12-review-work.txt; query PR comments/reviews for Cubic and save pass evidence to evidence/task-12-cubic.txt.
Expected: PR targets dev, CI exits 0, review-work has no blocking issues, and Cubic reports no issues found.
Evidence: evidence/task-12-ci.txt
Scenario: merged branch worktree cleanup
Tool: bash
Steps: after merge, run git worktree list | tee evidence/task-12-worktrees-before-cleanup.txt; remove only /Users/yeongyu/local-workspaces/gpt 5.5 xhigh with git worktree remove; run git worktree list | tee evidence/task-12-worktrees-after-cleanup.txt.
Expected: after-cleanup evidence does not contain /Users/yeongyu/local-workspaces/gpt 5.5 xhigh.
Evidence: evidence/task-12-worktrees-after-cleanup.txt
```
Commit: NO | Message: `n/a` | Files: [`/tmp/pull-request-prompt-hang-race-<timestamp>.md`, `evidence/task-12-pr.json`, `evidence/task-12-ci.txt`, `evidence/task-12-review-work.txt`, `evidence/task-12-cubic.txt`]
## Final verification wave (MANDATORY — after all implementation tasks)
> Runs in PARALLEL. ALL must APPROVE. Surface results to the caller and wait for an explicit "okay" before declaring complete.
- [ ] F1. Plan compliance audit — every task done, every acceptance criterion met
- [ ] F2. Code quality review — diagnostics clean, idioms match, no dead code
- [ ] F3. Real manual QA — every QA scenario executed with evidence captured
- [ ] F4. Scope fidelity — nothing extra shipped beyond Must-Have, nothing Must-NOT-Have introduced
## Commit strategy
- One logical change per commit. Conventional Commits (`<type>(<scope>): <subject>` body + footer).
- Atomic: every commit builds and passes tests on its own.
- No "WIP" / "fix typo squash later" commits on the final branch — clean up before merge.
- Recommended commit sequence:
- `test(debugging): document prompt async race evidence`
- `fix(prompt-gate): release failed dispatch reservations`
- `test(prompt-gate): audit internal prompt routes`
- `test(model-fallback): cover prompt async error overlap`
- `fix(runtime-fallback): clear failed retry prompt state`
- `test(background-agent): cover failed prompt wake recovery`
- `test(team-mode): cover failed live prompt delivery`
- Reference the plan file path in the final commit footer: `Plan: plans/fix-prompt-hang-race.md`.
## Success criteria
- All Must-Have shipped; all QA scenarios pass with captured evidence; F1F4 approved; commit history clean.
+60
View File
@@ -0,0 +1,60 @@
# Migrate Legacy Workspace State To .omo
## Goal
Move project-scoped OMO state from `.sisyphus` to `.omo`, while preserving existing legacy state by copying `.sisyphus` into `.omo` on plugin startup when `.sisyphus` is detected.
## Task Graph
1. Add shared legacy workspace migration helper.
- Depends on: none.
- Acceptance: copies nested files from `.sisyphus` into `.omo`, creates missing directories, does not overwrite existing `.omo` files, and returns whether anything migrated.
2. Invoke migration at plugin startup.
- Depends on: task 1.
- Acceptance: startup calls the helper before managers/tools/hooks are created.
3. Switch runtime state constants to `.omo`.
- Depends on: task 1.
- Acceptance: Boulder and run-continuation writes land under `.omo`.
4. Switch guardrails and prompt-facing workspace paths to `.omo`.
- Depends on: task 3.
- Acceptance: Prometheus, Atlas, notepad, write-existing guard, and plan extraction surfaces point at `.omo`.
5. Update docs, ignore rules, and generated schema.
- Depends on: tasks 3-4.
- Acceptance: user-facing storage docs and schema examples no longer advertise `.sisyphus` for active workspace state.
6. Verify and ship.
- Depends on: tasks 1-5.
- Acceptance: focused tests, typecheck, full test suite, build, manual QA, CI, GPT-5.2 review, and Cubic all pass before merge.
## Dependency Matrix
| Task | Blocks | Verification |
| --- | --- | --- |
| Migration helper | Startup invocation, path switch | `bun test src/shared/legacy-workspace-migration.test.ts` |
| Startup invocation | Manual QA | `bun test src/testing/create-plugin-module.test.ts` or equivalent startup test |
| Runtime constants | Hook and CLI behavior | Boulder/run-continuation focused tests |
| Guardrails/prompts | Agent workflow behavior | Prometheus/Atlas/write guard focused tests |
| Docs/schema | PR review and release docs | `bun run build:schema`, `rg "\\.sisyphus"` audit |
| Verification | Merge | local gates, PR CI, reviews |
## QA Scenarios
- Happy path: project has `.sisyphus/plans/a.md` and no `.omo`; startup creates `.omo/plans/a.md` with same content.
- Existing target: project has `.sisyphus/plans/a.md` and `.omo/plans/a.md`; migration leaves `.omo/plans/a.md` unchanged.
- Mixed tree: project has `.sisyphus/plans/a.md` and `.omo/notepads/note.md`; migration copies only missing legacy files.
- Runtime write: `writeBoulderState()` creates `.omo/boulder.json`.
- Continuation marker: `setContinuationMarkerSource()` writes `.omo/run-continuation/<session>.json`.
- Guardrail: Prometheus may write `.omo/plans/*.md` and is blocked outside `.omo`.
- Adjacent compatibility: rules injector still discovers legacy `.sisyphus/rules` alongside `.omo/rules` if kept for transition.
## Commit Strategy
1. `feat(workspace): migrate legacy sisyphus state to omo`
- Migration helper, startup invocation, runtime constants, and direct tests.
2. `docs(workspace): document omo workspace paths`
- Docs, `.gitignore`, schema output, and prompt/docs wording updates if separated cleanly.
+43
View File
@@ -0,0 +1,43 @@
# script/ -- Build/Publish Automation
**Generated:** 2026-05-18
## OVERVIEW
Build and publish automation scripts. Run via `bun run <script>` from root package.json. 13 files total (10 source + 3 test). Singular directory name (not "scripts/").
## SCRIPTS
| File | Purpose |
|------|---------|
| `build-binaries.ts` | 11 platform binaries via `bun compile` (darwin/linux/windows, AVX2 + baseline) |
| `build-schema.ts` | Zod schema to JSON Schema for `assets/oh-my-opencode.schema.json` |
| `build-schema-document.ts` | Helper: `createOhMyOpenCodeJsonSchema()` for build-schema.ts |
| `build-model-capabilities.ts` | Refresh `src/generated/model-capabilities.generated.json` from models.dev |
| `patch-node-require-shim.ts` | Patches `dist/index.js` for Node/Electron require compatibility |
| `publish.ts` | Local multi-package publish alternative (platform packages + npm) |
| `generate-changelog.ts` | Release notes from git log, filters bot commits |
| `run-ci-tests.ts` | Test isolation: separates `mock.module()` users from shared tests |
## TESTS
| File | Coverage |
|------|----------|
| `build-binaries.test.ts` | Platform target validation |
| `build-schema.test.ts` | JSON Schema generation |
| `publish-workflow.test.ts` | Publish logic |
| `run-ci-tests.test.ts` | Isolation runner |
## RUN VIA PACKAGE.JSON
- `bun run build:binaries` → build-binaries.ts
- `bun run build:schema` → build-schema.ts
- `bun run build:model-capabilities` → build-model-capabilities.ts
## TSCONFIG
`tsconfig.json` is script-specific (separate from `src/`). Includes only `publish-workflow.test.ts`.
## NOTE
`run-ci-tests.ts` is referenced by `ci.yml` but CI now uses plain `bun test` (no sharding or split isolation runner). The isolation logic remains for local use.
+15 -15
View File
@@ -1,10 +1,10 @@
# src/ — Plugin Source
**Generated:** 2026-05-15
**Generated:** 2026-05-18
## OVERVIEW
Entry `index.ts` orchestrates a 7-step initialization. Total: 1340 source files + 701 tests across the directories below. Cross-cutting helpers live in `shared/`; module boundaries are established by 122 barrel `index.ts` files.
Entry `index.ts` orchestrates a 7-step initialization. Total: 1351 source files + 722 tests across the directories below. Cross-cutting helpers live in `shared/`; module boundaries are established by 122 barrel `index.ts` files.
## KEY FILES
@@ -88,19 +88,19 @@ Total: 54 base, 61 with team-mode. Each tier produces an object whose values are
| Subdir | Files (.ts) | LOC | Purpose | Has AGENTS.md |
|--------|-------------|-----|---------|---------------|
| `agents/` | 102 | 19,660 | 11 agent factories + dynamic prompt builder | yes |
| `hooks/` | 581 | 78,030 | ~52 lifecycle hooks across 58 dirs | yes |
| `tools/` | 314 | 44,768 | 16 tool dirs producing 2039 tools | yes |
| `features/` | 400 | 70,934 | 20 feature modules (team-mode, background-agent, boulder-state, etc.) | yes |
| `shared/` | 278 | 32,847 | Cross-cutting utilities, barrel-exported | yes |
| `cli/` | 158 | 17,812 | Commander.js CLI: install, run, doctor, mcp-oauth, boulder | yes |
| `plugin/` | 56 | 12,390 | 10 OpenCode hook handlers + hook composition | yes |
| `config/` | 41 | 2,340 | 30 Zod v4 schema files | yes |
| `plugin-handlers/` | 27 | 5,841 | 6-phase config loading pipeline | yes |
| `openclaw/` | 26 | 3,293 | Bidirectional Discord/Telegram/HTTP integration | yes |
| `__tests__/` | 22 | 275 | Plugin-level integration tests + perf fixtures | — |
| `mcp/` | 7 | 205 | 3 built-in remote MCPs | yes |
| `testing/` | 2 | 225 | Test utilities | — |
| `agents/` | 104 | ~20k | 11 agent factories + dynamic prompt builder | yes (+ atlas, hephaestus, prometheus, sisyphus, sisyphus-junior, builtin-agents) |
| `hooks/` | 596 | ~78k | ~52 lifecycle hooks across 58 dirs | yes (+ atlas, anthropic-context-window-limit-recovery, auto-update-checker, claude-code-hooks, comment-checker, compaction-context-injector, keyword-detector, ralph-loop, rules-injector, runtime-fallback, session-recovery, todo-continuation-enforcer) |
| `tools/` | 317 | ~45k | 16 tool dirs producing 2039 tools | yes (+ ast-grep, background-task, call-omo-agent, delegate-task, hashline-edit, look-at, lsp, skill) |
| `features/` | 404 | ~71k | 20 feature modules (team-mode, background-agent, boulder-state, etc.) | yes (+ 11 sub-AGENTS.md including builtin-skills, team-mode, background-agent, claude-code-*) |
| `shared/` | 290 | ~33k | Cross-cutting utilities, barrel-exported | yes |
| `cli/` | 158 | ~18k | Commander.js CLI: install, run, doctor, mcp-oauth, boulder | yes (+ config-manager, doctor, run) |
| `plugin/` | 58 | ~12k | 10 OpenCode hook handlers + hook composition | yes |
| `config/` | 41 | ~2k | 30 Zod v4 schema files | yes |
| `plugin-handlers/` | 27 | ~6k | 6-phase config loading pipeline | yes |
| `openclaw/` | 26 | ~3k | Bidirectional Discord/Telegram/HTTP integration | yes |
| `__tests__/` | 22 | ~300 | Plugin-level integration tests + perf fixtures | — |
| `mcp/` | 7 | ~200 | 3 built-in remote MCPs | yes |
| `testing/` | 3 | ~225 | Test utilities | — |
## NOTES
+55
View File
@@ -0,0 +1,55 @@
---
name: atlas-agent
description: Developer reference for the Atlas todo-list orchestrator agent -- model variants, prompt sections, and routing.
---
# src/agents/atlas/ -- Todo-List Orchestrator
**Generated:** 2026-05-18
## OVERVIEW
17 files. Atlas agent -- todo-list orchestrator that delegates via `task()` to complete every checkbox in a plan until fully done. Mode `primary`. Color `#10B981`.
## FILES
| File | Purpose |
|------|---------|
| `agent.ts` | `createAtlasAgent()` factory, model-variant routing, `OrchestratorContext` |
| `index.ts` | Barrel exports |
| `default.ts` | Default/Claude prompt variant |
| `gemini.ts` | Gemini-optimized prompt variant |
| `gpt.ts` | GPT-optimized prompt variant |
| `kimi.ts` | Kimi K2.x prompt variant |
| `opus-4-7.ts` | Claude Opus 4.7 prompt variant |
| `default-prompt-sections.ts` | Default prompt section definitions |
| `gemini-prompt-sections.ts` | Gemini prompt section definitions |
| `gpt-prompt-sections.ts` | GPT prompt section definitions |
| `kimi-prompt-sections.ts` | Kimi prompt section definitions |
| `opus-4-7-prompt-sections.ts` | Opus 4.7 prompt section definitions |
| `prompt-section-builder.ts` | Composes category, agent, skills, and decision matrix sections |
| `shared-prompt.ts` | Shared prompt content: delegation system, parallel rules, auto-continue, notepad protocol, post-delegation rule, boulder completion |
| `atlas-prompt.test.ts` | Prompt composition tests |
| `prompt-checkbox-enforcement.test.ts` | Checkbox enforcement behavior tests |
| `prompt-routing.test.ts` | Model-variant routing tests |
## MODEL VARIANT ROUTING
Parent `agent.ts` selects variant by model name:
- `isGptModel()` -> `gpt.ts`
- `isGeminiModel()` -> `gemini.ts`
- `isKimiK2Model()` -> `kimi.ts`
- `isClaudeOpus47Model()` -> `opus-4-7.ts`
- Default -> `default.ts` (Claude 4.6 family)
## KEY BEHAVIORS
- Mode: `primary` (respects UI model selection)
- Temperature: 0.1
- Default model: `claude-sonnet-4-6`
- Denied tools: `task`, `call_omo_agent` (Atlas delegates; it does not run subagents directly)
- Checkbox enforcement in prompts (per `prompt-checkbox-enforcement.test.ts`)
- Auto-continue: never asks user for approval between plan steps
- Parallel fan-out by default; sequential only for named blocking dependencies
- Post-delegation rule: edit plan checkbox, read plan to confirm, then dispatch next task
- Registered via `createAtlasAgent` in `src/agents/builtin-agents/atlas-agent.ts`
+33
View File
@@ -0,0 +1,33 @@
---
name: builtin-agents-factory-layer
description: Conditional factory wrappers that apply overrides, model resolution, skill filtering, and provider gating to the 11 agent definitions.
---
# src/agents/builtin-agents/ -- Conditional Factory Layer
**Generated:** 2026-05-18
## OVERVIEW
Conditional factory layer beneath the 11 raw `createXXXAgent` factories in `src/agents/`. Each `maybeCreateXXXConfig` wrapper decides whether an agent registers, resolves its model via the 4-step pipeline, applies user overrides, filters skills, and returns the final `AgentConfig` -- or `undefined` if the agent is disabled or requirements are not met. Outputs feed `createPluginInterface`.
## FILE CATALOG
| File | Purpose |
|------|---------|
| `agent-overrides.ts` | Applies user overrides: category expansion, `deepMerge` of model/temp/prompt/permissions, `file://` prompt resolution |
| `model-resolution.ts` | 4-step pipeline wrapper: `resolveModelPipeline` (override → category → provider fallback → system default) + `getFirstFallbackModel` |
| `resolve-file-uri.ts` | Converts `file://` paths to absolute paths, bounds them to project root, reads content for prompt append |
| `resolve-file-uri.test.ts` | Tests for URI decoding, path expansion, project-root bounds, missing-file handling |
| `environment-context.ts` | Appends `createEnvContext()` block to agent prompt unless `disableOmoEnv` is set |
| `available-skills.ts` | `buildAvailableSkills` -- merges builtin skills with discovered user skills, filters disabled |
| `available-skills.test.ts` | Tests for builtin + discovered skill merging and disabled filtering |
| `sisyphus-agent.ts` | `maybeCreateSisyphusConfig` -- checks disabled list, model requirements, applies overrides + frontier tool schema guard + GPT patch guard |
| `sisyphus-agent.test.ts` | Tests for disabled-agent filtering, model resolution, override application, first-run fallback behavior |
| `hephaestus-agent.ts` | `maybeCreateHephaestusConfig` -- provider gating (`requiresProvider`), category override support, variant defaulting to medium |
| `atlas-agent.ts` | `maybeCreateAtlasConfig` -- UI-selected model respect, variant resolution |
| `general-agents.ts` | `collectPendingBuiltinAgents` -- handles all non-special-cased agents (skips sisyphus/hephaestus/atlas/sisyphus-junior), bulk model resolution and override application |
## PIPELINE FIT
Phase 3 of `plugin-handlers/config-handler.ts` invokes these factories. The resulting `AgentConfig` array feeds `createPluginInterface`. Returns `undefined` for disabled agents or unmet model requirements.
+50
View File
@@ -0,0 +1,50 @@
---
name: sisyphus-junior-agent
description: Developer reference for the Sisyphus-Junior category-spawned executor agent -- model variants and discipline.
---
# src/agents/sisyphus-junior/ -- Category-Spawned Executor
**Generated:** 2026-05-18
## OVERVIEW
10 files. Sisyphus-Junior is a focused task executor spawned by `delegate-task` when category routing requires it. Runs in subagent mode with its own fallback chain. Does not delegate further; executes directly.
## FILES
| File | Purpose |
|------|---------|
| `agent.ts` | `createSisyphusJuniorAgentWithOverrides()` factory, model-variant routing, `SISYPHUS_JUNIOR_DEFAULTS` |
| `index.ts` | Barrel exports |
| `default.ts` | Base/Claude prompt: todo discipline, verification, termination rules |
| `gemini.ts` | Gemini-optimized prompt variant |
| `gpt.ts` | Base GPT prompt variant |
| `gpt-5-3-codex.ts` | GPT-5.3 Codex prompt variant |
| `gpt-5-4.ts` | GPT-5.4-native prompt variant |
| `gpt-5-5.ts` | GPT-5.5-native prompt variant |
| `kimi-k2-6.ts` | Kimi K2.6 prompt variant |
| `index.test.ts` | Unit tests |
## VARIANT SELECTION
Parent `agent.ts` selects prompt variant by model name:
- Contains "kimi-k2" -> `kimi-k2-6.ts`
- Contains "gpt-5.5" -> `gpt-5-5.ts`
- Contains "gpt-5.4" -> `gpt-5-4.ts`
- Contains "gpt-5.3-codex" -> `gpt-5-3-codex.ts`
- Contains "gpt" -> `gpt.ts`
- Contains "gemini" -> `gemini.ts`
- Default -> `default.ts` (Claude, GLM, etc.)
## KEY BEHAVIORS
- Mode: `subagent` (uses own fallback chain, ignores UI selection)
- Default model: `claude-sonnet-4-6`
- Default temperature: `0.1` (`SISYPHUS_JUNIOR_DEFAULTS`)
- Fallback chain: kimi-k2.6 -> gpt-5.5 medium -> minimax-m2.7 -> big-pickle
- Blocked tools: `task` (all models); `apply_patch` also blocked for GPT models
- `call_omo_agent` explicitly allowed so subagents can spawn explore/librarian
- Max tokens: 64000
- Thinking enabled for non-GPT/non-GLM models (budgetTokens: 32000)
- Reasoning effort "medium" for GPT models
+43
View File
@@ -0,0 +1,43 @@
# src/features/builtin-commands/ -- Built-in Slash Commands
**Generated:** 2026-05-18
## OVERVIEW
Registry of built-in commands shipped inside the plugin. Each command is a template literal with title, description, and instructions. Registered via `createBuiltinCommandDefinitions()` factory in `commands.ts`. Loaded by `claude-code-command-loader`.
## FILE CATALOG
| File | Purpose |
|------|---------|
| `commands.ts` | `createBuiltinCommandDefinitions()` factory + `loadBuiltinCommands()` filter |
| `index.ts` | Barrel exports |
| `types.ts` | `BuiltinCommandName` union type + `BuiltinCommandConfig` |
| `templates/` | One `.ts` file per command |
## TEMPLATES
| Command | Source File | Notes |
|---------|-------------|-------|
| `init-deep` | `templates/init-deep.ts` | Hierarchical AGENTS.md generator |
| `ralph-loop` | `templates/ralph-loop.ts` | Self-referential dev loop |
| `ulw-loop` | `templates/ralph-loop.ts` | Ultrawork loop variant |
| `cancel-ralph` | `templates/ralph-loop.ts` | Loop cancellation |
| `refactor` | `templates/refactor.ts` | LSP + AST-grep refactoring |
| `start-work` | `templates/start-work.ts` | Prometheus plan executor |
| `stop-continuation` | `templates/stop-continuation.ts` | Kill all continuations |
| `handoff` | `templates/handoff.ts` | Session context summary |
| `remove-ai-slops` | `templates/remove-ai-slops.ts` | AI code smell cleanup |
| `hyperplan` | `templates/hyperplan.ts` | Adversarial team-mode planning |
## STRUCTURE
Each template exports a string constant containing the command's system prompt. `commands.ts` wraps it in `<command-instruction>` XML and injects `$ARGUMENTS`, `$SESSION_ID`, and `$TIMESTAMP` where needed. Some commands append a team-mode addendum when `teamModeEnabled` is true.
## LOADING
Phase 6 of config loading (`command-config-handler.ts`) merges built-ins with user-installed commands from `.opencode/commands/` and Claude Code plugins. `disabled_commands` in config filters out specific built-ins by name. The `autoSlashCommand` hook in `src/hooks/` executes these on user input.
## TESTS
Co-located `.test.ts` files in `templates/` cover `ralph-loop` and `stop-continuation` logic.
@@ -0,0 +1,46 @@
# src/features/claude-code-agent-loader/ -- Claude Code Agent Compatibility Layer
**Generated:** 2026-05-18
## OVERVIEW
Sibling to `claude-code-mcp-loader`. Loads Claude Code agent definitions from `.opencode/agents/`, `~/.claude/agents/`, and inline `opencode.json` config, then translates them to OpenCode `AgentConfig`. 12 files.
## LOAD PIPELINE
```
loadUserAgents() / loadProjectAgents() / loadOpencodeGlobalAgents() / loadOpencodeProjectAgents()
-> loader.ts: discover .md files in agents/ directories
-> agent-definitions-loader.ts: parse YAML frontmatter + body, load from explicit paths
-> json-agent-loader.ts: parse .json / .jsonc agent definitions
-> opencode-config-agents-reader.ts: read inline agents from opencode.json
-> claude-model-mapper.ts: translate "sonnet" / "opus" / "haiku" -> OpenCode provider/model IDs
-> return Record<string, ClaudeCodeAgentConfig>
```
## KEY FILES
| File | Purpose |
|------|---------|
| `index.ts` | Barrel: all exports |
| `loader.ts` | `loadUserAgents()`, `loadProjectAgents()`, `loadOpencode*Agents()` main entry |
| `agent-definitions-loader.ts` | `parseMarkdownAgentFile()`, `loadAgentDefinitions()` |
| `json-agent-loader.ts` | `parseJsonAgentFile()` -- JSON/JSONC agent definitions |
| `claude-model-mapper.ts` | Claude aliases -> OpenCode `providerID/modelID` |
| `opencode-config-agents-reader.ts` | Reads inline `agents` and `agent_definitions` from `opencode.json` |
| `types.ts` | `ClaudeCodeAgentConfig`, `AgentScope`, `LoadedAgent` |
## INTEGRATION
Phase 3 of config loading (`src/plugin-handlers/agent-config-handler.ts`) calls this loader to populate the agent registry before the plugin interface is built.
## COMPANION LOADERS
- **`claude-code-plugin-loader`**: full plugins with commands, skills, hooks, MCPs
- **`claude-code-mcp-loader`**: Tier 2 MCPs from `.mcp.json`
## RELATED
- Phase 3 integration: `src/plugin-handlers/agent-config-handler.ts`
- Plugin loader: `src/features/claude-code-plugin-loader/`
- MCP loader: `src/features/claude-code-mcp-loader/`
+51
View File
@@ -0,0 +1,51 @@
# src/hooks/auto-update-checker/ -- npm Update Detection
**Generated:** 2026-05-18
## OVERVIEW
27 files. Session Tier hook on `session.created`. Checks the npm registry for newer plugin versions, compares against the installed version, and surfaces update availability via startup toasts. Caches results to avoid repeated registry fetches. Throttled per channel (`latest`, `next`, `beta`). Skips CLI run mode and subagent sessions.
## FILE CATALOG
| File | Purpose |
|------|---------|
| `index.ts` | Barrel exports: hook factory, checker, cache invalidation, channel helpers |
| `hook.ts` | `createAutoUpdateCheckerHook()` -- event handler, orchestrates startup toasts and background check |
| `checker.ts` | Barrel for `checker/` subdir -- version resolution, local dev detection, package entry finding |
| `cache.ts` | `invalidatePackage()` -- removes package from bun.lock, node_modules, and specifier cache |
| `version-channel.ts` | `extractChannel()` -- resolves dist-tags and prerelease versions to npm channels |
| `types.ts` | `UpdateCheckResult`, `AutoUpdateCheckerOptions`, `NpmDistTags` |
| `constants.ts` | Registry URL, timeouts, cache paths, accepted package names |
## SUBDIRECTORIES
- `checker/` -- 11 files. Core version checking logic: `check-for-update.ts`, `latest-version.ts`, `local-dev-version.ts`, `plugin-entry.ts`, `cached-version.ts`, `pinned-version-updater.ts`, `sync-package-json.ts`, plus helpers.
- `hook/` -- 9 files. Startup UX: `background-update-check.ts`, `deferred-startup-check.ts`, `startup-toasts.ts`, `update-toasts.ts`, `spinner-toast.ts`, `config-errors-toast.ts`, `connected-providers-status.ts`, `model-capabilities-status.ts`, `model-cache-warning.ts`.
## CACHE
File-based deduplication via `VERSION_FILE` in the OpenCode cache directory (`getOpenCodeCacheDir()`). Prevents excessive npm registry calls. `invalidatePackage()` forces a fresh check by purging the package from Bun's lockfile, node_modules, and specifier cache.
## VERSION CHANNELS
`extractChannel()` maps:
- Dist-tags (`next`, `beta`) → channel name directly
- Prerelease versions (`1.0.0-beta.1`) → channel from prerelease prefix (`alpha`, `beta`, `rc`, `canary`, `next`)
- Stable versions → `latest`
## INTEGRATION
Registered in `create-session-hooks.ts` as `autoUpdateChecker`. Part of the Session Tier hook composition.
## RELATED
Three `zauc-mocks-*` directories in `src/hooks/` exist specifically to test this hook with mocked dependencies:
- `zauc-mocks-cache/` -- tests cache invalidation paths
- `zauc-mocks-hook/` -- tests hook orchestration with mocked submodules
- `zauc-mocks-bg/` -- tests background check scheduling
## CROSS-REFERENCES
- Parent: [`src/hooks/AGENTS.md`](file:///Users/yeongyu/local-workspaces/omo/src/hooks/AGENTS.md) -- Session Tier hook list
- [`src/cli/AGENTS.md`](file:///Users/yeongyu/local-workspaces/omo/src/cli/AGENTS.md) -- CLI uses the same npm dist-tag helpers
@@ -0,0 +1,45 @@
# src/hooks/compaction-context-injector/ -- Post-Compaction Context Recovery
**Generated:** 2026-05-18
## OVERVIEW
Continuation Tier hook. Fires on `session.compacted` to re-inject critical context lost during context-window compaction. Prevents the agent from losing its bearings after OpenCode trims session history.
## TIER + EVENT
- **Tier:** Continuation
- **Event:** `session.compacted` (primary), `session.idle`, `session.deleted`, `message.updated`, `message.part.delta`, `message.part.updated`
## KEY FILES
| File | Purpose |
|------|---------|
| `hook.ts` | `createCompactionContextInjector()` -- composes capture, restore, inject, event |
| `recovery.ts` | `createRecoveryLogic()` -- rebuilds agent/model/tools after compaction |
| `tail-monitor.ts` | Tracks assistant output to detect no-text tails |
| `session-prompt-config-resolver.ts` | Walks session messages to resolve current agent/model/tools |
| `validated-model.ts` | `validateCheckpointModel()` -- model validation |
| `session-id.ts` | `resolveSessionID()`, `isCompactionAgent()` |
| `recovery-prompt-config.ts` | `createExpectedRecoveryPromptConfig()`, `isPromptConfigRecovered()` |
| `constants.ts` | `RECOVERY_COOLDOWN_MS`, `NO_TEXT_TAIL_THRESHOLD`, `RECENT_COMPACTION_WINDOW_MS` |
| `types.ts` | `CompactionContextInjector` interface |
| `compaction-context-prompt.ts` | `COMPACTION_CONTEXT_PROMPT` -- 8-section summary template |
| `index.ts` / `index.test.ts` | Barrel export + tests |
| `recovery.test.ts` / `session-prompt-config-resolver.test.ts` | Unit tests |
## HOW IT WORKS
1. **Capture:** Before compaction, saves agent/model/tools checkpoint via `setCompactionAgentConfigCheckpoint()`
2. **Inject:** Returns `COMPACTION_CONTEXT_PROMPT` with active delegated session history
3. **Recover:** On `session.compacted`, dispatches internal prompt to restore checkpointed config
4. **Tail monitor:** Detects consecutive assistant messages with no text output; triggers recovery if recent compaction
## INTEGRATION
Registered in `create-continuation-hooks.ts` as `compactionContextInjector`.
## DISTINCTION
- **`compactionTodoPreserver`:** Preserves todos only (sibling Continuation hook)
- **`anthropicContextWindowLimitRecovery`:** Prevents the limit preemptively (Session Tier)
+54
View File
@@ -0,0 +1,54 @@
# src/tools/ast-grep/ -- AST-Aware Search and Rewrite
**Generated:** 2026-05-18
## OVERVIEW
Two always-on tools: `ast_grep_search` (find AST patterns) and `ast_grep_replace` (rewrite AST patterns). 25 languages supported via `@ast-grep/napi` as primary backend with fallback to `sg` CLI.
Pattern syntax uses AST meta-variables, not regex. `$VAR` matches one AST node. `$$$` matches zero or more nodes. `$$$VAR` captures a named list. Patterns must be complete, parseable source code.
`ast_grep_replace` defaults to dry-run. Pass `dryRun=false` to apply changes.
## FILE CATALOG
| File | Role |
|------|------|
| `tools.ts` | `createAstGrepTools` factory -- returns Record with 2 tool entries |
| `cli.ts` | `runSg` -- spawns sg process, handles two-pass rewrite |
| `cli-binary-path-resolution.ts` | Async init wrapper with singleton promise dedup |
| `sg-cli-path.ts` | Resolve sg via node_modules, platform subpackages, Homebrew, or cache |
| `downloader.ts` | Auto-download from GitHub releases if missing |
| `environment-check.ts` | Verify CLI + NAPI availability at startup |
| `language-support.ts` | 25 CLI languages + 5 NAPI languages + extension map |
| `pattern-hints.ts` | Detect regex misuse and language-specific mistakes |
| `result-formatter.ts` | Format matches with file:line:column for LLM |
| `sg-compact-json-output.ts` | Parse `sg --json=compact` into `SgResult` |
| `tool-descriptions.ts` | Tool description constants |
| `process-output-timeout.ts` | 300s timeout wrapper for spawn |
| `types.ts` | `CliMatch`, `SgResult`, `AnalyzeResult`, etc. |
| `constants.ts` | Re-exports from language-support, environment-check, sg-cli-path |
| `index.ts` | Barrel |
## KEY BEHAVIORS
- Dual binary detection: NAPI primary, CLI fallback
- Fallback chain: node_modules → platform subpackage → Homebrew → cached download
- Dry-run protection: `ast_grep_replace` defaults to preview; pass `dryRun=false` to apply
- Two-pass rewrite: when rewrite + apply both requested, cli.ts runs `--json=compact` first, then `--update-all`
- Output limits: 1MB max output or 500 matches, whichever comes first
- Timeout: 300s cap via `process-output-timeout.ts`; kills process and returns truncated result
## LANGUAGES
25 CLI languages: bash, c, cpp, csharp, css, elixir, go, haskell, html, java, javascript, json, kotlin, lua, nix, php, python, ruby, rust, scala, solidity, swift, typescript, tsx, yaml.
5 NAPI languages (native bindings): html, javascript, tsx, css, typescript.
## PATTERN HINTS
When a search returns zero matches, `pattern-hints.ts` scans for regex-style misuse (`|`, `.*`, `\w`, `[a-z]`) and returns a corrective hint redirecting to ast-grep meta-variable syntax. Also catches language-specific mistakes like trailing colons in Python def/class patterns or incomplete function signatures in JS/Go/Rust.
## RELATED
Doctor check at `src/cli/doctor/checks/tools.ts` verifies both NAPI and CLI availability.
+52
View File
@@ -0,0 +1,52 @@
# src/tools/look-at/ -- Image and PDF Analysis Tool
**Generated:** 2026-05-18
## OVERVIEW
14 files. The `look_at` tool delegates image, PDF, and diagram analysis to the `multimodal-looker` subagent. Conditional gate: tool is only registered when `multimodal-looker` is not in `disabled_agents`. Default subagent model: gpt-5.5 medium. This is a summary extractor, not a precise reader.
## EXECUTION FLOW
1. **Args** (`look-at-arguments.ts`) -- normalize `file_path`/`image_data` aliases, validate one-of requirement, reject remote URLs
2. **Prep** (`look-at-input-preparer.ts`) -- resolve path, detect MIME from extension or Base64 header, convert unsupported images to JPEG
3. **Spawn** (`look-at-session-runner.ts`) -- create child session with `multimodal-looker` agent, attach file as message part, disable `task`/`call_omo_agent`/`look_at` to prevent recursion
4. **Poll** (`session-poller.ts`) -- wait until idle (1s interval, 120s timeout)
5. **Extract** (`assistant-message-extractor.ts`) -- pull latest assistant text from session messages
6. **Return** -- summary text back to caller
## FILE CATALOG
| File | Responsibility |
|------|----------------|
| `tools.ts` | `createLookAt()` factory -- tool schema + entry point |
| `look-at-arguments.ts` | Zod arg schema, normalize aliases, validate inputs |
| `look-at-input-preparer.ts` | Build `LookAtFilePart` from path or base64; trigger conversion if needed |
| `look-at-prompt.ts` | System prompt for the multimodal session |
| `look-at-session-runner.ts` | Orchestrate child session creation, prompt dispatch, message fetch |
| `session-poller.ts` | Poll session status until idle |
| `assistant-message-extractor.ts` | Extract latest assistant text from raw session messages |
| `image-converter.ts` | Convert HEIC/WebP/RAW/PSD to JPEG via sips or ImageMagick |
| `mime-type-inference.ts` | Detect MIME from file extension or Base64 header |
| `missing-file-error.ts` | Clear `ENOENT` error message when file is missing |
| `multimodal-agent-metadata.ts` | Resolve actual model for multimodal-looker from config or dynamic pipeline |
| `multimodal-fallback-chain.ts` | Build vision-capable fallback chain: kimi-k2.6, glm-4.6v, gpt-5-nano |
| `constants.ts` | `MULTIMODAL_LOOKER_AGENT`, `LOOK_AT_DESCRIPTION` |
| `types.ts` | `LookAtArgs` interface |
## GATE
Conditional. Tool is registered only when `multimodal-looker` is absent from `disabled_agents`.
## USE CASE
PDFs, screenshots, diagrams -- quick summary extraction. NOT for visual precision, aesthetic evaluation, or exact accuracy. Use the Read tool for those cases instead.
## DISTINCTION
This is the TOOL that DELEGATES TO the `multimodal-looker` AGENT. The agent lives in `src/agents/builtin-agents/multimodal-looker.ts`; this tool is the invocation harness.
## NOTES
- Temporary converted images are cleaned up in `finally` blocks
- The subagent has `read` tool disabled by default (`READ_ENABLED = false`); the file is passed as an attachment
+48
View File
@@ -0,0 +1,48 @@
# src/tools/skill/ -- Skill and Command Loader Tool
**Generated:** 2026-05-18
## OVERVIEW
The `skill` tool. Dual purpose: (1) load a skill by name to inject its SKILL.md content into context, (2) invoke a slash command by name (omit leading slash). Skills may spin up embedded MCP servers on demand. Commands route through the autoSlashCommand hook.
## FILE CATALOG
| File | Purpose |
|------|---------|
| `tools.ts` | `createSkillTool` factory -- resolves name, loads body, returns formatted output |
| `skill-body.ts` | Extracts `<skill-instruction>` block or full SKILL.md template |
| `skill-matcher.ts` | Exact match, short-name fallback, partial-match suggestions |
| `scope-priority.ts` | 4-scope priority: project (4) > user (3) > opencode (2) > builtin/plugin (1) |
| `native-skills.ts` | Merges `PluginInput.skills` entries into discovered skill list |
| `description-formatter.ts` | Builds LLM-visible `<available_items>` listing with scope tags |
| `mcp-capability-formatter.ts` | Lists skill-embedded MCP tools/resources/prompts for `skill_mcp` calls |
| `session-skill-cache.ts` | Dedupes repeated skill loads per session via `seenSessionIDs` |
| `types.ts` | `SkillArgs`, `SkillInfo`, `SkillLoadOptions` |
| `constants.ts` | Tool name and description prefix |
| `index.ts` | Barrel exports |
## EXECUTION FLOW
```
skill(name="git-master")
-> matchSkillByName() # exact, then short-name
-> ask(permission) # host skill permission gate
-> extractSkillBody() # load SKILL.md content
-> formatMcpCapabilities() # if skill has mcpConfig
-> return "## Skill: ..." + body + MCP info
```
## SCOPE PRIORITY
Project configs override user configs, which override opencode builtins. `sortByScopePriority` applies to both skills and slash commands in the `<available_items>` listing.
## TEST MOCKS
`zauc-mocks-skill-tools/` -- `mock.module()` setup for skill tool tests. Loads alphabetically before consuming tests via the `zauc-` prefix sort-order hack.
## INTEGRATION
- Discovery: `opencode-skill-loader` feature module scans `.opencode/skills/`, `~/.config/opencode/skills/`, and built-in paths
- MCP spawn: `skill-mcp-manager` feature module starts embedded MCP servers per session on demand
- Commands: `slashcommand/` module feeds discovered commands into the tool description