Repository navigation
Reliability, safety, and engineering-quality hardening of the behavioral runner.
Security
- Behavioral runner safety contract (
tests/harness/run-case.mjs, newtests/harness/safety.mjs): an unattended evaluation no longer enables unrestricted automatic permission approval silently. A model run is now refused (exit 4, before any side effect) unless the operator acknowledges it explicitly with new--allow-auto;--build-onlystays free of any acknowledgement. With acknowledgement, the runner (a) writes a least-privilege permission config to the evidence dir and delivers it through the documentedOPENCODE_CONFIGtier — hard-denying external-directory access,webfetch,websearch,*.envreads, andcurl/wgetbash commands (explicitdenyrules are the one thing OpenCode's auto mode does not approve) — and (b) builds the child environment from an allowlist (keeperEnv) so credential-shaped variables (API keys,*_TOKEN, SSH agents) are never inherited by the evaluated session — matched case-insensitively on Windows, whose real spellings arePath/ComSpec/SystemRoot(caught on a live machine: the original case-sensitive registry silently droppedComSpecandSystemRootfrom the evaluated session, degrading node/npm/PowerShell instead of merely scrubbing credentials), and case-sensitively on POSIX wherepathis a different variable; operator-named extras can be forwarded via--env-pass NAME,...(values never printed; the report records the count of dropped variables, never their names). Both A/B arms receive identical permission rules and identical env scrubbing. Documented honestly as containment, not an OS sandbox: bash/edit/read remain allow by default and the user's global OpenCode config still merges in (full isolation taxonomy now intests/harness/README.md; SECURITY.md scope extended to the harness).
Fixed
- Behavioral runner (unattended mode): the console summary printed the names of dropped credential-shaped environment variables while every report records only their count — contradicting the documented contract that names are never printed. Which variables exist on a machine is not public content, and runner console output often gets pasted into bug reports. The runner's console now shows the count-only summary via
scrubSummary()(tests/harness/safety.mjs); operator-forwarded--env-passnames are unchanged, because the operator supplied those names. Regression-tested in the free self-test (safety:scrub-summary-count-only,safety:scrub-summary-empty,runner:scrub-summary-helper-in-use). - Behavioral harness on POSIX:
runFixtureTestexecuted the fixture's command string withshell: falseon non-Windows platforms, sospawnSynctreated the whole string (node test/x.test.mjs) as a single filename — every seeded-state check failed withERRORon ubuntu CI while Windows passed (first CI run to execute the harness self-test on Linux). Commands now run through a shell on every platform, with a pre-flight executable probe (where/command -v) so a missing executable stays anERROR(broken environment) and can never be misread as aFAIL(red suite). Two free self-test checks guard both behaviors. - Installers (
install.ps1,install.sh): timestamped backups were created in place —commands/loop.md.backup-<ts>and, for the skill,skills/loop-engine.backup-<ts>— but OpenCode discovers every directory containing aSKILL.mdunderskills/, so the overlaid backup came back as a phantom skill (the just-replaced version, live again). Observed with a real overwrite install on 2026-11-04. Backups now go to a sibling root (<dest>-loop-backups\<timestamp>\<tree>) outside all discovery directories, tree structure preserved; legacy in-place backups from older installers are left untouched. Installer tests assert the new location and that no*backup*residue remains insidecommands/orskills/.
For maintainers
- Gates at this tag:
scripts/validate.mjs --all122/122 checks (79 structural + 27 mutation-injection checks that prove each new check can fail + 16 installer scenarios) andtests/harness/selftest.mjs79/79 checks, both exit 0 on the tagged commit. CI (validateworkflow, run 42059508351) passed on ubuntu-latest and windows-latest for this exact commit; CI never executes model sessions. - Tag re-anchor disclosure (for the record): this release was first published on 2026-11-05 from commit
c83acb0(self-test at 76/76 there, CI run 42053358009); the console-privacy fix listed under Fixed was folded intov0.3.1the same day per maintainer decision instead of creating a v0.3.2, re-anchoring the annotated tag to the amended commit (eed4199). Main's history was appended, not rewritten (c83acb0remains an ancestor); only the tag object moved, and only after both CI runners passed on the new commit. The re-anchor and the fix itself are disclosed here so the release page stays truthful about what the tag now contains. - History note (erratum, disclosed for the record): the installers fix was pushed on 2026-11-04 as commit
0b60dd7, whose message carried a false attribution clause ("Windows user report confirmed...") — the behaviors were tool-verified locally, and nothing external confirmed them — plus a non-existent GitHub runner name (linux-latest). Before the first publication, the message was corrected to "tool-verified locally, not a user report" andubuntu-latest + windows-latest, and the corrected line was force-pushed asaccadb4.git diffacross that rewrite is empty: it changed no file content, only the message text of that one commit (its three descendants were re-set on top of it). The code itself was correct before; only the message was misleading.