@awebai/oats 0.30.3 → 0.32.0

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.
@@ -35,39 +35,56 @@ variables point at are described in
35
35
 
36
36
  ## Backends
37
37
 
38
- `oats spawn --backend tmux|herdr` chooses the backend (default `tmux`). The
39
- backend binary must be installed on the execution host. The chosen session
40
- target is recorded twice: in `instance.json` and in an independent lifecycle
41
- receipt. Every session command checks that the two agree
42
- (`E_RUNTIME_AUTHORITY_MISMATCH` otherwise).
38
+ tmux is the only session backend. `oats spawn --backend tmux` is accepted (it
39
+ is the default); `tmux` must be installed on the execution host. A launched
40
+ home keeps the session it recorded: `session start` and `restart` never
41
+ re-read the defaults. The session target is recorded twice: in
42
+ `instance.json` and in an independent lifecycle receipt. Every session command
43
+ checks that the two agree (`E_RUNTIME_AUTHORITY_MISMATCH` otherwise).
43
44
 
44
45
  ### tmux
45
46
 
46
- Each instance is a window named after the instance in the tmux session
47
- `pi-agents` (override with `PI_AGENTS_TMUX_SESSION`). The receipt records the
47
+ Each instance is a window named after the instance in a tmux session: the
48
+ deployment's `session.tmuxSession`, else `OATS_TMUX_SESSION`, else
49
+ `PI_AGENTS_TMUX_SESSION` (the pre-0.31 variable), else `oats-agents`. Before
50
+ 0.31 the default was `pi-agents`; a home launched then keeps the `pi-agents`
51
+ session it recorded, and `oats status` reads each home on its recorded tmux
52
+ socket and session, never the caller's `$TMUX` server: a caller outside the
53
+ agents' tmux (ssh, cron, a plain terminal) sees the same liveness as `session
54
+ inspect`. A recorded server that cannot be read gives `running: null` with
55
+ `runtimeState: "unreachable"`. The environment variables are read when the spawn runs, and `oats
56
+ inspect --json` reports the session a new spawn would open in as `session`
57
+ (`{tmuxSession}`). A spawn refuses an instance name that is a live window in the
58
+ session it would open in (`E_INSTANCE_NAME_TAKEN`); a live window of that name
59
+ in another session, such as a `pi-agents` window after the default moved, does
60
+ not block it. Session commands target each home's exact recorded window, so
61
+ the two never mix. The receipt records the
48
62
  session, window and socket. The spawn result prints the attach command.
49
63
 
50
- ### Herdr
51
-
52
- Each instance is a Herdr workspace with one pane. The receipt records the
53
- binary, socket, workspace id, pane id, terminal id and protocol. The terminal
54
- id distinguishes a replacement occupant of the same pane.
55
-
56
- - `--herdr-socket <path>` uses an operator-managed Herdr server. OATS never
57
- starts a different server when that socket cannot be inspected.
58
- - Without it, OATS uses `$XDG_CONFIG_HOME/herdr/sessions/oats/herdr.sock`
59
- (default `~/.config/...`) and starts `herdr --session oats server` if no
60
- server is running there.
61
- - The adapter speaks the Herdr socket API at an explicit protocol version,
62
- 20 or 22. A new session records the protocol its server reports (20 for
63
- Herdr 0.8, 22 for 0.9) and refuses any other; before 0.30.3 it always
64
- selected 20, so Herdr 0.9 could not be spawned on. A recorded target is
65
- never renegotiated: each call, the kernel's and the Desktop's, checks that
66
- the server's snapshot reports the recorded protocol.
67
- - Herdr cannot give a pane a command at creation, so OATS types the launch
68
- command into the pane's shell. It waits until the shell has drawn its prompt
69
- (`herdr pane read`, at most 10 s): text typed earlier is cut at the
70
- terminal's line-buffer limit.
64
+ ### Herdr (removed in 0.31.0)
65
+
66
+ OATS no longer supports Herdr. Everything that meets it refuses with
67
+ `E_HERDR_REMOVED`, whose message starts `Herdr is no longer supported by OATS
68
+ (removed in 0.31.0); tmux is the only session backend.` and says what to do:
69
+
70
+ - `oats spawn --backend herdr` or `--herdr-socket`, local or routed with
71
+ `--server` (refused before the server is contacted), and `oats server add
72
+ --herdr`: remove the flag, or use tmux.
73
+ - A schedule's `backend: herdr` or a trigger's `spawn.backend: herdr`: the
74
+ message names the file and key; remove it, or use tmux. A stored local job
75
+ that names it is reported invalid and never runs.
76
+ - `session inspect`, `attach`, `input`, `start`, `restart` and `instance stop`
77
+ on a home a Herdr-era kernel recorded (its `instance.json` records a
78
+ `sessionTarget` or `backend: "herdr"`, or its receipt records a session
79
+ target): retire it and spawn a new instance, which opens in tmux.
80
+ - `oats retire` of such a home needs no Herdr: when no process works in the
81
+ home it proceeds as for an absent session; when one does, it refuses and
82
+ names the pids, so stop its Herdr pane first (for example `herdr --session
83
+ oats server stop`).
84
+
85
+ `oats status` lists such a home with `running: null`, `runtimeState:
86
+ "unsupported"` and the refusal as `runtimeError`. A server registration or
87
+ saved route that records `herdrPath` still loads; the field is ignored.
71
88
 
72
89
  ## Lifecycle
73
90
 
@@ -83,8 +100,8 @@ id distinguishes a replacement occupant of the same pane.
83
100
 
84
101
  `session start` keeps the instance's identity, work tree and notes. It runs no
85
102
  spawn hooks and creates no new home. It runs the recorded launch recipe on the
86
- recorded tmux session or Herdr server; a `--no-launch` home starts on the
87
- default tmux server.
103
+ recorded tmux session; a `--no-launch` home starts on the default tmux
104
+ server.
88
105
 
89
106
  - `--model`, `--launch-config <name>|none`, `--harness` and `--yolo` /
90
107
  `--no-yolo` re-resolve the recipe against the home's recorded context and
@@ -118,18 +135,18 @@ oats session input --home /abs/home --text-file message.txt --json
118
135
  oats session attach --home /abs/home
119
136
  ```
120
137
 
121
- - **inspect** reports `backend`, `present` and `state`: the Herdr agent state
122
- when available, `unknown` for a live harness, `shell` for a fallback shell,
138
+ - **inspect** reports `backend`, `present` and `state`: `unknown` for a live
139
+ harness, `shell` for a fallback shell,
123
140
  `stopped` for an absent or dead terminal, or `not-launched`. An unavailable
124
141
  backend is an error (`E_SESSION_UNAVAILABLE`), never a stopped result.
125
142
  - **input** submits UTF-8 text (stdin or `--text-file`, at most 256 KiB, no
126
- NUL) followed by Enter: bracketed paste in tmux, `pane run` in Herdr. The
127
- text is never run by a shell. A fallback shell, a stopped session or a split
143
+ NUL) followed by Enter, as a bracketed paste. The text is never run by a
144
+ shell. A fallback shell, a stopped session or a split
128
145
  tmux window is refused. `submitted: true` means the terminal accepted the
129
146
  text, not that the agent processed it. Wake schedules and messaging
130
147
  capabilities use this command ([schedules.md](schedules.md)).
131
- - **attach** is interactive and takes no `--json`. It opens a Herdr terminal
132
- viewer, or a temporary tmux session linked to the agent's window alone.
148
+ - **attach** is interactive and takes no `--json`. It opens a temporary tmux
149
+ session linked to the agent's window alone.
133
150
  Closing the viewer leaves the agent running.
134
151
 
135
152
  ### Attachments
@@ -44,6 +44,7 @@ published to npm. Its developer docs are in
44
44
  | module | owns |
45
45
  |---|---|
46
46
  | `remote.mjs` | repo refs, reading Git remotes, content digests |
47
+ | `local-inputs.mjs` | the local configuration a command read, for `observation.localRevision` |
47
48
  | `workspace.mjs` | workspace, membership and soul files; discovery |
48
49
  | `resolve.mjs` | a soul's resolution: capabilities, slots, provenance |
49
50
  | `packages.mjs` | `packages:`, the catalog, `oats sync`, `oats-lock.json` |
@@ -54,7 +55,7 @@ published to npm. Its developer docs are in
54
55
  | `schedule.mjs`, `schedule-host.mjs`, `triggers.mjs`, `automations.mjs` | schedules, triggers and the host timer |
55
56
  | `operator-dispatch.mjs` | capability commands run from a deployment, and its module store |
56
57
  | `instance-*.mjs` | inspection, lifecycle, events and Git views of an instance |
57
- | `herdr.mjs`, `tmux-config.mjs`, `session-*.mjs` | session backends and terminal input |
58
+ | `tmux-config.mjs`, `session-*.mjs` | the tmux session backend and terminal input |
58
59
  | `capability-contract.mjs`, `provider-binding.mjs` | manifest validation, the hook environment rules, the readiness wire |
59
60
  | `servers.mjs` | routing commands to a registered server |
60
61
 
@@ -62,6 +63,63 @@ The kernel is runtime-neutral: nothing in `lib/` depends on a harness or on
62
63
  a provider. Provider behaviour lives in capabilities; the kernel supplies
63
64
  their contracts ([layers](layers.md)).
64
65
 
66
+ ### The remote read path
67
+
68
+ Every CLI command owns one read session (`createReadSession` in
69
+ `remote.mjs`, carried to every remote call as `remoteOptions.session`; the
70
+ CLI closes it when the command ends). A library caller without a session
71
+ gets the plain per-call behaviour. Within a session:
72
+
73
+ - a head is observed once per (cache repo, ref), and a commit peeled once; at
74
+ most eight observations run at once (`OBSERVE_LIMIT`), each holding its slot
75
+ for all its git work (the `ls-remote` and the fetch of the commit it names,
76
+ or the fetch of a reused record's commit);
77
+ - a whole-workspace discovery prefetches its members' heads together with the
78
+ host's (`prefetchMembers` in `workspace.mjs`): the member list comes from the
79
+ host's last observation record and the parsed `workspace` entry at that
80
+ commit, never from a git process, and the answer still uses the list at the
81
+ host commit observed now. A prefetched failure is adopted by the member's own
82
+ observation, not retried in the command. A prefetch no caller adopts (the
83
+ host could not be observed, or the member was dropped since) is abandoned:
84
+ one still queued runs no git. `observeWorkspace` alone (the `teams` reads,
85
+ `inspect --home`) never prefetches;
86
+ - closing the session (the end of the command, or `process.exit`) rejects
87
+ every queued observation and aborts every git child still running for it
88
+ (the session's `AbortSignal` rides every `runGit`). `runGit` starts git as
89
+ its own process group, so a timeout, an output overflow or an abort kills
90
+ git's ssh or remote helper with it; before a capability
91
+ command runs its provider, the CLI ends the idle batch readers
92
+ (`closeBatches`);
93
+ - a commit's tree is listed once (`git ls-tree -r -t -l`, bounded by
94
+ `TREE_INDEX_BUDGET`; anything odd falls back to the per-path reads), and
95
+ blobs come from one `git cat-file --batch` reader per cache repo (at most
96
+ 12 open, killed through `process-group.mjs` on timeout and at close);
97
+ - discovery reads members eight at a time (`DISCOVERY_CONCURRENCY`) with
98
+ serial results: declaration order, the first failure in that order. The
99
+ observations and the member reads are two pools, so a discovery runs at
100
+ most sixteen short-lived git processes at once (eight of them fetches at
101
+ most), plus up to twelve cat-file readers: twenty-eight git processes. One
102
+ shared pool would deadlock: a member read holding a slot waits on its
103
+ member's observation, which needs a slot of its own.
104
+
105
+ Across commands, `memoAtCommit` keeps parsed reads under
106
+ `<cache>/.parsed/<kernel fingerprint>/`, keyed by (repo key, full commit,
107
+ item). The items: `workspace` (the workspace file), `membership` (a member's
108
+ backlink outcome), `enumerate` (a member's souls and capabilities),
109
+ `package-soul` and `external-soul` (one soul file each; their error handling
110
+ differs), `package-manifests` (a package's manifests), `list` (a skill
111
+ listing) and `tree-oids` (the tree ids of a set of directories). An entry is only ever a pure function of those bytes and this kernel's
112
+ code, never local state and never a transient error; it is written
113
+ atomically, a corrupt one is a miss, and `pruneStores` bounds the store
114
+ (`PARSED_LIMITS`, least recently used first). `--max-age` adds the observation
115
+ store `<cache>/.observed/` ([Observation reuse](desktop-cli-api.md#observation-reuse-feature-observe-max-age-oats-0311)):
116
+ one record per (repo key, ref args, url digest), so two spellings of one repo
117
+ keep a record each; the url itself is never written. Adding a cached item
118
+ means choosing an item name unique to its producer (the item string its
119
+ call site passes to `memoAtCommit`, or to `atCommit` in `workspace.mjs`) and
120
+ adding it to `test/parsed-cache.test.mjs`; `test/read-path-scale.test.mjs` pins the member
121
+ scaling by call count.
122
+
65
123
  ## Tests and gates
66
124
 
67
125
  | command | what it checks |
@@ -41,7 +41,7 @@ arrives from.
41
41
  # oats-workspace.yaml: one default per slot, for every soul
42
42
  packages:
43
43
  oats.okf: v4.0.5
44
- oats.aweb: v1.17.1
44
+ oats.aweb: v1.17.3
45
45
  oats.linear: v1.0.1
46
46
  oats.jira: v1.0.1
47
47
  defaults:
@@ -60,10 +60,19 @@
60
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."
61
61
  },
62
62
  "model": { "type": "string", "minLength": 1, "description": "Model for this configuration's harness; overrides the soul default when this configuration is selected." },
63
- "yolo": { "type": "boolean", "description": "Permission bypass for this configuration; overrides scope and soul defaults when selected." }
63
+ "yolo": { "type": "boolean", "description": "Permission bypass for this configuration; overrides scope and soul defaults when selected." },
64
+ "default": { "type": "boolean", "description": "This host's baseline for its harness (0.32, feature launch-config-default): a new launch that picks this harness without naming a configuration (a soul's or souls.launch preference, --harness, the host default) runs this configuration's executable, args, env and yolo; its model is the last fallback. At most one per harness. `--launch-config none` bypasses it. OATS 0.31 and older refuse the key." }
64
65
  }
65
66
  }
66
67
  },
68
+ "session": {
69
+ "type": "object",
70
+ "additionalProperties": false,
71
+ "description": "This host's terminal session defaults for NEW launches (0.31). A launched home keeps the session it recorded.",
72
+ "properties": {
73
+ "tmuxSession": { "type": "string", "pattern": "^[A-Za-z0-9_-]{1,64}$", "description": "The tmux session new tmux instances open their windows in. Absent: OATS_TMUX_SESSION, else PI_AGENTS_TMUX_SESSION (pre-0.31), else oats-agents." }
74
+ }
75
+ },
67
76
  "host": {
68
77
  "type": "object",
69
78
  "additionalProperties": false,
@@ -11,7 +11,7 @@ or workspace membership alone does not make a package official.
11
11
  |---|---|---|---|
12
12
  | `oats.framework` | `oats-framework/v1.4.1` (this repository) | `oats.core`, `oats.setup`, `oats.knowledge-theory` | `knowledge-theory-expert` |
13
13
  | `oats.okf` | `v4.0.5` | `oats.okf` (knowledge), `oats.okf-harvest`, `oats.okf-maintenance` | `knowledge-harvester`, `knowledge-maintainer` |
14
- | `oats.aweb` | `v1.17.1` | `oats.aweb` (messaging) | |
14
+ | `oats.aweb` | `v1.17.3` | `oats.aweb` (messaging) | |
15
15
  | `oats.engineering` | `v1.3.0` | `oats.engineering-expert`, `oats.developer`, `oats.code-review` | `code-reviewer` |
16
16
  | `oats.authoring` | `v1.0.3` | `oats.authoring` | |
17
17
  | `oats.jira` | `v1.0.1` | `oats.jira` (tasks) | |
package/docs/packages.md CHANGED
@@ -76,7 +76,7 @@ members:
76
76
  packages:
77
77
  oats.framework: v1.4.1
78
78
  oats.okf: v4.0.5
79
- oats.aweb: v1.17.1
79
+ oats.aweb: v1.17.3
80
80
  teams:
81
81
  platform: { team: "platform:acme.aweb.ai", description: Platform engineering }
82
82
  defaults:
@@ -134,7 +134,7 @@ Declaring a package in the workspace's `packages:` is the trust decision
134
134
  ## `oats package add | remove`
135
135
 
136
136
  ```bash
137
- oats package add oats.aweb v1.17.1 # a catalog version
137
+ oats package add oats.aweb v1.17.3 # a catalog version
138
138
  oats package add acme.tools git:github.com/acme/tools@v0.4.0
139
139
  oats package remove acme.tools
140
140
  ```
@@ -69,8 +69,11 @@ Found by the rehearsals, and fixed before the tag:
69
69
  ## After the tag
70
70
  - **Flag day:** the oats.engineering release with the souls' `launch:` (experts on Claude Code +
71
71
  Opus 5.5; `code-reviewer` on Codex + Astra; compat `>=0.30.0`), and committing the shared team id.
72
- - **0.30.1:**
73
- - the kernel items that miss the tag: retire of homes whose session is gone, automations.trust;
72
+ - **0.30.1:** one release, cut when the Desktop load work has merged (the kernel read-path PR with
73
+ `observe-max-age` and `observation.localRevision`, Desktop #321 and #322), or on 2026-09-30 18:00 UTC,
74
+ whichever comes first; what has not merged by then goes to 0.30.2. Retire of homes whose session is
75
+ gone (#299) and automations.trust (#300) shipped in 0.30.0. The kernel items below have no owner yet:
76
+ each rides 0.30.1 only if it merges before the cut.
74
77
  - deployment-scope capability commands without `--soul`;
75
78
  - the case-8 pre-check (`E_TEAM_UNCONFIGURED`).
76
79
  - the kernel refuses an unrecognised spawn argument (a bare `join=oats` without
@@ -79,5 +82,5 @@ Found by the rehearsals, and fixed before the tag:
79
82
  FIRST on the instance's PATH and records it in the launch recipe, so plain `oats` inside an
80
83
  instance is the kernel that made it (two kernels side by side: a 0.30 home ran the global 0.24.6
81
84
  and `oats aweb` was `E_UNKNOWN_COMMAND`; the injected instructions tell agents to run plain `oats`).
82
- - **Parked:** item L (`git rm agents/`, cli-dev-v2-native). It needs the GitHub `workflow` token
83
- scope from the human on the lead's machine.
85
+ - **Done:** item L merged in #314 (d4f68da9): the remaining legacy `agents/` soul trees and the
86
+ `validate:okf` gate are removed.
@@ -0,0 +1,130 @@
1
+ # OATS 0.31.0
2
+
3
+ ## Changed
4
+
5
+ - **New tmux instances open in the tmux session `oats-agents`, not
6
+ `pi-agents`.** Running instances are unaffected: a home launched before 0.31
7
+ keeps the `pi-agents` session it recorded, `session start` and `restart` put
8
+ it back there, a self-retire kills its recorded window, and `oats status` and
9
+ the Desktop read each home in its recorded session. To keep new instances in
10
+ `pi-agents`, add one line to the deployment's `oats-local.yaml`:
11
+
12
+ ```yaml
13
+ session: { tmuxSession: pi-agents }
14
+ ```
15
+
16
+ `OATS_TMUX_SESSION` also sets it, and `PI_AGENTS_TMUX_SESSION` is still
17
+ honoured, read when the spawn runs. `oats inspect --json` reports the session
18
+ a new spawn would open in as `session`.
19
+ - **Routed ssh calls share one connection per host and notice a dead link.**
20
+ Every `--server` call, `server roster` and `session attach --server` runs
21
+ ssh with `ControlMaster=auto`, `ControlPath=~/.oats/ssh/%C` and
22
+ `ControlPersist=60`, plus `ServerAliveInterval=15` and
23
+ `ServerAliveCountMax=3`. The probe, the command and an attached viewer
24
+ ride one authenticated connection. A viewer whose link dies ends within
25
+ about 60 s with a message naming the command to reattach, instead of
26
+ hanging. These options override any `Control*` settings for the host in
27
+ `~/.ssh/config`. When the control socket path would not fit the 104-byte
28
+ limit or has characters ssh reads as syntax, or `~/.oats/ssh` is not
29
+ private to you, calls run with connection sharing off
30
+ (`ControlPath=none`) and the command warns once per server
31
+ ([servers.md](../servers.md#connections)).
32
+ - **The version probe is asked once per process** per server and target.
33
+ - A server too old for `oats session` is refused with the tmux command for
34
+ the instance's recorded session and window, not a fixed `oats` session.
35
+ - **The Desktop accepts OATS CLIs `>=0.25.8 <0.32.0`** (was `<0.31.0`), so
36
+ it runs against this release's kernel.
37
+ - **oats.aweb 1.17.3** (catalog and workspace pin, and the bundled mirror).
38
+ Session-delivery guidance matches aw 1.36.15's host wake broker: it
39
+ presents either a line naming what is waiting or the full mail/chat event,
40
+ and recovery after an uncertain crash, compaction or restart goes by exact
41
+ message id (`aw mail show --message-id <id> --json`) or paged
42
+ `aw mail inbox --show-all --json` with `--cursor`. A resident grant's
43
+ `renew: launch` re-registers the wake broker with the new grant, and grant
44
+ seats start with `aw whoami`.
45
+
46
+ ## Added
47
+
48
+ - **Any instance on a server is controllable from here, whoever spawned it.**
49
+ `retire --server` and the routed `session` commands (inspect, attach,
50
+ start, restart, upload), `inspect`, `operation` and `launch-config` reach
51
+ an instance with no saved route by `--home`, or by a name the server's
52
+ roster lists once. A name two homes share there is refused with
53
+ `E_AMBIGUOUS`, listing the homes. Roster rows gain `addressable`, and
54
+ `savedRoute` is information only ([servers.md](../servers.md#run-there)).
55
+ - **The Desktop's reads and lifecycle plans route to the instance's
56
+ machine.** `readiness`, `instance events`, `instance git`, `instance diff`,
57
+ `instance stop --plan|--apply` and `retire --plan` (and its guarded apply)
58
+ take `--server <id>`; the host's kernel does the work, Git included, and
59
+ its envelope is relayed unchanged. A host that does not advertise the
60
+ feature is refused with `E_REMOTE_INCOMPATIBLE` before anything is sent.
61
+ `version --json` lists them in `remote`: `readiness`, `instance-events`,
62
+ `instance-git`, `lifecycle-plans`
63
+ ([desktop-cli-api.md](../desktop-cli-api.md#routed-reads-and-plans)).
64
+ - **Remote roster rows carry the host's facts.** `oats server roster --json`
65
+ instance rows relay `identity`, `identityAddress`, `teams`, `startedAt`,
66
+ `createdAt`, `model`, `runtimeState`, `parentInstance`, `siblingInstance`,
67
+ `relation`, `relativeTo` and `spawnOrigin` from the host's `status --json`,
68
+ `null` where the host does not supply them
69
+ ([desktop-cli-api.md](../desktop-cli-api.md#the-remote-roster-oats-server-roster---json)).
70
+ - **A remote terminal in the Desktop reconnects after a lost link.** A
71
+ viewer that ssh ends with exit 255 keeps its tab and scrollback, goes
72
+ inert, and re-attaches with backoff (1, 2, 4, 8, 15 s, then every 30 s),
73
+ with **Reconnect now** to try at once. It stops with the reason when the
74
+ server says the session is gone or refuses the attach, at once when ssh
75
+ cannot be run on this computer, or after four
76
+ attempts in a row in which this computer's `oats` gave no answer
77
+ ([desktop.md](../desktop.md#remote-terminals)). Reconnecting never touches
78
+ the agent on the server.
79
+
80
+ ## Fixed
81
+
82
+ - **`oats status` reads liveness from each instance's recorded tmux socket**,
83
+ not the caller's `$TMUX` server. Outside the agents' tmux (every `status
84
+ --server` over ssh, cron, a plain terminal) running instances read
85
+ `running: false`, and a row on the default server could read `true`. Now
86
+ `running` agrees with `session inspect`; a recorded server that cannot be
87
+ read gives `running: null` with `runtimeState: "unreachable"` (#347).
88
+ - **`session attach --server` exits 255 when ssh fails before the viewer
89
+ opens** (the version probe, or resolving a name through the host's roster),
90
+ as it does when ssh fails under the viewer; it exited 1, so a caller that
91
+ reconnects on 255 gave up on a flapping link. Every other refusal still
92
+ exits 1. An ssh failure now names the host: `ssh to <host> failed: …`
93
+ (#349).
94
+ - **An `E_SSH` envelope says when ssh never started.** A routed `--json`
95
+ command whose ssh could not be run on this machine (not installed, not
96
+ executable) answers `E_SSH` with `error.details: {"sshStarted": false}`, so
97
+ a caller can stop retrying; a failed link carries no details, as before.
98
+
99
+ ## Removed
100
+
101
+ - **Herdr.** tmux is the only session backend; cross-machine work goes over
102
+ ssh routing (`--server`), which never used Herdr. Nothing is migrated
103
+ silently: everything that meets Herdr refuses with `E_HERDR_REMOVED`, whose
104
+ message says what to do.
105
+ - `oats spawn --backend herdr` or `--herdr-socket` (local, or routed with
106
+ `--server`, refused before the server is contacted) and `oats server add
107
+ --herdr`: remove the flag, or use tmux. `--backend tmux` is still accepted,
108
+ and `oats version --json` advertises `sessionBackends: ["tmux"]`.
109
+ - A schedule's `backend: herdr` or a trigger's `spawn.backend: herdr`: the
110
+ message names the file and key; remove it, or use tmux. A stored local job
111
+ that names it is reported invalid and never runs.
112
+ - `session inspect`, `attach`, `input`, `start`, `restart` and `instance
113
+ stop` on an instance opened in Herdr: retire it (`oats retire <name>`) and
114
+ spawn a new instance; it opens in tmux. `oats status` lists such an
115
+ instance with `running: null`, `runtimeState: "unsupported"` and the
116
+ refusal as `runtimeError`, and never fails because of it.
117
+ - `oats retire` of an instance opened in Herdr works without Herdr. When a
118
+ process still works in its home it refuses, naming the pids: stop its
119
+ Herdr pane (for example `herdr --session oats server stop`), then retire.
120
+ - A server registration or saved route that records `herdrPath` still
121
+ loads; the field is ignored.
122
+ - **The Desktop** no longer offers Herdr as a session backend and never
123
+ opens a Herdr terminal; it refuses one with `E_HERDR_REMOVED`, including
124
+ for a remote host still on an older OATS. An instance opened in Herdr
125
+ stays in the roster with the reason; its Open, Start and Restart are
126
+ disabled, and Remove (retire) still works, so it can be retired and
127
+ spawned again in tmux.
128
+
129
+ An idle Herdr server that OATS started (session `oats`) is not OATS state;
130
+ stop it with `herdr --session oats server stop`.
@@ -0,0 +1,137 @@
1
+ # OATS 0.32.0
2
+
3
+ ## Added
4
+
5
+ - **A default launch configuration per harness.** `default: true` on a
6
+ launch configuration in `oats-local.yaml` makes it this machine's baseline
7
+ for its harness. Every new launch of that harness that names no
8
+ configuration runs its executable, args, env and `yolo`: a soul's own
9
+ `launch:`, an inline `souls.launch` preference, `--harness`, the host
10
+ default. For example, every claude instance on a machine runs with its
11
+ `CLAUDE_CONFIG_DIR`:
12
+
13
+ ```yaml
14
+ launch-configs:
15
+ mine:
16
+ harness: claude
17
+ default: true
18
+ env: { CLAUDE_CONFIG_DIR: /home/me/.claude-personal }
19
+ ```
20
+
21
+ The model still comes from whatever chose the harness; the default's
22
+ `model` is only the last fallback. A named configuration runs as declared,
23
+ and `--launch-config none` asks for the bare harness. One default per
24
+ harness (a second is refused, naming both). `oats launch-config list`,
25
+ `spawn --preview`, `launch-config preview`, `instance.json` and
26
+ `oats inspect --home` say when a launch came from the default
27
+ (`launchConfigDefault`), yolo included. Existing instances keep their
28
+ launch until `--reselect-launch` or a respawn, and meanwhile show the
29
+ `launch-changed` readiness warning. Feature
30
+ `launch-config-default`
31
+ ([configuration.md](../configuration.md#the-harness-default)).
32
+ **Declaring a default needs OATS 0.32+ for every kernel that reads this
33
+ deployment:** OATS 0.31 and older refuse the whole `oats-local.yaml`
34
+ (`E_WORKSPACE_SCHEMA`). A routed `launch-config set --server` with
35
+ `default: true` to an older host is refused (`E_REMOTE_INCOMPATIBLE`)
36
+ before anything is sent.
37
+
38
+ - **Every instance a server reports is first-class in the Desktop, whoever
39
+ spawned it.** A remote row opens its terminal, starts, restarts, stops and
40
+ retires, and shows its readiness, activity, Git and diffs, just like a local
41
+ one; only its pull request is not read here, since the forge reads this
42
+ computer's clones. Stop and Remove keep their confirmations: the plan and
43
+ its guarded apply run on the instance's machine. Every command goes to the
44
+ server by the instance's home (`--server <id> --home <path>`), never by a
45
+ bare name, so two instances with the same name stay apart. While a read is
46
+ in flight, the view says "Reading from <server>…". When the server refuses,
47
+ it shows the server's code and message under a headline that usually names
48
+ the server; it never falls back to a local read. A remote stop or remove
49
+ that loses its link reads as an unknown outcome, never a failure, and the
50
+ server's roster is read again at once
51
+ ([desktop.md](../desktop.md#instances-on-servers)).
52
+ - **A row that can't be opened says why, on the row.** The roster shows a short
53
+ reason where it said "state unknown": Herdr no longer supported, gone from
54
+ <server>, not reachable on <server>, or <server> not reached. The full
55
+ sentence stays in the row's tooltip and its actions menu. For an instance a
56
+ server no longer lists, it names the command that removes it from this
57
+ computer (`oats server forget <server> --instance <name>`).
58
+ - **This computer's OATS 0.31 decides which server rows can be used.** The
59
+ Desktop accepts OATS CLIs up to 0.32.x. With a local OATS before 0.31, which
60
+ does not report that fact, an instance spawned from this computer stays
61
+ usable through its saved route, as before; its routed reads and plans ask you
62
+ to update OATS here.
63
+ - **A team card lists its members wherever they run.** On a local workspace's
64
+ Teams page, each team with a provider id lists every instance in it: this
65
+ workspace's, and every registered server's whose messaging identity is in
66
+ that team. They are grouped by machine ("This computer" first), with each
67
+ one's state in words. A member's name shows its roster row, where every
68
+ action lives; **Terminal** opens a running member there. A server whose
69
+ last roster read failed keeps its last-known members under "<server> · not
70
+ reached"; servers with nothing to show are named under the page head. The
71
+ card's count reads "N members · M on other machines". The Desktop reads
72
+ this from what it already holds and runs nothing new
73
+ ([desktop-teams.md](../../packages/desktop/docs/desktop-teams.md#team-members)).
74
+ - **The spawn dialog asks "Where to run" up front** (it was "Run on", under
75
+ Developer settings). "This computer" comes first, then each registered
76
+ server. A server is disabled, with the reason, when it wasn't reached or
77
+ the roster knows only an older registration of it. With a server chosen,
78
+ the dialog says where the instance will run, and the relationship picker
79
+ lists only that server's instances: a relation never crosses machines. When
80
+ the server refuses the spawn (a soul it doesn't offer, for example), the
81
+ dialog says so in the server's own words.
82
+ - **The Desktop keeps and sets a harness's default launch configuration.**
83
+ Editing a configuration marked `default: true` saves it with its default
84
+ intact, and with an OATS that has `launch-config-default` the editor offers
85
+ "Default for <harness> on this machine" for this computer's own
86
+ configurations. A remote host's default survives editing too, but is set on
87
+ that host. A second default for the same harness is refused in OATS's own
88
+ words; unset the old one first.
89
+
90
+ - **Faster read verbs, and `--max-age <s>` to reuse recent remote heads**
91
+ (feature `observe-max-age`). `status`, `workspace status`, `souls`,
92
+ `capabilities` and `inspect` start far fewer git processes, cache what
93
+ they parse by commit, and start the members' remote reads together with the
94
+ host's. With `--max-age` they reuse a head observed in the last `<s>`
95
+ seconds and report `observation {observedAt, reused, localRevision}`
96
+ (`localRevision` changes whenever the local configuration read for the
97
+ answer does). See
98
+ [Observation reuse](../desktop-cli-api.md#observation-reuse-feature-observe-max-age-oats-0311).
99
+
100
+ ## Fixed
101
+
102
+ - **A remote spawn with an empty opening instruction starts it waiting**, as
103
+ the dialog says. It was refused ("--task needs a value").
104
+ - **Focus stays put on the Workspace's Teams, Capabilities and Setup pages.**
105
+ Every roster poll rebuilt the page, since the workspace status it compared
106
+ carries a fresh observation time each time, so keyboard focus (and the
107
+ caret in the add-team form) fell back to the page every few seconds.
108
+ - **A click on a Desktop instance's actions menu does what it says.** With the
109
+ pointer over the open menu, the row no longer counted as hovered, so its
110
+ tools, the menu included, went hidden and a click on an item did nothing;
111
+ the keyboard still worked.
112
+ - **The Stop and Remove confirmations show only their own choices.** Stop
113
+ showed Remove's "delete the worktree" and "delete the local branch" options
114
+ (enabled, and ignored), and Remove showed Stop's "include recorded children".
115
+
116
+ - **A remote read that times out no longer leaves its ssh or https helper
117
+ running.** The kernel killed git but not the ssh or `git-remote-https` it
118
+ had started, which stayed connected to an unreachable host until its own
119
+ connection gave up. Git now runs as its own process group, and a timeout
120
+ (or the end of the command) kills the whole group.
121
+
122
+ - **A capability command whose provider dies of a signal exits as the shell
123
+ reports it** (128 + the signal number, 130 for a Ctrl-C), not 1.
124
+
125
+ - **`oats --help` piped into another program is no longer cut off at 8 KB.**
126
+ The top-level usage (about 20 KB) now arrives whole, including the
127
+ "Observation reuse" section; exit codes are unchanged.
128
+
129
+ ## Removed
130
+
131
+ - **`oats-claude-config` is no longer read.** The one-line file that named
132
+ the claude binary for every deployment below it is replaced by the claude
133
+ default launch configuration. A new claude launch with one in reach is
134
+ refused with `E_CLAUDE_CONFIG_REMOVED`, naming the file and the
135
+ configuration to declare instead (`harness: claude`, `executable: <the
136
+ name it held>`, `default: true`); then delete the file. Instances already
137
+ launched with it keep their recorded executable.
package/docs/schedules.md CHANGED
@@ -54,7 +54,10 @@ both are evaluated by the croner library.
54
54
  launchConfig?, harness?, model?, yolo?, wake?}` — every due minute launches
55
55
  one disposable instance of `agent` with the options `oats spawn` takes.
56
56
  `agentsRoot`, when given, must be the deployment's `agents/` root; `repo`
57
- is the work repository, as `--repo`. `model` is a model id (a letter or
57
+ is the work repository, as `--repo`. `backend` is `tmux`; `backend: herdr`
58
+ is refused (`E_HERDR_REMOVED`, naming the file and key: Herdr was removed in
59
+ 0.31.0), and a stored job that names it is reported invalid and never runs.
60
+ `model` is a model id (a letter or
58
61
  digit, then letters, digits and `. _ : / @ + - [ ]`, at most 128
59
62
  characters) or `@native-default`; `agent` and `repo` never start with `-`,
60
63
  so no value can be read as an option of the child `oats spawn`. Each run is