Repository navigation
v0.8.0
Fixed
-
Benchmark harness no longer charges the treatment arm for a cold plugin
install. Loading a plugin makes opencode npm-install@opencode-ai/plugin
intoXDG_CONFIG_HOME/opencode, andbench/run.mjsgives every run a fresh
HOMEand config dir, so that install ran from scratch on every run: 66.7 s
measured viaopencode debug config. Both arms install, but only on treatment
does it block startup, where it was published as plugin wall-time overhead.
Seeding the npm cache alone still costs 9.2 s, so the harness now resolves the
tree once per invocation and hardlinksnode_modules+package.json+
package-lock.jsoninto each run's config dir for both arms before
opencodestarts. Verified: the plugin-load window drops to 0.213 s,
inside the baseline range. The re-run is complete
(power-string-sweep-v2.jsonl, 40/40, $0.2923): window 0.158 s vs 0.278 s
per arm, wall difference +2.6 s, p = 0.83 — was +27.4 s, p = 0.018. No
wall-time separation remains. -
session.deletedevents missinginfo.id, and user messages missing
sessionID, no longer throw into the event handler's catch-all (which also
skipped handoff arming and the task-boundary check for that event).
Changed
-
Benchmark wall-time claim corrected: harness artifact, not plugin cost.
docs/benchmark.mdreported the power study's only significant separation as
wall time, +27.4 s per run in the treatment arm (p = 0.018). A paired per-run
decomposition of all 40 runs (from each record's preservedrun_dir— the
session DB for message/tool timings, the opencode log for phase boundaries)
shows the gap is entirely startup, and inside startup it is one window:
plugin loading, baseline mean 0.25 s vs treatment 23.22 s. Cause is the
harness's per-runHOMEisolation — only the treatment arm loads a plugin, so
only it blocks on the plugin dependency install during startup (npm, not bun;
see Fixed above). Plugin runtime is not involved: importingdist/plugin.js
costs 0.24 s under bun, the tool hook 0.004 ms, the audit spawn ~45 ms.
Adjusted for that window the difference is +4.45 s, paired positive in 13 of
20 rather than 17 of 20 — superseded by the fixed-harness re-run at
+2.6 s, p = 0.83. -
README first screen. The install command moved from line 69 to line 19.
Badges cut from seven to three (version, test, license), the hand-written
table of contents dropped in favour of GitHub's own heading outline, the
duplicated handoff statistic removed (the same numbers are in
## Observed behavior), the demo caption folded into the intro paragraph, and
the before/after diagram relocated todocs/how-it-works.md, next to the
architecture diagram it belongs with. No content was lost, only moved or
de-duplicated. -
Unified policy state machine. Severity is now computed once, in
src/budget/policy.ts, as a monotone state machine
(HEALTHY<ATTENTION<PRESSURE<HANDOFF_RECOMMENDED<BLOCKED)
over three axes -- call count, configured budgets, and the context window.
The mode is applied after the max, not inside an axis. Previously eight
independent decision sites in the tool hook each owned a threshold and a
latch, which allowed three separate<system-reminder>blocks and two toasts
on a single tool call. The plugin now emits at most one block per tool
call, with a state header naming the driving axis and a fixed section
order: boundary, announce, audit, budget, handoff. At most one toast, titled
by state. -
Reminders no longer suppress each other. The early returns after the
task-boundary and announce reminders are gone. Deferral was a side effect of
those returns rather than a design goal: it spread co-occurring thresholds
across separate tool calls and skipped the budget check entirely on those
calls. Everything due on a call now lands together in the one block. -
token_norm_statusanswers from the enforcement machine. Therecommend()
ladder insrc/status.ts, which re-derived severity from
(pressured, exceeded, mode), is replaced by a direct state map. The snapshot
gains astatefield alongsiderecommendation. Two visible contract
changes: a session at or pastTOKEN_NORM_ANNOUNCE_ATwith no budget
pressure now reportswarnwhere it reportedcontinue; and in handoff mode,
pressure without a pause now reportswarnrather thanhandoff, matching
what the plugin would actually do -- a handoff has always required a natural
pause as well. -
TOKEN_NORM_SETTLE_MSandTOKEN_NORM_SWITCH_WAIT_MSare read inconfig.ts
like every other setting, so they are validated and diagnosed too. -
The opencode client and event payloads are no longer typed as
any.src/host.ts
declares narrow interfaces covering only the host fields this plugin actually
reads, so a renamed or removed host method now fails at build time instead of
disappearing into a runtime catch.
Added
- Release provenance. Every build writes
dist/provenance.jsonrecording the
gitShait was built from, whether the working tree was dirty, and the sha256
of both files that leave npm's integrity story once installed --
dist/plugin.jsandscripts/usage-audit.py. The installer copies those two
into~/.config/opencode, where nothing else attests to them and the audit
script is then executed. The digests ship in the tarball, are rendered as a
table in the GitHub release notes, andprovenance.jsonis attached as a
release asset, so an installed copy can be checked withshasum -a 256.
test/packaging.test.tsrehashes the packed bytes, so a stale or wrong
digest fails the build rather than shipping. - Config diagnostics: a malformed or misspelled
TOKEN_NORM_*setting is
reported once at load (log line plus a toast) instead of silently falling back
to its default. Covers non-numeric or non-positive thresholds and budgets, an
out-of-rangeTOKEN_NORM_CONTEXT_WARN, a kill switch set to anything but0
or1, an emptyTOKEN_NORM_CHEAP_TOOLS, and unrecognizedTOKEN_NORM_*
keys. Values still fall back rather than failing the session. - Handoff integration-test matrix: child-session events, a late
session.created,
the settle floor,appendPrompt/submitPromptfailures, and concurrent
handoffs. - Benchmark statistics: per-cell summaries now carry
n,median,sd, and a
seeded bootstrap 95% CI for the mean, plus a per-task treatment-vs-baseline
contrast using a two-sided permutation test. Cells below five runs per arm are
flaggedunderpoweredso a small batch cannot be read as a result. bench/run.mjs --resummarize <jsonl>recomputes a summary from existing run
records with no paid runs and without writing to the input file.- Power study (
bench/results/power-string-sweep.jsonl, 40 paid runs, n=20 per
arm) refuting the earlier N=210-string-sweephypothesis: tool calls differ
by -3.05 (p = 0.65) and both arms are bimodal. Wall time is the one metric that
separates, +27.4 s in the treatment arm (p = 0.018), cause unattributed.
Provenance
Built from 60d4ef36b0b31b7d8b443ac4afac87e109f0886c.
| artifact | sha256 |
|---|---|
dist/plugin.js |
455e2c600b64ccd3612cdaf0c4c6c16c8f9c459360e0567e395f1a240f8a7343 |
scripts/usage-audit.py |
ad5d1c4e18eac2c73dfc7267ff6c8a1f553783d7f62cd9d32d54f25f2ffa0c73 |
Verify an installed copy:
shasum -a 256 ~/.config/opencode/plugins/opencode-token-norm.js
shasum -a 256 ~/.config/opencode/scripts/usage-audit.py