opencode-harness-kit 0.2.1 → 0.3.4

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 (51) hide show
  1. package/CHANGELOG.md +303 -0
  2. package/GLOBAL.md +50 -13
  3. package/MIGRATION.md +140 -0
  4. package/README.md +233 -116
  5. package/bin/oc-harness.mjs +474 -96
  6. package/dist/index.js +378 -155
  7. package/docs/KNOWN-LIMITS.md +189 -4
  8. package/docs/RESEARCH.md +80 -5
  9. package/docs/ROUTING.md +254 -0
  10. package/lanes.json +136 -0
  11. package/lanes.schema.json +55 -0
  12. package/package.json +12 -8
  13. package/profiles/beam-free.json +51 -0
  14. package/profiles/big-pickle.json +45 -0
  15. package/profiles/deepseek-v4.1-flash-free.json +49 -0
  16. package/profiles/fleet-all.json +139 -0
  17. package/profiles/fleet-safe.json +139 -0
  18. package/profiles/glm-5.3-flash-free.json +49 -0
  19. package/profiles/index.json +120 -37
  20. package/profiles/longcat-2.5-preview-free.json +14 -10
  21. package/profiles/mimo-v2.6-flash-free.json +44 -0
  22. package/profiles/minimax-m3.1-flash-free.json +51 -0
  23. package/profiles/nemotron-3-ultra-free.json +44 -0
  24. package/profiles/nemotron-3.5-lightning-free.json +44 -0
  25. package/profiles/nemotron-3.5-super-vl-free.json +52 -0
  26. package/profiles/schema.json +30 -11
  27. package/{agents → prompts}/build.md +2 -35
  28. package/prompts/doc.md +42 -0
  29. package/{agents → prompts}/locate.md +1 -16
  30. package/prompts/longform.md +34 -0
  31. package/{agents → prompts}/plan.md +2 -25
  32. package/{agents → prompts}/reviewer.md +2 -22
  33. package/prompts/schema.md +30 -0
  34. package/prompts/sweep.md +29 -0
  35. package/{agents → prompts}/verify.md +6 -29
  36. package/prompts/vision.md +33 -0
  37. package/retention.yml +75 -57
  38. package/skills/space-build/SKILL.md +16 -0
  39. package/skills/space-review/SKILL.md +8 -0
  40. package/src/config.js +94 -69
  41. package/src/effort.js +59 -22
  42. package/src/index.ts +15 -2
  43. package/src/lane-usage.js +142 -0
  44. package/src/profile.js +502 -212
  45. package/src/router.ts +148 -39
  46. package/src/telemetry.ts +98 -0
  47. package/examples/muse-spark-1.3-contributor-free.json +0 -41
  48. package/profiles/deepseek-v4-flash-free.json +0 -41
  49. package/profiles/muse-split-free.json +0 -64
  50. package/profiles/space-bunny-free.json +0 -42
  51. package/profiles/x-preview-f-free.json +0 -41
package/CHANGELOG.md ADDED
@@ -0,0 +1,303 @@
1
+ # Changelog
2
+
3
+ All notable changes to `opencode-harness-kit` are recorded here. The format
4
+ follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
5
+ project uses [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ This kit sits between a provider catalog and a config file: capability and
8
+ privacy metadata in, a working harness out, with a command to re-check that
9
+ the metadata has not rotted. Most of the entries below are therefore about
10
+ **silent failures** — a config that looks correct while the thing it promises
11
+ is not happening.
12
+
13
+ ---
14
+
15
+ ## [Unreleased]
16
+
17
+ ## [0.3.4] — 2026-10-20
18
+
19
+ > 0.3.2 and 0.3.3 were never published. 0.3.2 carried the fix below but the
20
+ > publish path it needed was not set up yet; 0.3.3 was **staged** by a
21
+ > diagnostic that turned out to have side effects, and a staged version cannot
22
+ > be published over directly. Both are folded into this release.
23
+
24
+ ### Added
25
+
26
+ - **The publish job can use npm trusted publishing (OIDC), so the repository
27
+ needs no npm secret at all.** npm exchanges the runner's OIDC identity for a
28
+ short-lived publish token, scoped to the workflow that asked for it. There
29
+ is no long-lived token in the repository to leak, rotate or expire - and npm
30
+ is revoking the legacy automation tokens anyway.
31
+ - The job installs npm 11.5.1 or later, which is what OIDC needs and what
32
+ node 22 does not ship.
33
+ - The registry is configured by hand instead of through `setup-node`'s
34
+ `registry-url`, because that helper always writes an auth line, and an
35
+ empty auth line makes npm believe it is authenticated and skip the OIDC
36
+ exchange entirely.
37
+ - The credential check is conditional: `npm whoami` is the right check for
38
+ a token, and **the wrong** one for OIDC, because an OIDC identity is not
39
+ reflected by `npm whoami` at all. Asking it unconditionally failed a
40
+ publish that would have worked.
41
+ - A token still works: if `NPM_TOKEN` is set, the job uses it.
42
+ - **`npm run release:trust` configures the trusted publisher from the command
43
+ line.** npm's web form has four fields and npm does not validate what you
44
+ type into it, so a typo is discovered at publish time and reported as
45
+ "OIDC token exchange error - package not found" - which reads like a missing
46
+ package. npm ships `npm trust` for exactly this, and it can read what is
47
+ stored, so the configuration is now compared against the identity the
48
+ workflow will actually present and replaced when it differs.
49
+ - The workflow is named after its file, so the value that has to be typed on
50
+ npmjs.com is the value the Actions tab shows.
51
+
52
+ ### Fixed
53
+
54
+ - **The publish preflight rejected the very token it wanted.** It called `npm
55
+ access list packages` and required the package to appear in the list. That
56
+ command lists the packages a *user* has access to, which needs
57
+ account-level read permission - and a granular token scoped to one package
58
+ is answered **403** for it. A correctly scoped publish token would have
59
+ been refused, and the user sent looking for a problem that was in the
60
+ check. Only `npm whoami` is checked now, which any valid token passes; the
61
+ write permission is proven by the publish itself, and its failure message
62
+ says what was missing.
63
+
64
+ ## [0.3.1] — 2026-10-20
65
+
66
+ ### Fixed
67
+
68
+ - **`oc-harness verify` failed a good install over somebody else's skill
69
+ naming.** It checked every skill directory in the config and required the
70
+ `name:` in the frontmatter to match the directory name. OpenCode identifies
71
+ a skill by its **directory**, not by the frontmatter, so a skill whose two
72
+ differ loads perfectly well. An install carrying 220-odd community skills
73
+ reported five failures that were not failures at all, and the command is
74
+ the one people put in a commit hook.
75
+ - The check now covers the kit's **own** skills, which do have to agree with
76
+ their directory — the loop refers to them by name, and one whose frontmatter
77
+ drifted is a step of the loop that will not load. Everyone else's skills are
78
+ counted and left alone.
79
+
80
+ ## [0.3.0] — 2026-10-20
81
+
82
+ ### The fleet
83
+
84
+ The default profile is no longer a model. It is **ten jobs, ten free models,
85
+ one session**.
86
+
87
+ - `lanes.json` declares the jobs and says nothing about models. A profile
88
+ declares which lanes it enables and which model serves each one.
89
+ - Each lane is an OpenCode agent with its own model, its own context window
90
+ and its own effort level. Delegating a job is how you use a second model;
91
+ switching models by hand never happens.
92
+ - **A lane's model is a chain, not a pin.** Free routes get withdrawn in
93
+ days — four of this kit's five original profiles were dead within a month —
94
+ so every lane lists the models that could serve it, best first, and the
95
+ router walks the same chain when a route is spent.
96
+ - **The chain is ordered inside the profile's own ceiling.** A profile
97
+ declares `maxRetention`; a lane takes the first rung at or below it. One
98
+ fleet file therefore covers ten jobs at two privacy levels: `fleet-safe`
99
+ (zero-retention only, the default) and `fleet-all` (every lane on the model
100
+ built for it).
101
+ - `retention.yml` was rewritten against the OpenCode privacy page: seven new
102
+ entries, four dead models dropped, and a third value — `retained` — for
103
+ Beam, which does not train but keeps prompts for 30 days and is therefore
104
+ not zero-retention.
105
+ - `oc-harness lanes` reports which lanes were actually used, so the fleet can
106
+ be cut to what earns its place rather than defended on principle.
107
+
108
+ ### Fixed — the measurement that decided the fleet was counting the wrong thing
109
+
110
+ - **The lane counter counted events, not messages.** `opencode` emits
111
+ `message.updated` several times for a single assistant message — created,
112
+ streamed, completed — so one request was recorded three times, and
113
+ "build 7 calls" was really two messages. A lane record now carries the
114
+ message id and the reader merges on it.
115
+ - **`chat.message` could not attribute a request to a lane.** On OpenCode
116
+ 1.19 its `agent` field is `undefined`. It was the natural place to count a
117
+ call before the model answered, so that a call that never came back still
118
+ counted; it cannot, so it is no longer used for this.
119
+ - **`oc-harness lanes` read the wrong file.** The plugin writes the telemetry
120
+ to the project root; the command read `process.cwd()`. Run from a
121
+ subdirectory, it reported "never called" for every lane — a fleet that
122
+ looked entirely unused.
123
+ - **The unit is now named what it is.** A request is one assistant message,
124
+ which is one round trip to the provider, and a lane that makes three tool
125
+ calls makes three of them. The column says `reqs`.
126
+
127
+ ### Fixed — the fleet did not survive a spent route
128
+
129
+ - **A spent session model collapsed the whole fleet onto one model.** The
130
+ router replaced the entire profile, which set every lane of the fallback
131
+ profile onto one model and left the other five lanes pointing at routes
132
+ that were also spent — the opposite of what the function's own docstring
133
+ promised. A fleet now keeps its lanes: the session model moves, each lane
134
+ moves down its own chain, and the router publishes the model it chose so
135
+ the effort hook pins the session to that one rather than back to the base
136
+ model that is spent.
137
+ - **`effectiveRetention().worst` had two meanings.** It was a retention class
138
+ on one path and a model id on the other, and the header prints it as a
139
+ model name. A fleet that resolved to a `retained` lane said "but retained
140
+ makes the whole file retained". It is always a model id now.
141
+
142
+ ### Fixed — the numbers on screen did not mean what the label said
143
+
144
+ - **The "input cap" was the base model's, labelled as the narrowest lane's.**
145
+ The config header, `oc-harness show` and `oc-harness list` each printed a
146
+ different number next to a prune threshold derived from another one, and
147
+ the README quoted a fourth. There is now one number: the input cap the
148
+ config can actually use.
149
+ - **`profiles/index.json` carried a context of 200,000 for `fleet-all` and
150
+ `big-pickle`.** The cap that governs is 160,000, and the list recomputes it
151
+ from the profile rather than trusting the copy.
152
+ - **The safe fleet inherited the unrestricted fleet's chain order**, so
153
+ bumping into the zero-retention ceiling dropped every lane onto whatever
154
+ rung happened to be third. Two lanes were genuinely mis-served: `locate`
155
+ asked for `low` effort and landed on a model that publishes no effort
156
+ levels at all, so the request was dropped on the floor; and `vision` landed
157
+ on the same model at 9–13 s when MiniMax reads the same image in 7.7 s.
158
+ The safe fleet now orders its own chains.
159
+ - **The docs said four lanes lost their model under the ceiling. Six do.**
160
+ The number is now computed and asserted by a test, and the two fleets'
161
+ reserves (24,000 and 19,200) are both stated.
162
+ - **Three measured latencies were wrong.** GLM 5.3 is 4.4–5.8 s on the work
163
+ a lane does, not 16–27 s; MiMo reads an image in 19.2 s, not 8 s; and
164
+ Nemotron Super VL does *not* hang on a tool read, only on a file attached
165
+ to the prompt — it answered a `grep` sweep in 4.2 s. The rule is "never
166
+ hand it a file as an attachment", not "it is broken".
167
+
168
+ ### Fixed — a shadowing agent file could hide in four places
169
+
170
+ - **An agent markdown file replaces the config entry wholesale**, and a file
171
+ with no `model:` line makes the agent inherit the caller's model. Both were
172
+ measured on OpenCode 1.19 with the plugin out of the way (`--pure`): the
173
+ file's model wins. The model therefore lives in `agent.<lane>` in the
174
+ generated config, and the prompt bodies live in `prompts/`.
175
+ - **`verify` checked one directory.** OpenCode reads `agent/` and `agents/`
176
+ alike, in the config dir and in `.opencode/`, and the plural one wins on a
177
+ name clash — so a shadow in the singular directory was invisible. All four
178
+ are scanned now.
179
+ - `oc-harness install` removes the agent files an earlier version of this kit
180
+ left behind, from either directory.
181
+
182
+ ### Fixed — three lists of lane names, drifting apart
183
+
184
+ - **`profiles/schema.json` named an `audit` lane that no longer existed and
185
+ was missing `longform`.** With `additionalProperties: false` the shipped
186
+ fleet profiles violated the shipped schema, and nothing said so.
187
+ - **`lanes.json` pointed at `./lanes.schema.json`, which was not in the
188
+ repository.** It is now, and a test pins the lane table, the profile schema
189
+ and `LANE_NAMES` to each other.
190
+ - `MANAGED_ROLES` was a hand-written list of nine lanes that had drifted, and
191
+ was no longer read by anything. It is derived from the lane table now.
192
+
193
+ ### Removed
194
+
195
+ - `agents/` — the prompt bodies moved to `prompts/`, where a file cannot
196
+ shadow a config entry.
197
+ - The `space-bunny-free`, `x-preview-f-free`, `deepseek-v4-flash-free`,
198
+ `muse-spark-1.3-contributor-free` and `muse-split-free` profiles. Their
199
+ models were withdrawn from the provider. `oc-harness list` now names them
200
+ under **no longer on the tier**, with what replaced them.
201
+
202
+ ### Migration
203
+
204
+ 0.2.x → 0.3.0 changes the profile ids, the config layout and the meaning of
205
+ the telemetry. See [MIGRATION.md](MIGRATION.md).
206
+
207
+ ---
208
+
209
+ ## [0.2.1] — 2026-10-19
210
+
211
+ Shipped: the `space-finish` skill's duplicated steps removed, one demo video
212
+ instead of three, and the package metadata pointed at the right repository.
213
+
214
+ ### Fixed
215
+
216
+ - **`oc-harness use` deleted hand-tuned config.** The JSONC parser returned
217
+ `{}` for a config it could not read, so the merge was onto an empty base
218
+ and the user's own settings were written back out deleted, with no message.
219
+ The parser walks the text once, tracking string state, so comments and
220
+ trailing commas are both handled correctly.
221
+ - **Five further defects that lose data, break CI, or send users nowhere** —
222
+ including `--dryrun` being accepted and then ignored, so a dry run silently
223
+ overwrote the real config.
224
+ - **`oc-harness agents` reported "no project agents found" forever** while
225
+ none of them were installed at all, because it scanned only the project and
226
+ the kit's own agents live in the global config directory.
227
+ - The demo video was replaced with a real recorded take: the slide renderer
228
+ was describing a test count it was not measuring.
229
+
230
+ ---
231
+
232
+ ## [0.2.0] — 2026-10-15
233
+
234
+ Shipped: the evaluation harness, integration tests that load the built plugin
235
+ the way OpenCode does, npm packaging, `oc-harness link`, `oc-harness agents`,
236
+ the tool-output caps, and the `scout` → `locate` agent rename.
237
+
238
+ ### Fixed
239
+
240
+ - **The guard matched text a shell would not run.** `cd repo && rm -rf /`,
241
+ `sudo rm -rf /` and `bash -c "rm -rf /"` are now caught, while
242
+ `git log --grep push` and `echo push` are not. A heredoc body is data, not
243
+ commands.
244
+ - **`tool_output` was pinned at 200 lines / 8 KB**, about 6× tighter than
245
+ OpenCode's default. It truncated 415 of 2,966 `read` calls and 418 calls
246
+ were issued with an `offset` to fetch the rest: the truncation *causes* the
247
+ re-read, one for one, at a measured 17.3 s median per request. The cap is
248
+ now the default, and pruning rather than truncation bounds the context.
249
+ - **`tail_turns: 15` was inert config.** Compaction is keyed off the full
250
+ context ceiling that a normal session never reaches; it had fired zero times
251
+ in 153 measured sessions. The key was dropped rather than tuned.
252
+ - **The permission-pressure figure was a transcription of the allowlist**, so
253
+ the measured number was whatever the copy looked like when it was last
254
+ edited. `baseline` imports the real list now.
255
+ - **The `46.6%` that effort tiering saves was a projection, not a
256
+ measurement** — cross-model medians, because the tiering had never run on
257
+ the zero-retention model. `eval` now prints it as `PROJECTION` with
258
+ `measured: no`.
259
+
260
+ ### Known at the time
261
+
262
+ - `use --refresh` and the eval harness could not reach a user's real
263
+ install from CI, so the live half of the gate ran only locally.
264
+ - The `scout` agent shadowed OpenCode's own built-in `scout` subagent, which
265
+ exists to read a dependency's source.
266
+
267
+ ---
268
+
269
+ ## [0.1.0] — 2026-10-12
270
+
271
+ First release: profiles, the effort resolver, the config compiler, the
272
+ `oc-harness` CLI, the four plugins (guard, prune, router, telemetry),
273
+ `GLOBAL.md`, the agent files, the skills, the test suite and
274
+ `baseline.mjs`.
275
+
276
+ ### What it promised
277
+
278
+ A model-agnostic harness kit. Everything model-specific lives in
279
+ `profiles/<id>.json`; no plugin in `src/` names a model. Switching models is
280
+ a data edit, not a code change.
281
+
282
+ ### Fixed in later releases
283
+
284
+ The defects in 0.1.0 and 0.2.0 are listed under those releases, because the
285
+ fix and the version that carried it are the same fact. Two of them were
286
+ silent in the worst way:
287
+
288
+ - **`oc-harness use` could write back a config it had failed to read**, with
289
+ the user's own settings deleted and no message. Fixed in 0.2.1.
290
+ - **The default profile pointed at a model the provider had withdrawn.** The
291
+ config kept validating and kept claiming zero-retention. The tier turned
292
+ over again within a month, which is why 0.3.0 gives every lane a chain
293
+ instead of a pin.
294
+
295
+ ---
296
+
297
+ [Unreleased]: https://github.com/pham-canh/space-harness/compare/v0.3.4...HEAD
298
+ [0.3.4]: https://github.com/pham-canh/space-harness/compare/v0.3.1...v0.3.4
299
+ [0.3.1]: https://github.com/pham-canh/space-harness/compare/v0.3.0...v0.3.1
300
+ [0.3.0]: https://github.com/pham-canh/space-harness/compare/v0.2.1...v0.3.0
301
+ [0.2.1]: https://github.com/pham-canh/space-harness/compare/v0.2.0...v0.2.1
302
+ [0.2.0]: https://github.com/pham-canh/space-harness/compare/v0.1.0...v0.2.0
303
+ [0.1.0]: https://github.com/pham-canh/space-harness/releases/tag/v0.1.0
package/GLOBAL.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  You are working inside a harness that has already been tuned for the model in
4
4
  use. Everything the config and the hooks can enforce is enforced already: the
5
- tools, the permissions, the command allowlist, the guard, the per-role effort
5
+ tools, the permissions, the command allowlist, the guard, the per-lane effort
6
6
  and step caps. This file is only what is left over once those are subtracted --
7
7
  the judgement calls no plugin can make for you.
8
8
 
@@ -35,12 +35,47 @@ Everything else is reversible and yours. Guessing at an import path, a
35
35
  function name, a file location, a flag, or a test command: look it up and move
36
36
  on. If you looked and could not find it, say that.
37
37
 
38
+ A `git push` is not one of those decisions. Committing the branch you were
39
+ asked to finish and pushing it is the last step of the job, not something to
40
+ escalate. Push, then say what you pushed.
41
+
38
42
  When you are genuinely blocked, ask **one** question, not five, and make it
39
43
  answerable with a choice. "Should I use `X` or `Y`" is a question. "What are
40
44
  your thoughts on the architecture" is a delay.
41
45
 
42
46
  Never ask what a `grep` would answer.
43
47
 
48
+ ## Send the work to the lane that has it
49
+
50
+ Each lane is a separate agent on its own model, with its own context window
51
+ and its own effort level. You do not switch models: you hand the job over.
52
+
53
+ | The job | The lane | What it owns |
54
+ |---|---|---|
55
+ | Turn a vague request into a plan | `plan` | the requirement, the tasks, the verify commands |
56
+ | Write and verify the change | `build` | the edit, the test run, the report |
57
+ | Read a change in two passes | `reviewer` | blockers first, then quality |
58
+ | Find where something lives | `locate` | `file:line` evidence, and nothing else |
59
+ | Run the project's checks | `verify` | the real output of a real command |
60
+ | Read an image, a screenshot, a diagram | `vision` | what is in the picture |
61
+ | Read a long document or a spec | `doc` | what it says, and where the code disagrees |
62
+ | Produce JSON or a JSON schema | `schema` | output a program can parse |
63
+ | Sweep a lot of code mechanically | `sweep` | matches, with no judgement |
64
+ | Read a megabyte, or write alternatives | `longform` | very long context, or several answers |
65
+
66
+ Rules for the hand-off:
67
+
68
+ - **Delegate the job, not the whole session.** A lane returns an answer; you
69
+ keep the thread. Do not hand over the conversation.
70
+ - **One lane per job.** `sweep` finds the matches, `reviewer` judges them.
71
+ Asking one lane to do both gets you the weaker answer.
72
+ - **Do the lane's job yourself when it is small.** Handing a one-line lookup to
73
+ a separate model costs a round trip and buys nothing. The lanes exist for
74
+ the jobs that need their own context window, their own model, or their own
75
+ effort level.
76
+ - **A lane that reports a blocker is the loop working.** Read the file:line it
77
+ gives you and fix that.
78
+
44
79
  ## Locate before you read
45
80
 
46
81
  On the measured install, `read` ran 2,966 times against 135 `grep` and 114
@@ -54,7 +89,9 @@ to avoid this.
54
89
 
55
90
  ## Never
56
91
 
57
- - Ask before `git push`, publish, or opening a pull request. Publishing is a human decision.
92
+ - Force-push, rewrite published history, delete a branch or tag, or publish to
93
+ a registry (`npm publish`, `gh release`). Push without being asked; ask before
94
+ any of these, because they are the ones with no undo.
58
95
  - Commit a secret. If you find one, report it; do not move it.
59
96
  - Read or write `.env`, `.npmrc`, `.netrc`, `.git-credentials`. The guard
60
97
  blocks these and will keep blocking them.
@@ -77,12 +114,12 @@ next session more than a small code change.
77
114
 
78
115
  ## Reasoning budget
79
116
 
80
- Per-role effort and step caps are set in `opencode.json` and enforced by the
81
- harness, not by anything written here. `plan` and `reviewer` run at the highest
82
- available effort, `build` in the middle, `locate` and `verify` at the lowest,
83
- because finding a thing and checking a thing are not design work. If you are
84
- playing one of the low-effort roles and feel yourself designing an
85
- architecture, stop and report the evidence instead.
117
+ Per-lane effort and step caps are set in the generated config and enforced by
118
+ the harness, not by anything written here. `plan` and `reviewer` run at the
119
+ highest available effort, `build` runs at the top of the range too, and
120
+ `locate` and `verify` at the lowest, because finding a thing and checking a
121
+ thing are not design work. If you are playing one of the low-effort lanes and
122
+ feel yourself designing an architecture, stop and report the evidence instead.
86
123
 
87
124
  ## When context grows
88
125
 
@@ -96,10 +133,10 @@ you need from it and get only that.
96
133
 
97
134
  ## Model etiquette
98
135
 
99
- If a request fails with a rate limit or a usage cap, **stop**. Do not retry the
100
- same model; free routes often stay blocked for days, and the daily window only
101
- resets at 00:00 UTC. Report it and ask for a new session. No plugin can swap
102
- the model of a live session, so a new session is the actual fix.
136
+ If a request fails with a rate limit or a usage cap, **stop**. Do not retry
137
+ the same model; free routes often stay blocked for days, and the daily window
138
+ only resets at 00:00 UTC. Report it and ask for a new session. No plugin can
139
+ swap the model of a live session, so a new session is the actual fix.
103
140
 
104
141
  If the model you are on is not the one described in the config, say so before
105
142
  starting anything expensive.
@@ -107,4 +144,4 @@ starting anything expensive.
107
144
  ## When you are stuck
108
145
 
109
146
  Say what you tried, what you expected, and what happened instead. A precise
110
- report is more useful than a third attempt at the same approach.
147
+ report is more useful than a third attempt at the same approach.
package/MIGRATION.md ADDED
@@ -0,0 +1,140 @@
1
+ # Migrating from 0.2.x to 0.3.0
2
+
3
+ 0.3.0 replaces the single-model profile with a **fleet**: ten jobs, ten free
4
+ models, one session. Three things change for a user — the profile ids, the
5
+ config layout, and what the telemetry file means.
6
+
7
+ Nothing in 0.2.x is deleted by the upgrade in a way that loses data. The
8
+ profiles whose models were withdrawn from the provider are gone because the
9
+ models are gone; `oc-harness list` names them under **no longer on the tier**
10
+ and says what replaced them.
11
+
12
+ ---
13
+
14
+ ## 1. The profile ids changed
15
+
16
+ The old ids named a model. The new ones name a fleet, because that is what
17
+ they are.
18
+
19
+ | 0.2.x | 0.3.0 | note |
20
+ |---|---|---|
21
+ | `space-bunny-free` (default) | `fleet-safe` (default) | the model was withdrawn; this is the safe fleet |
22
+ | `muse-split-free` | `fleet-all` | the unrestricted fleet: every lane on the model built for it |
23
+ | `deepseek-v4-flash-free` | `deepseek-v4.1-flash-free` | the old one was withdrawn |
24
+ | `x-preview-f-free` | — | withdrawn |
25
+ | `muse-spark-1.3-contributor-free` | — | withdrawn |
26
+ | `longcat-2.5-preview-free` | `longcat-2.5-preview-free` | unchanged |
27
+ | — | `beam-free`, `big-pickle`, `glm-5.3-flash-free`, `mimo-v2.6-flash-free`, `minimax-m3.1-flash-free`, `nemotron-3-ultra-free`, `nemotron-3.5-lightning-free`, `nemotron-3.5-super-vl-free` | new single-model profiles for the rest of the tier |
28
+
29
+ ```bash
30
+ oc-harness use fleet-safe
31
+ oc-harness install
32
+ oc-harness verify
33
+ ```
34
+
35
+ If you had `OPENCODE_HARNESS_PROFILE` set, it names the old id and will not
36
+ resolve:
37
+
38
+ ```bash
39
+ export OPENCODE_HARNESS_PROFILE=fleet-safe
40
+ ```
41
+
42
+ A single-model profile still exists for every model, so a user who wants one
43
+ route and wants to know exactly which one still has it:
44
+
45
+ ```bash
46
+ oc-harness use glm-5.3-flash-free
47
+ ```
48
+
49
+ ## 2. The config layout changed
50
+
51
+ The prompt bodies moved out of the agent files and into `prompts/`, and the
52
+ model moved into the config.
53
+
54
+ | | 0.2.x | 0.3.0 |
55
+ |---|---|---|
56
+ | where the model lives | `agents/<lane>.md` frontmatter | `agent.<lane>.model` in the generated config |
57
+ | where the prompt lives | the same file | `prompts/<lane>.md`, referenced with `{file:...}` |
58
+ | lanes | five: plan, build, reviewer, locate, verify | ten: the five above plus vision, doc, schema, sweep, longform |
59
+ | `compaction.reserved` | sized by the base model | sized by the **narrowest** lane model |
60
+
61
+ **The move was not cosmetic.** An agent markdown file of the same name
62
+ replaces the config entry wholesale, and a file with no `model:` line makes
63
+ the agent inherit the caller's model. Both were measured on OpenCode 1.19.
64
+ With the model in the config, a lane cannot be quietly re-pointed at a
65
+ different provider by a file that happens to share its name.
66
+
67
+ `oc-harness install` removes the old agent files for you, and `oc-harness
68
+ verify` fails if one of them is still shadowing a lane.
69
+
70
+ The lanes are OpenCode agents, so a delegation reaches a different model
71
+ inside one session. That was measured, not assumed:
72
+
73
+ ```
74
+ agent=build modelID=minimax-m3.1-flash-free
75
+ agent=reviewer modelID=nemotron-3-ultra-free
76
+ agent=build modelID=minimax-m3.1-flash-free
77
+ ```
78
+
79
+ **Your own agent files are not touched** unless they are named after a lane
80
+ this kit manages. A file called `scout.md` or `deploy.md` is left alone.
81
+
82
+ ## 3. The telemetry file means something different
83
+
84
+ `.space/telemetry.jsonl` is gitignored and local. In 0.2.x the kit wrote
85
+ `session`, `guard` and `provider-event` records to it; in 0.3.0 it also
86
+ writes one record per assistant message, carrying the lane, the model and the
87
+ duration.
88
+
89
+ If you were reading that file, the units changed:
90
+
91
+ - **0.2.x and earlier: one record per event.** `opencode` emits
92
+ `message.updated` several times per message, so a request was recorded
93
+ three times.
94
+ - **0.3.0: one request = one assistant message**, deduplicated by message
95
+ id. A lane that makes three tool calls makes three requests.
96
+
97
+ Records written by an older version have no message id and are **not
98
+ counted** — the number they produce is inflated threefold. Delete the file
99
+ to start clean:
100
+
101
+ ```bash
102
+ rm -f .space/telemetry.jsonl
103
+ ```
104
+
105
+ `oc-harness lanes` says which file it read, and how many records it could not
106
+ count.
107
+
108
+ ## 4. The privacy gate now reads the whole fleet
109
+
110
+ `oc-harness use` refuses a profile whose models have no stated retention
111
+ terms, and demands `--yes` when any model in the fleet may train. In 0.2.x it
112
+ read the **base** model's terms only, so a profile whose base was
113
+ zero-retention while its roles were may-train installed with no warning at
114
+ all.
115
+
116
+ For `fleet-safe` nothing changes: every model is zero-retention, so there is
117
+ nothing to consent to. For `fleet-all` the consent is now asked for, and the
118
+ warning names the model responsible.
119
+
120
+ ## 5. What did not change
121
+
122
+ - The plugins keep their hook names and their behaviour: `guard`,
123
+ `prune`, `router`, `telemetry`.
124
+ - `retention.yml` keeps its shape, with one new value: `retained`, for a
125
+ provider that does not train but keeps the data for a stated window.
126
+ - `GLOBAL.md` and the skills are installed the same way.
127
+ - `oc-harness baseline`, `oc-harness doctor`, `oc-harness agents`,
128
+ `oc-harness show` and `oc-harness refresh` keep their arguments.
129
+
130
+ ## 6. If you want the old behaviour
131
+
132
+ Install a single-model profile instead of a fleet. One route, one model, and
133
+ you know exactly which one:
134
+
135
+ ```bash
136
+ oc-harness use glm-5.3-flash-free
137
+ ```
138
+
139
+ The fleet is a default, not a requirement. `docs/ROUTING.md` explains which
140
+ lane each model owns and what was measured.