opencode-harness-kit 0.2.1 → 0.3.3

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 +301 -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,301 @@
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.3] — 2026-10-20
18
+
19
+ > 0.3.2 was never published: it carried the fix below, but the publish it
20
+ > needed had not been set up yet. It is folded into this release.
21
+
22
+ ### Added
23
+
24
+ - **The publish job can use npm trusted publishing (OIDC), so the repository
25
+ needs no npm secret at all.** npm exchanges the runner's OIDC identity for a
26
+ short-lived publish token, scoped to the workflow that asked for it. There
27
+ is no long-lived token in the repository to leak, rotate or expire - and npm
28
+ is revoking the legacy automation tokens anyway.
29
+ - The job installs npm 11.5.1 or later, which is what OIDC needs and what
30
+ node 22 does not ship.
31
+ - The registry is configured by hand instead of through `setup-node`'s
32
+ `registry-url`, because that helper always writes an auth line, and an
33
+ empty auth line makes npm believe it is authenticated and skip the OIDC
34
+ exchange entirely.
35
+ - The credential check is conditional: `npm whoami` is the right check for
36
+ a token, and **the wrong** one for OIDC, because an OIDC identity is not
37
+ reflected by `npm whoami` at all. Asking it unconditionally failed a
38
+ publish that would have worked.
39
+ - A token still works: if `NPM_TOKEN` is set, the job uses it.
40
+ - **`npm run release:trust` configures the trusted publisher from the command
41
+ line.** npm's web form has four fields and npm does not validate what you
42
+ type into it, so a typo is discovered at publish time and reported as
43
+ "OIDC token exchange error - package not found" - which reads like a missing
44
+ package. npm ships `npm trust` for exactly this, and it can read what is
45
+ stored, so the configuration is now compared against the identity the
46
+ workflow will actually present and replaced when it differs.
47
+ - The workflow is named after its file, so the value that has to be typed on
48
+ npmjs.com is the value the Actions tab shows.
49
+
50
+ ### Fixed
51
+
52
+ - **The publish preflight rejected the very token it wanted.** It called `npm
53
+ access list packages` and required the package to appear in the list. That
54
+ command lists the packages a *user* has access to, which needs
55
+ account-level read permission - and a granular token scoped to one package
56
+ is answered **403** for it. A correctly scoped publish token would have
57
+ been refused, and the user sent looking for a problem that was in the
58
+ check. Only `npm whoami` is checked now, which any valid token passes; the
59
+ write permission is proven by the publish itself, and its failure message
60
+ says what was missing.
61
+
62
+ ## [0.3.1] — 2026-10-20
63
+
64
+ ### Fixed
65
+
66
+ - **`oc-harness verify` failed a good install over somebody else's skill
67
+ naming.** It checked every skill directory in the config and required the
68
+ `name:` in the frontmatter to match the directory name. OpenCode identifies
69
+ a skill by its **directory**, not by the frontmatter, so a skill whose two
70
+ differ loads perfectly well. An install carrying 220-odd community skills
71
+ reported five failures that were not failures at all, and the command is
72
+ the one people put in a commit hook.
73
+ - The check now covers the kit's **own** skills, which do have to agree with
74
+ their directory — the loop refers to them by name, and one whose frontmatter
75
+ drifted is a step of the loop that will not load. Everyone else's skills are
76
+ counted and left alone.
77
+
78
+ ## [0.3.0] — 2026-10-20
79
+
80
+ ### The fleet
81
+
82
+ The default profile is no longer a model. It is **ten jobs, ten free models,
83
+ one session**.
84
+
85
+ - `lanes.json` declares the jobs and says nothing about models. A profile
86
+ declares which lanes it enables and which model serves each one.
87
+ - Each lane is an OpenCode agent with its own model, its own context window
88
+ and its own effort level. Delegating a job is how you use a second model;
89
+ switching models by hand never happens.
90
+ - **A lane's model is a chain, not a pin.** Free routes get withdrawn in
91
+ days — four of this kit's five original profiles were dead within a month —
92
+ so every lane lists the models that could serve it, best first, and the
93
+ router walks the same chain when a route is spent.
94
+ - **The chain is ordered inside the profile's own ceiling.** A profile
95
+ declares `maxRetention`; a lane takes the first rung at or below it. One
96
+ fleet file therefore covers ten jobs at two privacy levels: `fleet-safe`
97
+ (zero-retention only, the default) and `fleet-all` (every lane on the model
98
+ built for it).
99
+ - `retention.yml` was rewritten against the OpenCode privacy page: seven new
100
+ entries, four dead models dropped, and a third value — `retained` — for
101
+ Beam, which does not train but keeps prompts for 30 days and is therefore
102
+ not zero-retention.
103
+ - `oc-harness lanes` reports which lanes were actually used, so the fleet can
104
+ be cut to what earns its place rather than defended on principle.
105
+
106
+ ### Fixed — the measurement that decided the fleet was counting the wrong thing
107
+
108
+ - **The lane counter counted events, not messages.** `opencode` emits
109
+ `message.updated` several times for a single assistant message — created,
110
+ streamed, completed — so one request was recorded three times, and
111
+ "build 7 calls" was really two messages. A lane record now carries the
112
+ message id and the reader merges on it.
113
+ - **`chat.message` could not attribute a request to a lane.** On OpenCode
114
+ 1.19 its `agent` field is `undefined`. It was the natural place to count a
115
+ call before the model answered, so that a call that never came back still
116
+ counted; it cannot, so it is no longer used for this.
117
+ - **`oc-harness lanes` read the wrong file.** The plugin writes the telemetry
118
+ to the project root; the command read `process.cwd()`. Run from a
119
+ subdirectory, it reported "never called" for every lane — a fleet that
120
+ looked entirely unused.
121
+ - **The unit is now named what it is.** A request is one assistant message,
122
+ which is one round trip to the provider, and a lane that makes three tool
123
+ calls makes three of them. The column says `reqs`.
124
+
125
+ ### Fixed — the fleet did not survive a spent route
126
+
127
+ - **A spent session model collapsed the whole fleet onto one model.** The
128
+ router replaced the entire profile, which set every lane of the fallback
129
+ profile onto one model and left the other five lanes pointing at routes
130
+ that were also spent — the opposite of what the function's own docstring
131
+ promised. A fleet now keeps its lanes: the session model moves, each lane
132
+ moves down its own chain, and the router publishes the model it chose so
133
+ the effort hook pins the session to that one rather than back to the base
134
+ model that is spent.
135
+ - **`effectiveRetention().worst` had two meanings.** It was a retention class
136
+ on one path and a model id on the other, and the header prints it as a
137
+ model name. A fleet that resolved to a `retained` lane said "but retained
138
+ makes the whole file retained". It is always a model id now.
139
+
140
+ ### Fixed — the numbers on screen did not mean what the label said
141
+
142
+ - **The "input cap" was the base model's, labelled as the narrowest lane's.**
143
+ The config header, `oc-harness show` and `oc-harness list` each printed a
144
+ different number next to a prune threshold derived from another one, and
145
+ the README quoted a fourth. There is now one number: the input cap the
146
+ config can actually use.
147
+ - **`profiles/index.json` carried a context of 200,000 for `fleet-all` and
148
+ `big-pickle`.** The cap that governs is 160,000, and the list recomputes it
149
+ from the profile rather than trusting the copy.
150
+ - **The safe fleet inherited the unrestricted fleet's chain order**, so
151
+ bumping into the zero-retention ceiling dropped every lane onto whatever
152
+ rung happened to be third. Two lanes were genuinely mis-served: `locate`
153
+ asked for `low` effort and landed on a model that publishes no effort
154
+ levels at all, so the request was dropped on the floor; and `vision` landed
155
+ on the same model at 9–13 s when MiniMax reads the same image in 7.7 s.
156
+ The safe fleet now orders its own chains.
157
+ - **The docs said four lanes lost their model under the ceiling. Six do.**
158
+ The number is now computed and asserted by a test, and the two fleets'
159
+ reserves (24,000 and 19,200) are both stated.
160
+ - **Three measured latencies were wrong.** GLM 5.3 is 4.4–5.8 s on the work
161
+ a lane does, not 16–27 s; MiMo reads an image in 19.2 s, not 8 s; and
162
+ Nemotron Super VL does *not* hang on a tool read, only on a file attached
163
+ to the prompt — it answered a `grep` sweep in 4.2 s. The rule is "never
164
+ hand it a file as an attachment", not "it is broken".
165
+
166
+ ### Fixed — a shadowing agent file could hide in four places
167
+
168
+ - **An agent markdown file replaces the config entry wholesale**, and a file
169
+ with no `model:` line makes the agent inherit the caller's model. Both were
170
+ measured on OpenCode 1.19 with the plugin out of the way (`--pure`): the
171
+ file's model wins. The model therefore lives in `agent.<lane>` in the
172
+ generated config, and the prompt bodies live in `prompts/`.
173
+ - **`verify` checked one directory.** OpenCode reads `agent/` and `agents/`
174
+ alike, in the config dir and in `.opencode/`, and the plural one wins on a
175
+ name clash — so a shadow in the singular directory was invisible. All four
176
+ are scanned now.
177
+ - `oc-harness install` removes the agent files an earlier version of this kit
178
+ left behind, from either directory.
179
+
180
+ ### Fixed — three lists of lane names, drifting apart
181
+
182
+ - **`profiles/schema.json` named an `audit` lane that no longer existed and
183
+ was missing `longform`.** With `additionalProperties: false` the shipped
184
+ fleet profiles violated the shipped schema, and nothing said so.
185
+ - **`lanes.json` pointed at `./lanes.schema.json`, which was not in the
186
+ repository.** It is now, and a test pins the lane table, the profile schema
187
+ and `LANE_NAMES` to each other.
188
+ - `MANAGED_ROLES` was a hand-written list of nine lanes that had drifted, and
189
+ was no longer read by anything. It is derived from the lane table now.
190
+
191
+ ### Removed
192
+
193
+ - `agents/` — the prompt bodies moved to `prompts/`, where a file cannot
194
+ shadow a config entry.
195
+ - The `space-bunny-free`, `x-preview-f-free`, `deepseek-v4-flash-free`,
196
+ `muse-spark-1.3-contributor-free` and `muse-split-free` profiles. Their
197
+ models were withdrawn from the provider. `oc-harness list` now names them
198
+ under **no longer on the tier**, with what replaced them.
199
+
200
+ ### Migration
201
+
202
+ 0.2.x → 0.3.0 changes the profile ids, the config layout and the meaning of
203
+ the telemetry. See [MIGRATION.md](MIGRATION.md).
204
+
205
+ ---
206
+
207
+ ## [0.2.1] — 2026-10-19
208
+
209
+ Shipped: the `space-finish` skill's duplicated steps removed, one demo video
210
+ instead of three, and the package metadata pointed at the right repository.
211
+
212
+ ### Fixed
213
+
214
+ - **`oc-harness use` deleted hand-tuned config.** The JSONC parser returned
215
+ `{}` for a config it could not read, so the merge was onto an empty base
216
+ and the user's own settings were written back out deleted, with no message.
217
+ The parser walks the text once, tracking string state, so comments and
218
+ trailing commas are both handled correctly.
219
+ - **Five further defects that lose data, break CI, or send users nowhere** —
220
+ including `--dryrun` being accepted and then ignored, so a dry run silently
221
+ overwrote the real config.
222
+ - **`oc-harness agents` reported "no project agents found" forever** while
223
+ none of them were installed at all, because it scanned only the project and
224
+ the kit's own agents live in the global config directory.
225
+ - The demo video was replaced with a real recorded take: the slide renderer
226
+ was describing a test count it was not measuring.
227
+
228
+ ---
229
+
230
+ ## [0.2.0] — 2026-10-15
231
+
232
+ Shipped: the evaluation harness, integration tests that load the built plugin
233
+ the way OpenCode does, npm packaging, `oc-harness link`, `oc-harness agents`,
234
+ the tool-output caps, and the `scout` → `locate` agent rename.
235
+
236
+ ### Fixed
237
+
238
+ - **The guard matched text a shell would not run.** `cd repo && rm -rf /`,
239
+ `sudo rm -rf /` and `bash -c "rm -rf /"` are now caught, while
240
+ `git log --grep push` and `echo push` are not. A heredoc body is data, not
241
+ commands.
242
+ - **`tool_output` was pinned at 200 lines / 8 KB**, about 6× tighter than
243
+ OpenCode's default. It truncated 415 of 2,966 `read` calls and 418 calls
244
+ were issued with an `offset` to fetch the rest: the truncation *causes* the
245
+ re-read, one for one, at a measured 17.3 s median per request. The cap is
246
+ now the default, and pruning rather than truncation bounds the context.
247
+ - **`tail_turns: 15` was inert config.** Compaction is keyed off the full
248
+ context ceiling that a normal session never reaches; it had fired zero times
249
+ in 153 measured sessions. The key was dropped rather than tuned.
250
+ - **The permission-pressure figure was a transcription of the allowlist**, so
251
+ the measured number was whatever the copy looked like when it was last
252
+ edited. `baseline` imports the real list now.
253
+ - **The `46.6%` that effort tiering saves was a projection, not a
254
+ measurement** — cross-model medians, because the tiering had never run on
255
+ the zero-retention model. `eval` now prints it as `PROJECTION` with
256
+ `measured: no`.
257
+
258
+ ### Known at the time
259
+
260
+ - `use --refresh` and the eval harness could not reach a user's real
261
+ install from CI, so the live half of the gate ran only locally.
262
+ - The `scout` agent shadowed OpenCode's own built-in `scout` subagent, which
263
+ exists to read a dependency's source.
264
+
265
+ ---
266
+
267
+ ## [0.1.0] — 2026-10-12
268
+
269
+ First release: profiles, the effort resolver, the config compiler, the
270
+ `oc-harness` CLI, the four plugins (guard, prune, router, telemetry),
271
+ `GLOBAL.md`, the agent files, the skills, the test suite and
272
+ `baseline.mjs`.
273
+
274
+ ### What it promised
275
+
276
+ A model-agnostic harness kit. Everything model-specific lives in
277
+ `profiles/<id>.json`; no plugin in `src/` names a model. Switching models is
278
+ a data edit, not a code change.
279
+
280
+ ### Fixed in later releases
281
+
282
+ The defects in 0.1.0 and 0.2.0 are listed under those releases, because the
283
+ fix and the version that carried it are the same fact. Two of them were
284
+ silent in the worst way:
285
+
286
+ - **`oc-harness use` could write back a config it had failed to read**, with
287
+ the user's own settings deleted and no message. Fixed in 0.2.1.
288
+ - **The default profile pointed at a model the provider had withdrawn.** The
289
+ config kept validating and kept claiming zero-retention. The tier turned
290
+ over again within a month, which is why 0.3.0 gives every lane a chain
291
+ instead of a pin.
292
+
293
+ ---
294
+
295
+ [Unreleased]: https://github.com/pham-canh/space-harness/compare/v0.3.3...HEAD
296
+ [0.3.3]: https://github.com/pham-canh/space-harness/compare/v0.3.1...v0.3.3
297
+ [0.3.1]: https://github.com/pham-canh/space-harness/compare/v0.3.0...v0.3.1
298
+ [0.3.0]: https://github.com/pham-canh/space-harness/compare/v0.2.1...v0.3.0
299
+ [0.2.1]: https://github.com/pham-canh/space-harness/compare/v0.2.0...v0.2.1
300
+ [0.2.0]: https://github.com/pham-canh/space-harness/compare/v0.1.0...v0.2.0
301
+ [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.