opencode-harness-kit 0.5.4 → 0.5.6

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 CHANGED
@@ -12,6 +12,210 @@ is not happening.
12
12
 
13
13
  ---
14
14
 
15
+ ## [0.5.6] — 2026-10-21
16
+
17
+ Published. The tag is what cut it: `git tag v0.5.6 && git push origin main
18
+ v0.5.6`, and the publish job watches for exactly that.
19
+
20
+ A patch, and it is a patch of the same kind as 0.5.5: the three holes the
21
+ guard had left, one measurement that did not measure what it claimed, and
22
+ one measurement that had never produced a number at all.
23
+
24
+ ### Fixed
25
+
26
+ - **`rm /tmp/x -rf` was allowed.** The rule that blocks a recursive delete
27
+ read the first flag and then looked for a path after it, so a flag placed
28
+ after the path was invisible - and GNU rm permutes its options. The rule
29
+ now asks two separate questions: is any flag recursive or force, and is
30
+ there a path.
31
+
32
+ - **`chmod --recursive 777 /` was allowed**, by the rule that blocks
33
+ `chmod 777`: the rule's flag position only accepted short letter flags,
34
+ and a long option put the mode out of reach.
35
+
36
+ - **`curl <url> | grep y | sh` was allowed.** The pipe-to-a-shell rule
37
+ required the download to feed the shell directly, and a filter in between
38
+ hid the shell from it. A filter does not change what the shell runs.
39
+
40
+ - **The pruner measurement and the pruner's own log measured different
41
+ things.** The log reports the REQUEST's size delta - the part before the
42
+ transform against the part after it - and the measurement script counted
43
+ the TEXT inside each part, so the two disagreed and only the log's number
44
+ matched the kit's claim. The script measures the request now, the way the
45
+ pruner does, and reports the text measure beside it under its own name.
46
+ The gate is computed from the request measure.
47
+
48
+ - **The lane record was never written.** Not once, in any session, on any
49
+ machine. The plugin read the lane name from `agent` on the assistant
50
+ message, and opencode's assistant message has no `agent` field - so
51
+ `oc-harness lanes` was reading a file that could not exist, and the
52
+ advice to cut the lanes nobody calls had nothing behind it. The name is
53
+ on the request, and the assistant message names that request's user
54
+ message as its parent, so the two join there.
55
+
56
+ ## [0.5.5] — 2026-10-21
57
+
58
+ Published. The tag is what cut it: `git tag v0.5.5 && git push origin main
59
+ v0.5.5`, and the publish job watches for exactly that.
60
+
61
+ A patch, and a large one: thirty-odd defects, nearly all of them the kind
62
+ this kit exists to catch - a command that reported success while the thing
63
+ it promised was not happening. Most of them were found by reading the code
64
+ with the question "what does this say when it did nothing?", and every one
65
+ of them now has a test that fails without the fix.
66
+
67
+ ### Fixed
68
+
69
+ **The installer**
70
+
71
+ - **The install said "installed into" when it had linked nothing.** A link
72
+ that could not be created was a warning in a corner of the output, and the
73
+ command exited 0. A config directory the user could not write to produced a
74
+ green install and a kit that was not there. A failed link is now exit 1.
75
+
76
+ - **A file of the user's own was left alone, and the install said
77
+ "installed".** The installer refuses to destroy a file it did not put
78
+ there, which is right; the tick was not. A target that was left alone is
79
+ now named, in place of the tick, and `verify` reports the file that is not
80
+ the kit's.
81
+
82
+ - **Answering "yes" at the overwrite prompt replaced nothing.** The answer
83
+ was collected and thrown away: the linker refused to replace a real file
84
+ whatever the user had said. "yes" now replaces the file, and "no" leaves
85
+ it.
86
+
87
+ - **`verify` called a prompt installed when it was the user's own file.** The
88
+ check asked whether a file existed at the path, not whether it was the
89
+ kit's - so a user with their own `prompts/build.md` had a lane running on
90
+ their own instructions, with a green tick next to it. The same is now
91
+ checked for `GLOBAL.md` and for the kit's skills.
92
+
93
+ - **`verify` failed an agent file that mentioned its own config.** The
94
+ pattern read `opencode/opencode.jsonc` as a model that was not in the
95
+ profile.
96
+
97
+ - **`verify` called a relative instruction path resolved.** opencode ignores
98
+ relative instruction paths, and the check resolved it against the
99
+ directory the command happened to be run from - so the verdict was a
100
+ statement about the shell's working directory.
101
+
102
+ - **`uninstall` reported links it could not remove.** A directory the user
103
+ could not write to was reported as "removed prompts/ (10 link(s))" while
104
+ all ten links were still there.
105
+
106
+ - **`use` overwrote the config when the backup could not be written.** The
107
+ prompt promises "a dated backup is kept in backups/", and the backup was
108
+ best-effort: a failure was not even mentioned. The overwrite is refused
109
+ now, and the backups directory keeps ten configs instead of one.
110
+
111
+ - **One malformed line lost the whole prune log.** The parse was wrapped
112
+ around the loop, so a single bad record threw the file away and `cost`
113
+ said the pruner had never run.
114
+
115
+ **The guard**
116
+
117
+ - **The pipe-to-a-shell rule missed `curl … | sh; echo done`.** A `;` inside
118
+ a pipe stage left the shell's name glued to the punctuation, so the stage
119
+ was not read as a shell - and the guard was the only layer that had it.
120
+
121
+ - **`bash -c"rm -rf /"` was never inspected.** The glued form of the flag
122
+ defeated the payload extraction.
123
+
124
+ - **A wrapper named by its path defeated the prefix stripping.**
125
+ `/usr/bin/env rm -rf /` was allowed, while `env rm -rf /` was blocked.
126
+
127
+ - **`rm --recursive --force /` was allowed** by the rule that exists to
128
+ block it, which knew only the short flags.
129
+
130
+ - **A credential name with a prefix was not a credential name.**
131
+ `GITHUB_TOKEN=$(curl …)` was allowed, because the word boundary never
132
+ fires after an underscore.
133
+
134
+ - **`chmod 0777` was allowed** by the rule that blocks `chmod 777`.
135
+
136
+ - **A tool's own global flags stepped over the denylist.**
137
+ `gh -R owner/repo pr create` and `npm --prefix /tmp publish` were allowed.
138
+
139
+ - **A patch that deleted a file outside the project was allowed.** The
140
+ deletion names the file in the `---` header, and that header was not read,
141
+ so the path was never scope-checked.
142
+
143
+ **The merge and the profiles**
144
+
145
+ - **The user's own bash rules were deleted from their config.** The kit's
146
+ allowlist and denylist replaced the map wholesale, so every rule the user
147
+ had was gone after the next `oc-harness use`. The user's rules are merged
148
+ in, and the kit's deny rules still win.
149
+
150
+ - **A string-valued `instructions` was dropped** from the user's config.
151
+
152
+ - **The utility route was not counted towards the retention the file
153
+ claimed.** A may-train model could be written into the config as
154
+ `small_model` while the header said "zero-retention" - and the file says
155
+ "zero-retention" to a human who reads it.
156
+
157
+ - **`resolveEffort` could snap a request to `none`**, which is reasoning
158
+ off rather than a rung.
159
+
160
+ - **`validate()` accepted a limit that was not an integer** (a context of
161
+ `1.5` produced a negative prune threshold), an effort level that is not
162
+ on the scale, a retention of "unknown", a base model nobody had recorded,
163
+ and a retention claim that was safer than the models allowed. A claim that
164
+ is *worse* than reality is still allowed: cautious is not wrong.
165
+
166
+ - **A profile whose `id` did not match its file name was loaded anyway**,
167
+ and the config then told the user to edit a file that does not exist.
168
+
169
+ **The runtime**
170
+
171
+ - **The router never recorded a spent route.** It read the model from
172
+ `properties.modelID`, and no opencode event carries a model there - the
173
+ event that has one nests it under `properties.info`. So the failover was
174
+ dead code: the health file was never written, the warning never fired, and
175
+ the fallback chain was never walked.
176
+
177
+ - **Any "429" anywhere in an event spent a live route.** The classifier ran
178
+ over the whole serialised event, so a message whose token count was 429
179
+ was recorded as a failed route.
180
+
181
+ - **A spent route never came back.** The entry carries a timestamp and
182
+ nothing ever read it, so one transient rate limit demoted the model for
183
+ the life of the project directory. The window is the provider's day.
184
+
185
+ - **The pruner booked a saving on turns where the request got bigger.** The
186
+ stub's own metadata is part of the request that goes out, and the log
187
+ measured the text inside the part - so the number was a claim about
188
+ something other than the request. It is the request's size now, and a turn
189
+ that grew reports a negative saving.
190
+
191
+ - **A task marked `blocked` lost its block** when it had an `output:` field:
192
+ the status line was written inside the output block, and the parser folded
193
+ the output back into it. A task nested under another task's output was
194
+ DELETED by the rewrite.
195
+
196
+ - **The telemetry rotation destroyed the previous generation.** The writer
197
+ renamed the current file straight to `.1`, and a rename replaces the
198
+ destination - so the oldest history was gone after two rotations and the
199
+ third generation the reader looks for never existed.
200
+
201
+ - **The trusted-publishing setup could hang for hours.** The wait for the
202
+ browser answer had no timeout, so the documented window did not bound it.
203
+ The list of trusted publishers is also read in npm's own format now, which
204
+ is one JSON object per line rather than an array.
205
+
206
+ - **The two sqlite backends disagreed** about a query with two statements:
207
+ the command line ran both and the python fallback refused.
208
+
209
+ ### Changed
210
+
211
+ - **The install's exit code follows `docs/EXIT-CODES.md`**: a target left
212
+ alone is exit 0 with the reason printed, and a step that failed is exit 1.
213
+
214
+ - **`savedChars` is the request's size delta**, not the text delta, and it
215
+ can be negative. `cost` says "characters ADDED" when it is negative,
216
+ because a number with the wrong sign on it is a lie in a different
217
+ direction.
218
+
15
219
  ## [0.5.4] — 2026-10-21
16
220
 
17
221
  Published. The tag is what cut it: `git tag v0.5.4 && git push origin main
@@ -1233,7 +1437,9 @@ silent in the worst way:
1233
1437
 
1234
1438
  ---
1235
1439
 
1236
- [Unreleased]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.4...HEAD
1440
+ [Unreleased]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.6...HEAD
1441
+ [0.5.6]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.5...v0.5.6
1442
+ [0.5.5]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.4...v0.5.5
1237
1443
  [0.5.4]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.3...v0.5.4
1238
1444
  [0.5.3]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.2...v0.5.3
1239
1445
  [0.5.2]: https://github.com/pham-canh/opencode-harness-kit/compare/v0.5.1...v0.5.2