Browse Source

docs(council): update guide and config references for simplified architecture (#356)

Update documentation to reflect removal of council-master agent.
Council agent now synthesizes directly; no separate master session.
ReqX 3 months ago
parent
commit
9b220167b1
6 changed files with 50 additions and 202 deletions
  1. 0 1
      .slim/codemap.json
  2. 1 1
      README.md
  3. 8 16
      docs/configuration.md
  4. 34 142
      docs/council.md
  5. 1 2
      docs/mcps.md
  6. 6 40
      oh-my-opencode-slim.schema.json

+ 0 - 1
.slim/codemap.json

@@ -34,7 +34,6 @@
     "scripts/generate-schema.ts": "007f340e39adf6c3fd76feda72b71df1",
     "scripts/verify-opencode-host-smoke.ts": "a87fdb08b123501edf81618a49bc421d",
     "scripts/verify-release-artifact.ts": "83259be1926459412809013ce16e6fbb",
-    "src/agents/council-master.ts": "f013987f32d30178efb3bfa3030b3ce8",
     "src/agents/council.ts": "afa3ed4c40b2f91bdc907b850a7e68d3",
     "src/agents/councillor.ts": "e12284c5f632ba071e97924608375f9c",
     "src/agents/designer.ts": "6b5786ea6de1fb41b367103824762c9d",

+ 1 - 1
README.md

@@ -267,7 +267,7 @@ If any agent fails to respond, check your provider authentication and config fil
   </tr>
   <tr>
     <td colspan="2">
-      <b>Default Setup:</b> <code>Config-driven</code> — council master + councillors are chosen from your configured preset
+      <b>Default Setup:</b> <code>Config-driven</code> — councillors are chosen from your configured preset and the council agent synthesizes their responses
     </td>
   </tr>
   <tr>

+ 8 - 16
docs/configuration.md

@@ -103,22 +103,14 @@ All config files support **JSONC** (JSON with Comments):
 | `fallback.timeoutMs` | number | `15000` | Time before aborting and trying next model |
 | `fallback.retryDelayMs` | number | `500` | Delay between retry attempts |
 | `fallback.chains.<agent>` | string[] | — | Ordered fallback model IDs for an agent |
-| `fallback.retry_on_empty` | boolean | `true` | Treat silent empty provider responses (0 tokens) as failures and retry. Set `false` to accept empty responses |
-| `council.master.model` | string | — | **Required if using council.** Council master model |
-| `council.master.variant` | string | — | Council master variant |
-| `council.master.prompt` | string | — | Optional synthesis guidance for the master |
-| `council.presets` | object | — | **Required if using council.** Named councillor presets |
-| `council.presets.<name>.<councillor>.model` | string | — | Councillor model |
-| `council.presets.<name>.<councillor>.variant` | string | — | Councillor variant |
-| `council.presets.<name>.<councillor>.prompt` | string | — | Optional role guidance for the councillor |
-| `council.presets.<name>.master.model` | string | — | Override global master model for this preset |
-| `council.presets.<name>.master.variant` | string | — | Override global master variant for this preset |
-| `council.presets.<name>.master.prompt` | string | — | Override global master prompt for this preset |
-| `council.default_preset` | string | `"default"` | Default preset when none is specified |
-| `council.master_timeout` | number | `300000` | Master synthesis timeout (ms) |
-| `council.councillors_timeout` | number | `180000` | Per-councillor timeout (ms) |
-| `council.master_fallback` | string[] | — | Fallback models for the council master |
-| `council.councillor_retries` | number | `3` | Max retries per councillor and master on empty provider response (0–5) |
+ | `fallback.retry_on_empty` | boolean | `true` | Treat silent empty provider responses (0 tokens) as failures and retry. Set `false` to accept empty responses |
+ | `council.presets` | object | — | **Required if using council.** Named councillor presets |
+ | `council.presets.<name>.<councillor>.model` | string | — | Councillor model |
+ | `council.presets.<name>.<councillor>.variant` | string | — | Councillor variant |
+ | `council.presets.<name>.<councillor>.prompt` | string | — | Optional role guidance for the councillor |
+ | `council.default_preset` | string | `"default"` | Default preset when none is specified |
+ | `council.timeout` | number | `180000` | Councillor timeout (ms) |
+ | `council.councillor_retries` | number | `3` | Max retries per councillor on empty provider response (0–5) |
 | `todoContinuation.maxContinuations` | integer | `5` | Max consecutive auto-continuations before stopping (1–50) |
 | `todoContinuation.cooldownMs` | integer | `3000` | Delay in ms before auto-continuing — gives user time to abort (0–30000) |
 | `todoContinuation.autoEnable` | boolean | `false` | Automatically enable auto-continue when session has enough todos |

+ 34 - 142
docs/council.md

@@ -18,13 +18,13 @@ Multi-LLM consensus system that runs several models in parallel and synthesises
 
 ## Overview
 
-The **Council agent** sends your prompt to multiple LLMs (councillors) in parallel, then passes all responses to a **council master** that synthesises the optimal answer. Think of it as asking three experts and having a senior referee pick the best parts.
+The **Council agent** sends your prompt to multiple LLMs (councillors) in parallel, then the council agent itself synthesises the optimal answer from all councillor responses.
 
 ### Key Benefits
 
 - **Higher confidence** — consensus across models reduces single-model blind spots
 - **Diverse perspectives** — different architectures catch different issues
-- **Graceful degradation** — if the master fails, the best councillor response is returned
+- **Graceful degradation** — the council agent synthesises from whatever councillor results came back
 - **Configurable presets** — different council compositions for different tasks
 
 ### How It Works
@@ -41,12 +41,11 @@ User prompt
     └──────────────┴──────────────┘
-            Council Master
-            (synthesis model)
-            🔒 no tools
+          Council Agent
+          (synthesises)
-           Synthesised response
+         Synthesised response
 ```
 
 ---
@@ -60,7 +59,6 @@ Edit `~/.config/opencode/oh-my-opencode-slim.json` (or `.jsonc`):
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "presets": {
       "default": {
         "alpha": { "model": "openai/gpt-5.4-mini" },
@@ -95,29 +93,21 @@ Configure in `~/.config/opencode/oh-my-opencode-slim.json` (or `.jsonc`):
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "default_preset": "default",
     "presets": {
       "default": { /* councillors */ }
     },
-    "master_timeout": 300000,
-    "councillors_timeout": 180000
+    "timeout": 180000
   }
 }
 ```
 
 | Setting | Type | Default | Description |
 |---------|------|---------|-------------|
-| `master` | object | — | **Required.** Council master configuration (see below) |
-| `master.model` | string | — | **Required.** Model ID in `provider/model` format |
-| `master.variant` | string | — | Optional variant for the master model |
-| `master.prompt` | string | — | Optional guidance for the master's synthesis (see [Role Prompts](#role-prompts)) |
 | `presets` | object | — | **Required.** Named councillor presets (see below) |
 | `default_preset` | string | `"default"` | Which preset to use when none is specified |
-| `master_timeout` | number | `300000` | Master synthesis timeout in ms (5 minutes) |
-| `councillors_timeout` | number | `180000` | Per-councillor timeout in ms (3 minutes) |
-| `master_fallback` | string[] | — | Optional fallback models for the master. Tried in order if the primary model fails or times out |
-| `councillor_retries` | number | `3` | Max retries per councillor and master on empty provider response (0–5). Each retry creates a fresh session |
+| `timeout` | number | `180000` | Per-councillor timeout in ms (3 minutes) |
+| `councillor_retries` | number | `3` | Max retries per councillor on empty provider response (0–5). Each retry creates a fresh session |
 
 ### Councillor Configuration
 
@@ -129,40 +119,11 @@ Each councillor within a preset:
 | `variant` | string | No | Model variant (e.g., `"high"`, `"low"`) |
 | `prompt` | string | No | Role-specific guidance injected into the councillor's user prompt (see [Role Prompts](#role-prompts)) |
 
-### Per-Preset Master Override
-
-Each preset can optionally override the global master's `model`, `variant`, and `prompt` using a reserved `"master"` key:
-
-```jsonc
-{
-  "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
-    "presets": {
-      "fast-review": {
-        "master": { "model": "openai/gpt-5.4" },
-        "alpha": { "model": "openai/gpt-5.4-mini" },
-        "beta":  { "model": "google/gemini-3-pro" }
-      }
-    }
-  }
-}
-```
-
-| Field | Type | Required | Description |
-|-------|------|----------|-------------|
-| `presets.<name>.master.model` | string | No | Overrides the global master model for this preset |
-| `presets.<name>.master.variant` | string | No | Overrides the global master variant for this preset |
-| `presets.<name>.master.prompt` | string | No | Overrides the global master prompt for this preset |
-
-**Merge behaviour:** Each field uses nullish coalescing — if a field is omitted in the preset override, the global value is used. If no `"master"` key exists in the preset, the global master is used as-is.
-
-**Reserved key:** `"master"` inside a preset is reserved for this override and is not treated as a councillor name. Any councillor named `"master"` will be ignored.
-
 ### Constraints
 
 - Councillors run as **agent sessions with read-only codebase access** — they can read files, search by name (glob), search by content (grep), search by AST pattern (codesearch), and query the language server (LSP). They cannot modify files, run shell commands, or spawn subagents. This makes council responses grounded in actual code rather than guessing.
-- The council master also runs as an agent session with zero permissions — synthesis is purely analytical.
-- Councillor and council-master agents can be configured (model, temperature, MCPs, skills) via the standard `agents.councillor` and `agents.council-master` preset overrides.
+- The council agent itself synthesises the final answer from councillor results using its own model.
+- Councillor agents can be configured (model, temperature, MCPs, skills) via the standard `agents.councillor` preset override.
 
 ---
 
@@ -175,7 +136,6 @@ Use a single councillor when you want a second model's take without overhead:
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "presets": {
       "second-opinion": {
         "reviewer": { "model": "openai/gpt-5.4" }
@@ -185,7 +145,7 @@ Use a single councillor when you want a second model's take without overhead:
 }
 ```
 
-**When to use:** Quick sanity check from a different model. The master still reviews the single response and can refine it.
+**When to use:** Quick sanity check from a different model.
 
 ### 2-Councillor: Compare & Contrast
 
@@ -194,7 +154,6 @@ Two councillors with different models:
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "presets": {
       "compare": {
         "analyst":  { "model": "openai/gpt-5.4" },
@@ -214,7 +173,6 @@ The default setup — three diverse models:
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "presets": {
       "default": {
         "alpha": { "model": "openai/gpt-5.4-mini" },
@@ -235,7 +193,6 @@ As many councillors as you need — the system runs them all in parallel:
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "presets": {
       "full-board": {
         "alpha":   { "model": "anthropic/claude-opus-4-6" },
@@ -245,12 +202,12 @@ As many councillors as you need — the system runs them all in parallel:
         "echo":    { "model": "openai/gpt-5.4-mini" }
       }
     },
-    "councillors_timeout": 300000
+    "timeout": 300000
   }
 }
 ```
 
-**When to use:** High-stakes design reviews or complex architectural decisions where maximum model diversity matters. Increase `councillors_timeout` since there are more responses to collect.
+**When to use:** High-stakes design reviews or complex architectural decisions where maximum model diversity matters. Increase `timeout` since there are more responses to collect.
 
 ### Multiple Presets
 
@@ -259,7 +216,6 @@ Define several presets and choose at invocation time:
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "default_preset": "balanced",
     "presets": {
       "quick": {
@@ -291,7 +247,7 @@ Define several presets and choose at invocation time:
 
 ### Role Prompts
 
-Both councillors and the master accept an optional `prompt` field that injects role-specific guidance into the user prompt. This lets you steer each participant's behaviour without changing the system prompt.
+Councillors accept an optional `prompt` field that injects role-specific guidance into the user prompt. This lets you steer each participant's behaviour without changing the system prompt.
 
 **Councillor prompt** — prepended to the user prompt before the divider:
 
@@ -301,29 +257,15 @@ Both councillors and the master accept an optional `prompt` field that injects r
 <user prompt>
 ```
 
-**Master prompt** — appended after the synthesis instruction:
-
-```
-<synthesis instruction>
-
----
-**Master Guidance**:
-<role prompt>
-```
-
 #### Example: Specialised Review Board
 
-Both councillors and the master accept an optional `prompt` field. The master prompt can be set globally (`council.master.prompt`) or per-preset (`presets.<name>.master.prompt`):
+Councillors accept an optional `prompt` field:
 
 ```jsonc
 {
   "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
     "presets": {
       "review-board": {
-        "master": {
-          "prompt": "Prioritise correctness and security over creativity. Flag any risks."
-        },
         "reviewer": {
           "model": "openai/gpt-5.4",
           "prompt": "You are a meticulous code reviewer. Focus on edge cases, error handling, and potential bugs."
@@ -342,29 +284,7 @@ Both councillors and the master accept an optional `prompt` field. The master pr
 }
 ```
 
-#### Example: Per-Preset Master Model + Councillor Prompt
-
-Override the master model for a specific preset while customising one councillor's role:
-
-```jsonc
-{
-  "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
-    "presets": {
-      "fast": {
-        "master": { "model": "openai/gpt-5.4" },
-        "alpha": { "model": "openai/gpt-5.4-mini" },
-        "beta": {
-          "model": "google/gemini-3-pro",
-          "prompt": "Respond as a devil's advocate. Challenge assumptions and find weaknesses."
-        }
-      }
-    }
-  }
-}
-```
-
-Without a `prompt`, the councillor or master uses its default behaviour — no changes to the prompt.
+Without a `prompt`, the councillor uses its default behaviour — no changes to the prompt.
 
 ---
 
@@ -378,7 +298,7 @@ Talk to the council agent like any other agent:
 @council Should we use event sourcing or CRUD for the order service?
 ```
 
-The council agent delegates to `council_session` internally and returns the synthesised result.
+The council agent runs councillors in parallel and synthesises the result directly.
 
 ### Orchestrator Delegation
 
@@ -420,19 +340,16 @@ If some councillors failed:
 
 | Timeout | Default | Scope |
 |---------|---------|-------|
-| `councillors_timeout` | 180000 ms (3 min) | Per-councillor — each councillor gets this much time |
-| `master_timeout` | 300000 ms (5 min) | Master synthesis — one timeout for the whole synthesis phase |
+| `timeout` | 180000 ms (3 min) | Per-councillor — each councillor gets this much time |
 
-Councillors that don't respond in time are marked `timed_out`. The master proceeds with whatever results came back.
+Councillors that don't respond in time are marked `timed_out`. The council agent proceeds with whatever results came back.
 
 ### Graceful Degradation
 
 | Scenario | Behaviour |
 |----------|-----------|
-| Some councillors fail | Master synthesises from the survivors |
-| All councillors fail | Returns error immediately — master is never invoked |
-| Master primary model fails | Tries `master_fallback` models in order before degrading |
-| All master models fail | Returns best single councillor response prefixed with `(Degraded — master failed, using <name>'s response)` |
+| Some councillors fail | Council agent synthesises from the available results |
+| All councillors fail | Returns error immediately |
 | Councillor gets empty response | Retries up to `councillor_retries` times with fresh sessions |
 
 ### Empty Response Detection
@@ -440,7 +357,7 @@ Councillors that don't respond in time are marked `timed_out`. The master procee
 Providers sometimes silently drop requests — returning zero tokens with no error. This is detected automatically:
 
 - **Background tasks** (`@explorer`, `@fixer`, etc.): Empty responses trigger the fallback chain (next model in `fallback.chains`). Controlled by `fallback.retry_on_empty` (default `true`). Set to `false` to accept empty responses without retrying.
-- **Council councillors and master**: Empty responses trigger up to `councillor_retries` fresh sessions (default `3`). Only "Empty response from provider" errors are retried — timeouts and other failures return immediately.
+- **Council councillors**: Empty responses trigger up to `councillor_retries` fresh sessions (default `3`). Only "Empty response from provider" errors are retried — timeouts and other failures return immediately.
 
 To disable empty-response retry globally:
 
@@ -450,22 +367,6 @@ To disable empty-response retry globally:
 }
 ```
 
-### Master Fallback Chain
-
-The council master can be configured with fallback models. If the primary master model fails (timeout, API error, rate limit), the system tries each fallback in order before degrading to the best councillor response. This uses the same abort-retry pattern as the foreground failover system.
-
-```jsonc
-{
-  "council": {
-    "master": { "model": "anthropic/claude-opus-4-6" },
-    "master_fallback": ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4"],
-    "presets": { /* ... */ }
-  }
-}
-```
-
-When not configured, the master uses a single model with no fallback.
-
 ---
 
 ## Troubleshooting
@@ -479,7 +380,7 @@ When not configured, the master uses a single model with no fallback.
    ```bash
    cat ~/.config/opencode/oh-my-opencode-slim.json | grep -A 5 '"council"'
    ```
-2. Ensure `master.model` and at least one preset with one councillor are defined
+2. Ensure at least one preset with one councillor is defined
 3. Restart OpenCode after config changes
 
 ### All Councillors Timing Out
@@ -488,9 +389,9 @@ When not configured, the master uses a single model with no fallback.
 
 **Solutions:**
 1. **Increase timeout:**
-   ```jsonc
-   { "council": { "councillors_timeout": 300000 } }
-   ```
+    ```jsonc
+    { "council": { "timeout": 300000 } }
+    ```
 2. **Verify model IDs** — models must be in `provider/model` format and available in your OpenCode configuration
 3. **Check provider connectivity** — ensure the model providers are reachable
 
@@ -529,9 +430,8 @@ Choose models from **different providers** for maximum perspective diversity:
 
 ### Cost Considerations
 
-- Each councillor is one agent session → N councillors = N sessions + 1 master session. Councillors may use multiple tool calls within their session (read, grep, etc.), which increases token usage but grounds responses in actual code.
-- Use smaller/faster models as councillors and a stronger model as master, unless you are willing to spend the tokens on parallel frontier models.
-- The 1-councillor preset is the most cost-effective (2 calls total)
+- Each councillor is one agent session → N councillors = N sessions. Councillors may use multiple tool calls within their session (read, grep, etc.), which increases token usage but grounds responses in actual code.
+- The 1-councillor preset is the most cost-effective (1 call total)
 
 ### Council Agent Mode
 
@@ -542,9 +442,9 @@ The council agent is registered with `mode: "all"` in the OpenCode SDK, meaning
 
 This is intentional: council is useful both as a user-facing tool for deliberate consensus-seeking and as a subagent the orchestrator can invoke for high-stakes decisions.
 
-### Customising Councillor & Master Agents
+### Customising Councillor Agents
 
-Councillor and council-master are registered agents, so you can customise them using the standard `agents` override system:
+Councillor is a registered agent, so you can customise it using the standard `agents` override system:
 
 ```jsonc
 {
@@ -553,10 +453,6 @@ Councillor and council-master are registered agents, so you can customise them u
       "model": "openai/gpt-5.4",
       "temperature": 0.3,
       "mcps": ["grep_app", "context7"]
-    },
-    "council-master": {
-      "model": "anthropic/claude-opus-4-6",
-      "variant": "high"
     }
   }
 }
@@ -566,7 +462,6 @@ Councillor and council-master are registered agents, so you can customise them u
 | Agent | Model | MCPs | Skills | Temperature |
 |-------|-------|------|--------|-------------|
 | `councillor` | `openai/gpt-5.4-mini` | none | none | 0.2 |
-| `council-master` | `openai/gpt-5.4-mini` | none | none | 0.1 |
 
 **Note:** Per-councillor model overrides in the council config (`presets.<name>.<councillor>.model`) take precedence over the agent-level default.
 
@@ -583,15 +478,12 @@ Councillor and council-master are registered agents, so you can customise them u
 │    ├── SubagentDepthTracker (recursion guard)           │
 │    │                                                     │
 │    └── Agent Sessions                                    │
-│        ├── councillor (read-only, 🔍)                   │
-│        │   └── deny all + allow: read, glob, grep,      │
-│        │       lsp, list, codesearch                     │
-│        └── council-master (zero tools, 🔒)              │
-│            └── deny all + question: deny                 │
+│        └── councillor (read-only, 🔍)                   │
+│            └── deny all + allow: read, glob, grep,      │
+│                lsp, list, codesearch                     │
 │                                                         │
 │  Agent Registration                                     │
 │    ├── council: mode "all" (user + orchestrator)        │
-│    ├── councillor: mode "subagent", hidden              │
-│    └── council-master: mode "subagent", hidden          │
+│    └── councillor: mode "subagent", hidden              │
 └─────────────────────────────────────────────────────────┘
 ```

+ 1 - 2
docs/mcps.md

@@ -24,8 +24,7 @@ Built-in Model Context Protocol (MCP) servers ship with oh-my-opencode-slim and
 | `oracle` | none |
 | `explorer` | none |
 | `fixer` | none |
-| `councillor` | none |
-| `council-master` | none |
+ | `councillor` | none |
 
 ---
 

+ 6 - 40
oh-my-opencode-slim.schema.json

@@ -352,7 +352,7 @@
       }
     },
     "disabled_agents": {
-      "description": "Agent names to disable completely. Disabled agents are not instantiated and cannot be delegated to. Orchestrator and council internal agents (councillor, council-master) cannot be disabled. By default, 'observer' is disabled. Remove it from this list and configure a vision-capable model to enable.",
+      "description": "Agent names to disable completely. Disabled agents are not instantiated and cannot be delegated to. Orchestrator and council internal agents (councillor) cannot be disabled. By default, 'observer' is disabled. Remove it from this list and configure a vision-capable model to enable.",
       "type": "array",
       "items": {
         "type": "string"
@@ -578,26 +578,6 @@
     "council": {
       "type": "object",
       "properties": {
-        "master": {
-          "type": "object",
-          "properties": {
-            "model": {
-              "type": "string",
-              "pattern": "^[^/\\s]+\\/[^\\s]+$",
-              "description": "Model ID for the council master (e.g. \"anthropic/claude-opus-4-6\")"
-            },
-            "variant": {
-              "type": "string"
-            },
-            "prompt": {
-              "description": "Optional role/guidance injected into the master synthesis prompt",
-              "type": "string"
-            }
-          },
-          "required": [
-            "model"
-          ]
-        },
         "presets": {
           "type": "object",
           "propertyNames": {
@@ -617,28 +597,16 @@
             }
           }
         },
-        "master_timeout": {
-          "default": 300000,
-          "type": "number",
-          "minimum": 0
-        },
-        "councillors_timeout": {
+        "timeout": {
           "default": 180000,
           "type": "number",
-          "minimum": 0
+          "minimum": 0,
+          "description": "Councillor timeout (ms)"
         },
         "default_preset": {
           "default": "default",
           "type": "string"
         },
-        "master_fallback": {
-          "description": "Fallback models for the council master. Tried in order if the primary model fails. Example: [\"anthropic/claude-sonnet-4-6\", \"openai/gpt-5.4\"]",
-          "type": "array",
-          "items": {
-            "type": "string",
-            "pattern": "^[^/\\s]+\\/[^\\s]+$"
-          }
-        },
         "councillor_execution_mode": {
           "default": "parallel",
           "description": "Execution mode for councillors. \"serial\" runs them one at a time (required for single-model systems). \"parallel\" runs them concurrently (default, faster for multi-model systems).",
@@ -650,14 +618,12 @@
         },
         "councillor_retries": {
           "default": 3,
-          "description": "Number of retry attempts for councillors and master that return empty responses (e.g. due to provider rate limiting). Default: 3 retries.",
+          "description": "Number of retry attempts for councillors that return empty responses (e.g. due to provider rate limiting). Default: 3 retries.",
           "type": "integer",
           "minimum": 0,
-          "maximum": 5
-        }
+        "maximum": 5
       },
       "required": [
-        "master",
         "presets"
       ]
     }