@awebai/oats 0.26.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -47,7 +47,7 @@ A self-contained package has an `oats.json`:
47
47
  "requires": [
48
48
  { "command": "team-chat", "why": "send and receive messages" },
49
49
  {
50
- "runtime": "pi",
50
+ "harness": "pi",
51
51
  "package": "npm:team-chat-pi",
52
52
  "why": "real-time push events in pi sessions"
53
53
  }
@@ -134,15 +134,15 @@ A self-contained package has an `oats.json`:
134
134
  the operator to clean up by hand.
135
135
  - `requires` declares what must exist before the capability works. Two kinds:
136
136
  - a **host command** (`command`), satisfied by a binary on `PATH`;
137
- - a **runtime package** (`runtime` + `package`, optionally `marketplace`),
138
- satisfied by that runtime's own package manager — `npm:@scope/name` for pi,
137
+ - a **harness package** (`harness` + `package`, optionally `marketplace`),
138
+ satisfied by that harness's own package manager — `npm:@scope/name` for pi,
139
139
  `plugin@marketplace` for Claude Code. It is raised only for deployments that use the named
140
- runtime — a Claude-only deployment is never asked to install a pi package —
141
- and is verified in the runtime's package list, never on `PATH`. A version
140
+ harness — a Claude-only deployment is never asked to install a pi package —
141
+ and is verified in the harness's package list, never on `PATH`. A version
142
142
  selector is allowed and ignored for identity, so `@latest` and a pinned
143
143
  version are one requirement.
144
- A runtime package is **verified at spawn, never installed there**: installing
145
- would mutate the operator's runtime configuration without asking, in the
144
+ A harness package is **verified at spawn, never installed there**: installing
145
+ would mutate the operator's harness configuration without asking, in the
146
146
  middle of a spawn. A missing, uninstalled or disabled package fails the spawn
147
147
  with the consent command that fixes it.
148
148
  - OATS never installs a host requirement silently. A missing host command is
@@ -250,7 +250,7 @@ team: [engineering, reviewers]
250
250
  discovery warning (`unmapped-team-label`, one per label naming its souls); a
251
251
  label not in `teams:` at all is the `E_TEAM_UNKNOWN` problem.
252
252
 
253
- ## Exact runtime composition
253
+ ## Exact harness composition
254
254
 
255
255
  Every spawned instance receives:
256
256
 
@@ -334,7 +334,7 @@ existing manifests load, and change nothing.
334
334
  Hooks receive `OATS_EVENT`, `OATS_CAPABILITY`, `OATS_LAYER`, `OATS_INSTANCE`,
335
335
  `OATS_HOME`, `OATS_AGENT`, `OATS_SOUL`, `OATS_CONTEXT`, `OATS_WORKSPACE`,
336
336
  `OATS_ROOT`, `OATS_LEVEL`, `OATS_SETTINGS`, and `OATS_META`. A final JSON line may
337
- return `meta`, `brief`, `warning`, or runtime-specific `launch` arguments. A
337
+ return `meta`, `brief`, `warning`, or harness-specific `launch` arguments. A
338
338
  **spawn hook only** may also return an `env` object for the launched process;
339
339
  returning `env` from retire or soul-scaffold is an explicit contract error.
340
340
 
@@ -378,7 +378,7 @@ boundary — adding a new launch variable requires a visible manifest change
378
378
  `OATS_*`, `PI_AGENT_*`, kernel launch variables, and known shell/bootstrap/loader
379
379
  names are also rejected as defense in depth. The denylist includes current Node,
380
380
  JVM, .NET, Python, Perl, Ruby, Lua, PHP, ELF, and dyld surfaces, but is explicitly
381
- not the authority boundary: runtime bootstrap names are open-ended, so the
381
+ not the authority boundary: harness bootstrap names are open-ended, so the
382
382
  manifest declaration and trust review enforce what an artifact may contribute.
383
383
  Two capabilities claiming the same name is an error even when their values
384
384
  match.
@@ -394,7 +394,7 @@ without a retire hook uses the standard retryable quarantine instead. Ordinary
394
394
  advisory hook execution failure itself contributes no environment.
395
395
 
396
396
  The environment prefix applies to the initial Pi or Claude process. `--no-launch`
397
- validates command preparation but has no runtime consumer. The fallback shell
397
+ validates command preparation but launches no harness. The fallback shell
398
398
  after that process exits does not inherit command-scoped assignments, and OATS
399
399
  has no restart command or replay policy yet. The generated command is persisted
400
400
  as before; hooks must contribute locators, selectors, or broker endpoints—not
@@ -117,17 +117,28 @@
117
117
  "additionalProperties": false
118
118
  },
119
119
  {
120
- "title": "runtime package requirement",
121
- "description": "A package that must be installed into a specific runtime's own package manager. Raised only for deployments using that runtime, satisfied by the runtime's package list rather than by PATH, and installed only with explicit consent.",
120
+ "title": "harness package requirement",
121
+ "description": "A package that must be installed into a specific harness's own package manager. Raised only for deployments using that harness, satisfied by the harness's package list rather than by PATH, and installed only with explicit consent. The harness is named by `harness` (0.27.0) or by `runtime`, its pre-0.27 name that released manifests use: exactly one of the two.",
122
122
  "type": "object",
123
123
  "required": [
124
- "runtime",
125
124
  "package",
126
125
  "why"
127
126
  ],
127
+ "oneOf": [
128
+ { "required": ["harness"], "not": { "required": ["runtime"] } },
129
+ { "required": ["runtime"], "not": { "required": ["harness"] } }
130
+ ],
128
131
  "properties": {
132
+ "harness": {
133
+ "type": "string",
134
+ "enum": [
135
+ "pi",
136
+ "claude"
137
+ ]
138
+ },
129
139
  "runtime": {
130
140
  "type": "string",
141
+ "description": "The pre-0.27 name of `harness`, accepted for released manifests.",
131
142
  "enum": [
132
143
  "pi",
133
144
  "claude"
@@ -135,7 +146,7 @@
135
146
  },
136
147
  "package": {
137
148
  "type": "string",
138
- "description": "Source spec in that runtime's own naming: \"npm:@scope/name\" for pi, \"plugin@marketplace\" for Claude."
149
+ "description": "Source spec in that harness's own naming: \"npm:@scope/name\" for pi, \"plugin@marketplace\" for Claude."
139
150
  },
140
151
  "why": {
141
152
  "type": "string"
@@ -155,7 +166,7 @@
155
166
  },
156
167
  "minVersion": {
157
168
  "type": "string",
158
- "description": "Lowest acceptable installed version of the package, read from the package.json under the install directory the runtime's listing names; an older or absent manifest fails the requirement with the install remedy.",
169
+ "description": "Lowest acceptable installed version of the package, read from the package.json under the install directory the harness's listing names; an older or absent manifest fails the requirement with the install remedy.",
159
170
  "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+"
160
171
  },
161
172
  "ifInstalled": {
@@ -37,7 +37,7 @@ souls: # optional — souls this machine d
37
37
 
38
38
  launch-configs: # optional — named ways this host starts a harness
39
39
  personal:
40
- runtime: claude
40
+ harness: claude
41
41
  executable: "./bin/claude-wrapper.sh" # relative → against this deployment directory
42
42
  args: ["--verbose"]
43
43
  env:
@@ -55,7 +55,7 @@ refused (`E_WORKSPACE_SCHEMA`).
55
55
  | `clones` | `<canonical repo key>: <absolute path>` — where a member's clone lives when it is not at `<deployment>/<member name>/`. Only a soul's **work target** (`work: worktree \| checkout`) needs a clone. Lookup order: `spawn --repo`, then this map (keys normalised through `parseRepoRef`, so any ref spelling of the same repo matches), then `<deployment>/<member name>` (a member named `agents` → `<deployment>/agents-repo`, since `agents/` is the instance root); none → `E_CLONE_MISSING`; a directory whose `origin` is another repo → `E_CLONE_MISMATCH`. |
56
56
  | `settings.<cap>.<key>` | Host-owned provider values the capability's manifest asks for — absolute paths, state roots, delivery modes. The workspace file **refuses** absolute paths; this is where they go. Merged into the capability's provider payload after the soul's own payload and before any `--provider` flag (see [three homes](workspaces.md#provider-payloads-have-three-homes)). |
57
57
  | `souls.disabled` | Soul names not run on this machine; reported by `oats sync` ("disabled here"). |
58
- | `launch-configs.<name>` | A named way to start a harness on this host (0.26.0; lead decision 2 — a spawn-time host choice, never a soul field): `runtime` (`pi` \| `claude` \| `codex`, required), `executable` (a bare name looked up on `PATH`, or a path — relative to this deployment directory), `args` (literal, no shell), `env` (a literal string, non-secret by contract and always redacted, or `{ fromEnv: NAME }` resolved on the host at start), `model`, `yolo`. Selected with `--launch-config <name>` on `oats spawn` and `oats session start \| restart`; explicit flags override its fields. Written by `oats launch-config set <name> --file <json>` / `remove <name>`, which rewrite only this block. Earlier kernels read `launch-configs:` from a scope's `oats-config.yaml`; 0.26.0 refuses it there with a message naming this move. |
58
+ | `launch-configs.<name>` | A named way to start a harness on this host (0.26.0; lead decision 2 — a spawn-time host choice, never a soul field): `harness` (`pi` \| `claude` \| `codex`, required; named `runtime` before 0.27.0, which is still read with a `deprecated-runtime-name` warning), `executable` (a bare name looked up on `PATH`, or a path — relative to this deployment directory), `args` (literal, no shell), `env` (a literal string, non-secret by contract and always redacted, or `{ fromEnv: NAME }` resolved on the host at start), `model`, `yolo`. Selected with `--launch-config <name>` on `oats spawn` and `oats session start \| restart`; explicit flags override its fields. Written by `oats launch-config set <name> --file <json>` / `remove <name>`, which rewrite only this block. Earlier kernels read `launch-configs:` from a scope's `oats-config.yaml`; 0.26.0 refuses it there with a message naming this move. |
59
59
 
60
60
  ## Where it sits and how it is found
61
61
 
@@ -48,8 +48,8 @@ Pi with ambient skill and context discovery disabled and the one instance path
48
48
  explicit; that exclusion is gone.)* Claude runs provider-native: it reads the
49
49
  instance's `.claude/skills` and `CLAUDE.md` symlinks, and the operator's own
50
50
  user and project configuration — skills, plugins, settings — stays in effect.
51
- Neither runtime gets a redirected config home.
52
- `composition.materialized.runtimePosture` in `instance.json` records what each
51
+ Neither harness gets a redirected config home.
52
+ `composition.materialized.harnessPosture` in `instance.json` records what each
53
53
  instance actually exposes. `oats-getting-started` is the sole pre-workspace
54
54
  ambient bootstrap.
55
55
 
@@ -28,7 +28,7 @@ refusals below remain authoritative.
28
28
 
29
29
  Optional features are negotiated from the probe's `features` array. Starting
30
30
  an existing home requires `session-start`; named launch configurations and
31
- runtime/permission overrides require `launch-config`; restarting a running
31
+ harness/permission overrides require `launch-config`; restarting a running
32
32
  home also requires `session-restart`. Desktop checks the corresponding
33
33
  `remote` entries before offering these operations for a server. The router
34
34
  then probes the execution host before sending a mutation. An absent feature
@@ -48,6 +48,63 @@ no progress prose (progress goes to stderr):
48
48
  - success (exit 0): `{"schemaVersion":1,"ok":true,"result":{...}}`
49
49
  - failure (nonzero exit): `{"schemaVersion":1,"ok":false,"error":{"code":"...","message":"..."}}`
50
50
 
51
+ Either may also carry `"warnings":[…]` (0.27.0+), present only when there is
52
+ something to say. The one warning so far is `deprecated-runtime-name` (see
53
+ [the harness rename](#the-harness-rename-feature-harness-oats-0270)). `oats status
54
+ --json`, whose document is not an envelope, carries the same `warnings` beside
55
+ its `problems`. In text mode the warning is one `oats: warning: …` line on stderr.
56
+
57
+ ## The harness rename (feature `harness`, OATS 0.27.0)
58
+
59
+ What starts an instance (pi, claude or codex) is its **harness**. 0.27.0 renames
60
+ the kernel's `runtime` to `harness` everywhere it means that:
61
+
62
+ - Outputs speak only the new names.
63
+ - Every input written before 0.27.0 still works. The rule is read either,
64
+ write new: the old spelling is read as the new one, and the next write
65
+ records the new one.
66
+ - A command that read an old spelling answers **one** warning:
67
+ `{"code":"deprecated-runtime-name","key":"runtime","replacement":"harness","sources":[…],"message":"…"}`.
68
+ `sources` names each place it read the old spelling (a flag, a file and its
69
+ key, a home).
70
+ - A pair that disagrees is refused rather than guessed, for example
71
+ `--harness pi --runtime claude`, or both keys with different values.
72
+ - A later release drops the old spellings.
73
+
74
+ Gate on the feature `harness`. A kernel without it speaks the old names: the
75
+ routed commands (`--server`) already translate for such a host, sending
76
+ `--runtime` and `runtime` keys to it and reading its `runtimes` list.
77
+
78
+ | Surface | Before 0.27.0 | 0.27.0 | Old spelling still accepted? |
79
+ |---|---|---|---|
80
+ | `oats version --json` | `runtimes: [pi, claude, codex]` | `harnesses: [...]`, feature `harness` | **Dropped**: no `runtimes` alias; gate on the feature |
81
+ | Flag on `spawn` (and `--preview`), `session start`/`restart`, `launch-config preview`, and their `--server` forms | `--runtime <h>` | `--harness <h>` | Yes, with the warning (the okf 2.1.5 harvest worker passes `--runtime` to spawn). Both flags disagreeing → `E_BAD_ARGS` |
82
+ | `oats status --json`: `agents[]` rows (soul default) and `agents[].instances[]` rows | `runtime` | `harness` | Output only |
83
+ | `oats status --json`: instance rows' `composition.materialized` | `runtimePackages`, `runtimePosture` | `harnessPackages`, `harnessPosture` | Output only |
84
+ | `oats inspect --json` `souls[]` rows, remote roster rows | `runtime` | `harness` | Output only; a pre-0.27 host's `runtime` rows are read as `harness` |
85
+ | `oats inspect --home --json` `instance` | `runtime` | `harness` | Output only |
86
+ | The launch plan's package check (`launch-config preview` `problems[]`) | `runtime-packages` | `harness-packages` | Output only |
87
+ | Soul `soul.yaml` (member souls and capability-defined agents) | `runtime:` | `harness:` | Yes, **without** a warning: released capabilities (oats.aweb 1.13.1) ship `runtime:`, and the operator cannot fix a provider's file. Both, disagreeing → `E_BAD_MANIFEST` |
88
+ | `oats-local.yaml` `launch-configs.<name>` | `runtime:` | `harness:` | Yes, with the warning. Both, disagreeing → `E_WORKSPACE_SCHEMA`. `launch-config set` writes `harness` (a `runtime` in its `--file` definition too) |
89
+ | `launch-config list/preview --json` rows and `selection` | `runtime` | `harness` | Output only |
90
+ | Home `instance.json` | `runtime`; launch recipe `launch` version 1 `{runtime}` | `harness`; recipe version **2** `{harness}` | Yes, with the warning naming the home: a 0.26.0 home inspects, starts, restarts and retires; its next start or restart records the new names |
91
+ | `oats spawn … --preview` decision | `effective.runtime` | `effective.harness` | Output only. The revision digests the key names, so a decision previewed by a 0.26 kernel is `E_DECISION_STALE` at apply (with the fresh decision) |
92
+ | `spawn`/`session` results, `spawned` event `data` | `runtime` | `harness` | Output only |
93
+ | Schedule definitions (`schedule add/update --spec-json`, stored jobs, `schedule list`) | `runtime` | `harness` | Yes, with the warning. A stored job is read in the new name and saved in it next time. Both, disagreeing → `E_SCHEDULE_INVALID` (a stored job: invalid on its own) |
94
+ | Schedule run records (`lastRun`, `recentRuns`) | `startedRuntime` | `startedHarness` | Output; a 0.26.0 record's `startedRuntime` is still read |
95
+ | Error codes | `E_UNSUPPORTED_RUNTIME`, `E_RUNTIME_PACKAGE`, `E_RUNTIME_RESOURCE_MISSING` | `E_UNSUPPORTED_HARNESS`, `E_HARNESS_PACKAGE`, `E_HARNESS_RESOURCE_MISSING` | Output only |
96
+ | Hook environment (spawn and launch hooks) | `OATS_RUNTIME`, `OATS_PREVIOUS_RUNTIME` | `OATS_HARNESS`, `OATS_PREVIOUS_HARNESS` | Both are set, with the same values, for released hooks |
97
+ | Capability manifest `requires[]` harness package | `runtime` | `harness` | Yes, **without** a warning (a provider's file); a row naming both is refused |
98
+ | Package verification `loadedBy` | `runtime-discovery` | `harness-discovery` | Output only |
99
+
100
+ Unchanged, because they do not name the harness: the session endpoint
101
+ vocabulary (`runtimeAuthority`, `runtimeState`/`runtimeError` in liveness,
102
+ `E_RUNTIME_ENDPOINT_UNKNOWN`, `E_RUNTIME_AUTHORITY_MISMATCH`,
103
+ `E_RUNTIME_QUIESCE_FAILED`); `capabilityRuntime`; the retirement baseline's
104
+ `runtime`; oats.okf's `harvest-runtime` setting; and the kernel's
105
+ "runtime-neutral" design. Hook stdin carries no `launch.runtime` (no 0.26 hook
106
+ emitter wrote it).
107
+
51
108
  ## Inspect, readiness and operation run on the workspace model (`operationsApi: 2`, `soulsApi: 2`, `readinessApi: 2`, OATS 0.26.0)
52
109
 
53
110
  On a workspace deployment (an `oats-local.yaml` in reach of `--dir`), and for
@@ -121,7 +178,7 @@ payload the spawn recorded for a home, or the resolution computes for a soul.
121
178
  "subject":{"kind":"instance","instance":"release-manager-x","home":"/w/agents/release-manager/instances/release-manager-x","soul":"release-manager"},
122
179
  "workspace":{"key":"github.com/northwind/agents","name":null,"deployment":"/w","commit":"461b9c24…","standalone":false},
123
180
  "souls":[{"soulsApi":2,"name":"release-manager","repoKey":"github.com/northwind/agents","commit":"461b9c24…","team":"engineering",
124
- "kind":null,"path":null,"description":"Cuts, verifies and announces platform releases.","work":"worktree","runtime":null,"model":null,
181
+ "kind":null,"path":null,"description":"Cuts, verifies and announces platform releases.","work":"worktree","harness":null,"model":null,
125
182
  "declarations":{"requires":null,"defaults":null,"knowledge":{"owns":"release-manager","reads":["platform-engineer"]},"teams":null,"resources":null,"children":null,
126
183
  "capabilities":{"nw-release-tooling":{"from":"here"},"nw-deploy":{"from":"package"}}},
127
184
  "declarationProblems":[],
@@ -138,7 +195,7 @@ payload the spawn recorded for a home, or the resolution computes for a soul.
138
195
  "operations":[{"name":"inspect","kind":"view","command":"inspect","context":"home","description":"…","args":[],"argv":["okf","inspect"],"available":true,"reason":null}]}],
139
196
  "knowledge":{"provider":"oats.okf","version":"2.1.3","operations":[{"name":"inspect","kind":"view","available":true,"reason":null}]},
140
197
  "instance":{"home":"/w/agents/release-manager/instances/release-manager-x","instance":"release-manager-x","agent":"release-manager",
141
- "runtime":"pi","model":null,"yolo":null,"launched":false,"createdAt":"<iso>","resolution":"7217670b…",
198
+ "harness":"pi","model":null,"yolo":null,"launched":false,"createdAt":"<iso>","resolution":"7217670b…",
142
199
  "soulDir":"/w/agents/release-manager/souls/461b9c24929c",
143
200
  "instructions":{"file":"/w/agents/release-manager/instances/release-manager-x/AGENTS.md","text":"…","truncated":false,
144
201
  "sources":[{"source":"kernel:instance-boundary","file":"…"},{"source":"capability:oats.okf","file":"…/.oats/modules/oats.okf/injects/okf.md"}]}},
@@ -471,7 +528,7 @@ home's removal, so a retired instance's `retired` event is still readable).
471
528
 
472
529
  ```json
473
530
  {"eventsApi":1,"instance":"dev-1","home":"/abs/home","count":7,"returned":7,"truncated":false,
474
- "events":[{"eventsApi":1,"at":"<iso>","instance":"dev-1","home":"/abs/home","producer":"kernel","kind":"spawned","data":{"agent":"dev","work":"worktree","branch":"agents/dev-1","runtime":"claude","model":null,"parentInstance":null,"relation":null,"launched":true}},
531
+ "events":[{"eventsApi":1,"at":"<iso>","instance":"dev-1","home":"/abs/home","producer":"kernel","kind":"spawned","data":{"agent":"dev","work":"worktree","branch":"agents/dev-1","harness":"claude","model":null,"parentInstance":null,"relation":null,"launched":true}},
475
532
  {"…":"launched | restarted | stopped | stop-refused | retire-planned | worktree-retained | worktree-removed | branch-deleted | retired | child-spawn-refused"}],
476
533
  "lastEvent":{"kind":"stopped","at":"<iso>","producer":"kernel"},
477
534
  "waitingOnYou":null,
@@ -628,12 +685,12 @@ unchecked, echoed a stored `definition.id` without checking it, and named a
628
685
  The Spawn modal's fields are backed by the kernel's own decision, taken **before
629
686
  any side effect**: `oats spawn <agent> [same flags as a real spawn] --preview --json`
630
687
  runs every preflight a spawn runs (placement, composition, resources,
631
- executable, runtime packages, child-spawn policy) and returns what the spawn
688
+ executable, harness packages, child-spawn policy) and returns what the spawn
632
689
  *would* do — then returns without creating a home, branch or worktree.
633
690
 
634
691
  ```json
635
692
  {"spawnPreviewApi":1,"preview":true,"agent":"dev","kind":"persistent","instance":"dev-fix-login","home":"/abs/agents/dev/instances/dev-fix-login",
636
- "repo":"/abs/repo","work":"worktree","runtime":"claude","model":"opus","modelSource":"explicit","launchConfig":null,"yolo":false,"backend":"tmux",
693
+ "repo":"/abs/repo","work":"worktree","harness":"claude","model":"opus","modelSource":"explicit","launchConfig":null,"yolo":false,"backend":"tmux",
637
694
  "branch":"agents/dev-fix-login","base":{"ref":"HEAD","oid":"<oid>"},"worktree":"/abs/agents/dev/instances/dev-fix-login/work",
638
695
  "relation":null,"parentInstance":null,"policy":{"childSpawns":{"allowed":true,"origin":{"kind":"default","detail":"…"}}},
639
696
  "executable":"/abs/bin/claude","capabilities":["oats.core"],"skills":["oats-operate","oats-souls"],"task":"…"}
@@ -650,7 +707,7 @@ executable, runtime packages, child-spawn policy) and returns what the spawn
650
707
  the worktree **from that exact oid**.
651
708
  - **Model**: `model`/`modelSource` are the resolved selection. Omitting
652
709
  `--model` **inherits** the launch configuration's or soul's preference;
653
- `--model @native-default` is the explicit "use the runtime's own default"
710
+ `--model @native-default` is the explicit "use the harness's own default"
654
711
  (`modelSource: "native default (explicit)"`). These are different requests
655
712
  and the UI must not relabel one as the other.
656
713
  - **Policy**: `policy.childSpawns` is what this instance will record (soul
@@ -722,11 +779,11 @@ the pre-fix marker and is never accepted for dispatch.
722
779
  - **Bounded preflight**: every native probe a preview runs (`pi --list-models`,
723
780
  `pi list`, `claude plugin list`) shares ONE budget (20 s default), runs in its
724
781
  own process group and is group-killed on timeout; `preflight {status:
725
- complete|timeout, budgetMs, elapsedMs}` says which. A hanging runtime cannot
782
+ complete|timeout, budgetMs, elapsedMs}` says which. A hanging harness CLI cannot
726
783
  hang a preview.
727
784
  - **Confirmed apply contract** (0.24.10+, feature `spawn-apply-2`,
728
785
  `spawnApplyApi: 1`) — what a GUI may promise at "Confirm spawn":
729
- - `decision` gains **`effective {repo, work, runtime, model, launchConfig,
786
+ - `decision` gains **`effective {repo, work, harness, model, launchConfig,
730
787
  yolo, backend, childSpawns, relation{kind, anchor{instance, agentsRoot}}}`**
731
788
  and `revision` hashes placement + effective. An inherited default that would
732
789
  change what launches (the soul's model edited between preview and apply,
@@ -1342,7 +1399,7 @@ are described in [the operations contract](design/operations-contract.md).
1342
1399
 
1343
1400
  ```text
1344
1401
  oats session start --home /absolute/home [--server id] \
1345
- [--launch-config name] [--runtime pi|claude|codex] \
1402
+ [--launch-config name] [--harness pi|claude|codex] \
1346
1403
  [--model id] [--yolo|--no-yolo] --json
1347
1404
  oats session restart --home /absolute/home [the same options] --json
1348
1405
  ```
@@ -1366,7 +1423,7 @@ oats launch-config list [--dir /scope | --home /home | --soul name --agents-root
1366
1423
  oats launch-config set name --file /private/definition.json [--keep-env] --dir /scope --json
1367
1424
  oats launch-config remove name --dir /scope --json
1368
1425
  oats launch-config preview (--home /home | --soul name --agents-root /scope/agents --dir /scope) \
1369
- [--launch-config name] [--runtime runtime] [--model id] [--yolo|--no-yolo] --json
1426
+ [--launch-config name] [--harness harness] [--model id] [--yolo|--no-yolo] --json
1370
1427
  ```
1371
1428
 
1372
1429
  All accept `--server id`. Scope edits follow the registration; inspection and
@@ -1375,7 +1432,7 @@ is serialized to SSH stdin and read on the host with `--file -`; the local
1375
1432
  filename is never passed to the server as though it existed there.
1376
1433
 
1377
1434
  The list result supplies `context`, `selected` and `configurations`. Each
1378
- configuration has a name, runtime, executable, literal argument array,
1435
+ configuration has a name, harness, executable, literal argument array,
1379
1436
  environment, model, permission choice and declaring `source`. Environment
1380
1437
  literals appear as `{ "redacted": true }`; references appear as
1381
1438
  `{ "fromEnv": "VARIABLE_NAME" }`. Optional executable/model/yolo fields can be
@@ -1410,7 +1467,7 @@ See [launch configuration syntax](configuration.md) and
1410
1467
  | `warnings` | string[] | non-fatal warnings (always an array) |
1411
1468
  | `tmux` | {session,window} \| null | tmux target |
1412
1469
 
1413
- Additional informative fields: `repo`, `runtime`, `model`, `parent`,
1470
+ Additional informative fields: `repo`, `harness`, `model`, `parent`,
1414
1471
  `sibling` (explicit sibling cluster link when a root-level sibling relation
1415
1472
  was declared, else null), `relation` (`child`/`sibling`/`parent` when a
1416
1473
  relation was declared at spawn, else null), `spawnOrigin`, `attach`.
@@ -9,7 +9,7 @@ The Desktop roster is the place to return to it:
9
9
  - The hierarchy's action popover offers **Start…** for a stopped instance.
10
10
  - An unknown status is shown as unknown, not as permission to launch another process.
11
11
 
12
- The Start/Restart dialog names the existing instance, runtime and host. Choose
12
+ The Start/Restart dialog names the existing instance, harness and host. Choose
13
13
  a named launch configuration or keep the recorded launch. Without a selected
14
14
  configuration, the harness can also be changed directly. A named configuration
15
15
  fixes its harness; model and permission choices can override its defaults.
@@ -53,7 +53,7 @@ Use the server's workspace to manage configurations defined on that server.
53
53
  Desktop sends `POST /api/start/<instance>?ws=…&home=…` (and `server=…` for a
54
54
  remote instance). The backend resolves that exact roster identity and calls
55
55
  `oats session start --home <absolute-home> [--server <id>] [--model <model>] --json`.
56
- Launch choices add `--launch-config`, `--runtime` or `--yolo`/`--no-yolo`.
56
+ Launch choices add `--launch-config`, `--harness` or `--yolo`/`--no-yolo`.
57
57
  Restart uses `POST /api/restart/<instance>?ws=…&home=…` and the single kernel
58
58
  command `oats session restart` with the same selectors and choices.
59
59
  Configuration inspection and editing use `POST /api/launch-configs?ws=…`,
@@ -103,7 +103,7 @@ oats spawn backend-expert --purpose first-fix --task "Fix one small issue, run t
103
103
  oats status
104
104
  ```
105
105
 
106
- `--runtime pi|claude|codex` picks the harness; complete any native folder
106
+ `--harness pi|claude|codex` picks the harness; complete any native folder
107
107
  trust or authentication prompt in the printed session. The instance home is
108
108
  `agents/<soul>/instances/<instance>/`; `work/` is its repository view;
109
109
  `.oats/modules/<cap>/` and `.agents/skills/<cap>/` are the copied capabilities;
@@ -162,7 +162,7 @@ instance homed inside a repository with its own `.claude/skills` sees those
162
162
  too. Project *settings* — hooks, plugins, permissions, custom agents — resolve
163
163
  from the instance home rather than from ancestors.
164
164
 
165
- Codex is available with `--runtime codex`. It starts in the instance home,
165
+ Codex is available with `--harness codex`. It starts in the instance home,
166
166
  reads `AGENTS.md` and `.agents/skills` natively, and receives `TASK.md` as its
167
167
  initial prompt. User configuration, approval policy, ancestor instructions and
168
168
  ambient skill sources remain native. Worktrees are already below the instance
@@ -175,8 +175,8 @@ The Desktop model field accepts a native id without using Pi's model catalog.
175
175
  This launch support does not supply an aweb channel for Codex: agents can use
176
176
  `aw` from their home, with automatic wake delivery tracked separately.
177
177
 
178
- All runtimes record what they actually expose in `instance.json` under
179
- `composition.materialized.runtimePosture`: the OATS-composed set, what is
178
+ All harnesses record what they actually expose in `instance.json` under
179
+ `composition.materialized.harnessPosture`: the OATS-composed set, what is
180
180
  curtailed, and what remains ambient. The deviation from strict composition is
181
181
  auditable rather than implied.
182
182
 
@@ -42,10 +42,11 @@
42
42
  "additionalProperties": {
43
43
  "type": "object",
44
44
  "additionalProperties": false,
45
- "required": ["runtime"],
45
+ "anyOf": [{ "required": ["harness"] }, { "required": ["runtime"] }],
46
46
  "properties": {
47
- "runtime": { "type": "string", "enum": ["pi", "claude", "codex"], "description": "The harness family this configuration starts; it decides the kernel's own baseline arguments (skills, instructions, task) and how model/yolo are expressed." },
48
- "executable": { "type": "string", "minLength": 1, "description": "The program to run instead of the runtime's default binary: a bare name is looked up on PATH on the execution host; a path containing a slash is resolved against the deployment directory (where this oats-local.yaml lives) when relative. Checked to exist and be executable before any start; never executed just to probe it." },
47
+ "harness": { "type": "string", "enum": ["pi", "claude", "codex"], "description": "The harness this configuration starts (named `runtime` before 0.27.0); it decides the kernel's own baseline arguments (skills, instructions, task) and how model/yolo are expressed." },
48
+ "runtime": { "type": "string", "enum": ["pi", "claude", "codex"], "deprecated": true, "description": "The pre-0.27 name of `harness`, still read (a deprecation warning); both, disagreeing, are refused. `oats launch-config set` writes `harness`." },
49
+ "executable": { "type": "string", "minLength": 1, "description": "The program to run instead of the harness's default binary: a bare name is looked up on PATH on the execution host; a path containing a slash is resolved against the deployment directory (where this oats-local.yaml lives) when relative. Checked to exist and be executable before any start; never executed just to probe it." },
49
50
  "args": { "type": "array", "items": { "type": "string" }, "description": "Extra arguments, each passed literally (no shell interpretation): native configuration files or profiles go here as ordinary arguments." },
50
51
  "env": {
51
52
  "type": "object",
@@ -58,7 +59,7 @@
58
59
  },
59
60
  "description": "Environment for the harness. A string is a literal (non-secret by contract; still redacted in every answer). {fromEnv: NAME} is resolved from the execution host's environment at start time; the reference, never the value, is recorded. A missing reference refuses the start before anything stops."
60
61
  },
61
- "model": { "type": "string", "minLength": 1, "description": "Model for this configuration's runtime; overrides the soul default when this configuration is selected." },
62
+ "model": { "type": "string", "minLength": 1, "description": "Model for this configuration's harness; overrides the soul default when this configuration is selected." },
62
63
  "yolo": { "type": "boolean", "description": "Permission bypass for this configuration; overrides scope and soul defaults when selected." }
63
64
  }
64
65
  }
package/docs/packages.md CHANGED
@@ -74,7 +74,7 @@ members:
74
74
  packages:
75
75
  oats.framework: v1.1.3
76
76
  oats.okf: v2.1.5
77
- oats.aweb: v1.13.1
77
+ oats.aweb: v1.14.2
78
78
  teams:
79
79
  global: { description: Org-wide }
80
80
  engineering: { description: Platform }
@@ -286,5 +286,5 @@ says so.
286
286
  replacement (`details.removed` / `details.replacement` in `--json`). There is
287
287
  no installed-capability directory, no config template adoption, no host
288
288
  requirement installer. A manifest's `requires` still describes what must exist
289
- on the host (runtime packages are verified at spawn; host commands are the
289
+ on the host (harness packages are verified at spawn; host commands are the
290
290
  operator's to install).
@@ -0,0 +1,100 @@
1
+ # OATS 0.27.0
2
+
3
+ What starts an instance (pi, claude or codex) is now called its **harness**
4
+ everywhere the kernel used to say `runtime`: in flags, configuration, records,
5
+ JSON and error codes. Nothing already written stops working. The rule is read
6
+ either, write new: every spelling from before 0.27.0 is still read, and the
7
+ next write records the new one. A command that reads an old spelling answers
8
+ one `deprecated-runtime-name` warning, and a later release drops the old
9
+ spellings. See [Upgrading from 0.26](#upgrading-from-026).
10
+
11
+ ## Changed
12
+
13
+ - **runtime → harness.** [The Desktop CLI API](../desktop-cli-api.md#the-harness-rename-feature-harness-oats-0270)
14
+ has the complete table. In short:
15
+ - Flags: `--harness` replaces `--runtime` on `spawn` (and `--preview`),
16
+ `session start|restart` and `launch-config preview`, including their
17
+ `--server` forms. `--runtime` is still accepted, with the warning. The
18
+ two given with different values are refused (`E_BAD_ARGS`).
19
+ - `oats-local.yaml`: a launch configuration names `harness:`. `runtime:` is
20
+ still read, with the warning. Both, disagreeing, is a schema error
21
+ (`E_WORKSPACE_SCHEMA`). `oats launch-config set` writes `harness`.
22
+ - Schedules: a spawn job names `harness`. A job stored with `runtime` is
23
+ read as `harness`, with the warning, and saved in the new name next time.
24
+ Both, disagreeing, makes that job invalid (`E_SCHEDULE_INVALID`).
25
+ `lastRun`/`recentRuns` record `startedHarness` (was `startedRuntime`).
26
+ - Homes: `instance.json` records `harness`, and the launch recipe is version
27
+ **2** (`harness`; version 1 said `runtime`). A home spawned by 0.26.0 is
28
+ read in the new names, with the warning naming the home. It inspects,
29
+ starts, restarts and retires as before, and its next start or restart
30
+ records the new names.
31
+ - `soul.yaml` names `harness:`. `runtime:` is still read, **without** a
32
+ warning, because released capabilities ship it (oats.aweb 1.13.1's agents)
33
+ and an operator cannot fix a provider's file. Both, disagreeing:
34
+ `E_BAD_MANIFEST`. The same holds for a capability manifest's
35
+ `requires[]` harness package (`harness`, or `runtime`, never both).
36
+ - Hooks get `OATS_HARNESS` and `OATS_PREVIOUS_HARNESS` beside
37
+ `OATS_RUNTIME` and `OATS_PREVIOUS_RUNTIME`, with the same values.
38
+ - JSON outputs speak only the new names:
39
+ - `oats version --json` lists `harnesses` (no `runtimes`) and the feature
40
+ `harness`;
41
+ - `harness` replaces `runtime` in the status, inspect, spawn, preview,
42
+ session, launch-config, schedule and event documents;
43
+ - `harnessPackages` and `harnessPosture` replace `runtimePackages` and
44
+ `runtimePosture` in `composition.materialized`;
45
+ - the launch plan's package check (`launch-config preview` `problems[]`)
46
+ is `harness-packages`, and package verification's `loadedBy` is
47
+ `harness-discovery`.
48
+ - Error codes: `E_UNSUPPORTED_HARNESS`, `E_HARNESS_PACKAGE` and
49
+ `E_HARNESS_RESOURCE_MISSING` replace `E_UNSUPPORTED_RUNTIME`,
50
+ `E_RUNTIME_PACKAGE` and `E_RUNTIME_RESOURCE_MISSING`.
51
+ - JSON envelopes may carry `warnings: [...]`, only when there is something
52
+ to say. `oats status --json` carries it beside `problems`. In text mode,
53
+ a warning is one `oats: warning: …` line on stderr.
54
+ - Routed commands (`--server`) speak each host's own vocabulary. A host
55
+ without the `harness` feature (0.26.x and older) is sent `--runtime` and
56
+ `runtime` keys, and its `runtimes` list and `runtime` roster rows are read
57
+ as harnesses.
58
+ - A spawn decision previewed by a 0.26 kernel is `E_DECISION_STALE` at a
59
+ 0.27 apply (the revision covers the key names), with the fresh decision
60
+ attached.
61
+ - Unchanged, because they do not name the harness: the session endpoint's
62
+ `runtimeAuthority`, `runtimeState` and `runtimeError`, the
63
+ `E_RUNTIME_ENDPOINT_*`, `E_RUNTIME_AUTHORITY_MISMATCH` and
64
+ `E_RUNTIME_QUIESCE_FAILED` codes, `capabilityRuntime`, the retirement
65
+ baseline, and oats.okf's `harvest-runtime` setting.
66
+
67
+ ## Desktop
68
+
69
+ - **The Desktop speaks the harness names** on a kernel that declares feature
70
+ `harness` (`--harness`, `harness`/`harnesses`), and the old names on released
71
+ 0.25.8–0.26.x kernels. It reads either spelling everywhere, shows only the
72
+ harness the kernel reports (no `pi` default for an instance that doesn't say),
73
+ and accepts kernels `>=0.25.8 <0.28.0`.
74
+ - **Core capabilities name their origin** (soul, workspace or team) on the soul
75
+ page and the instance sidebar, on a kernel with feature `layers-from`.
76
+ - The unused "the workspace's team settings" origin label is gone (0.26.0's
77
+ teams amendment K stopped emitting it).
78
+
79
+ ## Fixed
80
+
81
+ - **0.26.0's notes said per-workspace personal teams need undeployed aweb
82
+ server support.** The aweb service (0.8.13 and later) and CLI (1.36.8 and
83
+ later) already carry personal enrollment; what remains is oats.aweb 1.15
84
+ adopting it. The bundled oats.aweb is still 1.13.1 (the personal team only);
85
+ the team verbs come with oats.aweb 1.14.1 in a 0.27.x patch.
86
+
87
+ ## Upgrading from 0.26
88
+
89
+ Nothing to do. Old spellings keep working with a `deprecated-runtime-name`
90
+ warning, and a later release drops them. To stop the warning:
91
+
92
+ - write `harness:` for `runtime:` in `oats-local.yaml` launch configurations
93
+ (or re-save them with `oats launch-config set`);
94
+ - pass `--harness` for `--runtime` in scripts;
95
+ - re-save any schedule the warning names (`oats schedule update`). A stored
96
+ job is also rewritten on its next save.
97
+
98
+ A 0.26.0 home stops warning after its next start or restart. A Desktop that
99
+ gates on the `harness` feature reads the new names; an older Desktop reads
100
+ `runtimes` and `runtime` and needs its update.
@@ -0,0 +1,29 @@
1
+ # OATS 0.27.1
2
+
3
+ ## Changed
4
+
5
+ - **oats.aweb 1.14.2 is bundled and pinned** (was 1.13.1): **joining teams
6
+ beyond the personal one now works.** An instance joins the eligible teams it
7
+ is given at spawn (`--provider oats.aweb join=<labels>`) or later with
8
+ `oats aweb join <labels>` / the Desktop's Teams controls, and leaves with
9
+ `oats aweb leave <labels>`. Each joined team gets its own identity under the
10
+ instance home. Joined teams **poll** (their mail is read between tasks); live
11
+ receive for joined teams is planned for oats.aweb 1.15. Joining needs
12
+ **aw 1.36.12 or later** (older aw answers `E_TEAM_AW_FLOOR`; the primary
13
+ identity still mints). A leave removes the local identity only after aweb
14
+ confirms the membership is released, so a failed leave can be retried.
15
+ Retire leaves every joined team before releasing the primary identity.
16
+ - The framework workspace (`oats-workspace.yaml`) pins oats.aweb v1.14.2.
17
+ - Skip oats.aweb 1.14.0 and 1.14.1 (tagged, never pinned by an OATS release): their binding check answered a `teams` key that OATS rejects, so `oats readiness` showed messaging unknown; 1.14.2 fixes it, and 1.14.0's joins were refused by aw.
18
+
19
+ ## Known limitations
20
+
21
+ - The bundled oats.okf is still 2.1.5: its harvest worker spawns with `--runtime`, so each harvest answers one `deprecated-runtime-name` warning on 0.27.x. It's harmless; oats.okf 2.1.6 passes `--harness`.
22
+
23
+ - A per-workspace personal team still needs oats.aweb 1.15 (the aweb service
24
+ and CLI already carry personal enrollment); until then "personal" is the
25
+ person's active aweb team.
26
+
27
+ ## Upgrading from 0.27.0
28
+
29
+ - To join teams: upgrade aw to 1.36.12 or later (and restart the host's wake daemon), then `oats sync` so the deployment's lock takes oats.aweb 1.14.2. Existing instances keep their primary identity; they join teams with `oats aweb join <labels>`.
package/docs/schedules.md CHANGED
@@ -39,7 +39,7 @@ see [Captured definitions](#captured-definitions-removed-in-026).
39
39
  ## Kinds
40
40
 
41
41
  - **spawn** `{id, enabled, cron, tz, kind: "spawn", agent, agentsRoot?,
42
- repo?, backend?, purpose?, task, runtime?, model?, yolo?, wake?}` — every
42
+ repo?, backend?, purpose?, task, harness?, model?, yolo?, wake?}` — every
43
43
  due minute launches one disposable instance of `agent` with the same
44
44
  options `oats spawn` takes. `agentsRoot` names the exact agents root that
45
45
  holds the soul (it must lie inside the workspace and defaults to the
@@ -136,7 +136,7 @@ running}], scheduler: {installed, active, lastTick, maxConcurrent, ...}}`.
136
136
  ## What a run reports
137
137
 
138
138
  `launched` (spawn or command returned), `active` (the instance is running;
139
- a home whose retirement is pending still counts, its runtime may be alive),
139
+ a home whose retirement is pending still counts, its harness may be alive),
140
140
  `ended` (its home is gone), `stopped` (home present, nothing running: needs
141
141
  attention, never removed for you), `launch-failed`, `unknown`, and for wake
142
142
  jobs `delivered`, `started` or `skipped`. The kernel never claims a task
@@ -159,13 +159,13 @@ unknown; check the roster and the host by hand, then
159
159
  slot (or `remove --force` forgets the job).
160
160
 
161
161
  A wake job that starts a stopped home holds a launch slot while that
162
- runtime is starting, active, retiring or unobservable, and releases it when
163
- the runtime is proven stopped (the session start receipt's exit marker for
162
+ harness is starting, active, retiring or unobservable, and releases it when
163
+ the harness is proven stopped (the session start receipt's exit marker for
164
164
  that launch, or a home that no longer has a session) or the home is gone.
165
165
  A persistent home that outlives its process does not keep a slot. Delivering
166
166
  a message to a home that is already running takes no slot. The host tick
167
167
  observes every registered scope first, then admits due jobs in one
168
- host-wide order, least recently launched first (only an actual runtime
168
+ host-wide order, least recently launched first (only an actual harness
169
169
  launch counts; a skipped or pending job keeps its place at the front), so
170
170
  one frequent job in one scope cannot keep the only slot forever. An invalid
171
171
  or malformed definition is reported on that job and the rest of the tick
@@ -177,7 +177,7 @@ execution identity, kind and target cannot
177
177
  change; cron, tz and enabled can. A cold wake persists its slot before
178
178
  the session start runs and keeps it on any start exception, whatever its code
179
179
  (the kernel can refuse while recording, after the session exists); the next
180
- observation releases it once the runtime is proven stopped or absent, one tick
180
+ observation releases it once the harness is proven stopped or absent, one tick
181
181
  at worst.
182
182
  `remove` refuses while the job's instance is still tracked (`--force`
183
183
  forgets the job without stopping anything). Retiring an instance removes the