diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6072716..2ac6768 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,17 @@ cut and that this clone does not carry.
## [Unreleased]
+## [0.0.69] - 2026-09-08
+
+Native lifecycle evidence runs every provider command in the same isolated
+HOME/XDG environment. Antigravity is launched against its provider-reported
+documented home, including update, rollback and inactive-version removal.
+The measurement no longer describes its supported launch as undeclared.
+
+Grok Build vendor artifacts are refreshed to 1.0.24 from verified upstream
+platform bytes. Previous artifact pins remain available for rollback.
+The OpenCode provider retains the existing protocol and preservation contract.
+
## [0.0.68] - 2026-09-07
Complete native setup preservation captures user additions, installed plugin
diff --git a/Cargo.lock b/Cargo.lock
index b299e06..9a6e867 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -66,7 +66,7 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "harness-runtime"
-version = "0.0.68"
+version = "0.0.69"
dependencies = [
"provider-v3",
"serde",
@@ -128,7 +128,7 @@ dependencies = [
[[package]]
name = "opencode-setup-system"
-version = "0.0.68"
+version = "0.0.69"
dependencies = [
"harness-runtime",
"provider-v3",
@@ -147,7 +147,7 @@ dependencies = [
[[package]]
name = "provider-v3"
-version = "0.0.68"
+version = "0.0.69"
dependencies = [
"serde",
"serde_json",
@@ -209,7 +209,7 @@ dependencies = [
[[package]]
name = "setup-core"
-version = "0.0.68"
+version = "0.0.69"
dependencies = [
"miniz_oxide",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index 5d7f456..8c6650b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ members = [
]
[workspace.package]
-version = "0.0.68"
+version = "0.0.69"
edition = "2024"
rust-version = "1.89"
license = "AGPL-3.0-or-later"
@@ -23,9 +23,9 @@ sha2 = "0.11"
# `setup-core::archive`); an inflate loop is not, because its bugs are
# memory-safety bugs and it is not improved by being hand-written here.
miniz_oxide = "0.9"
-setup-core = { path = "crates/setup-core", version = "0.0.68" }
-provider-v3 = { path = "crates/provider-v3", version = "0.0.68" }
-harness-runtime = { path = "crates/harness-runtime", version = "0.0.68" }
+setup-core = { path = "crates/setup-core", version = "0.0.69" }
+provider-v3 = { path = "crates/provider-v3", version = "0.0.69" }
+harness-runtime = { path = "crates/harness-runtime", version = "0.0.69" }
[workspace.lints.rust]
unsafe_code = "forbid"
diff --git a/README.md b/README.md
index 03cd142..7165d06 100644
--- a/README.md
+++ b/README.md
@@ -179,7 +179,7 @@ release is a convenience, not the authorised copy.
```bash
docker run --rm -v "$HOME/.config:/config" \
- ghcr.io/nddev-opennetwork/opencode-setup-system:0.0.68 \
+ ghcr.io/nddev-opennetwork/opencode-setup-system:0.0.69 \
status --target /config/
--json
```
diff --git a/install.ps1 b/install.ps1
index 6cc5b74..d69ef89 100644
--- a/install.ps1
+++ b/install.ps1
@@ -7,7 +7,7 @@
# powershell -ExecutionPolicy Bypass -File install.ps1 -Version 0.1.0
[CmdletBinding()]
param(
- [string]$Version = "0.0.68",
+ [string]$Version = "0.0.69",
[string]$InstallDir = "$env:LOCALAPPDATA\Programs\opencode-setup-system"
)
$ErrorActionPreference = "Stop"
diff --git a/install.sh b/install.sh
index bb6aa7c..9822926 100644
--- a/install.sh
+++ b/install.sh
@@ -14,7 +14,7 @@ set -eu
REPO="NDDev-OpenNetwork/opencode-setup-system"
BINARY="opencode-setup-system"
-VERSION="${1:-0.0.68}"
+VERSION="${1:-0.0.69}"
PREFIX="${OPENCODE_INSTALL_DIR:-$HOME/.local/bin}"
case "$(uname -s)" in
diff --git a/scripts/evidence.py b/scripts/evidence.py
index faa1038..b102b49 100644
--- a/scripts/evidence.py
+++ b/scripts/evidence.py
@@ -105,7 +105,7 @@ class NothingToProve(Exception):
def run_text(argv: list[str]) -> str:
"""Run one provider command that answers a person, and return what it said."""
- done = subprocess.run(argv, capture_output=True, text=True)
+ done = subprocess.run(argv, capture_output=True, text=True, timeout=180)
if done.returncode != 0:
raise Failed(f"{argv[1]} exited {done.returncode}: {done.stderr.strip()}")
return done.stdout
@@ -113,7 +113,7 @@ def run_text(argv: list[str]) -> str:
def run_json(argv: list[str]) -> dict:
"""Run one provider command that answers a machine, and parse its envelope."""
- done = subprocess.run(argv, capture_output=True, text=True)
+ done = subprocess.run(argv, capture_output=True, text=True, timeout=180)
try:
answer = json.loads(done.stdout)
except json.JSONDecodeError:
@@ -819,10 +819,24 @@ def software_lifecycle(
# needs Python 3.10, which is a second thing to be right about on three
# runner images. `ignore_errors` on the removal has always been there.
scratch = tempfile.mkdtemp(prefix="evidence-")
+ home_keys = ("HOME", "USERPROFILE", "XDG_CONFIG_HOME", "XDG_DATA_HOME")
+ previous_environment = {key: os.environ.get(key) for key in home_keys}
try:
room = Path(scratch)
+ # Every provider subprocess must agree on the isolated documented home,
+ # including plan/apply and the launch inside rollback/remove checks.
+ isolated = contained(room)
+ os.environ.update({key: isolated[key] for key in home_keys})
target, prefix = room / "target", room / "prefix"
- target.mkdir()
+ if harness == "antigravity":
+ description = run_text([binary])
+ homes = [line.split(":", 1)[1].strip().split()[0]
+ for line in description.splitlines()
+ if "configuration home" in line.lower()]
+ if len(homes) != 1 or not homes[0].startswith("~/"):
+ raise Failed("provider did not report one documented user configuration home")
+ target = Path(isolated["HOME"]) / homes[0][2:]
+ target.mkdir(parents=True)
prefix.mkdir()
print("plan ", end="", flush=True)
@@ -880,7 +894,6 @@ def software_lifecycle(
launches = "launch" in info["supported_commands"]
if not launches:
- # Antigravity, and the refusal is the declaration keeping its word.
print("launch -> not declared, so this build does not start a product")
cross_two_releases(binary, target, prefix, room, info)
remove_the_program(binary, target, prefix, info)
@@ -1025,6 +1038,11 @@ def software_lifecycle(
cross_two_releases(binary, target, prefix, room, info)
remove_the_program(binary, target, prefix, info)
finally:
+ for key, value in previous_environment.items():
+ if value is None:
+ os.environ.pop(key, None)
+ else:
+ os.environ[key] = value
shutil.rmtree(scratch, ignore_errors=True)