teamrunner 0.0.2__tar.gz → 0.0.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. {teamrunner-0.0.2 → teamrunner-0.0.3}/.github/ISSUE_TEMPLATE/bug_report.yml +1 -1
  2. {teamrunner-0.0.2 → teamrunner-0.0.3}/.github/ISSUE_TEMPLATE/question.yml +1 -1
  3. {teamrunner-0.0.2 → teamrunner-0.0.3}/.github/workflows/tests.yml +9 -0
  4. {teamrunner-0.0.2 → teamrunner-0.0.3}/CHANGELOG.md +24 -0
  5. {teamrunner-0.0.2 → teamrunner-0.0.3}/PKG-INFO +32 -136
  6. teamrunner-0.0.3/PREVIEW.md +128 -0
  7. {teamrunner-0.0.2 → teamrunner-0.0.3}/README.md +31 -135
  8. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/against.md +0 -1
  9. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/chair.md +3 -3
  10. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/fact_checker.md +4 -4
  11. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/for.md +0 -1
  12. teamrunner-0.0.3/examples/fact_checked/judge.md +20 -0
  13. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/sandbox.md +1 -1
  14. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/team.yaml +2 -0
  15. teamrunner-0.0.3/examples/fact_checked/web.md +19 -0
  16. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/models/member.yaml +3 -3
  17. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/team.yaml +1 -1
  18. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/multi_agent_debate/against.md +0 -1
  19. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/multi_agent_debate/for.md +0 -1
  20. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/multi_agent_debate/judge.md +0 -1
  21. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/multi_agent_debate/team.yaml +2 -0
  22. teamrunner-0.0.3/examples/orchestrators_lite/models/engineer.yaml +7 -0
  23. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/team.yaml +3 -3
  24. teamrunner-0.0.3/examples/reference_check/catalogue.md +19 -0
  25. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/context_checker.md +3 -2
  26. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/editor.md +4 -2
  27. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/intake.md +1 -1
  28. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/metadata_checker.md +11 -5
  29. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/reader.md +7 -2
  30. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/team.yaml +2 -0
  31. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/round_robin/01_for.md +0 -1
  32. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/round_robin/02_against.md +0 -1
  33. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/round_robin/03_judge.md +0 -1
  34. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/round_robin/team.yaml +2 -0
  35. {teamrunner-0.0.2 → teamrunner-0.0.3}/pyproject.toml +1 -1
  36. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/__init__.py +1 -1
  37. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/authoring/AGENTS.md +6 -1
  38. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/build.py +20 -12
  39. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/checks.py +109 -43
  40. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/console.py +22 -0
  41. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/ir.py +18 -1
  42. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/litellm_model.py +14 -1
  43. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/new.py +2 -2
  44. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/run.py +4 -0
  45. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/runs.py +3 -0
  46. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/schemas/member.schema.json +2 -2
  47. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/schemas/model_profile.schema.json +2 -2
  48. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/schemas/team.schema.json +2 -2
  49. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/show.py +4 -0
  50. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/validate.py +14 -2
  51. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_new.py +2 -1
  52. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_run.py +112 -0
  53. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_show.py +11 -0
  54. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_validate.py +29 -0
  55. {teamrunner-0.0.2 → teamrunner-0.0.3}/uv.lock +1 -1
  56. teamrunner-0.0.2/examples/fact_checked/judge.md +0 -19
  57. teamrunner-0.0.2/examples/fact_checked/web.md +0 -13
  58. teamrunner-0.0.2/examples/orchestrators_lite/models/engineer.yaml +0 -7
  59. teamrunner-0.0.2/examples/reference_check/catalogue.md +0 -22
  60. {teamrunner-0.0.2 → teamrunner-0.0.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  61. {teamrunner-0.0.2 → teamrunner-0.0.3}/.github/workflows/publish.yml +0 -0
  62. {teamrunner-0.0.2 → teamrunner-0.0.3}/.gitignore +0 -0
  63. {teamrunner-0.0.2 → teamrunner-0.0.3}/CLA.md +0 -0
  64. {teamrunner-0.0.2 → teamrunner-0.0.3}/CONTRIBUTING.md +0 -0
  65. {teamrunner-0.0.2 → teamrunner-0.0.3}/LICENSE +0 -0
  66. {teamrunner-0.0.2 → teamrunner-0.0.3}/NOTICE +0 -0
  67. {teamrunner-0.0.2 → teamrunner-0.0.3}/SECURITY.md +0 -0
  68. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/LICENSE +0 -0
  69. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/fact_checked/.gitignore +0 -0
  70. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/chair.md +0 -0
  71. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/chen.md +0 -0
  72. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/clerk.md +0 -0
  73. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/diaz.md +0 -0
  74. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/hiring_committee/okafor.md +0 -0
  75. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/multi_agent_debate/.gitignore +0 -0
  76. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/multi_agent_debate/chair.md +0 -0
  77. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/.gitignore +0 -0
  78. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/chair.md +0 -0
  79. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/derek.md +0 -0
  80. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/emma.md +0 -0
  81. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/lily.md +0 -0
  82. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/noah.md +0 -0
  83. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/orchestrators_lite/zoe.md +0 -0
  84. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/chair.md +0 -0
  85. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/reference_check/library.md +0 -0
  86. {teamrunner-0.0.2 → teamrunner-0.0.3}/examples/round_robin/.gitignore +0 -0
  87. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/__main__.py +0 -0
  88. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/authoring/LICENSE +0 -0
  89. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/authoring/__init__.py +0 -0
  90. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/builtin_tools.py +0 -0
  91. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/cli.py +0 -0
  92. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/describe.py +0 -0
  93. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/desk.py +0 -0
  94. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/engine.py +0 -0
  95. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/environment.py +0 -0
  96. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/events.py +0 -0
  97. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/explain.py +0 -0
  98. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/flow.py +0 -0
  99. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/gather.py +0 -0
  100. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/ids.py +0 -0
  101. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/inputs.py +0 -0
  102. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/keys.py +0 -0
  103. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/loader.py +0 -0
  104. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/minutes.py +0 -0
  105. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/model.py +0 -0
  106. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/prices.py +0 -0
  107. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/problems.py +0 -0
  108. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/rehearsal.py +0 -0
  109. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/runfolder.py +0 -0
  110. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/sandbox/__init__.py +0 -0
  111. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/sandbox/run.js +0 -0
  112. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/schema_check.py +0 -0
  113. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/tool_registry.py +0 -0
  114. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/turns.py +0 -0
  115. {teamrunner-0.0.2 → teamrunner-0.0.3}/src/teamrunner/vocabulary.py +0 -0
  116. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_authoring.py +0 -0
  117. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_engine.py +0 -0
  118. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_explain.py +0 -0
  119. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_flow.py +0 -0
  120. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_minutes.py +0 -0
  121. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_prices.py +0 -0
  122. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_runs.py +0 -0
  123. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_sandbox.py +0 -0
  124. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_tools.py +0 -0
  125. {teamrunner-0.0.2 → teamrunner-0.0.3}/tests/test_turns.py +0 -0
@@ -20,7 +20,7 @@ body:
20
20
  attributes:
21
21
  label: TeamRunner version
22
22
  description: The output of `teamrunner --version`
23
- placeholder: teamrunner 0.0.2
23
+ placeholder: teamrunner 0.0.3
24
24
  validations:
25
25
  required: true
26
26
  - type: input
@@ -15,4 +15,4 @@ body:
15
15
  id: version
16
16
  attributes:
17
17
  label: TeamRunner version, if it matters
18
- placeholder: teamrunner 0.0.2
18
+ placeholder: teamrunner 0.0.3
@@ -68,3 +68,12 @@ jobs:
68
68
  teamrunner explain debate
69
69
  teamrunner run debate --rehearse -m "Should cities ban cars from their centres?"
70
70
  teamrunner runs debate
71
+
72
+ # The website's tutorial (teamrunner.ai/tutorial.html), replayed with this build:
73
+ # a change that would break the tutorial fails here, before it is released
74
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
75
+ with:
76
+ repository: teamrunner/teamrunner.ai
77
+ path: website
78
+ - name: The website's tutorial
79
+ run: python website/tests/test_tutorial.py website/tutorial.html
@@ -4,6 +4,30 @@ TeamRunner is in preview: 0.0.x versions are a soft launch, and until 0.1.0
4
4
  is announced a version may change in ways that break teams and run folders
5
5
  made with an earlier one. Each such change is listed here.
6
6
 
7
+ ## 0.0.3
8
+
9
+ - **Backup models**: `model:` and `default_model:` may name several models,
10
+ separated by commas, in order of preference
11
+ (`default_model: openai/gpt-6-luna, anthropic/claude-sonnet-4-5`). Before the
12
+ first turn each member gets the first one available, for the whole run; the
13
+ console says so whenever a member is not on its first choice, and `run.json`,
14
+ `events.jsonl` and `teamrunner runs` record it. A profile's `name:` still
15
+ names one model.
16
+ - The examples name their model once, as `default_model:` in `team.yaml`.
17
+ - All six examples use Gemma 4 31B through OpenRouter with one API key.
18
+ The fact checker reads public sources instead of requiring Brave Search;
19
+ reference lookups use Crossref without a catalogue key. Reference lists
20
+ and findings are shared in the meeting messages so the next checker and
21
+ the editor can read them.
22
+ - Gemma 4 tool calls on OpenRouter no longer compete with strict JSON
23
+ formatting: during tool use the final format is requested in the prompt,
24
+ and the runner still validates the declared outputs.
25
+ - The README's tutorial and its five ideas hold only what works today; what is
26
+ designed but not built yet (experiments, the whiteboard) is in `PREVIEW.md`.
27
+ The README installs with uv or pip and needs one key.
28
+ - Comments in the examples, and the `.gitignore` `teamrunner new` writes, no
29
+ longer cite internal design notes.
30
+
7
31
  ## 0.0.2 — the first preview
8
32
 
9
33
  What the roadmap calls 0.1, the org package: a folder of text files that gets
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: teamrunner
3
- Version: 0.0.2
3
+ Version: 0.0.3
4
4
  Summary: The experimentation platform for agent organizations — run multi-agent orchestrations and factorial experiments entirely from text files.
5
5
  Project-URL: Homepage, https://github.com/teamrunner/teamrunner
6
6
  Project-URL: Repository, https://github.com/teamrunner/teamrunner
@@ -53,7 +53,7 @@ experiments/advocate_model/ [v0.2]
53
53
  └── experiment.yaml ← base: ../../debate — GPT vs. Claude as advocate, 20 replications
54
54
  ```
55
55
 
56
- > Everything unmarked is in the first release, **0.1**. **[v0.2]** arrives with experiments; **[v0.3]** with richer organizations (managers, private information, subteams, parallel work).
56
+ > Everything unmarked works today, in the preview. Marked parts are designed but not built yet: **[v0.2]** arrives with experiments; **[v0.3]** with richer organizations (managers, private information, subteams, parallel work).
57
57
 
58
58
  ```bash
59
59
  teamrunner run debate --message "Remote work makes teams more productive"
@@ -68,13 +68,13 @@ No Python. No graph-building API. No framework vocabulary. If you can edit a tex
68
68
  - [Why we built it](#why-we-built-it)
69
69
  - [The five ideas](#the-five-ideas)
70
70
  - [Tutorial: your first debate in 10 minutes](#tutorial-your-first-debate-in-10-minutes)
71
- - [Tutorial part 2: your first experiment](#tutorial-part-2-your-first-experiment)
72
71
  - [What lands in a run folder](#what-lands-in-a-run-folder)
73
72
  - [What your runs keep, and where your words go](#what-your-runs-keep-and-where-your-words-go)
74
73
  - [Going further: the worked examples](#going-further-the-worked-examples)
75
74
  - [What TeamRunner is not](#what-teamrunner-is-not)
76
75
  - [Status and roadmap](#status-and-roadmap)
77
76
  - [Questions, bugs and ideas](#questions-bugs-and-ideas)
77
+ - [What's coming](PREVIEW.md): designed, not built yet
78
78
 
79
79
  ---
80
80
 
@@ -142,7 +142,7 @@ not your own opinion of the motion.
142
142
 
143
143
  **2. A folder is a team if it contains `team.yaml`.** Everything inside is a member, and a member is one of three things: a **worker** (an agent with a goal), a **team** (**[v0.3]** a subfolder with its own `team.yaml` — just another node in its parent's flow: *its turn means running its own internal flow to completion*, like a subcommittee: it deliberates on its own whiteboard, and the parent hears only its report — `report_from:` in its `team.yaml` says whose words those are), or a **toolkeeper** (the member who holds the keys to a tool). Complexity is contained by hierarchy, not by syntax. There is no registry file listing your agents; the file browser is the org chart.
144
144
 
145
- Tools are not members. An organization doesn't hand every worker every tool; a factory has a tool crib, with someone at the window who checks who may take what, and everyone else asks there. So the only members who touch tools are toolkeepers — a toolkeeper holds the guardrails (timeouts, no network, which folders), decides by a plain access list rather than a prompt, and its text body is simply what the team is told about it. Who may use a toolkeeper, and on what terms, is up to the toolkeeper's own access list — one toolkeeper can give different members different terms, the way a library gives professors and students different cards. The whole folder — team and toolkeepers — is a **package**: copy it anywhere and it gets the same job done. The work itself comes from outside: you hand the team a topic when you run it, or it uses the default written in `team.yaml`.
145
+ The whole folder — team and toolkeepers — is a **package**: copy it anywhere and it gets the same job done. The work itself comes from outside: you hand the team a topic when you run it, or it uses the default written in `team.yaml`.
146
146
 
147
147
  **3. Every team has a chair, and there are no hidden if-statements.** Someone always decides who acts next, and TeamRunner makes you say who:
148
148
 
@@ -152,45 +152,38 @@ Tools are not members. An organization doesn't hand every worker every tool; a f
152
152
 
153
153
  Every routing decision is logged as an event attributed to whoever made it. Which means *protocol vs. discretion* — a script vs. a manager exercising judgment — is itself something you can A/B test by swapping one file for the other (**[v0.3]**).
154
154
 
155
- **4. One whiteboard for who sees what.** Everything an agent knows arrives through one of:
155
+ **4. Tools sit with a toolkeeper.** Tools are not members. An organization doesn't hand every worker every tool; a factory has a tool crib, with someone at the window who checks who may take what, and everyone else asks there. So the only members who touch tools are toolkeepers — a toolkeeper holds the guardrails (timeouts, no network, which folders), decides by a plain access list rather than a prompt, and its text body is simply what the team is told about it. Who may use a toolkeeper, and on what terms, is up to the toolkeeper's own access list — one toolkeeper can give different members different terms, the way a library gives professors and students different cards.
156
156
 
157
- | Layer | Org analogue | Who reads | Who writes |
158
- |---|---|---|---|
159
- | **The whiteboard** (`whiteboard.yaml`) | the meeting, and the official record | everyone, unless a `read_by` line says otherwise | per item `written_by`; whatever anyone says is posted automatically; always logged |
160
- | **Memory** | what you were in the room for | you | nobody — history is append-only |
157
+ **5. Every run is kept, and no agent can touch the record.** Every run gets its own folder in `runs/` with every prompt, everything said, every decision about who acts next, every tool call, the model that answered, tokens, time and cost (see [What lands in a run folder](#what-lands-in-a-run-folder)). The run log is the researcher's camera, outside the simulation: **no agent can ever read or write it** — a hard guarantee, without which every study of who knows what leaks.
161
158
 
162
- Everything said is posted on the whiteboard, open to all by default, so a team with no whiteboard file is a meeting room — and that is all of v0.1. **[v0.3]** `whiteboard.yaml` adds the rest: seal the reviewers from each other, or make the team a relay where each person hears only who spoke before them, with a `read_by` line for what was said — one line each.
163
-
164
- Information asymmetry is not a feature we had to invent; it is a narrow `read_by`. Incentives are numeric items on the whiteboard. Private briefs are items only one party can read.
165
-
166
- And one thing that is emphatically *not* a layer: the run log. It is the researcher's camera, outside the simulation. **No agent can ever read or write it** — a hard guarantee, without which every asymmetry design leaks.
167
-
168
- **5. Experiments are overlays.** **[v0.2]** Your package stays clean and runnable. An `experiment.yaml` lives *outside* it, names it with `base:`, and declares factors as *patches* against it, plus replications and a seed. Single runs and 200-run factorials go through the same engine. When a variant wins, `teamrunner promote` copies the exact version that ran back into the package — same agent, new version — and `export` hands you a standalone copy for production.
159
+ Two more ideas are designed but not built yet — a whiteboard for who sees what, and experiments as overlays — and are described in [PREVIEW.md](PREVIEW.md).
169
160
 
170
161
  ---
171
162
 
172
163
  ## Tutorial: your first debate in 10 minutes
173
164
 
174
- We'll build a three-agent debate, run it, give it rounds, a fact checker and the web, and then (**[v0.2]**) turn it into a 40-run experiment. Zero code throughout.
165
+ We'll build a three-agent debate, run it, give it rounds, a fact checker and the web. Zero code throughout. Turning it into a 40-run experiment comes with 0.2: see [PREVIEW.md](PREVIEW.md#experiments-v02).
175
166
 
176
- > Everything in this tutorial not marked **[v0.2]** or **[v0.3]** works in the preview. See [Status](#status-and-roadmap).
167
+ > Everything in this tutorial works in the preview. See [Status](#status-and-roadmap).
177
168
 
178
169
  ### Step 0 — install
179
170
 
180
171
  ```bash
181
- pip install teamrunner
172
+ uv tool install teamrunner # uv fetches the right Python for you: https://docs.astral.sh/uv/
173
+ pip install teamrunner # or this, if you already have Python 3.10 or later
182
174
  ```
183
175
 
184
- Put your provider keys in TeamRunner's key file, `~/.teamrunner/.env` (in your home folder, outside every team):
176
+ Put your key in TeamRunner's key file, `~/.teamrunner/.env` (in your home folder, outside every team). One key is enough: the one for the provider your team's model uses. This tutorial's team runs on OpenAI:
185
177
 
186
178
  ```
187
179
  OPENAI_API_KEY=sk-...
188
- ANTHROPIC_API_KEY=sk-ant-...
189
180
  ```
190
181
 
182
+ A team on another provider takes that provider's key instead (`ANTHROPIC_API_KEY`, `GEMINI_API_KEY`, …). The shipped examples run on OpenRouter, so they need only `OPENROUTER_API_KEY`. If a key is missing, the run stops before spending anything and says which one. You can try every step without a key: `teamrunner run --rehearse` runs the whole team with no model, for free.
183
+
191
184
  Keys stay with you, never with your teams: team folders get copied, zipped and sent to people, and a key inside one goes with it. If you already have a key set in your shell, TeamRunner offers to save it to the key file the first time it needs it (or to use it just this once). If it finds a `.env` inside a team folder, it offers to move the key out and delete it there. And it will **refuse to run** if it finds something that looks like an API key inside a spec file.
192
185
 
193
- Models on your own machine work too, free: install [Ollama](https://ollama.com), pull a model (`ollama pull llama3.2`), and write `model: ollama/llama3.2`. If Ollama runs somewhere other than this computer, put its address in the key file as `OLLAMA_API_BASE`. A word of warning: the smallest models often fail at exactly what teams need most — answering in a fixed form and asking toolkeepers properly — so they test the wiring better than they stand in for a real team. Before the first turn of every run, TeamRunner checks that each model your team names is available and lists every problem it finds in one table, so you never pay for half a run. `teamrunner validate --online` runs the same check without a run.
186
+ Models on your own machine work too, free: install [Ollama](https://ollama.com), pull a model (`ollama pull gpt-oss:20b`), and write `default_model: ollama/gpt-oss:20b` in `team.yaml`. If Ollama runs somewhere other than this computer, put its address in the key file as `OLLAMA_API_BASE`. Choose one of about 20 billion parameters or more: the smallest models often fail at exactly what teams need most — answering in a fixed form and asking toolkeepers properly — so they test the wiring better than they stand in for a real team. Before the first turn of every run, TeamRunner checks that each model your team names is available and lists every problem it finds in one table, so you never pay for half a run. `teamrunner validate --online` runs the same check without a run.
194
187
 
195
188
  ### Step 1 — make a team
196
189
 
@@ -208,12 +201,14 @@ Two other ways to start, when you'd rather not begin from nothing:
208
201
 
209
202
  This tutorial starts from nothing, so you see every file.
210
203
 
211
- `team.yaml` is the only required file. It marks the folder as a team and says what the team does:
204
+ `team.yaml` is the only required file. It marks the folder as a team, says what the team does, and names the model its agents run on:
212
205
 
213
206
  ```yaml
214
207
  # team.yaml
215
208
  name: debate
216
209
  description: Two debaters argue a topic; a judge decides
210
+
211
+ default_model: openai/gpt-6-luna
217
212
  ```
218
213
 
219
214
  Two things worth knowing now:
@@ -229,7 +224,6 @@ An agent file is Markdown. Frontmatter configures, body prompts.
229
224
  <!-- advocate.md -->
230
225
  ---
231
226
  description: Argues in favor of the topic
232
- model: openai/gpt-6-luna
233
227
  ---
234
228
  You are debating in favor of this topic:
235
229
 
@@ -244,7 +238,6 @@ recent point before adding your own. Be persuasive but honest.
244
238
  <!-- critic.md -->
245
239
  ---
246
240
  description: Argues against the topic
247
- model: openai/gpt-6-luna
248
241
  ---
249
242
  You are debating against this topic:
250
243
 
@@ -259,7 +252,6 @@ Be persuasive but honest.
259
252
  <!-- judge.md -->
260
253
  ---
261
254
  description: Decides who argued better
262
- model: openai/gpt-6-luna
263
255
  output:
264
256
  winner: [advocate, critic]
265
257
  reasoning: ""
@@ -275,7 +267,7 @@ explain your decision in one paragraph.
275
267
 
276
268
  Four things just happened:
277
269
 
278
- **Every agent names its model.** The model is part of the agent — it's who you hired. When a whole team uses the same one, `team.yaml` can say it once (`default_model: openai/gpt-6-luna`), and a model with its settings can live in a profile, `models/careful.yaml`, that agents name as `model: careful.yaml`. `teamrunner show judge` prints exactly which model and settings the judge will run with. `examples/orchestrators_lite` does both: its five engineers share one profile, `models/engineer.yaml`, named once as the team's `default_model:`.
270
+ **The team names the model once.** No agent above names a model, so all three run on `default_model:` from `team.yaml`. An agent that should run on a different one says so itself (`model: anthropic/…`), and its own line wins. A model with its settings can live in a profile, `models/careful.yaml`, named as `default_model: careful.yaml` or `model: careful.yaml`. `teamrunner show judge` prints exactly which model and settings the judge will run with, and where each came from. `examples/orchestrators_lite` does both: its five engineers share one profile, `models/engineer.yaml`, named once as the team's `default_model:`. Models churn, so either line can name backups in order of preference, separated by commas: `default_model: openai/gpt-6-luna, anthropic/claude-sonnet-4-5`. Before the first turn TeamRunner picks the first one available, keeps it for the whole run, and says so whenever it isn't the first (`↪ judge runs on anthropic/claude-sonnet-4-5, its 2nd choice — …`); the run's record says so too.
279
271
 
280
272
  **Alphabetical order is the flow.** With no `chair.md`, members act in alphabetical filename order, one turn each — here `advocate → critic → judge`. That is also why the files above have no `name:` line: an agent's name is its filename. The folder listing *is* the running order, visible in any file browser. Rename to reorder.
281
273
 
@@ -382,7 +374,6 @@ Suppose we want each speech fact-checked before it stands. Write a fact checker
382
374
  <!-- fact_checker.md -->
383
375
  ---
384
376
  description: Checks the factual claims in the latest speech
385
- model: openai/gpt-6-luna
386
377
  output:
387
378
  verdict: [pass, fail]
388
379
  reasons: ""
@@ -470,111 +461,6 @@ A toolkeeper step means the member just before it must end its turn with a reque
470
461
 
471
462
  TeamRunner 0.1 has four built-in tools: `web_search_brave` (Brave Search), `read_page` (a link in, the page's text out), `read_file` (a file the run was given with `--file`, its text out) and `run_code` (Python in a sandbox: no internet unless the toolkeeper allows it, and files written only in the run's own work folder). The sandbox is Python running in WebAssembly, and it installs with TeamRunner — nothing else to set up; it can use packages built for it (numpy, pandas, scipy, sympy and many more), but not every package. `examples/fact_checked` gives the fact checker a second toolkeeper, `sandbox.md`, so it can check the sums in a claim by running them. A toolkeeper holds one tool in 0.1, so its limits always mean one thing; a member who needs two tools asks two toolkeepers, as the fact checker does here. `examples/reference_check` uses `read_page` and `read_file`: it checks every reference in a paper.
472
463
 
473
- ### Step 8 — private information and shared records **[v0.3]**
474
-
475
- The debate doesn't need this, but the moment you study organizations you will. `whiteboard.yaml` is the team's whiteboard: declared, typed items, each with who may read and write it. Everything anyone says is posted there too, open to all unless a `read_by` line says otherwise.
476
-
477
- ```yaml
478
- # whiteboard.yaml
479
-
480
- # Bare list = a choice, starting at the first option.
481
- # Read by everyone, written by the chair — those are the defaults.
482
- stage: [opening, rebuttal, counter, closing, done]
483
-
484
- # A bare value = a starting value, read and written by the defaults.
485
- rounds_left: 3
486
-
487
- # Spell it out only when you change who reads or writes it.
488
- score:
489
- starts_as: 0
490
- written_by: [judge]
491
-
492
- # Information asymmetry IS a read permission.
493
- buyer_ceiling:
494
- starts_as: 850
495
- read_by: [buyer]
496
- ```
497
-
498
- Every field is declared up front — no ad-hoc keys appearing mid-run. Every write is harness-mediated and logged as a `state_write` event: an agent listed in `written_by` returns the new value as a declared output field; nothing pokes state directly. Agents reference readable fields in their prompts as `{{whiteboard.buyer_ceiling}}`, and the validator checks that permission before you spend a cent.
499
-
500
- This one file is doing a surprising amount of work. Private valuations, confidential negotiating briefs, payoffs and incentives, task inputs, an artifact handed from one department to the next — all of them are just whiteboard items with the right permissions.
501
-
502
- ---
503
-
504
- ## Tutorial part 2: your first experiment **[v0.2]**
505
-
506
- Here's the question that motivated the whole project: **does it matter which model plays the advocate?**
507
-
508
- Don't touch the debate. Make a folder next to it with one file:
509
-
510
- > **[v0.2]** Experiments are built after the package is right. The file below is the agreed shape: it lives *outside* the package (`experiments/advocate_model/experiment.yaml`) and names it with `base:`.
511
-
512
- ```yaml
513
- # experiments/advocate_model/experiment.yaml
514
- name: advocate-model-ab
515
- base: ../../debate
516
- task: tasks/remote_work
517
-
518
- factors:
519
- advocate_model:
520
- - label: gpt
521
- patch: {advocate.model: "openai/gpt-6-luna"}
522
- - label: claude
523
- patch: {advocate.model: "anthropic/claude-sonnet-4-5"}
524
-
525
- replications: 20 # 2 conditions × 20 = 40 runs
526
- seed: 42
527
-
528
- limits:
529
- max_concurrent: 4
530
- max_cost_usd: 40.00
531
- ```
532
-
533
- ```bash
534
- teamrunner experiment .
535
- ```
536
-
537
- What just became possible:
538
-
539
- **Factors are patches, so the base spec stays clean.** `advocate.model` means "the `model` field of the component `advocate`". Patch paths address any field of any component, including the prompt body (the field is called `prompt`), and any value can be `file:<path>` to swap in a whole file. `patch: {}` is the *unmodified base spec* — the control condition, written honestly as a level rather than smuggled in as "the normal run."
540
-
541
- **Anything in a file is a factor.** That's not a slogan, it's the mechanism. A few real examples from our worked recasts:
542
-
543
- ```yaml
544
- # A prompt/strategy A/B — same agent, two personalities
545
- - {label: tough, patch: {buyer.prompt: file:variants/buyer_tough.md}}
546
-
547
- # A treatment: one member is secretly self-interested
548
- - {label: saboteur, patch: {council.prompt: file:variants/saboteur.md}}
549
-
550
- # The communication structure ITSELF as the factor:
551
- # same reviewers, same paper — only who can read whose review changes
552
- - label: public
553
- patch:
554
- whiteboard.said.reviewer_1.read_by: everyone
555
- whiteboard.said.reviewer_2.read_by: everyone
556
- whiteboard.said.reviewer_3.read_by: everyone
557
- ```
558
-
559
- That last one is the founding claim of the product. Studying whether peer reviewers herd when they can see each other's reviews is, in TeamRunner, a two-level factor on who may read what each reviewer says.
560
-
561
- **`replications` has a minimum of 2.** Writing `replications: 1` requires an explicit `allow_single_run: true`. LLMs are stochastic; single runs are anecdotes; we made the anecdote the awkward thing to write.
562
-
563
- **One master `seed` fixes everything the harness controls** — condition assignment, ordering, per-run seed derivation. It does not, and cannot, make the model deterministic. See [Why we built it](#3-almost-nothing-published-about-multi-agent-systems-is-reproducible).
564
-
565
- **`limits` is a real budget ceiling.** `max_cost_usd` stops the experiment; `max_concurrent` keeps you inside your rate limits.
566
-
567
- Then get the data out:
568
-
569
- ```bash
570
- teamrunner runs # browse what you've run
571
- teamrunner export --experiment advocate-model-ab --format csv
572
- ```
573
-
574
- Long format, one row per observation, straight into R, Stata, or pandas. Your analysis never lives inside our dashboard — that's a promise, not an oversight.
575
-
576
- The declarative metrics layer (ground truth, LLM-as-judge with a separately pinned judge model, cost and time) is specified but its file format is still open. Today's `metrics:` references in the examples are placeholders.
577
-
578
464
  ---
579
465
 
580
466
  ## What lands in a run folder
@@ -634,7 +520,17 @@ replays that exact spec. Every run folder is a self-contained replication packag
634
520
 
635
521
  ## Going further: the worked examples
636
522
 
637
- Six examples ship with TeamRunner, in `examples/`, simplest first — each adds something the one before it doesn't have. Copy one by name to start from it: `teamrunner new my_team --from orchestrators_lite`. Most are published multi-agent papers or projects, recast as TeamRunner files.
523
+ Six examples ship with TeamRunner, in `examples/`, simplest first — each adds something the one before it doesn't have. Copy one by name to start from it: `teamrunner new my_team --from orchestrators_lite`. Most are published multi-agent papers or projects, recast as TeamRunner files. All six use `openrouter/google/gemma-4-31b-it` and need only an OpenRouter API key, with credits for model usage. They do not require OpenAI, Anthropic, Brave or catalogue keys.
524
+
525
+ Set the key outside the team folder (never commit it):
526
+
527
+ ```bash
528
+ export OPENROUTER_API_KEY="your-openrouter-key"
529
+ teamrunner new my_debate --from round_robin
530
+ teamrunner run my_debate
531
+ ```
532
+
533
+ On PowerShell, set `$env:OPENROUTER_API_KEY="your-openrouter-key"` instead. You can also keep `OPENROUTER_API_KEY` in `~/.teamrunner/.env`. Add `--rehearse` to check a flow without calling a model; a rehearsal does not verify live model or tool behavior. The two source-checking examples read public pages and Crossref without extra keys; access restrictions, rate limits and missing abstracts or PDFs can still limit their evidence. The fact checker's Python sandbox downloads its runtime on first use. The code-review example uses demo model settings rather than the source paper's model.
638
534
 
639
535
  | Example | Recast of | What it adds |
640
536
  |---|---|---|
@@ -642,7 +538,7 @@ Six examples ship with TeamRunner, in `examples/`, simplest first — each adds
642
538
  | **`multi_agent_debate/`** | muthuspark/multi-agent-debate | A chair: the debate as a script, with a repeated block of rounds. |
643
539
  | **`hiring_committee/`** | Stasser & Titus 1985 (hidden profile) | A committee picks a candidate when the key facts are spread across its members: facts only one member knows, kept in that member's own file; a stage in the chair; a clerk who records the decision; and both meeting formats in one `chair.md`, one commented out. |
644
540
  | **`orchestrators_lite/`** | Fukui 2026 (lite) | Five engineers review code against planted bugs: a model profile the whole team shares, and two inputs. |
645
- | **`fact_checked/`** | — | The debate with a fact checker: decisions that send a speech back, counted tries, and toolkeepers — a web search with different terms for different members, a required search step, and a Python sandbox. |
541
+ | **`fact_checked/`** | — | The debate with a fact checker: decisions that send a speech back, counted tries, and toolkeepers — a public-source reader with different terms for different members, a required source-reading step, and a Python sandbox. |
646
542
  | **`reference_check/`** | sciwrite-lint (Samsonau 2026) | Checks every reference in a paper: that it exists, that its details are right, and that the cited paper says what the citing sentence claims. A paper given as a link or a file (`--file`), toolkeepers holding `read_page` and `read_file`, the same tool under two toolkeepers with different `domains`, a decision that can stop the run, and batches of ten as a loop with a counted exit. |
647
543
 
648
544
  **Coming with 0.3**, recast on paper and waiting for the features they need: **Deb8flow** (subteams — each side a debater plus its own fact checker), **NegotiationGym** (private valuations, a loop either side can end), **LLM-Deliberation** (four parties, confidential briefs, one secretly given a hidden agenda), **AgentReview** (three reviewers at once, sealed reviews — do reviewers herd when reviews are public?), **ChatDev** (a three-level company, each department on its own whiteboard), and the full **Invisible Orchestrators** study (a hidden orchestrator whose words reach the team only as unsigned "policy updates").
@@ -665,7 +561,7 @@ The recasting campaign is the point, not decoration: each one is run *before* th
665
561
 
666
562
  ## Status and roadmap
667
563
 
668
- **Preview.** What the roadmap calls 0.1 is out as a soft launch, numbered 0.0.x: every command in the tutorial not marked **[v0.2]** or **[v0.3]** works. It may still change in ways that break earlier teams and runs until 0.1.0 is announced. What changed in each version is in the [changelog](https://github.com/teamrunner/teamrunner/blob/main/CHANGELOG.md).
564
+ **Preview.** What the roadmap calls 0.1 is out as a soft launch, numbered 0.0.x: every command in the tutorial works; what is designed but not built yet is in [PREVIEW.md](PREVIEW.md). It may still change in ways that break earlier teams and runs until 0.1.0 is announced. What changed in each version is in the [changelog](https://github.com/teamrunner/teamrunner/blob/main/CHANGELOG.md).
669
565
 
670
566
  | Version | |
671
567
  |---|---|
@@ -0,0 +1,128 @@
1
+ # What's coming
2
+
3
+ Designed, not built yet. **Nothing on this page runs in the current preview, and none of it is in the tutorial.** The shapes below are agreed, but details may change before they ship. What works today is in the [README](README.md); the order things arrive in is under [Status and roadmap](README.md#status-and-roadmap).
4
+
5
+ - [Who sees what: the whiteboard](#who-sees-what-the-whiteboard-v03) **[v0.3]**
6
+ - [Experiments](#experiments-v02) **[v0.2]**
7
+
8
+ ---
9
+
10
+ ## Who sees what: the whiteboard **[v0.3]**
11
+
12
+ Everything an agent knows arrives through one of:
13
+
14
+ | Layer | Org analogue | Who reads | Who writes |
15
+ |---|---|---|---|
16
+ | **The whiteboard** (`whiteboard.yaml`) | the meeting, and the official record | everyone, unless a `read_by` line says otherwise | per item `written_by`; whatever anyone says is posted automatically; always logged |
17
+ | **Memory** | what you were in the room for | you | nobody — history is append-only |
18
+
19
+ Everything said is posted on the whiteboard, open to all by default, so a team with no whiteboard file is a meeting room — and that is all of v0.1. **[v0.3]** `whiteboard.yaml` adds the rest: seal the reviewers from each other, or make the team a relay where each person hears only who spoke before them, with a `read_by` line for what was said — one line each.
20
+
21
+ Information asymmetry is not a feature we had to invent; it is a narrow `read_by`. Incentives are numeric items on the whiteboard. Private briefs are items only one party can read.
22
+
23
+ ### An example
24
+
25
+ The debate doesn't need this, but the moment you study organizations you will. `whiteboard.yaml` is the team's whiteboard: declared, typed items, each with who may read and write it. Everything anyone says is posted there too, open to all unless a `read_by` line says otherwise.
26
+
27
+ ```yaml
28
+ # whiteboard.yaml
29
+
30
+ # Bare list = a choice, starting at the first option.
31
+ # Read by everyone, written by the chair — those are the defaults.
32
+ stage: [opening, rebuttal, counter, closing, done]
33
+
34
+ # A bare value = a starting value, read and written by the defaults.
35
+ rounds_left: 3
36
+
37
+ # Spell it out only when you change who reads or writes it.
38
+ score:
39
+ starts_as: 0
40
+ written_by: [judge]
41
+
42
+ # Information asymmetry IS a read permission.
43
+ buyer_ceiling:
44
+ starts_as: 850
45
+ read_by: [buyer]
46
+ ```
47
+
48
+ Every field is declared up front — no ad-hoc keys appearing mid-run. Every write is harness-mediated and logged as a `state_write` event: an agent listed in `written_by` returns the new value as a declared output field; nothing pokes state directly. Agents reference readable fields in their prompts as `{{whiteboard.buyer_ceiling}}`, and the validator checks that permission before you spend a cent.
49
+
50
+ This one file is doing a surprising amount of work. Private valuations, confidential negotiating briefs, payoffs and incentives, task inputs, an artifact handed from one department to the next — all of them are just whiteboard items with the right permissions.
51
+
52
+ ---
53
+
54
+ ## Experiments **[v0.2]**
55
+
56
+ Your package stays clean and runnable. An `experiment.yaml` lives *outside* it, names it with `base:`, and declares factors as *patches* against it, plus replications and a seed. Single runs and 200-run factorials go through the same engine. When a variant wins, `teamrunner promote` copies the exact version that ran back into the package — same agent, new version — and `export` hands you a standalone copy for production.
57
+
58
+ ### Your first experiment
59
+
60
+ Here's the question that motivated the whole project: **does it matter which model plays the advocate?**
61
+
62
+ Don't touch the debate. Make a folder next to it with one file:
63
+
64
+ ```yaml
65
+ # experiments/advocate_model/experiment.yaml
66
+ name: advocate-model-ab
67
+ base: ../../debate
68
+ task: tasks/remote_work
69
+
70
+ factors:
71
+ advocate_model:
72
+ - label: gpt
73
+ patch: {advocate.model: "openai/gpt-6-luna"}
74
+ - label: claude
75
+ patch: {advocate.model: "anthropic/claude-sonnet-4-5"}
76
+
77
+ replications: 20 # 2 conditions × 20 = 40 runs
78
+ seed: 42
79
+
80
+ limits:
81
+ max_concurrent: 4
82
+ max_cost_usd: 40.00
83
+ ```
84
+
85
+ ```bash
86
+ teamrunner experiment .
87
+ ```
88
+
89
+ What just became possible:
90
+
91
+ **Factors are patches, so the base spec stays clean.** `advocate.model` means "the `model` field of the component `advocate`". Patch paths address any field of any component, including the prompt body (the field is called `prompt`), and any value can be `file:<path>` to swap in a whole file. `patch: {}` is the *unmodified base spec* — the control condition, written honestly as a level rather than smuggled in as "the normal run."
92
+
93
+ **Anything in a file is a factor.** That's not a slogan, it's the mechanism. A few real examples from our worked recasts:
94
+
95
+ ```yaml
96
+ # A prompt/strategy A/B — same agent, two personalities
97
+ - {label: tough, patch: {buyer.prompt: file:variants/buyer_tough.md}}
98
+
99
+ # A treatment: one member is secretly self-interested
100
+ - {label: saboteur, patch: {council.prompt: file:variants/saboteur.md}}
101
+
102
+ # The communication structure ITSELF as the factor:
103
+ # same reviewers, same paper — only who can read whose review changes
104
+ - label: public
105
+ patch:
106
+ whiteboard.said.reviewer_1.read_by: everyone
107
+ whiteboard.said.reviewer_2.read_by: everyone
108
+ whiteboard.said.reviewer_3.read_by: everyone
109
+ ```
110
+
111
+ That last one is the founding claim of the product. Studying whether peer reviewers herd when they can see each other's reviews is, in TeamRunner, a two-level factor on who may read what each reviewer says.
112
+
113
+ **`replications` has a minimum of 2.** Writing `replications: 1` requires an explicit `allow_single_run: true`. LLMs are stochastic; single runs are anecdotes; we made the anecdote the awkward thing to write.
114
+
115
+ **One master `seed` fixes everything the harness controls** — condition assignment, ordering, per-run seed derivation. It does not, and cannot, make the model deterministic. See [Why we built it](README.md#3-almost-nothing-published-about-multi-agent-systems-is-reproducible).
116
+
117
+ **`limits` is a real budget ceiling.** `max_cost_usd` stops the experiment; `max_concurrent` keeps you inside your rate limits.
118
+
119
+ Then get the data out:
120
+
121
+ ```bash
122
+ teamrunner runs # browse what you've run
123
+ teamrunner export --experiment advocate-model-ab --format csv
124
+ ```
125
+
126
+ Long format, one row per observation, straight into R, Stata, or pandas. Your analysis never lives inside our dashboard — that's a promise, not an oversight.
127
+
128
+ The declarative metrics layer (ground truth, LLM-as-judge with a separately pinned judge model, cost and time) is specified but its file format is still open. Today's `metrics:` references in the examples are placeholders.