diff --git a/CONTRACTS.md b/CONTRACTS.md index a53a75d..306e6ff 100644 --- a/CONTRACTS.md +++ b/CONTRACTS.md @@ -75,6 +75,17 @@ node "$OPC_HARNESS" synthesize
] Print merged OPC config w/ _source map");
+ console.log(" model-route --node ] List all runbooks");
diff --git a/pipeline/discussion-protocol.md b/pipeline/discussion-protocol.md
index f139bef..00ebcde 100644
--- a/pipeline/discussion-protocol.md
+++ b/pipeline/discussion-protocol.md
@@ -13,6 +13,8 @@ You are participating in a multi-agent discussion. Your goal is to reach a well-
Round-robin discussion, maximum 3 rounds. Each round builds on the previous.
+Before each participant is dispatched, run `opc-harness model-route --node {NODE_ID} --node-type discussion --role {ROLE} --dir {WORKING_DIR}` and pass the returned explicit `model` to the Agent tool. Keep all participants and rounds; routing changes models, not discussion topology.
+
### Round 1 — Independent Analysis
Each agent is dispatched serially (not parallel). Each agent reads the original task and upstream context, then outputs their independent analysis and proposed approach.
diff --git a/pipeline/evaluator-prompt.md b/pipeline/evaluator-prompt.md
index 1cb55ad..2ce6c88 100644
--- a/pipeline/evaluator-prompt.md
+++ b/pipeline/evaluator-prompt.md
@@ -2,7 +2,7 @@
**Orchestrator instructions (do not include in the subagent prompt):**
-Read this section, fill in the `{placeholders}` in the subagent prompt below, then pass everything from the `---` separator onward as the `prompt` parameter to the Agent tool with `subagent_type: "general-purpose"`. Strip this header section — the subagent should only see what's below the line.
+Read this section and fill in the `{placeholders}`. Before dispatch, run `opc-harness model-route --node {NODE_ID} --node-type review --role evaluator --dir {PROJECT_ROOT}`. Pass everything from the `---` separator onward as the Agent `prompt`, use `subagent_type: "general-purpose"`, and pass the returned route's `model` as the Agent model parameter. If routing fails or the host cannot honor the model, do not silently inherit the root model. Strip this header section — the subagent should only see what's below the line.
**Task-type selection:** In the "Your Approach" section, keep only the matching subsection (Build / Brainstorm / Plan) and delete the others. For review/analysis tasks, use the Build approach (code inspection + testing).
diff --git a/pipeline/implementer-prompt.md b/pipeline/implementer-prompt.md
index da207d9..372a2d6 100644
--- a/pipeline/implementer-prompt.md
+++ b/pipeline/implementer-prompt.md
@@ -2,7 +2,7 @@
**Orchestrator instructions (do not include in the subagent prompt):**
-Read this section, fill in the `{placeholders}` in the subagent prompt below, then pass everything from the `---` separator onward as the `prompt` parameter to the Agent tool with `subagent_type: "general-purpose"`. Strip this header section — the subagent should only see what's below the line.
+Read this section and fill in the `{placeholders}`. Before dispatch, run `opc-harness model-route --node {NODE_ID} --node-type build --role implementer --dir {PROJECT_ROOT}`. Pass everything from the `---` separator onward as the Agent `prompt`, use `subagent_type: "general-purpose"`, and pass the returned route's `model` as the Agent model parameter. If routing fails or the host cannot honor the model, do not silently inherit the root model. Strip this header section — the subagent should only see what's below the line.
---
diff --git a/pipeline/loop-protocol.md b/pipeline/loop-protocol.md
index 64e036a..76aba26 100644
--- a/pipeline/loop-protocol.md
+++ b/pipeline/loop-protocol.md
@@ -402,6 +402,7 @@ Tick ordering (CRITICAL — the gate must fire BEFORE any work):
2. Find the claimed unit's verify: and eval: lines in plan.md — these tell you HOW to verify it.
3. Execute the unit. Key rules to re-verify each tick:
- Review units MUST dispatch ≥2 independent subagents via Agent tool (never self-review)
+ - Before every Agent call, resolve and pass an explicit model with opc-harness model-route; never silently inherit the root model
- Implement/fix units MUST produce a git commit
- UI units MUST include a screenshot artifact
- Use the unit's verify: line to run the correct verification command
@@ -418,6 +419,7 @@ Tick ordering (CRITICAL — the gate must fire BEFORE any work):
Review units MUST use independent subagents (Agent tool). The orchestrator:
1. Dispatches 2-5 reviewer agents in parallel via Agent tool
+ - Resolve each role with `opc-harness model-route --node review --node-type review --role {role} --dir {PROJECT_ROOT}` and pass the returned explicit model
2. Each agent gets: file list, acceptance criteria, project context
3. Each agent produces eval-{role}.md with 🔴/🟡/🔵 findings
4. Orchestrator collects evals and writes handshake
diff --git a/pipeline/model-routing.md b/pipeline/model-routing.md
new file mode 100644
index 0000000..fb5a9ff
--- /dev/null
+++ b/pipeline/model-routing.md
@@ -0,0 +1,95 @@
+# Model Routing Protocol
+
+Model routing is independent from flow selection and role selection. Keep OPC's nodes, rounds, and reviewers unchanged; choose the least expensive model tier that can reliably perform each role.
+
+Claude Code uses the parent model when a subagent model is omitted. OPC therefore resolves a model before **every** Agent call and passes it explicitly. See the [Claude Code subagent model precedence](https://code.claude.com/docs/en/sub-agents#choose-a-model).
+
+## Resolve Before Dispatch
+
+```bash
+opc-harness model-route \
+ --node "$NODE_ID" \
+ --node-type "$NODE_TYPE" \
+ --role "$ROLE" \
+ --dir "$PROJECT_ROOT"
+```
+
+The command returns JSON:
+
+```json
+{
+ "ok": true,
+ "dispatch": true,
+ "tier": "economy",
+ "model": "haiku",
+ "source": "role:tester",
+ "premium": false
+}
+```
+
+- `dispatch: false` means the node is orchestrator/tool-only; do not spawn an Agent.
+- Pass `model` as the Claude Code Agent tool's per-invocation model parameter.
+- On another host, treat `model` as a host-native identifier and use that host's explicit model selector.
+- If the host cannot honor an explicit model, stop instead of silently inheriting the root model.
+- `PREMIUM_APPROVAL_REQUIRED` requires explicit user approval, then re-run with `--allow-premium`.
+- `CLAUDE_CODE_SUBAGENT_MODEL` has runtime precedence and is surfaced as `envOverride: true`.
+
+## Defaults
+
+| Work | Tier | Claude Code default |
+|---|---|---|
+| Test design and user/UX observer roles | economy | `haiku` |
+| Discussion, briefs, implementation, semantic review, hotfix | standard | `sonnet` |
+| Explicit premium escalation only | premium | `inherit` |
+| Execute and gate nodes | none | no Agent |
+
+These defaults prevent a premium root session from automatically turning every `general-purpose` child into the same premium model.
+
+## Layered Configuration
+
+Set `agentRouting` in `~/.opc/config.json` or repository `.opc/config.json`. Repository config overrides user config through OPC's existing deep-merge rules.
+
+```json
+{
+ "agentRouting": {
+ "defaultTier": "standard",
+ "unknownModelPolicy": "deny",
+ "allowPremiumByDefault": false,
+ "models": {
+ "economy": "haiku",
+ "standard": "sonnet",
+ "premium": "inherit"
+ },
+ "nodeTypes": {
+ "discussion": "standard",
+ "brief": "standard",
+ "build": "standard",
+ "review": "standard",
+ "execute": "none",
+ "gate": "none"
+ },
+ "nodes": {
+ "test-design": "economy",
+ "ux-simulation": "economy"
+ },
+ "roles": {
+ "tester": "economy",
+ "new-user": "economy",
+ "security": "standard"
+ },
+ "premiumModels": ["inherit", "opus"]
+ }
+}
+```
+
+Values in `models` are host-native identifiers. A Codex or other host adapter can replace them with its own available high-value and premium model IDs without changing OPC's flow definitions.
+
+Resolution precedence is:
+
+1. Host-wide runtime override (`CLAUDE_CODE_SUBAGENT_MODEL` on Claude Code)
+2. Role tier
+3. Node tier
+4. Node-type tier
+5. `defaultTier`
+
+Do not choose a tier from role prestige. `security`, `architect`, or `devil-advocate` are scopes, not automatic premium-model approvals. Escalate only for concrete risk or repeated failure of a lower tier.
diff --git a/pipeline/role-evaluator-prompt.md b/pipeline/role-evaluator-prompt.md
index 81fc6db..356ad01 100644
--- a/pipeline/role-evaluator-prompt.md
+++ b/pipeline/role-evaluator-prompt.md
@@ -2,7 +2,7 @@
**Orchestrator instructions (do not include in the subagent prompt):**
-Read this section, fill in the `{placeholders}` in the subagent prompt below, then pass everything from the `---` separator onward as the `prompt` parameter to the Agent tool with `subagent_type: "general-purpose"`. Strip this header section — the subagent should only see what's below the line.
+Read this section and fill in the `{placeholders}`. Before each role dispatch, run `opc-harness model-route --node {NODE_ID} --node-type {NODE_TYPE} --role {role_name} --dir {PROJECT_ROOT}`. Pass everything from the `---` separator onward as the Agent `prompt`, use `subagent_type: "general-purpose"`, and pass the returned route's `model` as the Agent model parameter. If routing fails or the host cannot honor the model, do not silently inherit the root model. Strip this header section — the subagent should only see what's below the line.
For each role evaluator, paste the full content of the role's `.md` file into the Identity/Expertise and Anti-Patterns placeholders.
diff --git a/pipeline/ux-simulation-protocol.md b/pipeline/ux-simulation-protocol.md
index 2a270b8..7f59bcc 100644
--- a/pipeline/ux-simulation-protocol.md
+++ b/pipeline/ux-simulation-protocol.md
@@ -130,6 +130,8 @@ If no previous run exists, this is a first run — skip delta, use absolute gate
Dispatch **3 parallel subagents** using the Agent tool, each receiving:
+Before dispatching each observer, run `opc-harness model-route --node ux-simulation --node-type execute --role {role} --dir {PROJECT_ROOT}` and pass the returned explicit `model` to the Agent tool. Keep all three observers; routing changes models, not observer topology.
+
- **Base prompt:** `role-evaluator-prompt.md` with the role `.md` content (including Observation Mode section)
- **Observation appendix:** full content of `ux-observer-protocol.md`
- **Tier:** `flow-state.tier`