You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Oct 17, 2026. It is now read-only.
Repository navigation
Commit b3b03a5
Browse filesBrowse the repository at this point in the historyBrowse files
| review/verification | critic (once at delivery, after all plan waves) |
70
70
| research/discovery | explore |
71
71
| browser/visual | vision |
72
72
73
+
## Orchestration contract (v0.6.2+)
74
+
75
+
Default path for a user request:
76
+
77
+
1. Bob classifies. Trivial 1–2 files → `general` (no Plan; Critic only if high-risk).
78
+
2. Non-trivial → **Plan once**. Plan may fan out 1–3 `explore` calls, writes `.bob/plans/*.md`, returns `Status: done` → **frozen**.
79
+
3. Bob dispatches waves from the frozen graph. `parallel: yes` = several `task()` in **one** assistant turn. Manager only if ≥6 workers; Manager never spawns Plan or Critic.
80
+
4. Workers execute the annotated step. They do not re-plan or review.
81
+
5.**One Critic** after every implementation wave. UI work → Critic delegates Vision.
82
+
6. Re-plan only with `INVALIDATE_PLAN` in the Plan prompt (user changed scope, worker `Status: blocked` because the plan is wrong, or delivery Critic said the plan is unexecutable).
83
+
84
+
Hard wall: `getTaskPermissions()` in [src/permissions.ts](src/permissions.ts). Runtime freeze: [src/features/plan-lifecycle/](src/features/plan-lifecycle) + [src/hooks/plan-lifecycle-gate.ts](src/hooks/plan-lifecycle-gate.ts). Freeze is **per session** in `.bob/plans/.lifecycle.json` (plus plan-file frontmatter). A new session in the same repo is not blocked by another session's plan.
85
+
86
+
**Native OpenCode UI (do not reinvent):**
87
+
- Tasks: Bob calls `todowrite` after Plan returns — phase rows + indented `N.M` steps. Host `task()` sessions are the parent/child tree.
88
+
- Question: direct Plan (picker / Tab) uses the native `question` tool for complex/unclear **user-owned** facts. Plan invoked **through Bob** never interviews (`host-interaction-gate` denies `question` when the session has `parentID`).
89
+
- Memory: native `memory` tool first; `hiai_memory_search` only if that misses.
90
+
91
+
| Agent | May `task()`|
92
+
|---|---|
93
+
| bob | manager + all workers |
94
+
| manager | explore, build, general, designer, writer, vision, **critic (phase close only)**|
95
+
| plan | explore |
96
+
| critic | vision, explore |
97
+
| build | explore (missing path only) |
98
+
| explore, designer, writer, vision, general | none |
99
+
73
100
## Change Map
74
101
75
102
When you need to change something, edit the right file first.
@@ -82,7 +109,8 @@ When you need to change something, edit the right file first.
| Plan freeze / single Critic |[src/features/plan-lifecycle/](src/features/plan-lifecycle) + [src/hooks/plan-lifecycle-gate.ts](src/hooks/plan-lifecycle-gate.ts)|
All plugin settings go through **one**`bob.json` (or `bob.jsonc`). Search order (first found wins): current dir → each parent up to filesystem root (`bob.json`, then `.opencode/bob.json`, then jsonc) → `~/.config/hiai-opencode/bob.json`. CLI `doctor` uses the same order. Credentials stay in `bob.env` / Connect, not in JSON.
144
+
145
+
`loop.enabled` (default true) keeps Bob running on idle until the frozen plan and todos are done. Do not wait for the user to re-prompt.
146
+
115
147
Use `{env:VAR_NAME}` placeholders in config JSON — never raw keys, never `${VAR}` (blocked for names containing `KEY`/`TOKEN`/`SECRET`). Check with `grep -E '(fc-\|ctx7sk-\|sk-\|key-)' bob.json` — should return 0 matches.
116
148
117
149
## Mental Map
118
150
119
151
```
120
152
AGENTS: bob (orchestrator) · build (impl) · plan (architecture) · explore (discovery)
@@ -178,6 +210,9 @@ Headed mode (`AGENT_BROWSER_HEADED=1`) works only with Chrome — Lightpanda is
178
210
### Agent prompt correct in source but wrong at runtime
179
211
The runtime prompt is assembled in layers: (1) `src/agents/<agent>.ts`, (2) `src/prompt-library/*.ts` imports, (3) runtime hooks (closure-injector, caveman-system-injector), (4) `hooks.config` in [src/index.ts](src/index.ts) applies model/visibility/permissions. Inspect `hooks.config` first when runtime output diverges from source.
180
212
213
+
### Plan is called again or Critic runs after every step
214
+
Expected contract since v0.6.2: one Plan, frozen until done; one Critic at delivery. If you still see mid-plan Plan/Critic calls, check (1) agent prompts import the freeze/delivery wording, (2) `plan-lifecycle-gate` is not in `hooks.disabled`, (3) logs for `[hiai-opencode] plan-lifecycle: denied_replan` / `denied_early_critic`. Invalidate only with `INVALIDATE_PLAN` in the Plan prompt.
215
+
181
216
### Circuit breaker triggered
182
217
Sessions are aborted at 20+ consecutive identical tool calls (default) or 4000+ total tool calls (default). Search logs for `[background-agent] Circuit breaker:`. Adjust via `background_manager.circuit_breaker.consecutive_threshold` / `max_tool_calls` in config. State is in-memory only — lost on restart.
183
218
@@ -192,6 +227,9 @@ Windows/OpenCode may fail to spawn local MCP processes (`sequential-thinking`, n
192
227
-**`{env:VAR}` not `${VAR}`** in hiai-opencode config files — the latter blocks names containing `KEY`/`TOKEN`/`SECRET`.
193
228
-**Never hardcode API keys** in JSON. Use `{env:VARIABLE_NAME}` placeholders.
194
229
-**Bob's prompt is the routing source of truth** — there is no separate TypeScript routing map.
230
+
-**Do not re-plan a frozen plan.** Execute waves. Invalidate only with `INVALIDATE_PLAN`.
231
+
-**Do not call Critic after each todo.** Delivery Critic once. Skills that say otherwise (`subagent-driven-development`) are opt-in paranoid mode.
232
+
-**Leaf agents cannot spawn Plan/Critic.** If a prompt still shows `task({subagent_type: "explore"})` on Writer, that call is denied at the permission wall — report the gap to Bob instead.
-**Per-agent permission maps** — [src/permissions.ts](src/permissions.ts): `applyAgentPermissions()` converts `agent_restrictions` into `permission.deny` / `tools.<key>=false`. Applied in `src/index.ts``hooks.config`.
250
-
-**Completion controller** — [src/features/completion-controller/](src/features/completion-controller): state machine that gates task completion. `decide()` (decide.ts) requires: no blocker, no incomplete todos, quality gate passed, LSP diagnostics run (if edits made), and Critic approval (if `require_critic` and changed files exist).
253
+
-**Plan lifecycle gate** — [src/features/plan-lifecycle/](src/features/plan-lifecycle) + [src/hooks/plan-lifecycle-gate.ts](src/hooks/plan-lifecycle-gate.ts): freezes a Plan `Status: done` result; blocks a second Plan spawn and blocks Critic until implementation waves finish.
254
+
-**Host interaction gate** — [src/hooks/host-interaction-gate.ts](src/hooks/host-interaction-gate.ts): blocks the native `question` tool on any session with a `parentID` (Plan-via-Bob cannot interview). Direct Plan and Bob keep `question: allow`.
255
+
-**Completion controller** — [src/features/completion-controller/](src/features/completion-controller): state machine that gates task completion. `decide()` (decide.ts) requires: no blocker, no incomplete todos, quality gate passed, LSP diagnostics run (if edits made), frozen plan not still executing, and Critic approval (if `require_critic` and changed files exist). Critic is a delivery gate, not a per-step gate.
251
256
-**Circuit breaker** — [src/hooks/circuit-breaker.ts](src/hooks/circuit-breaker.ts) + [src/features/background-manager/index.ts](src/features/background-manager/index.ts): feeds every `tool.execute.after` call into `BackgroundManager.recordSessionToolCall()`; trips on N consecutive identical calls (default 20) or total tool calls (default 4000), aborting the session via `client.session.abort`.
252
257
-**Agent-browser guard** — [src/tools/agent-browser/index.ts](src/tools/agent-browser/index.ts): `browserGateGuard()` throws for non-`vision`/`general` agents.
253
258
-**LSP sandbox** — [src/tools/lsp/index.ts](src/tools/lsp/index.ts): throws on paths resolving outside `ctx.directory`.
-**Lifecycle hooks** — [src/hooks/worktree-lifecycle.ts](src/hooks/worktree-lifecycle.ts): auto-create on plan-start signals, auto-remove on `<CLOSURE>`
282
+
-**Lifecycle hooks** — [src/hooks/worktree-lifecycle.ts](src/hooks/worktree-lifecycle.ts): track explicit `hiai_worktree_create` calls; clean up on session delete / dispose. No auto-create from user-message regex.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+27Lines changed: 27 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,33 @@ All notable changes to this project will be documented in this file.
5
5
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
8
+
## [0.6.2] — 2026-08-23
9
+
10
+
### ⚡ Orchestration contract: plan once, parallel waves, critic once
11
+
12
+
Breaking agent-behavior change. Multi-point work is no longer re-planned mid-flight and no longer sends Critic after every specialist.
13
+
14
+
-**Plan freeze.** Plan is invoked once, then frozen. A second Plan spawn is denied unless the prompt contains `INVALIDATE_PLAN` (scope change, worker proved the plan wrong, or delivery Critic said it is unexecutable). Runtime: `src/features/plan-lifecycle/` + `src/hooks/plan-lifecycle-gate.ts`.
15
+
-**Delivery Critic once.** Bob/Manager/Build prompts no longer require Critic after every specialist. Critic is blocked while a frozen plan is still executing and incomplete todos remain. The completion controller will not inject a review prompt mid-plan.
16
+
-**Spawn matrix.**`getTaskPermissions()`: Manager cannot spawn Plan/Critic; Plan spawns explore only; Build spawns explore only; Critic spawns vision+explore; other workers are leaves.
17
+
-**Parallel waves.** Prompts require concurrent `task()` in the **same** assistant message for `parallel: yes` steps. Manager no longer re-reads the plan after every delegation.
18
+
-**Skills.**`subagent-driven-development` is opt-in paranoid mode. `writing-plans` hands off to frozen `.bob/plans/` waves. Code review is at delivery, not after each task.
19
+
-**Worktree lifecycle.** No auto-create on user-message regex; no teardown on arbitrary `<CLOSURE>`. Explicit `hiai_worktree_create` only.
20
+
-**Quality gate.** Bare `\bfailed\b` no longer trips the gate (false positives). Counts like `1 failed` and `FAIL` still do.
-**Native OpenCode UI.** Bob/`todowrite` mirrors the frozen plan as phase parents + indented step children so the TUI task list is hierarchical. `task()` remains the parent/child session tree.
23
+
-**Question tool.** Plan and Bob have `question: allow`. Direct Plan interviews on complex/unclear user-owned facts (native `question` UI). Plan spawned by Bob cannot interview — `host-interaction-gate` blocks `question` when the session has a `parentID`. Other agents `question: deny`.
24
+
-**Memory.** Prompts use OpenCode native `memory` first; `hiai_memory_search` is forensic fallback.
25
+
-**Manager.** Only for a **large phase** (not 1–2 file / `general` work). May spawn **one Critic at phase close** (that slice only). Bob still runs one **delivery Critic** after all phases. Phase-close critic does not mark the frozen plan `done`.
26
+
-**Freeze persists** in `.bob/plans/.lifecycle.json` plus YAML frontmatter (`status`, `checksum`) so a restart does not unlock re-plan.
27
+
-**Progress line.** Bob must lead user-facing messages with Plan / status / phase N/M / what is running.
28
+
-**Skills.**`subagent-driven-development` is loadable only as `plan/subagent-driven-development` — not a short-name default hit.
29
+
-**Doctor.** Missing/empty `models.bob`, `models.plan`, or `models.build` is a hard fail.
30
+
-**Single `bob.json`.** Plugin and CLI walk ancestors the same way (`bob.json` → `.opencode/bob.json` → jsonc → `~/.config/hiai-opencode`). All tunables (loop, completion, models, mcp) live there.
31
+
-**Run-to-completion loop.** Default `loop.enabled=true`. Canonical budget is `loop.max_auto_continues` (default 50; aliases `loop.maxIterations` and `completion.max_auto_continues`). Idle `session.prompt` is a fallback only — skipped when native `actor.postStop` continue is already running (no double TUI loop). `question` is blocked while a plan is executing.
32
+
-**Per-session freeze.**`.bob/plans/.lifecycle.json` is a map of session → plan. A new chat in the same repo can start a new Plan. Todos snapshot with the freeze; Bob restores via `todowrite` after restart of the same session.
33
+
-**Doctor.** First check prints the resolved `bob.json` absolute path (or warn if defaults).
0 commit comments