opencode-harness-kit 0.5.4 → 0.5.5

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