Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/check-skill-names.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ jobs:
with:
node-version: 22
- run: node scripts/check-skill-names.mjs
- run: node scripts/check-models.mjs
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ cursor already has a great plan mode which works great with oddstack. but person

type [`/automate-me`](./skills/automate-me/SKILL.md). it mines your recent transcripts, drafts a `<your-name>-mode` skill from how you've actually worked, and routes through oddstack underneath. you keep oddstack as the base and end up with your own routing skill alongside `odd-mode`.

models are configurable too. type [`/setup-oddstack`](./skills/setup-oddstack/SKILL.md). it detects the models this harness can select and edits [`skills/odd-mode/models.txt`](./skills/odd-mode/models.txt), the map every skill reads. cursor, claude code, codex, t3 code, and grok bot all see it, because the file lives inside the odd-mode skill and each skill links to it as a sibling. a `~/.cursor/rules/oddstack-models.mdc` file, when you still have one, overrides the map on that machine.
models are configurable too. type [`/setup-oddstack`](./skills/setup-oddstack/SKILL.md). it detects the models this harness can select and edits [`skills/odd-mode/models.txt`](./skills/odd-mode/models.txt), the map every skill reads. cursor, claude code, codex, t3 code, and grok bot all see it, because the file lives inside the odd-mode skill and each skill links to it as a sibling. [`harness.md`](./skills/odd-mode/harness.md) beside it says how each harness spawns an entry.

when a default changes, a map edited before the change still pins the old default. delete those role lines, then run `/setup-oddstack` again. a rerun keeps any role whose model differs from the default by family, list, or alias.

Expand Down
2 changes: 1 addition & 1 deletion docs/dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ For a bot (such as Grok Bot) that starts T3 Code threads through the T3 MCP tool

## The lead

Launch every lead thread with `modelSelection: {"instanceId":"claudeAgent","model":"claude-opus-5-5","options":{"effort":"high"}}`, the `judgment and prose` line of `models.txt`. Without it, a launch from outside T3 gets the project's default model. The lead plans, routes each role per `models.txt`, reviews, and never builds. Never launch a lead on `cursor`. A `cursor` thread never reaches a terminal state in T3 Code (see odd-mode's Subagents section), so you would never see it finish.
Launch every lead thread with `modelSelection: {"instanceId":"claudeAgent","model":"claude-opus-5-5","options":{"effort":"high"}}`, the `judgment and prose` line of `models.txt`. Without it, a launch from outside T3 gets the project's default model. The lead plans, routes each role per `models.txt`, reviews, and never builds. Never launch a lead on `cursor`. The lead's work is judgment, which stays on Claude Opus, and a `cursor` thread on a long path never finishes (see odd-mode's Subagents section).

Do not pick models for the lead's children. The lead reads `models.txt` from the repo it runs in.

Expand Down
6 changes: 3 additions & 3 deletions docs/guide/01-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ Run:

The committed map runs at `high` reasoning, the same as the `medium` budget. `large` raises each model to `xhigh`. `unlimited` lifts each model to its highest tier, up to `max`. Opus goes to `max`. Grok tops out at `xhigh`, so it stays there. `small` lowers the reasoning and spends fewer tokens.

You only override what you care about. A role with no line in the map keeps the skill's default. To restore a default, delete that role's line. A rerun of `/setup-oddstack` keeps any role whose model differs from the default by family, list, or alias. When a default changes, a map edited before the change still pins the old default, so delete those role lines, then run `/setup-oddstack` again. An old `~/.cursor/rules/oddstack-models.mdc` still overrides the map on that machine until you delete it.
Every role needs a line in the map. A skill that finds no line for its role stops and says so, rather than guessing a model. To restore the committed choices, check out `skills/odd-mode/models.txt` from git. A rerun of `/setup-oddstack` keeps any role whose model differs from the committed map by provider, model, list, or `inherit`.

You might be wondering what happens if you use Auto. Set a role to `inherit-parent` or `auto` and oddstack omits the subagent `model` field, so the subagent inherits your parent chat model. Both values mean the same thing, and neither is a model slug. For a panel role the value is a list, and one subagent runs per entry, so the list length sets the panel size. Setup also configures `swarm workers`, the default model for every `/swarm` worker unless a race names a model for each arm.
You might be wondering what happens if you use Auto. Set a role to `inherit` and oddstack omits the subagent `model` field, so the subagent inherits your parent chat model. For a panel role the value is a list, and one subagent runs per entry, so the list length sets the panel size. Setup also configures `swarm workers`, the default model for every `/swarm` worker unless a race names a model for each arm.

## Accept the verification offer, or don't

Expand All @@ -43,7 +43,7 @@ The map applies the next time a skill is read. A Cursor override file, if you ke
oddstack spends extra tokens on subagents and review panels. That's the price of the rigor. To spend fewer:

- Rerun `/setup-oddstack` and pick a smaller reasoning budget or cheaper models. A strong model in the main chat with cheaper, faster models in the code roles is a good split.
- Set a role to `auto` or `inherit-parent` so it runs on the chat's own model.
- Set a role to `inherit` so it runs on the chat's own model.
- Shorten a panel list. Each entry runs one subagent.
- Save `/odd-mode` for work that needs rigor. A small, obvious edit doesn't.

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/10-recipes-and-pitfalls.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ That's the whole prompt. [`/bro`](../../skills/bro/SKILL.md) restates the last m
- **Correcting the same mistake by hand.** A correction in chat helps one run. `/correct` fixes the repo so no later run repeats it.
- **Using `/arena` for coverage.** `/arena` repeats one design or code brief, then picks a base and grafts the best parts. `/swarm` partitions slices or declared race arms and aggregates one report.
- **Accepting every review comment.** Bots and humans both file real catches and noise in one list. `/interrogate` sorts findings into act-on and dismissed buckets with reasons, and you can override either way.
- **Treating `auto` as a model slug.** `auto` and `inherit-parent` mean "omit the model field so the subagent inherits the parent chat model." [Setup](./01-setup.md) covers the roles.
- **Treating `inherit` as a model.** `inherit` means "omit the model field so the subagent inherits the parent chat model." [Setup](./01-setup.md) covers the roles.
- **Reporting success off a green build.** A build proves it compiles. Ask for the real command, flow, stored value, or profile, and expect the evidence in the reply.
- **Writing a `SKILL.md` freehand.** Route it through the [Authoring or modifying a skill playbook](../../skills/odd-mode/playbooks/authoring-a-skill.md) so validation and review happen.

Expand Down
46 changes: 46 additions & 0 deletions scripts/check-models.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env node
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import process from "node:process";

const mapPath = "skills/odd-mode/models.txt";
const entryPattern = /^(inherit|\S+ \S+( [A-Za-z_]+=\S+)*)$/;
const errors = [];

const roles = new Map();
for (const [index, raw] of fs.readFileSync(mapPath, "utf8").split(/\r?\n/).entries()) {
const line = raw.trim();
if (!line || line.startsWith("#")) continue;
const where = `${mapPath}:${index + 1}`;
const colon = line.indexOf(":");
if (colon === -1) {
errors.push(`${where}: no "<role>:"`);
continue;
}
const role = line.slice(0, colon).trim();
if (roles.has(role)) errors.push(`${where}: duplicate role "${role}"`);
roles.set(role, where);
for (const entry of line.slice(colon + 1).split("|").map((e) => e.trim())) {
if (!entryPattern.test(entry)) errors.push(`${where}: bad entry "${entry}"`);
}
}

const docs = execFileSync("git", ["ls-files", "-z", "--", "skills/*.md"], { encoding: "utf8" })
.split("\0")
.filter(Boolean);
const referencePatterns = [/`([^`]+)` line\b/g, /\(`([^`]+)` in `\.\.\/models\.txt`/g, /\| `([^`]+)` \| `references\//g];
let referenceCount = 0;
for (const doc of docs) {
const text = fs.readFileSync(doc, "utf8");
for (const pattern of referencePatterns) {
for (const match of text.matchAll(pattern)) {
if (match[1].includes(":")) continue;
referenceCount++;
if (!roles.has(match[1])) errors.push(`${doc}: reads role "${match[1]}", which ${mapPath} has no line for`);
}
}
}

for (const error of errors) console.error(error);
if (errors.length) process.exit(1);
console.log(`${roles.size} roles parse, and ${referenceCount} role reads in skills all resolve`);
2 changes: 1 addition & 1 deletion skills/architect/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Skip Phase A only when the work is genuinely greenfield with no surrounding syst

Run the **arena** skill with the design-sketch task and the Phase A grounding artifacts. Pass `references/runner-prompt.md` as each runner's prompt. Each candidate produces a design package shaped per `references/rationale-template.md`.

Take the runners from the `architect runners` line in [`../odd-mode/models.txt`](../odd-mode/models.txt), in place of the `arena runners` line. If that line is missing, use `claude-opus-5-5-high` and `grok-4.7-high-fast`. A same-named line in `~/.cursor/rules/oddstack-models.mdc` overrides the map when that file exists. Alias entries, a `grok-*` seat this harness cannot select, and a `claude-*` seat this harness cannot select follow the runner rules in the **arena** skill's Phase A. An Opus seat is flagged for a Claude thread and is never filled with Grok.
Take the runners from the `architect runners` line in [`../odd-mode/models.txt`](../odd-mode/models.txt), in place of the `arena runners` line. Spawn each entry per the Models section of [`../odd-mode/harness.md`](../odd-mode/harness.md).

Design it twice. Require at least two structurally distinct candidates before synthesis, even when the first looks sufficient. This is the **exhaust-the-design-space** principle skill made concrete. Whole-shape alternatives, not point fixes inside one shape.

Expand Down
4 changes: 2 additions & 2 deletions skills/arena/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The N candidates will receive the same prompt, so the prompt is the contract.

1. State the artifact each candidate is producing.
2. Derive the rubric. State what success looks like for *this* task, then turn it into 3-6 concrete gradeable criteria. The rubric is the picker's tool in Phase D. Candidates only see the task.
3. Pick the runners from the `arena runners` line in [`../odd-mode/models.txt`](../odd-mode/models.txt). If that line is missing, default to one each on `claude-opus-5-5-high` and `grok-4.7-high-fast`. Read the file and follow its `# harness` section. The relative path is the odd-mode skill's `models.txt`, and it resolves through a per-skill symlink. If it misses, open the odd-mode skill and read `models.txt` there. A same-named line in `~/.cursor/rules/oddstack-models.mdc` overrides the map when that file exists. An `auto` or `inherit-parent` entry in this line or the cross-judge line means the parent model, so omit `model` for it. Never fill a `claude-*` seat with Grok. Spawn the seats this harness can run. When this harness should be able to select the family and the tool rejects the exact slug, use the closest valid slug of the same family (`claude-*` or `grok-*`) and say so. Spawn more when the arena covers multiple design directions. Same model N times when the work is generation-bound rather than judgment-sensitive.
3. Pick the runners from the `arena runners` line in [`../odd-mode/models.txt`](../odd-mode/models.txt). Spawn each entry per the Models section of [`../odd-mode/harness.md`](../odd-mode/harness.md). Spawn more when the arena covers multiple design directions. Same model N times when the work is generation-bound rather than judgment-sensitive.
4. Assign output paths. Each candidate writes to its own location (a git worktree where possible, otherwise `/tmp/arena-<slug>/candidate-<n>/`), per the **separate-before-serializing-shared-state** principle skill.

## Phase B: Fan out
Expand All @@ -38,7 +38,7 @@ If a candidate fails to produce output, proceed with N-1 and note the dropout in

## Phase C: Cross-judge

After all Phase B candidates complete, choose one model from the `arena cross-judge pool` line in [`../odd-mode/models.txt`](../odd-mode/models.txt). If that line is missing, choose from `claude-opus-5-5-high` and `grok-4.7-high-fast`. The same line in `~/.cursor/rules/oddstack-models.mdc` overrides the map when that file exists. Prefer a different model family from the parent's, among seats this harness can select. Follow the file's `# harness` section. A `claude-*` seat is never replaced with Grok. If no different-family seat can run here, say that and judge from the parent. Spawn one readonly judge subagent on the chosen model. It sees the rubric and the candidates by path label, scores each criterion, and recommends a base with rationale. It runs in parallel with the parent's reading in Phase D, not with the candidates themselves. Don't spawn the judge while candidates are still writing.
After all Phase B candidates complete, choose one model from the `arena cross-judge pool` line in [`../odd-mode/models.txt`](../odd-mode/models.txt). Prefer an entry on a different provider from the parent's, among those this harness can route per the Models section of [`../odd-mode/harness.md`](../odd-mode/harness.md). If no different-provider entry can run here, say that and judge from the parent. Spawn one readonly judge subagent on the chosen model. It sees the rubric and the candidates by path label, scores each criterion, and recommends a base with rationale. It runs in parallel with the parent's reading in Phase D, not with the candidates themselves. Don't spawn the judge while candidates are still writing.

## Phase D: Pick a base

Expand Down
8 changes: 4 additions & 4 deletions skills/how/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ disable-model-invocation: true

Explore the codebase to answer "how does X work?" questions. Produce architectural explanations at the level of a senior engineer onboarding onto a subsystem, enough to build a working mental model, not so much that it reads like annotated source code.

Each spawn below names a role line in [`../odd-mode/models.txt`](../odd-mode/models.txt) and a default. Read that file and follow its `# harness` section. The relative path is the odd-mode skill's `models.txt`, and it resolves through a per-skill symlink. If it misses, open the odd-mode skill and read `models.txt` there. A same-named line in `~/.cursor/rules/oddstack-models.mdc` overrides the map when that file exists. Leave `model` unset when the value is `auto` or `inherit-parent`. Never run a `claude-*` seat on Grok. When this harness should be able to select the family and the tool rejects the exact slug, use the closest valid slug of the same family and say so.
Each spawn below names a role line in [`../odd-mode/models.txt`](../odd-mode/models.txt). Spawn each entry per the Models section of [`../odd-mode/harness.md`](../odd-mode/harness.md).

## Step 1. Assess Complexity

Expand All @@ -24,7 +24,7 @@ When in doubt, take the simple path.
Decompose the question into 2 to 4 exploration angles, each a distinct slice of the subsystem. Spawn all explorers in a single message:

- `subagent_type`: `generalPurpose`
- `model`: the `how explorer` line, default `grok-4.7-high-fast`
- `model`: the `how explorer` line
- `readonly`: `true`

Each explorer gets the prompt in `references/explorer-prompt.md` with its angle filled in. Then go to Step 3.
Expand All @@ -34,7 +34,7 @@ Each explorer gets the prompt in `references/explorer-prompt.md` with its angle
Spawn one Task subagent that explores and explains in one pass:

- `subagent_type`: `generalPurpose`
- `model`: the `how explainer` line, default `claude-opus-5-5-high`
- `model`: the `how explainer` line
- `readonly`: `true`

Build its prompt from `references/explainer-prompt.md` without the explorer-findings section. Go to Step 4.
Expand All @@ -44,7 +44,7 @@ Build its prompt from `references/explainer-prompt.md` without the explorer-find
Once all explorers have returned, spawn one Task subagent to synthesize their findings into one explanation:

- `subagent_type`: `generalPurpose`
- `model`: the `how explainer` line, default `claude-opus-5-5-high`
- `model`: the `how explainer` line
- `readonly`: `true`

Build its prompt from `references/explainer-prompt.md` with every explorer's findings filled in.
Expand Down
13 changes: 3 additions & 10 deletions skills/interrogate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,13 @@ Write one clear paragraph. If you're unsure about the intent, ask the user befor

## Step 3, Spawn Reviewers

Launch all reviewers in a single message using the Task tool. Read the `interrogate reviewers` line in [`../odd-mode/models.txt`](../odd-mode/models.txt) and follow its `# harness` section. The relative path is the odd-mode skill's `models.txt`, and it resolves through a per-skill symlink. If it misses, open the odd-mode skill and read `models.txt` there. A same-named line in `~/.cursor/rules/oddstack-models.mdc` overrides the map when that file exists. Spawn one reviewer per entry this harness can run, extending or shrinking the Reviewer A/B labels below to that count. If the line is missing, use the table defaults.
Launch all reviewers in a single message using the Task tool. Read the `interrogate reviewers` line in [`../odd-mode/models.txt`](../odd-mode/models.txt). Spawn each entry per the Models section of [`../odd-mode/harness.md`](../odd-mode/harness.md). Spawn one reviewer per entry this harness can run, labeled Reviewer A, B, and so on.

| Subagent | Default model |
|----------|---------------|
| Reviewer A | `claude-opus-5-5-high` |
| Reviewer B | `grok-4.7-high-fast` |

For each reviewer this harness can run:
For each reviewer:
- `subagent_type`: `generalPurpose`
- `model`: the configured `interrogate reviewers` entry, or the table default with no configured line. For an `auto` or `inherit-parent` entry, omit `model` so that reviewer runs on the parent model.
- `model`: that reviewer's entry
- `readonly`: `true`

Never fill a `claude-*` seat with Grok. When this harness should be able to select the family and the tool rejects the exact slug, use the closest valid slug of the same family (`claude-*` or `grok-*`) and say so. If it rejects that too, check the valid slugs in the tool's error message, pick the closest same-family equivalent, spawn with it, and open a separate PR to update the default table. Do not block the review on the slug issue. Never treat an alias entry as a rejected slug.

Read `references/reviewer-prompt.md` and fill in the template with:
1. The stated intent
2. The diff or file contents
Expand Down
Loading
Loading