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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ powershell -ExecutionPolicy Bypass -File install.ps1
```

Each fetches the release artifact for this platform, checks it against the
release's own `SHA256SUMS`, and places it at a predictable path: `~/.local/bin`
on Linux and macOS, `%LOCALAPPDATA%\Programs` on Windows. Neither needs
release's own `SHA256SUMS`, and places it at a predictable path:
`~/.local/bin/opencode-setup-system` on Linux and macOS,
`%LOCALAPPDATA%\Programs\opencode-setup-system\opencode-setup-system.exe` on Windows. Neither needs
privilege and neither registers anything anywhere.

Somewhere else instead:
Expand Down
7 changes: 4 additions & 3 deletions SUPPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ in a JSON file it owns it strips the keys it added rather than taking the
file. Anything under those paths this build never wrote stays. Emptying every
owned namespace is a separate, explicitly named operation: `reset`.

No credential-free command is measured writing this product's home -- the dated measurement lives in `references/` and the absence is recorded, not assumed. The receipt discipline is the same for whatever arrives later: a file this provider wrote is captured into a slot before the next `install`, withdrawn by `remove`, and returned byte for byte by `restore`.
This product does write its own configuration without credentials -- `mcp add` is one such command -- but the write lands in the real `~/.config/opencode/opencode.json` even when the configuration directory is pointed elsewhere, so the demonstration would touch a person's actual home rather than a target. It is deliberately not run for evidence. The receipt discipline is the same for whatever arrives later: a file this provider wrote is captured into a slot before the next `install`, withdrawn by `remove`, and returned byte for byte by `restore`.

So: point `--target` at a home you are willing to have managed. `backups
--target <dir>` names every earlier state and which setup each preceded, and
Expand Down Expand Up @@ -169,8 +169,9 @@ Configuration home as the product documents it: `~/.config/opencode`.
| `plugins` | `plugin` | [source](https://opencode.ai/docs/plugins) |
| `tui.json` | -- | [source](https://opencode.ai/docs/tui) |

A path routing no component kind is owned so a setup can carry it;
nothing compiles a component to it.
A custody row like `tui.json` routes no component kind because no setup
can ever fill it: it is owned so a backup captures it and `remove`
withdraws it, and so a posture switch does not empty it.

### A second target: `target_scope: user_root`

Expand Down
2 changes: 1 addition & 1 deletion crates/harness-runtime/src/catalog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub struct CatalogComponentRef {
/// per-harness: `minimal` means the same thing to someone moving from codex to
/// pi as it did before they moved.
///
/// A harness may carry more -- cursor and antigravity each ship a builder
/// A harness may carry more -- each of the seven also ships an `nddev-builder`
/// toolkit -- but never fewer.
pub const UNIVERSAL_SETUPS: &[&str] = &["baseline", "full-auto", "minimal"];

Expand Down
4 changes: 2 additions & 2 deletions crates/harness-runtime/src/facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ pub struct Harness {
/// and one that can execute code the chosen setup never carried.
///
/// A fact rather than a conclusion, carrying how it was established, because
/// the five that are complete are not equally well established: three were
/// measured by asking the product what it resolved, one by making it write,
/// the five that are complete are not equally well established: two were
/// measured by asking the product what it resolved, two by making it write,
/// and one rests on a vendor page because no credential-free command of that
/// product writes its home.
pub launch_binding: LaunchBinding,
Expand Down
5 changes: 3 additions & 2 deletions crates/harness-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,9 @@ fn print_help(harness: &Harness) {
println!("something to return to. The pool rolls, so a long series of changes");
println!("eventually evicts the oldest: `hold` keeps one until `release` lets");
println!("it go, which is how a baseline survives more captures than the pool.");
println!("Over the wire, install and replace arrive as a bundle and refuse --");
println!("this build reads setups from its own catalog.");
println!("Over the wire a bundle arrives with install or replace, and this");
println!("build applies it; this human surface refuses bundles and reads");
println!("setups from its own catalog.");
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion crates/harness-runtime/src/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ pub(crate) fn launch(
// from one field here. Guessing produced a refusal that told cursor
// callers *"this build installs no software"* -- false, it installs
// and removes it, and the actual reason is that the product follows
// its variable for one of the eight surfaces this provider owns.
// its variable for one of the seven surfaces this provider owns.
format!(
"{} does not declare launch: {}",
harness.provider_id,
Expand Down
6 changes: 5 additions & 1 deletion crates/harness-runtime/src/surfaces.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,11 @@ fn never_touch_is_disclaimed(harness: &Harness, baseline: &Value, found: &mut Ve
/// other direction: a namespace declared, owned, routing nothing, and written
/// to by nobody.
///
/// Three of them exist: claude's `rules`, opencode's `tui.json`, pi's `themes`.
/// Seventeen of them exist -- claude's `rules`, `workflows`, `output-styles`,
/// `routines` and `themes`; grok's `sandbox.toml`, `workflows`, `rules`,
/// `commands`, `personas` and `roles`; pi's `APPEND_SYSTEM.md`, `SYSTEM.md` and
/// `themes`; antigravity's `antigravity-cli/keybindings.json` and
/// `antigravity-cli/plugins`; opencode's `tui.json`.
/// Each is defensible -- claude's row explains that `instruction` already
/// routes to `CLAUDE.md` and the namespace is owned so a setup *could* carry a
/// rule -- and the point is not to remove them. It is that the answer should be
Expand Down
4 changes: 2 additions & 2 deletions crates/opencode-setup-system/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,6 @@ pub const OPENCODE: Harness = Harness {
pinned binary; the collision was measured on skills",
},
],
// The product's own: credentials and runtime caches. Never read, never
// written, and never copied into a backup slot.
// Owned, and nothing this build can install ever lands here: no
// component kind routes to them and no setup in this catalogue
// carries files there. So a posture selecting itself must not empty
Expand All @@ -131,6 +129,8 @@ pub const OPENCODE: Harness = Harness {
],
excluded: &["auth.json", "cache"],
}],
// The product's own: credentials and runtime caches. Never read, never
// written, and never copied into a backup slot.
never_touch: &["auth.json", "cache"],
// No near neighbour measured for this product. A marker listed here is a
// refusal waiting to happen, so nothing is listed without evidence.
Expand Down
2 changes: 1 addition & 1 deletion references/opencode-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@
"version": "1.18.33",
"verified_at": "2026-09-28T14:27:53+00:00"
},
"setup_catalogue_digest": "sha256:7a5ac4414efe1db02165df91cabb8453b4ed8c94423154f2afdb8c9a870e980d",
"setup_catalogue_digest": "sha256:d0cf7ef9be83eba56d3d496069de6a5eba3e9f19cc7b27690c2c7860f59ef602",
"previous_software_artifacts": {
"command": "opencode",
"shape": "gzip-tar",
Expand Down
2 changes: 1 addition & 1 deletion scripts/evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ def remove_the_program(
# Capture what the *same exposed command* says before removal. Not every
# vendor puts its release into `--version`: Pi's two Windows standalone
# builds both answer `0.0.0`, while their archive digests and provider
# manifests correctly distinguish 0.84.3 from 0.84.4. Requiring the
# manifests correctly distinguish 0.85.1 from 0.87.1. Requiring the
# expected version here tests the vendor's string, not whether removing
# an inactive tree moved our command.
launch_before = run_text(
Expand Down
2 changes: 1 addition & 1 deletion setups/minimal/home/AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# NDDev minimal

Nothing beyond the product's own defaults in the instructions this setup
ships; the autonomous approval and sandbox posture travels in the setup's own
ships; the autonomous permission posture travels in the setup's own
configuration, not here. Useful as a clean state to return to, and as the thing
a restore proves it can reach.
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Stopping at the command's output would have concluded this file is inert under p
| `pi` | `AGENTS.md` | file |

**They are not interchangeable, and the difference is not only the
name.** One of the seven takes a *directory* of rules rather than a
name.** Two of the seven take a *directory* of rules rather than a
single document, so a file moved between the two is not a rename.

**Some products read a neighbour's.** `references/surfaces.md` records
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@ belongs.

**A comment is not a stylistic choice.** In a strict-JSON file a `//` is
a parse error, and the product does not start rather than starting
without your setting. Two of the seven take comments; the rest do not,
and one of those takes them at two spellings of the same file.
without your setting. Three of the seven take comments; the rest do not, and one of those takes them at two spellings of the same file.

## Before you write one

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# The second target this harness owns
# The scoped target this harness owns

## `target_scope: user_root`, rooted at `~/.agents`

**`~/.agents` is not this product's configuration home.** It is a
different target, reached by a consumer naming the scope on the
request, and every path below is relative to that root rather than
to the home -- writing the root into the path again would nest it
twice, which is a mistake this estate has made and shipped.
request, and every path below is relative to that root rather
than to the home -- writing the root into the path again would
nest it twice, which is a mistake this estate has made and
shipped.

| path | routes | decided by | exercised by |
|---|---|---|---|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,11 @@ surfaces makes a consumer's route ambiguous, and the guard in

## A second target: `target_scope: user_root`

Rooted at `~/.agents`, which is **not** this product's configuration
home. A consumer reaches it by naming the scope on the request, and
every path below is relative to that root rather than to the home
above -- writing the root into the path again would nest it twice.
Rooted at `~/.agents`, which is **not** this product's
configuration home. A consumer reaches it by naming the scope
on the request, and every path below is relative to that root
rather than to the home above -- writing the root into the
path again would nest it twice.

| path | routes | shape | decided by | exercised by |
| --- | --- | --- | --- | --- |
Expand Down
Loading