opencode-harness-kit 0.2.0 → 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.
- package/CHANGELOG.md +301 -0
- package/GLOBAL.md +50 -13
- package/MIGRATION.md +140 -0
- package/README.md +276 -122
- package/bin/oc-harness.mjs +571 -135
- package/dist/index.js +453 -159
- package/docs/KNOWN-LIMITS.md +189 -4
- package/docs/RESEARCH.md +80 -5
- package/docs/ROUTING.md +254 -0
- package/lanes.json +136 -0
- package/lanes.schema.json +55 -0
- package/package.json +13 -11
- package/profiles/beam-free.json +51 -0
- package/profiles/big-pickle.json +45 -0
- package/profiles/deepseek-v4.1-flash-free.json +49 -0
- package/profiles/fleet-all.json +139 -0
- package/profiles/fleet-safe.json +139 -0
- package/profiles/glm-5.3-flash-free.json +49 -0
- package/profiles/index.json +120 -37
- package/profiles/longcat-2.5-preview-free.json +14 -10
- package/profiles/mimo-v2.6-flash-free.json +44 -0
- package/profiles/minimax-m3.1-flash-free.json +51 -0
- package/profiles/nemotron-3-ultra-free.json +44 -0
- package/profiles/nemotron-3.5-lightning-free.json +44 -0
- package/profiles/nemotron-3.5-super-vl-free.json +52 -0
- package/profiles/schema.json +30 -11
- package/{agents → prompts}/build.md +2 -35
- package/prompts/doc.md +42 -0
- package/{agents → prompts}/locate.md +1 -16
- package/prompts/longform.md +34 -0
- package/{agents → prompts}/plan.md +2 -25
- package/{agents → prompts}/reviewer.md +2 -22
- package/prompts/schema.md +30 -0
- package/prompts/sweep.md +29 -0
- package/{agents → prompts}/verify.md +6 -29
- package/prompts/vision.md +33 -0
- package/retention.yml +75 -57
- package/skills/space-build/SKILL.md +16 -0
- package/skills/space-finish/SKILL.md +0 -10
- package/skills/space-review/SKILL.md +8 -0
- package/src/config.js +94 -69
- package/src/driver/run.mjs +237 -0
- package/src/effort.js +59 -22
- package/src/index.ts +15 -2
- package/src/lane-usage.js +142 -0
- package/src/profile.js +615 -221
- package/src/router.ts +148 -39
- package/src/tasks.js +248 -0
- package/src/telemetry.ts +98 -0
- package/docs/demo/demo.gif +0 -0
- package/docs/demo/demo.mp4 +0 -0
- package/docs/demo/render.py +0 -146
- package/examples/muse-spark-1.3-contributor-free.json +0 -41
- package/profiles/deepseek-v4-flash-free.json +0 -41
- package/profiles/muse-split-free.json +0 -64
- package/profiles/space-bunny-free.json +0 -42
- 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-
|
|
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
|
-
-
|
|
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-
|
|
81
|
-
harness, not by anything written here. `plan` and `reviewer` run at the
|
|
82
|
-
available effort, `build`
|
|
83
|
-
because finding a thing and checking a
|
|
84
|
-
playing one of the low-effort
|
|
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
|
|
100
|
-
same model; free routes often stay blocked for days, and the daily window
|
|
101
|
-
resets at 00:00 UTC. Report it and ask for a new session. No plugin can
|
|
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.
|