Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ jobs:

- uses: DeterminateSystems/determinate-nix-action@021c8a1bd3570eb21f5c20a054812b0c4d9ca614 # v3

- name: Build and smoke-test the opencode package
- name: Build and smoke-test the V1 and V2 packages
run: |
set -euo pipefail
nix build .#opencode --print-build-logs
version="$(nix eval --raw .#opencode.version)"
./result/bin/opencode --version | grep -F "$version"
for package in opencode opencode-v2; do
nix build ".#$package" --print-build-logs
version="$(nix eval --raw ".#$package.version")"
./result/bin/opencode --version | grep -Fx -e "$version" -e "opencode v$version"
done

notify-ci:
needs: [check, package-build, flakehub-publish, flakehub-verify]
Expand Down
10 changes: 10 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,13 @@ update:
# Pin the packaged opencode to a specific version
update-to VERSION:
./scripts/update-opencode.sh --version {{VERSION}}

# V2 is an independent official npm binary lane; V1 recipes stay unchanged.
update-v2-check:
./scripts/update-opencode-v2.sh --check

update-v2:
./scripts/update-opencode-v2.sh

update-v2-to VERSION:
./scripts/update-opencode-v2.sh --version {{quote(VERSION)}}
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ PRs. This repository's required CI builds and smoke-tests the package
before merge. Merges to main are tagged `opencode-vX.Y.Z`
and published to FlakeHub as rolling releases.

### V2 binary lane

`packages.opencode-v2` / `homeManagerModules.withPackageV2` select the official
`@opencode/cli-linux-x64` binary; V1 remains the default. Use
`just update-v2-check`, `just update-v2`, or `just update-v2-to 2.X.Y` to check
or update its npm version and fixed-output integrity hash without building.
The required `package-build` job builds and checks both V1 and V2 versions;
`checks.updater-v2-mutation-only` tests V2 updater isolation and metadata validation.
Publish the leaf and pin its revision before activating a consumer.

## Trust modes

- **Rolling** (FlakeHub wildcard / `main`): every commit that passed the
Expand Down
8 changes: 8 additions & 0 deletions checks.nix
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,14 @@ in {
"nix MCP registered despite mcpNixos.enable = false";
pkgs.runCommand "opencode-defaults-posture" {} "touch $out";

updater-v2-mutation-only =
pkgs.runCommand "opencode-v2-updater-mutation-only" {
nativeBuildInputs = [pkgs.bash pkgs.coreutils pkgs.gnugrep pkgs.gnused pkgs.jq pkgs.just];
} ''
bash ${self}/tests/opencode-v2-contract.sh
touch "$out"
'';

updater-mutation-only = pkgs.runCommand "opencode-updater-mutation-only" {} ''
cp -r ${self} source
chmod -R u+w source
Expand Down
3 changes: 3 additions & 0 deletions docs/behaviors/opencode-v2-migration/behavior.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lets migrate backends to v2. Lets also check if everything is working through our flake. Lets update packages and plugins lets explore context fold and usage sidebar. Lets also explore keyboard shortcuts for multiple session managing

lets also apply all proposed improvements
36 changes: 36 additions & 0 deletions docs/behaviors/opencode-v2-migration/migration.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Behavior contract; runnable bindings and observed results are recorded separately.
Feature: Managed OpenCode V2 workspace backends
Scenario: Independent native workspace backends
Given the homelab and dataplatform workspaces have preserved V1 databases
When the verified Endeavour user environment is activated
Then both explicitly addressed workspace backends report the pinned V2 version
And native ol and ow clients connect to their respective backends
And each backend uses a separate V2 database and its intended provider route
And neither original V1 database content or schema is modified by V2
And the homelab legacy database receives only the approved owner-only permission change

Scenario: Verified essential integrations
Given the exact V2 package and pinned plugins are installed through the flake
When a disposable model and shell roundtrip is performed
Then provider-scoped session headers reach only their intended providers
And RTK executes without weakening approval of the original shell command
And configured MCP servers connect and web fetch returns source content

Scenario: Enforceable guardrails
Given native V2 permission prompts remain enabled
When an agent requests secret files or unapproved shell operations
Then protected direct reads are denied
And shell commands outside narrow inspection allowances require approval
And documented authorized deployment commands are not permanently denied

Scenario: Optional context and quota experiments
Given Context Fold and usage sidebar have immutable enable-time pins
When the default managed environment starts
Then neither optional plugin loads
And no quota fallback is represented as custom gateway usage

Scenario: Native session management
Given the global V2 CLI configuration is managed declaratively
When the operator manages multiple sessions
Then native session and tab shortcuts remain available
And terminal-specific delivery limitations are documented without claiming unobserved UI success
46 changes: 46 additions & 0 deletions docs/behaviors/opencode-v2-migration/stage-one-pager.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# OpenCode V2 migration: planning and delivery

**Status:** leaf package reviewed and locally GREEN; CI/publication and consumer integration remain. No activation.
**Stage / revision:** RV / 5, 2026-10-13.
**Basis:** [verbatim human seed](behavior.md), [behavior contract](migration.feature).

## Outcome and why

Both existing Endeavour workspace backends will use native V2 clients with isolated V2 databases. Old V1 database contents and schema remain untouched; history import is separate. Current Desktop origin/main already includes V2 packaging selection and safe RTK/gateway hooks. Earlier inspection of an older canonical branch must not be used as implementation baseline.

## Decisions and boundaries

- Q-1 accepted: native V2 backends and ol/ow launchers; no Rust/V1 client pairing.
- Q-2 accepted: Context Fold and usage sidebar remain opt-in and absent from default plugin lists.
- Q-3 accepted: Endeavour activation through the documented recipe after green checks.
- Q-4 accepted: publish reviewed green commits/task branches and leaf-first PRs in both repositories. Merging remains separately authorized. Consumer deployment waits for a published updated leaf.
- Q-5 accepted: restrict existing homelab V1 database from mode 0644 to 0600 through declarative activation. This explicit permission-only exception does not authorize reading or migrating its contents.
- Apply all six improvements: managed package, essential plugins, global CLI/compaction, V2 checks, permissions, pinned MCP/context cleanup.
- Only OpenCode-related dependencies are in scope. No fleet-wide upgrade, history import or additional orchestration framework.

## Delivery slices and verification

1. Leaf package: update official Linux-x64 V2 binary/hash; extend updater and required CI coverage. First observe assertion-failing tests for missing V2 coverage, then run those tests and build the exact V2 package. Preserve the V1 package lane.
2. Workspace routing: restore two explicit loopback V2 units with new database paths and native explicit-server launchers. Extend evaluated configuration tests first; then verify units, ports, provider routing, argument forwarding and preserved PATH behavior.
3. Settings and integrations: manage one global cli.json, supported compaction, update compatible pinned plugins and Donsetch, enforce secret-read/shell guardrails, remove redundant MCP overrides and duplicated startup guidance. Use existing header/credential tests and smallest necessary negative/positive checks.
4. Runtime: disposable exact-package header/tool/MCP smoke checks, then independent review and owner lint, format and dry-build gates. After a published immutable leaf pin exists, activate only Endeavour and query both explicit endpoints; do not use auto-discovery as evidence.

Success requires zero exit plus actual assertions. Infrastructure errors are not RED; static evaluation is not E2E. Desktop project checks observed assertion failures for unpinned MCP and unrestricted shell before changes; all five current checks pass, including rejection of compound commands and secret-reading Git arguments. The opt-in fragment check is metadata validation, not plugin runtime. Each failure hypothesis gets at most three repair attempts.

Leaf RED observed missing V2 updater/check/required-CI coverage, then additional malformed-metadata and exact-version failures. `bash tests/opencode-v2-contract.sh` passes with `PASS: V2 updater isolation, metadata validation, checks and required CI coverage`. Targeted updater checks and the exact V2 package build pass; `--version` reports `opencode v2.0.27`. The first remote build failed on missing acl-2.4.0; local retry succeeded. V1 remains 1.19.0 and full flake checks are reserved for CI.

Desktop backend configuration/wrapper tests pass 27 cases, including approved permission-only legacy DB fixtures. A disposable exact-2.0.27 API smoke confirms its own PID, native explicit-server client attachment, authenticated health and unauthenticated 401. One test harness repair handled the CLI version prefix; another closed the negative-control HTTP response. No production backend was changed. Essential plugin/tool and final owner gates are still outstanding.

Fresh leaf review reproduced one P2 finding: an unquoted Just recipe version could execute shell syntax and mask the updater failure. The added recipe-level regression failed before the fix, then passed with `quote(VERSION)`. Both targeted Nix lint and sandboxed updater checks pass after the fix. Review confirmed unchanged V1 behavior, sequential required CI coverage for both packages and exact registry integrity. The first-draft patch receipt is retained outside the repository; no acceptance or RED evidence was backdated.

On 2026-10-13, exact 2.0.27 also completed a synthetic model → native RTK shell → model continuation. Desktop settings/project checks and all four touched-host dry-builds pass. Native plugin/permission checks and Ponytail request injection fail, so consumer deployment is blocked despite static success. The bounded repair loop stopped without weakening assertions. Optional runtime trials were cancelled and remain unverified. See the [reconciled test contract](test-contract.md) for per-scenario evidence boundaries.

## Risks and recovery

The independent grill identified publication, database namespaces, server-side routing, approval order and controlling-session continuity as mandatory boundaries. Keep original V1 databases and a reachable matching recovery generation. Do not change live configuration manually, use an unpublished sibling tree for production, or restart the controlling trial session. Restore only task-owned experiments.

Separate clean task worktrees preserve unrelated work. Pending tmux PATH and isolated-trial MCP work must be reconciled before activation; their original worktrees stay unchanged. Global XDG variables from this harness point at trial roots, so documented HM activation needs explicit normal-user XDG roots.

## Continuing work

Reviewed green PR publication is authorized; leaf merge still needs approval. Publication is a real dependency for deploying the updated leaf, not a new activation approval gate. The leaf first-draft review is reconciled; consumer review and exact integration gates continue. Runtime results and unresolved gates remain explicit until observed. The Gherkin contract is traceability, not a claim that a feature runner executed.
69 changes: 69 additions & 0 deletions docs/behaviors/opencode-v2-migration/test-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# OpenCode V2 migration: reconciled test contract

**Status:** final traceability draft, 2026-10-13; reconciles accepted planning with delivered checks, not retrospective planning.

**Basis:** [human seed](behavior.md), [accepted scenarios](migration.feature), [delivery evidence](stage-one-pager.md). Gherkin has no executable step bindings; checks cover specific assertions, not whole-scenario completion.

Commands run from the indicated repository. `$V2` identifies the realized leaf 2.0.27 executable; `$PONYTAIL` identifies the realized managed Ponytail package directory.

## Scenario bindings

### Independent native workspace backends

Desktop:

```sh
python3 -m unittest discover -s tests/local-workspace-sessions -p 'test_*.py'
"$V2" --version
python3 tests/opencode-project/smoke_auth.py "$V2" 2.0.27
```

Reported 27 tests cover evaluated units, isolated database/XDG paths, routing, launcher forwarding, PATH and synthetic chmod fixtures. Fixtures preserve inode/size/mtime and work-database metadata; they do not establish production V1 byte/schema preservation.

Disposable auth smoke establishes exact version/PID, explicit native API addressing, authenticated `/api/info` and unauthenticated 401. Neither check activates production backends.

### Verified essential integrations

Leaf:

```sh
bash tests/opencode-v2-contract.sh
nix build .#opencode-v2 --no-link
```

Recorded GREEN covers updater isolation, malformed metadata, required CI wiring and exact package build; CI execution remains separate evidence.

Desktop:

```sh
node tests/opencode-profiles/test_headers.mjs
node tests/opencode-profiles/test_plugins.mjs "$PONYTAIL"
OPENCODE_TEST_BINARY="$V2" python3 tests/opencode-profiles/test_header_integration.py
OPENCODE_TEST_BINARY="$V2" python3 tests/opencode-profiles/test_runtime_v2.py
OPENCODE_TEST_BINARY="$V2" python3 -m unittest discover -s tests/local-workspace-sessions -p test_tool_roundtrip.py
```

Hook checks cover provider filters, original RTK permission input and Ponytail transforms. The exact-version synthetic tool roundtrip passed with real RTK execution and model continuation. Loopback header test deliberately returns HTTP 400, but its Ponytail-injection assertion currently fails. Runtime API checks plugin activation and permission decisions; these currently fail. Latest results belong in the delivery record.

### Enforceable guardrails

```sh
python3 tests/opencode-profiles/test_settings.py
python3 tests/opencode-project/test_configuration.py
```

Settings/project checks inspect rules. Runtime checks exercise native read denial despite agent override, shell approval, narrow inspection allowances and destructive denial. Shell approval is not sandboxing.

### Optional context and quota experiments

Settings/project checks verify default absence, immutable fragments, Fold debug disabled and sidebar `default: none`. Optional runtime trials were cancelled; compatibility, quota retrieval and savings remain unverified.

### Native session management

Settings checks verify global CLI, prompt permissions, native tabs and unchanged default bindings. Desktop's `docs/guides/opencode-v2-sessions.md` documents shortcuts. Actual keyboard delivery remains manual/unobserved.

## Delivery boundaries

Q1–Q5 persist: native isolation; optional plugins off; Endeavour activation after GREEN; reviewed green commit/push/PR authorized, merges not; existing homelab V1 chmod-only 0600 exception. Run owner lint/fmt/docs/dry gates. Obtain separately authorized leaf merge/publication and immutable consumer pin before activation. Never substitute sibling source for production.

Real-provider completion, MCP connectivity/source return and two live endpoint acceptance remain required deployment evidence, not established by static or loopback checks. Consumer's leaf remains 2.0.21 until publication. Full flake checks belong in CI.
4 changes: 2 additions & 2 deletions packages/opencode-v2/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
versionCheckHook,
writableTmpDirAsHomeHook,
}: let
version = "2.0.21";
version = "2.0.27";
in
stdenvNoCC.mkDerivation {
pname = "opencode-v2";
inherit version;

src = fetchurl {
url = "https://registry.npmjs.org/@opencode/cli-linux-x64/-/cli-linux-x64-${version}.tgz";
hash = "sha512-intPGPRrEoZk+5Qq2dzYcq3Wv07vWvQrv56WieurupI9jGJSEAfT4a9Pi6YMW4tEW6SapAds/95K0egVS2YwiA==";
hash = "sha512-QPGrzBSSs5ATG3nM4XjS4tmWXUB22Di36H0vfMuhKwk6i3PR3Nb4sm+l00gvTdcyCRohYsKwqjvD67T0DPcQLQ==";
};

nativeBuildInputs = [autoPatchelfHook];
Expand Down
61 changes: 61 additions & 0 deletions scripts/update-opencode-v2.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
set -euo pipefail

log() { printf '[opencode-v2-update] %s\n' "$*" >&2; }
die() { log "ERROR: $*"; exit 2; }

check_only=false
target=""
while [ "$#" -gt 0 ]; do
case "$1" in
--check) check_only=true; shift ;;
--version)
[ "$#" -ge 2 ] || die '--version requires a version'
target="$2"
[[ "$target" =~ ^2\.[0-9]+\.[0-9]+$ ]] || die 'expected a stable V2 version'
shift 2 ;;
--help)
printf 'Usage: scripts/update-opencode-v2.sh [--check] [--version 2.X.Y]\n'
exit 0 ;;
*) die "unknown option: $1" ;;
esac
done

readonly package='packages/opencode-v2/package.nix'
[ -f flake.nix ] && [ -f "$package" ] || die 'run from opencode-flake root'
for command in curl jq sed; do
command -v "$command" >/dev/null 2>&1 || die "$command is required"
done

current="$(sed -n 's/^ version = "\([^"]*\)";/\1/p' "$package")"
current_hash="$(sed -n 's/^ hash = "\([^"]*\)";/\1/p' "$package")"
[[ "$current" =~ ^2\.[0-9]+\.[0-9]+$ ]] && [ -n "$current_hash" ] || die 'malformed package version/hash'
metadata="$(curl -fsSL "https://registry.npmjs.org/@opencode%2fcli-linux-x64/${target:-latest}")" || die 'npm metadata fetch failed'
validated="$(printf '%s\n' "$metadata" | jq -ser --arg target "$target" '
select(length == 1) | .[0] | select(type == "object")
| select(.name == "@opencode/cli-linux-x64")
| select(.version | type == "string" and test("^2\\.[0-9]+\\.[0-9]+$"))
| select($target == "" or .version == $target)
| select(.dist.integrity | type == "string" and test("^sha512-[A-Za-z0-9+/]{85}[AQgw]==$"))
| select(.dist.tarball == ("https://registry.npmjs.org/@opencode/cli-linux-x64/-/cli-linux-x64-" + .version + ".tgz"))
| [.version, .dist.integrity] | @tsv
')" || die 'malformed npm package metadata'
IFS=$'\t' read -r latest hash <<< "$validated"

log "current: $current"
log "latest: $latest"
if [ "$current" = "$latest" ] && [ "$current_hash" = "$hash" ]; then
log 'already up to date'
exit 0
fi
if [ "$check_only" = true ]; then
log "update available: $current -> $latest"
exit 1
fi

# npm supplies the fixed-output SRI hash; updating never builds or runs Nix.
sed -i \
-e "s|^ version = \"[^\"]*\";| version = \"$latest\";|" \
-e "s|^ hash = \"[^\"]*\";| hash = \"$hash\";|" \
"$package"
log "updated $package"
Loading
Loading