@awebai/oats 0.25.0 → 0.25.2
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.
- package/bin/oats.mjs +219 -56
- package/docs/configuration.md +3 -3
- package/docs/conventions.md +51 -24
- package/docs/design/2026-09-20-redesign-program-board.md +1 -1
- package/docs/design/2026-09-20-workspace-and-portable-adoption-plan.md +1 -1
- package/docs/design/2026-09-23-simplified-workspace-model.md +5 -5
- package/docs/design/2026-09-23-workspace-module-contracts.md +257 -0
- package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +5 -5
- package/docs/desktop-cli-api.md +33 -1
- package/docs/desktop-succession.md +9 -2
- package/docs/desktop.md +9 -4
- package/docs/execution-targets.md +16 -4
- package/docs/first-team.md +3 -3
- package/docs/implementation.md +35 -7
- package/docs/integrations.md +5 -3
- package/docs/knowledge-migration.md +16 -8
- package/docs/knowledge.md +50 -17
- package/docs/migration-from-oas.md +20 -9
- package/docs/rebuild-to-v2.md +295 -25
- package/docs/release-notes/v0.25.1.md +94 -0
- package/docs/release-notes/v0.25.2.md +80 -0
- package/docs/schedules.md +12 -6
- package/docs/souls-and-instances.md +36 -18
- package/docs/workspaces.md +73 -27
- package/lib/core.mjs +76 -10
- package/lib/instance-resolution.mjs +228 -23
- package/lib/materialize.mjs +29 -0
- package/lib/operator-dispatch.mjs +117 -0
- package/lib/packages.mjs +62 -1
- package/lib/remote.mjs +128 -49
- package/lib/resolve.mjs +70 -8
- package/lib/workspace.mjs +25 -6
- package/package.json +1 -1
package/docs/rebuild-to-v2.md
CHANGED
|
@@ -16,6 +16,19 @@ schemaVersion 2 only; found 1"`, `E_LOCK_SCHEMA`), never a silent fallback.
|
|
|
16
16
|
Keep the 0.24 kernel installed until the last 0.24 deployment you care about is
|
|
17
17
|
rebuilt; the two do not share files.
|
|
18
18
|
|
|
19
|
+
**One thing a 0.25 kernel changes for a classic home it does launch.** Decision
|
|
20
|
+
13 ("harnesses start normally") is a property of the 0.25 *launcher*, not of the
|
|
21
|
+
v2 files: every `pi` launch a 0.25 kernel performs — `oats spawn`, `oats session
|
|
22
|
+
start|restart`, scheduled runs — starts pi with cwd = the instance home and pi's
|
|
23
|
+
own skill and context discovery intact (`--append-system-prompt <home>/AGENTS.md`,
|
|
24
|
+
no `--no-skills` / `--no-context-files` / `--no-prompt-templates` exclusion).
|
|
25
|
+
That holds for a classic 0.24 home (no `oats-local.yaml`, spawned through the
|
|
26
|
+
pre-v2 compose path that 0.25 still carries) exactly as for a module home. If you
|
|
27
|
+
relied on 0.24's ambient-skill exclusion to hide machine-level or repo-level
|
|
28
|
+
skills from an instance, that isolation is gone the moment a 0.25 kernel
|
|
29
|
+
launches it — keep the 0.24 kernel for those homes, or accept the ambient set
|
|
30
|
+
(the spawn preview lists composed skill names so a clash is visible).
|
|
31
|
+
|
|
19
32
|
## 1. Decide the one workspace
|
|
20
33
|
|
|
21
34
|
One workspace per organisation. Pick the repo that **hosts**
|
|
@@ -35,6 +48,8 @@ capabilities plus `oats.core`), so a public soul stays usable.
|
|
|
35
48
|
Two teams that need two different messaging identities (an open-source team
|
|
36
49
|
and a hosted-operations team, say) stay in ONE workspace: `team:` is a label,
|
|
37
50
|
and the provider payload is addressed by label under `messaging.byTeam` (§2).
|
|
51
|
+
Read §8b before relying on it: the kernel merges `byTeam`, but oats.aweb 1.11.2
|
|
52
|
+
does not yet read the `team` it delivers.
|
|
38
53
|
|
|
39
54
|
## 2. Write `oats-workspace.yaml` v2 in the host repo
|
|
40
55
|
|
|
@@ -93,6 +108,29 @@ Delete `oats.yaml`. Its `exports:` lists are gone: every `souls/*/soul.yaml` and
|
|
|
93
108
|
`capabilities/*/oats.json` is discoverable; add `private: true` to the ones that
|
|
94
109
|
should stay internal. The host repo backlinks to itself like any member.
|
|
95
110
|
|
|
111
|
+
## 3b. Move the souls: `agents/<name>/soul/` → `souls/<name>/`
|
|
112
|
+
|
|
113
|
+
In 0.24 a repo's souls lived at `agents/<name>/soul/` beside that soul's
|
|
114
|
+
instances. Under v2 discovery looks **only** at `souls/<name>/soul.yaml`; the
|
|
115
|
+
`agents/` directory belongs to the *deployment* (instance homes and, under the
|
|
116
|
+
kernel's per-commit soul cache, the fetched soul copies — see §7b) and is not
|
|
117
|
+
read as a soul source. Move every soul as a tracked rename so history follows:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
mkdir -p souls
|
|
121
|
+
git mv agents/release-manager/soul souls/release-manager
|
|
122
|
+
# … one line per soul; then
|
|
123
|
+
git rm -r --cached agents 2>/dev/null; echo 'agents/' >> .gitignore # instances were never meant to be tracked
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`souls/<name>/` keeps its `AGENTS.md`, `CLAUDE.md → AGENTS.md` alias, `skills/`,
|
|
127
|
+
`knowledge/` and `soul.yaml` (rewritten in §4); the directory name must equal
|
|
128
|
+
`soul.yaml#name`. Then fix whatever enumerates the old path: repo tests, scripts,
|
|
129
|
+
CI checks and any `oats.yaml`-era `exports:` tooling that globbed
|
|
130
|
+
`agents/*/soul/soul.yaml` (`git grep -n 'agents/.*/soul'` finds them) — under v2
|
|
131
|
+
they enumerate `souls/*/soul.yaml`. A soul left under `agents/` is invisible to
|
|
132
|
+
`oats souls` and to `oats spawn`; nothing warns about it.
|
|
133
|
+
|
|
96
134
|
## 4. Edit every `soul.yaml` to v2
|
|
97
135
|
|
|
98
136
|
| 0.24 | v2 |
|
|
@@ -105,7 +143,7 @@ should stay internal. The host repo backlinks to itself like any member.
|
|
|
105
143
|
| `stores.inherit` | delete (stores are declared once in the workspace) |
|
|
106
144
|
| `imports` | delete |
|
|
107
145
|
| `kind`, `type`, `repo`, `runtime`, `model`, `launch-config` | delete — model/runtime/launch config are spawn-time choices; `team:` replaces `type:` as the grouping |
|
|
108
|
-
| `knowledge:` / `messaging:` payload | keep as is (opaque provider payload); `none` empties the slot |
|
|
146
|
+
| `knowledge:` / `messaging:` payload | keep as is (opaque provider payload); `none` empties the slot. **For `oats.okf` see the box below: the payload is the binding's SETTINGS keys only; what the soul owns/reads stays in `okf.json`** |
|
|
109
147
|
| — | `compatibility: { <cap>: ">=x.y" }` if you want a floor |
|
|
110
148
|
|
|
111
149
|
```yaml
|
|
@@ -116,9 +154,8 @@ work: worktree
|
|
|
116
154
|
team: engineering
|
|
117
155
|
capabilities:
|
|
118
156
|
acme-release-tooling: { from: here }
|
|
119
|
-
knowledge:
|
|
120
|
-
|
|
121
|
-
reads: [platform-engineer]
|
|
157
|
+
# knowledge: — nothing here for oats.okf: the workspace default fills the slot and
|
|
158
|
+
# souls/release-manager/okf.json (below) says what this soul owns and reads.
|
|
122
159
|
messaging:
|
|
123
160
|
channels: [acme-eng]
|
|
124
161
|
```
|
|
@@ -128,31 +165,133 @@ are required. Capabilities the repo exports live at
|
|
|
128
165
|
`capabilities/<name>/oats.json` — the manifest is unchanged; you may add
|
|
129
166
|
`private: true` / `team:`.
|
|
130
167
|
|
|
168
|
+
**`oats.okf` 2.1.3 reads `souls/<name>/okf.json`, not a `knowledge:` payload.**
|
|
169
|
+
Earlier drafts of this guide showed `knowledge: { owns: …, reads: … }` or
|
|
170
|
+
`knowledge: { store, root }` on the soul; **no shipped provider consumes those
|
|
171
|
+
keys**. What OKF 2.1.3 actually reads at spawn is two things:
|
|
172
|
+
|
|
173
|
+
1. **`<soul>/okf.json`** (travels with the soul, fetched into the per-commit
|
|
174
|
+
soul cache like `AGENTS.md`) — the soul's knowledge declaration, exactly
|
|
175
|
+
these keys and no others:
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{ "version": 1,
|
|
179
|
+
"owner": "release-manager",
|
|
180
|
+
"owns": ["org/release-manager"],
|
|
181
|
+
"reads": ["org/platform-engineer"] }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`owner` is the stable owner id (what `owners.json` pins, §7b); `owns` /
|
|
185
|
+
`reads` are `<base alias>/<node>` references into the bases the machine's
|
|
186
|
+
bindings file declares (`oats okf init` / `oats okf migrate` write it;
|
|
187
|
+
`capabilities/oats-okf/lib/config.mjs#validateDeclaration` is the
|
|
188
|
+
authority). Keep the file where 0.24 had it — it moves with the soul in
|
|
189
|
+
§3b. A soul without `okf.json` whose slot resolves to `oats.okf` fails the
|
|
190
|
+
required spawn hook (`soul has no okf.json`), by design.
|
|
191
|
+
2. **The merged payload, as `OATS_SETTINGS`** — the binding's **settings
|
|
192
|
+
keys only**, the list in `capabilities/oats-okf/oats.json#settings`:
|
|
193
|
+
`bindings-file`, `state-dir` (both required, absolute host paths →
|
|
194
|
+
`oats-local.yaml`, §5), `harvest-runtime`, `harvest-model` (optional). Any
|
|
195
|
+
other key — `owns`, `reads`, `store`, `root`, `stores` — is refused
|
|
196
|
+
(`unknown OATS_SETTINGS property`). So for `oats.okf` the soul's
|
|
197
|
+
`knowledge:` payload is normally **absent** (the workspace default
|
|
198
|
+
`defaults.knowledge: { oats.okf: { from: package } }` fills the slot) or
|
|
199
|
+
carries a soul-true binding setting such as `harvest-runtime: claude`;
|
|
200
|
+
`knowledge: none` opts the soul out.
|
|
201
|
+
|
|
202
|
+
`stores:` in the workspace file names repositories for the **workspace**; where
|
|
203
|
+
OKF's bases live inside them is a **bindings-file** concern today (`bases.<alias>`
|
|
204
|
+
with `repository` + `root`), not a soul payload key. A soul payload grammar for
|
|
205
|
+
OKF (`owns`/`reads`/`root` on `soul.yaml`) is an OKF follow-up (it lands with an
|
|
206
|
+
`oats.okf` release that declares it in its binding, and this guide will say so);
|
|
207
|
+
until then the kernel forwards the payload opaquely and OKF refuses what it does
|
|
208
|
+
not know.
|
|
209
|
+
|
|
210
|
+
**Carry `team:` on every soul, or on its repo's membership.** A soul's team is
|
|
211
|
+
`soul.yaml#team`, else `oats-membership.yaml#team`, else *unassigned*
|
|
212
|
+
(`null`). Labels never gate anything, but the kernel addresses provider payload
|
|
213
|
+
by label: an unlabelled soul receives the messaging **base** payload only —
|
|
214
|
+
`workspace.messaging` minus `byTeam`, no `byTeam.<label>` block, and no
|
|
215
|
+
`defaults.byTeam.<label>` capabilities either. If your 0.24 deployment had one
|
|
216
|
+
messaging identity per team (§1), a soul that loses its label silently lands
|
|
217
|
+
outside every team-addressed payload; nothing refuses it. Label the membership
|
|
218
|
+
when a whole repo belongs to one team, and the soul when it does not.
|
|
219
|
+
|
|
220
|
+
**Per-soul memory-harvest opt-out:** not available in OKF 2.1.3 — an OKF 2.1.4
|
|
221
|
+
item. Neither `okf.json` (`version`, `owner`, `owns`, `reads`) nor the settings
|
|
222
|
+
payload (`bindings-file`, `state-dir`, `harvest-runtime`, `harvest-model`) has a
|
|
223
|
+
key that keeps a soul registered for reads while excluding it from harvest. A
|
|
224
|
+
soul that must not be harvested today says `knowledge: none` (no OKF at all for
|
|
225
|
+
that soul) or `oats.okf: off`; do not invent a key — both readers refuse unknown
|
|
226
|
+
keys.
|
|
227
|
+
|
|
131
228
|
## 5. Write `oats-local.yaml` on each machine
|
|
132
229
|
|
|
133
230
|
```
|
|
134
|
-
~/acme
|
|
231
|
+
~/acme/ # the directory YOU choose — an existing folder with your clones is the usual case
|
|
135
232
|
├── oats-local.yaml
|
|
136
|
-
├── agents/ # instance homes
|
|
137
|
-
└── platform/ # member clones,
|
|
233
|
+
├── agents/ # instance homes — created by `oats sync` if absent (0.25.2)
|
|
234
|
+
└── platform/ # member clones, wherever you keep them (here, or named in clones:)
|
|
138
235
|
```
|
|
139
236
|
|
|
140
237
|
```yaml
|
|
141
238
|
schemaVersion: 2
|
|
142
239
|
workspace: git:github.com/acme/agents
|
|
240
|
+
clones: # optional: member clones that are NOT at <deployment>/<member name>
|
|
241
|
+
github.com/acme/platform: /Users/ana/src/acme-platform
|
|
143
242
|
settings: # what used to be `settings:` under capabilities.layers.* in oats-config.yaml
|
|
144
243
|
oats.okf:
|
|
145
|
-
bindings-file: /Users/ana/.oats/okf-bindings.json
|
|
146
|
-
state-dir: /Users/ana/.oats/okf
|
|
244
|
+
bindings-file: /Users/ana/.oats/okf-bindings.json # required by the OKF binding: absolute host path
|
|
245
|
+
state-dir: /Users/ana/.oats/okf-state # required by the OKF binding: absolute host path; FRESH for a rebuilt deployment (§7b)
|
|
246
|
+
harvest-runtime: pi # optional: pi | claude | codex (default pi)
|
|
247
|
+
oats.aweb:
|
|
248
|
+
delivery: channel # channel (default) | session — see capabilities/oats-aweb/oats.json#settings.delivery
|
|
147
249
|
souls:
|
|
148
250
|
disabled: [data-analyst]
|
|
149
251
|
```
|
|
150
252
|
|
|
253
|
+
**Where the kernel looks for a member clone** (a `work: worktree | checkout`
|
|
254
|
+
soul needs one; nothing else does). In this order, first hit wins:
|
|
255
|
+
|
|
256
|
+
1. `oats spawn … --repo <abs path>` — this spawn only.
|
|
257
|
+
2. `oats-local.yaml` `clones: { <repo key>: <abs path> }` — the key is the
|
|
258
|
+
member's **canonical key** (`github.com/acme/platform`; any ref spelling you
|
|
259
|
+
write is normalised through `parseRepoRef`, so `git:github.com/acme/platform`
|
|
260
|
+
and `https://github.com/acme/platform.git` address the same entry).
|
|
261
|
+
3. The convention: `<deployment>/<member name>` — the last path segment of the
|
|
262
|
+
repo key (`platform` for `github.com/acme/platform`). One exception: a member
|
|
263
|
+
whose name is `agents` is looked for at `<deployment>/agents-repo`, because
|
|
264
|
+
`<deployment>/agents/` is the instance root (above).
|
|
265
|
+
4. None found → `E_CLONE_MISSING`, naming the three remedies. A directory that
|
|
266
|
+
*is* found but whose `origin` remote is a **different repo** →
|
|
267
|
+
`E_CLONE_MISMATCH` (the clone is not the member; nothing is spawned into it).
|
|
268
|
+
|
|
269
|
+
This order was documented before 0.25.2 but the kernel did not honour it (a
|
|
270
|
+
clone had to be `--repo`'d or sit at the convention); 0.25.2 implements it as
|
|
271
|
+
written here. If your host repo is named `agents`, clone it as
|
|
272
|
+
`<deployment>/agents-repo` or name it in `clones:`.
|
|
273
|
+
|
|
274
|
+
`settings.<cap>` is merged into that capability's payload after the soul's
|
|
275
|
+
slot payload and before `spawn --provider` (decision 14); the keys are the
|
|
276
|
+
capability's own (`oats.json#settings`). For **`oats.okf` 2.1.3** the binding
|
|
277
|
+
requires both `bindings-file` and `state-dir` as normalized absolute host
|
|
278
|
+
paths (`setting state-dir is required (absolute host path)` is a refusal, not a
|
|
279
|
+
default) and accepts `harvest-runtime` / `harvest-model`. For **`oats.aweb`**
|
|
280
|
+
the one machine-level key is `delivery`: `channel` (the native aweb channel
|
|
281
|
+
packages wake the instance; default) or `session` (delivery is external —
|
|
282
|
+
`AWEB_DELIVERY=session`, the host wake broker registers the instance once it
|
|
283
|
+
exists; requires an `aw` that ships `aw wake`). `identity.source` is also legal
|
|
284
|
+
here but see §8 for why it belongs at spawn.
|
|
285
|
+
|
|
151
286
|
Move host paths from `oats-config.yaml` `settings:` here; the `souls:` blocks of
|
|
152
287
|
`oats-config.yaml` become `--provider` flags at spawn (step 8). Delete
|
|
153
288
|
`oats-config.yaml`; it is not read. Do not commit `oats-local.yaml`.
|
|
154
|
-
(`oats onboard <dir> --workspace <repo ref>` writes a minimal `oats-local.yaml
|
|
155
|
-
and runs the first `sync` for you; add `settings:` afterwards.
|
|
289
|
+
(`oats onboard <dir> --workspace <repo ref>` writes a minimal `oats-local.yaml`,
|
|
290
|
+
creates `agents/` and runs the first `sync` for you; add `settings:` afterwards.
|
|
291
|
+
Its `next.clone` list names **every** member that lacks a clone at the
|
|
292
|
+
convention — the host included: the host is a member like any other, and a
|
|
293
|
+
soul that lives in it and says `work: worktree` needs its clone too. Under an
|
|
294
|
+
explicit `standalone:` header the list says so and names only that repo.)
|
|
156
295
|
|
|
157
296
|
## 6. `oats sync`
|
|
158
297
|
|
|
@@ -162,11 +301,17 @@ From the deployment directory:
|
|
|
162
301
|
oats sync
|
|
163
302
|
```
|
|
164
303
|
|
|
165
|
-
It
|
|
166
|
-
|
|
167
|
-
`
|
|
168
|
-
|
|
169
|
-
|
|
304
|
+
It creates `agents/` if it is absent (0.25.2; a hand-written `oats-local.yaml`
|
|
305
|
+
no longer needs a `mkdir`), confirms every member (fix any `no-backlink` /
|
|
306
|
+
`backlink-elsewhere` / `cannot-read` row before going on), resolves `packages:`
|
|
307
|
+
to commits, writes `oats-lock.json` (lockfileVersion 3) and asks for executable
|
|
308
|
+
approval once per package version. The 0.24 lock is not read; delete it
|
|
309
|
+
(`E_LOCK_SCHEMA` names it if you leave it in the way).
|
|
310
|
+
|
|
311
|
+
The legacy "You run on OATS" block is no longer composed into `AGENTS.md` when
|
|
312
|
+
`oats.core` resolves as a module (0.25.2): an instance gets **one** such block,
|
|
313
|
+
the one `oats.core`'s inject carries. If you see two, the soul resolved without
|
|
314
|
+
`oats.core` (check `oats spawn <soul> --preview`).
|
|
170
315
|
|
|
171
316
|
## 7. Approve packages
|
|
172
317
|
|
|
@@ -174,10 +319,59 @@ Approval is **per package version, once, in the lock** — no `oats trust`, no
|
|
|
174
319
|
per-capability approval, no per-operator trust list. `oats sync` on a terminal
|
|
175
320
|
prints every executable (`commands.*` and `hooks.*.command` targets of every
|
|
176
321
|
capability the package provides) and asks `approve <id> <version>? [y/N]`.
|
|
177
|
-
Declined or non-interactive → exit `2`, the lock
|
|
178
|
-
unapproved, and spawns of souls using it are refused
|
|
179
|
-
until you run `oats sync` in a terminal and say yes.
|
|
180
|
-
approval: membership is the trust.
|
|
322
|
+
Declined, **Ctrl+D at the prompt**, or non-interactive → exit `2`, the lock
|
|
323
|
+
records the entry unapproved, and spawns of souls using it are refused
|
|
324
|
+
(`E_PACKAGE_UNAPPROVED`) until you run `oats sync` in a terminal and say yes.
|
|
325
|
+
Member capabilities need no approval: membership is the trust.
|
|
326
|
+
|
|
327
|
+
**Non-interactive approval (CI, scripted rebuilds):**
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
oats sync --approve oats.okf@v2.1.3 --approve oats.aweb@v1.11.2
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`--approve <id>@<version>` is repeatable and approves **exactly** the entry the
|
|
334
|
+
resolution contains for that id and version — the executables digest is always
|
|
335
|
+
computed by `sync` over the fetched tree and recorded in the lock; you never
|
|
336
|
+
type a digest. An `--approve` that names an id or version the resolution does
|
|
337
|
+
not contain is an error, not a silent skip; an entry the flags do not cover
|
|
338
|
+
stays unapproved (exit `2`, as above).
|
|
339
|
+
|
|
340
|
+
## 7b. OKF 2: start a FRESH `state-dir` — do not re-point the old one
|
|
341
|
+
|
|
342
|
+
OKF 2 pins each knowledge **owner** to a soul by path: at source registration
|
|
343
|
+
(the `oats.okf` spawn hook) it writes `owners.json` in `state-dir` as
|
|
344
|
+
`{ <owner id>: realpath(<home>/soul) }` and refuses a later registration whose
|
|
345
|
+
owner resolves to a different path (`E_OWNER stable owner ID already identifies
|
|
346
|
+
a different soul in this state namespace`).
|
|
347
|
+
|
|
348
|
+
Under v2 that path is no longer your checkout. `oats spawn` fetches the soul
|
|
349
|
+
from its member repo at the confirmed commit into the deployment's
|
|
350
|
+
**per-commit soul cache**, `agents/<name>/souls/<commit12>/` (immutable once
|
|
351
|
+
written; `agents/<name>/soul` is a kernel-swapped pointer to the current one),
|
|
352
|
+
and the instance's `<home>/soul` links **its own commit's directory** — so the
|
|
353
|
+
realpath the hook pins is `<deployment>/agents/<name>/souls/<commit12>`, which
|
|
354
|
+
never equals the 0.24 pin (`<repo>/agents/<name>/soul`) and changes whenever the
|
|
355
|
+
member moves. Two consequences:
|
|
356
|
+
|
|
357
|
+
- **Do not reuse the 0.24 `state-dir`.** Its `owners.json` pins every owner to
|
|
358
|
+
the old path; the first v2 spawn of each soul would be refused with `E_OWNER`.
|
|
359
|
+
Give the rebuilt deployment a fresh `state-dir` (§5) and a fresh
|
|
360
|
+
`bindings-file` if the old one names the old state root. The old `state-dir`
|
|
361
|
+
is **frozen custody**: read-only history (`oats okf inspect --source
|
|
362
|
+
<old-state>/sources/<id>/source.json …` still works against it), never edited,
|
|
363
|
+
never re-pointed at the new soul path. Accepted knowledge is not affected —
|
|
364
|
+
it lives in the bases, not in `state-dir`.
|
|
365
|
+
- **The owner pin is per commit.** OKF 2.1.3 records the realpath at first
|
|
366
|
+
registration and the kernel keeps that commit directory for as long as any
|
|
367
|
+
instance links it, so a running instance's pin stays valid; a *later* spawn of
|
|
368
|
+
the same soul at a newer member commit links a different directory and
|
|
369
|
+
registers under the same owner id → `E_OWNER` again. Until OKF re-bases the
|
|
370
|
+
pin on the owner identity rather than the path (an OKF 2.1.4 item), the
|
|
371
|
+
practical rule is: one `state-dir` per (deployment, soul commit) is safe;
|
|
372
|
+
moving a member that owns knowledge means a fresh `state-dir` for the new
|
|
373
|
+
commit's spawns (the previous one becomes frozen custody, as above). Plan
|
|
374
|
+
knowledge-owning souls' member commits deliberately.
|
|
181
375
|
|
|
182
376
|
## 8. Re-take a retained messaging seat with `spawn --provider`
|
|
183
377
|
|
|
@@ -186,32 +380,108 @@ In 0.24, an instance-specific messaging identity (a retained seat) was pinned in
|
|
|
186
380
|
**spawn**:
|
|
187
381
|
|
|
188
382
|
```bash
|
|
189
|
-
oats spawn release-manager --purpose seat --provider oats.aweb identity.source
|
|
383
|
+
oats spawn release-manager --purpose seat --provider oats.aweb identity.source=/abs/path/to/retained/.aw
|
|
190
384
|
```
|
|
191
385
|
|
|
192
386
|
`--provider <cap> key=value` is repeatable; dotted keys nest. The payload is
|
|
193
387
|
merged after the soul's `messaging:` and the machine's `settings.oats.aweb`, and
|
|
194
388
|
recorded in `instance.json.providers.oats.aweb`, so exactly one instance holds
|
|
195
|
-
the seat while other instances of the soul mint fresh identities.
|
|
196
|
-
|
|
389
|
+
the seat while other instances of the soul mint fresh identities.
|
|
390
|
+
|
|
391
|
+
**The value is the path itself.** `oats.aweb` reads `identity.source` as the
|
|
392
|
+
absolute path of the `.aw` directory to retain (it must hold `signing.key`); the
|
|
393
|
+
kernel does not resolve symbolic seat names. Because it is an absolute path it is
|
|
394
|
+
a fact about ONE machine, so its other legal home is `oats-local.yaml`
|
|
395
|
+
(`settings.oats.aweb.identity.source: /abs/path`) — never the workspace file
|
|
396
|
+
(absolute paths are refused there, decision 14). Prefer the spawn form: a
|
|
397
|
+
machine-level setting would give the seat to EVERY instance of every messaging
|
|
398
|
+
soul on that machine, and a seat can be held once. The Desktop's
|
|
197
399
|
confirmed apply carries the same map.
|
|
198
400
|
|
|
401
|
+
## 8b. Where the team `.aw` lives now (oats.aweb 1.11.2), and what `byTeam` does today
|
|
402
|
+
|
|
403
|
+
A freshly minted identity (every spawn without `identity.source`) needs an
|
|
404
|
+
**initialised aweb root**: a directory holding `.aw` with a team membership to
|
|
405
|
+
mint into. oats.aweb 1.11.2's spawn hook looks for `.aw` among these, first hit
|
|
406
|
+
wins: the declared team scope (`OATS_TEAM_SCOPE`, from the removed
|
|
407
|
+
`oats-config.yaml` `team:` block — **empty under v2**), the instance home, the
|
|
408
|
+
git repo containing the home, the resolution context (the soul's work repo) and
|
|
409
|
+
the git repo containing it, and the workspace root (`OATS_WORKSPACE`, which
|
|
410
|
+
under v2 is the **deployment directory** — the one holding `oats-local.yaml`).
|
|
411
|
+
None of these is the 0.24 team root you initialised with `oats aweb setup`, so
|
|
412
|
+
a rebuilt deployment mints nothing until you put `.aw` where the hook looks:
|
|
413
|
+
|
|
414
|
+
- **at the deployment directory** — `<deployment>/.aw`: one team for every
|
|
415
|
+
messaging soul spawned here; or
|
|
416
|
+
- **inside a member clone** (gitignored — add `.aw/` to the clone's
|
|
417
|
+
`.gitignore`; never commit `signing.key`): `<clone>/.aw` is found through the
|
|
418
|
+
soul's work repo, so souls whose `work:` targets *that* member mint into
|
|
419
|
+
*that* team.
|
|
420
|
+
|
|
421
|
+
`cp -R <old team root>/.aw <deployment>/.aw` (or into the clone) carries the
|
|
422
|
+
existing memberships over; `aw team list` from that directory shows the active
|
|
423
|
+
team. A `.aw` at your user home or above the deployment is **not** found on
|
|
424
|
+
purpose (a `.aw` there would be a different team; minting into it would be a
|
|
425
|
+
silent cross-team leak).
|
|
426
|
+
|
|
427
|
+
**Two teams, two identities — what actually decides the team in 1.11.2.** The
|
|
428
|
+
hook resolves the target team as: `OATS_TEAM_ID` / `OATS_TEAM_NAME` from the
|
|
429
|
+
removed `oats-config.yaml` `team:` block (empty under v2), else **the active
|
|
430
|
+
team at the `.aw` root it found**. It **does not read a `team` key from its
|
|
431
|
+
payload** (`OATS_SETTINGS`): the only payload keys 1.11.2 acts on are
|
|
432
|
+
`delivery` and `identity.source`/`identity.takeOver`. Consequently
|
|
433
|
+
`messaging.byTeam.<label>: { team: aweb:… }` is **kernel-merged and
|
|
434
|
+
delivered, but a NO-OP for oats.aweb 1.11.2** — the kernel does its part
|
|
435
|
+
(`spawn --preview` shows the merged `settings.oats.aweb` with the label's
|
|
436
|
+
`team`, and `instance.json.providers.oats.aweb` records it); the provider
|
|
437
|
+
ignores it until an oats.aweb release reads `team` from the payload. Until then
|
|
438
|
+
the only way to get per-label minting is **per-repo placement**: give each
|
|
439
|
+
team's member clone its own `.aw` whose active team is that team’s, and make
|
|
440
|
+
sure the souls of that team say `work: worktree | checkout` **on that repo**.
|
|
441
|
+
A soul with `work: directory | workspace` has no member clone as context and
|
|
442
|
+
falls through to `<deployment>/.aw` — one team only. Keep `byTeam` in the
|
|
443
|
+
workspace file anyway: it is the declared intent, the kernel honours it, and
|
|
444
|
+
the next oats.aweb picks it up without a workspace edit.
|
|
445
|
+
|
|
199
446
|
## 9. Spawn, and check drift
|
|
200
447
|
|
|
201
448
|
```bash
|
|
202
449
|
oats souls # every non-private soul of every confirmed member, with origin and team
|
|
203
450
|
oats capabilities # every capability, member (origin: member <key> @ <commit>) or package (package <id> v<ver>)
|
|
204
|
-
oats spawn <soul> --preview # modules[] with from/commit/changedSince, team, resolution revision
|
|
451
|
+
oats spawn <soul> --preview # modules[] with from/commit/changedSince, team, resolution revision,
|
|
452
|
+
# providers (the --provider map as given) and settings.<cap> (the merged payload each provider receives)
|
|
205
453
|
oats spawn <soul> --purpose x
|
|
206
|
-
oats status # per instance:
|
|
454
|
+
oats status # per instance: soul: <name> from <member> @ <c7> [member moved since …]
|
|
455
|
+
# modules … [member moved since (now @ …)] / [capability no longer present]
|
|
207
456
|
```
|
|
208
457
|
|
|
458
|
+
`--preview` (0.25.2) prints `providers` — exactly the `--provider <cap> k=v`
|
|
459
|
+
map you gave — and `settings.<cap>` — the **merged** payload the provider's
|
|
460
|
+
binding will receive (`workspace.messaging` base ⊕ `byTeam[team]` ⊕ soul slot
|
|
461
|
+
payload ⊕ `oats-local.yaml settings.<cap>` ⊕ `--provider`), so you can see
|
|
462
|
+
before creating anything that `state-dir` is the fresh one (§7b) and that the
|
|
463
|
+
team block reached the payload (§8b). `oats status` (0.25.2) shows drift for
|
|
464
|
+
the **soul source** as well as for modules: `soul: <name> from <member> @ <c7>`
|
|
465
|
+
with `[member moved since …]` when the member's default branch has moved past
|
|
466
|
+
the commit the instance was spawned from; `--json` carries it as
|
|
467
|
+
`instances[].soul { repoKey, commit, current, status }`. A moved soul is
|
|
468
|
+
information, not a fault — the running instance keeps its own commit (§7b);
|
|
469
|
+
re-spawn when you want the new one.
|
|
470
|
+
|
|
471
|
+
**Work modes and clones.** `work: worktree | checkout` needs the member clone
|
|
472
|
+
(§5 order); `work: directory` needs nothing; `work: workspace` (a coordination
|
|
473
|
+
soul) links `./work` to the **deployment directory** — the one holding
|
|
474
|
+
`oats-local.yaml`, with `agents/` and whatever clones sit beside it — read-only
|
|
475
|
+
across members, no branch (0.25.1). Such a soul finds a member whose clone is
|
|
476
|
+
elsewhere through `oats-local.yaml` `clones:`.
|
|
477
|
+
|
|
209
478
|
## What disappears
|
|
210
479
|
|
|
211
480
|
| Gone | Replaced by |
|
|
212
481
|
|---|---|
|
|
213
482
|
| `oats-config.yaml` (and the laptop/workspace/repo config chain, `agent-types`, `capabilities.layers`/`additive`, `souls:`, adopted config templates) | `oats-workspace.yaml` defaults + `soul.yaml` `capabilities:`; `oats-local.yaml` for host settings; `spawn --provider` for per-instance facts |
|
|
214
483
|
| `oats.yaml` | `oats-membership.yaml` |
|
|
484
|
+
| `agents/<name>/soul/` as the tracked soul source | `souls/<name>/` (tracked); `agents/` is deployment state — instance homes and the kernel's per-commit soul cache `agents/<name>/souls/<commit12>/` |
|
|
215
485
|
| `.agents/capabilities/installed/` and `owned/` | nothing is installed; `<instance>/.oats/modules/<cap>/` per instance; member capabilities under `<repo>/capabilities/` |
|
|
216
486
|
| `oats init`, `oats use`, `oats install`, `oats restore`, `oats trust`, `oats list`, `oats catalog`, `oats remove`, `oats migrate`, `oats config` | `oats sync`, `oats package add \| remove`, `oats workspace status`, `oats capabilities`, `oats souls` — each removed verb answers `E_UNKNOWN_COMMAND` naming its replacement |
|
|
217
487
|
| lock v1 / v2 | lock v3 (`packages` only, with `url`, `capabilities`, `approved`) |
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# OATS v0.25.1 — workspace-model fix round
|
|
2
|
+
|
|
3
|
+
Kernel/Pi **0.25.1**. Tag `v0.25.1` → the commit carrying these notes; the
|
|
4
|
+
version-bump commit lands after the tag. Consumers gate on `oats version --json`
|
|
5
|
+
`features[]` names and API integers — never on the version.
|
|
6
|
+
|
|
7
|
+
**No API change.** `workspaceApi: 2`, every other API integer and the
|
|
8
|
+
`features[]` list are exactly those of [0.25.0](v0.25.0.md). Every item below
|
|
9
|
+
is a correctness, security or documentation fix found by the team review of
|
|
10
|
+
the 0.25.0 workspace model; the normative record is the "0.25.1 fix round"
|
|
11
|
+
section of `docs/design/2026-09-23-workspace-module-contracts.md` and the
|
|
12
|
+
matching clarifications in
|
|
13
|
+
`agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md`.
|
|
14
|
+
|
|
15
|
+
## Kernel
|
|
16
|
+
|
|
17
|
+
- **M1 (high) — a running instance's soul no longer changes under it.** Souls
|
|
18
|
+
are fetched into a per-commit cache `agents/<name>/souls/<commit12>/`
|
|
19
|
+
(immutable); `agents/<name>/soul` is an atomically swapped pointer to the
|
|
20
|
+
current commit; each home links its own commit's directory. A 0.25.0 layout
|
|
21
|
+
is migrated in place on first use. OKF's path-pinned owner stays valid per
|
|
22
|
+
instance.
|
|
23
|
+
- **M2 — SSH remotes are fetched over SSH.** The canonical repo key is
|
|
24
|
+
unchanged; the fetch url honours the ref as written (`git@…`/`ssh://` → SSH,
|
|
25
|
+
`https://` → HTTPS, bare `git:` → HTTPS unless `remoteOptions.transport:
|
|
26
|
+
ssh`). A private repo is no longer probed over HTTPS and silently degraded to
|
|
27
|
+
the standalone view; the standalone fallback now reports the host failure
|
|
28
|
+
that triggered it.
|
|
29
|
+
- **M3 (high) — package approval is re-verified at spawn.** `resolveSoul`
|
|
30
|
+
recomputes the executables digest over the package tree at the locked commit
|
|
31
|
+
and refuses `E_PACKAGE_UNAPPROVED { reason: "digest-mismatch" }` when it
|
|
32
|
+
differs from the approved one; one shared `executablesDigestAt` serves
|
|
33
|
+
`sync` and `resolve`.
|
|
34
|
+
- **M4 — annotated tag OIDs are peeled.** `observeRemote` records the peeled
|
|
35
|
+
commit, never a tag object, in results, locks and `instance.json`.
|
|
36
|
+
- **B2 — `work: workspace` spawns on a v2 deployment.** `./work` is the
|
|
37
|
+
deployment directory (the one holding `oats-local.yaml`); no branch recorded;
|
|
38
|
+
the remedy names `oats-local.yaml`.
|
|
39
|
+
- **B3 — operator-level capability commands from the deployment.**
|
|
40
|
+
`oats <ns> <cmd> … --soul <name>` outside a home resolves exactly as a spawn
|
|
41
|
+
of that soul, fetches the module into `<deployment>/.oats/modules/<cap>@<commit12>/`
|
|
42
|
+
and dispatches there with the soul's merged payload (`oats okf init` before
|
|
43
|
+
any instance exists). `--soul` absent → `E_BAD_ARGS`; unknown namespace →
|
|
44
|
+
`E_UNKNOWN_COMMAND`.
|
|
45
|
+
- **L1 — slot `none` empties the slot.** A soul's `knowledge|messaging|tasks:
|
|
46
|
+
none` drops any layer-bearing capability the workspace defaults contributed
|
|
47
|
+
for that layer; only a layer-bearing capability the soul itself declares next
|
|
48
|
+
to `none` is `E_SLOT_CONFLICT`.
|
|
49
|
+
- **L2 — absolute-path refusal is scoped to ref/path fields.** Team
|
|
50
|
+
descriptions and the opaque messaging payload may contain `/`-rooted text.
|
|
51
|
+
- **L3 — one unsafe deep entry no longer blanks a member's souls.** Depth
|
|
52
|
+
filtering precedes the entry-name safety check.
|
|
53
|
+
- **L4 — listing failures are classified.** `maxBuffer` overflow is not
|
|
54
|
+
reported as `timeout`; an unclassified git listing failure becomes a
|
|
55
|
+
discovery problem row (`E_REMOTE_UNREADABLE { reason: "unknown" }`) instead
|
|
56
|
+
of an abort.
|
|
57
|
+
- **L6 — revision splits declarations from payload.** `revision =
|
|
58
|
+
hash(declRevision, payloadRevision)`; decision binding unchanged; preview can
|
|
59
|
+
report `changed since: declarations | payload | both`.
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
- **M5 — rebuild guide gaps closed** (`docs/rebuild-to-v2.md`): the tracked
|
|
64
|
+
`git mv agents/<name>/soul souls/<name>` step and the tests that enumerate
|
|
65
|
+
soul paths; OKF 2 owner re-registration (fresh `state-dir` for a rebuilt
|
|
66
|
+
deployment, the old one frozen custody); `oats-local.yaml` example with
|
|
67
|
+
`settings.oats.aweb.delivery` and `settings.oats.okf` `state-dir` +
|
|
68
|
+
`bindings-file`; unlabelled souls receive the messaging base payload only;
|
|
69
|
+
per-soul memory-harvest opt-out is not available in OKF 2.1.3 (an OKF 2.1.4
|
|
70
|
+
item).
|
|
71
|
+
- **L7 — decision 13 reach.** Every `pi` launch a 0.25 kernel performs starts
|
|
72
|
+
the harness normally, classic 0.24 homes included (rebuild guide §0,
|
|
73
|
+
`conventions.md`). `oats session recompose` is `E_UNSUPPORTED_MODE` for
|
|
74
|
+
module homes; `session-recompose` stays advertised for classic homes
|
|
75
|
+
(`desktop-cli-api.md`).
|
|
76
|
+
- **L8 — v1 no longer presented as live** in `schedules.md`, `conventions.md`,
|
|
77
|
+
`implementation.md`, `execution-targets.md`, `desktop.md`,
|
|
78
|
+
`desktop-succession.md`, `integrations.md`, `migration-from-oas.md`
|
|
79
|
+
(a 0.24.x procedure), `desktop-cli-api.md` (readiness producers are the 0.24
|
|
80
|
+
tier), `knowledge-migration.md` (`state-dir` is required; four settings) and
|
|
81
|
+
`knowledge.md` (operator-level `oats okf … --soul <x>` from the deployment).
|
|
82
|
+
|
|
83
|
+
## Known follow-ups (unchanged from 0.25.0 unless noted)
|
|
84
|
+
|
|
85
|
+
- The classic (no `oats-local.yaml`) spawn path still runs the pre-v2 compose;
|
|
86
|
+
`composeInstance` still reads `yolo` / `launch-configs` from an
|
|
87
|
+
`oats-config.yaml` chain when one sits above a deployment.
|
|
88
|
+
- The readiness quartet (`readinessApi: 1`) is still produced by the 0.24 tier
|
|
89
|
+
observers; re-basing it on `spawn --preview` / `sync` / `workspace status` is
|
|
90
|
+
a named follow-up.
|
|
91
|
+
- OKF 2's owner pin is per soul path (hence per commit under M1); re-basing it
|
|
92
|
+
on the owner identity is an OKF 2.1.4 item, as is a per-soul harvest opt-out.
|
|
93
|
+
- `oats-local.yaml` `transport:` (M2's per-machine SSH default) needs a schema
|
|
94
|
+
addition before it can be written.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# OATS v0.25.2 — operator-rebuild round
|
|
2
|
+
|
|
3
|
+
Kernel/Pi **0.25.2**. Tag `v0.25.2` → the commit carrying these notes; the
|
|
4
|
+
version-bump commit lands after the tag. Consumers gate on `oats version --json`
|
|
5
|
+
`features[]` names and API integers — never on the version.
|
|
6
|
+
|
|
7
|
+
**No API change.** `workspaceApi: 2`, every other API integer and the
|
|
8
|
+
`features[]` list are exactly those of [0.25.1](v0.25.1.md). The only surface
|
|
9
|
+
additions are **additive fields**: `oats spawn --preview` gains `providers` and
|
|
10
|
+
`settings`, `oats status --json` gains `instances[].soul`; `oats sync` gains
|
|
11
|
+
the `--approve` flag. Every item below comes from an operator's first rebuild
|
|
12
|
+
of a real two-team deployment on 0.25.0, following `docs/rebuild-to-v2.md`
|
|
13
|
+
literally — the guide is a contract the kernel honours. Normative record:
|
|
14
|
+
"0.25.2 operator-rebuild round" in
|
|
15
|
+
`docs/design/2026-09-23-workspace-module-contracts.md` and the matching
|
|
16
|
+
clarifications in
|
|
17
|
+
`agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md`.
|
|
18
|
+
|
|
19
|
+
## Kernel
|
|
20
|
+
|
|
21
|
+
- **R1 — member clones are found as the guide says.** For a `work: worktree |
|
|
22
|
+
checkout` soul: `--repo`, then `oats-local.yaml` `clones:` (keys normalised
|
|
23
|
+
through `parseRepoRef`), then `<deployment>/<member name>` (a member named
|
|
24
|
+
`agents` → `agents-repo`); none → `E_CLONE_MISSING` naming the three
|
|
25
|
+
remedies; a directory whose remotes name another repo → `E_CLONE_MISMATCH`.
|
|
26
|
+
- **R2 — `oats sync` creates `<deployment>/agents/` when absent.**
|
|
27
|
+
- **R3 — one "You run on OATS" block.** With `oats.core` resolved as a module
|
|
28
|
+
the kernel's legacy block is suppressed; the module's inject is the briefing.
|
|
29
|
+
- **R4 — `oats status` shows soul-source drift.** `soul: <name> from <member>
|
|
30
|
+
@ <c7> [member moved since …]` beside the module rows; `--json`
|
|
31
|
+
`instances[].soul { repoKey, commit, current, status }`.
|
|
32
|
+
- **R5 — `spawn --preview` shows the payload.** `providers` (the `--provider`
|
|
33
|
+
map as given) and `settings.<cap>` (the merged payload each provider
|
|
34
|
+
receives).
|
|
35
|
+
- **R9 — non-interactive approval.** `oats sync --approve <id>@<version>`
|
|
36
|
+
(repeatable) approves exactly the locked entry at that version; the digest is
|
|
37
|
+
always computed, never typed; an id/version not in the resolution is
|
|
38
|
+
`E_BAD_ARGS`. Ctrl+D at the interactive prompt is a decline (exit `2`).
|
|
39
|
+
- **R10 — `oats onboard` lists the host** among the clones to make, like any
|
|
40
|
+
member; an explicit `standalone:` header is named as such in the next steps.
|
|
41
|
+
|
|
42
|
+
## Documentation
|
|
43
|
+
|
|
44
|
+
- **R6** — the rebuild guide's work-mode section states that a coordination
|
|
45
|
+
soul's (`work: workspace`) `./work` is the deployment directory (0.25.1 B2).
|
|
46
|
+
- **R7 — where the team `.aw` lives now.** Rebuild guide §8b: oats.aweb 1.11.2
|
|
47
|
+
finds `.aw` among the home, the home's git repo, the soul's work repo and the
|
|
48
|
+
deployment directory — never the 0.24 team root; place it inside the member
|
|
49
|
+
clone (gitignored) or at the deployment directory. **`messaging.byTeam` is
|
|
50
|
+
kernel-merged but a no-op for oats.aweb 1.11.2**, which does not read `team`
|
|
51
|
+
from its payload; per-repo `.aw` placement is the working alternative
|
|
52
|
+
(`workspaces.md` byTeam paragraph updated accordingly).
|
|
53
|
+
- **R8 — OKF 2.1.3 reads `okf.json`, not a soul payload.** The guide, `workspaces.md`,
|
|
54
|
+
`souls-and-instances.md` and `knowledge.md` no longer show `knowledge: { owns,
|
|
55
|
+
reads }` / `{ store, root }` on `soul.yaml`: the soul's declaration is
|
|
56
|
+
`souls/<name>/okf.json` (`version`, `owner`, `owns`, `reads`); the `knowledge:`
|
|
57
|
+
payload may carry only the binding's settings (`bindings-file`, `state-dir`,
|
|
58
|
+
`harvest-runtime`, `harvest-model`); a base's `root` is the bindings file's.
|
|
59
|
+
Decision 24's example is corrected. §7b (fresh `state-dir`) confirmed.
|
|
60
|
+
- `configuration.md` `clones` row states the R1 order; `workspaces.md` and
|
|
61
|
+
`souls-and-instances.md` describe the R4/R5 fields and the per-commit soul
|
|
62
|
+
cache paths.
|
|
63
|
+
|
|
64
|
+
## Provider follow-ups (not kernel)
|
|
65
|
+
|
|
66
|
+
- **oats.aweb follow-up:** read `team` from the spawn payload (so
|
|
67
|
+
`messaging.byTeam` yields per-label identities) and accept the deployment
|
|
68
|
+
directory as a first-class aweb root. Until that release, 1.11.2 behaves as
|
|
69
|
+
§8b describes.
|
|
70
|
+
- **oats.okf follow-up:** a soul-payload grammar (`owns`/`reads` on
|
|
71
|
+
`soul.yaml`), declared in the binding when it lands; owner pin re-based on
|
|
72
|
+
identity rather than path, and per-soul harvest opt-out (2.1.4 items, unchanged).
|
|
73
|
+
|
|
74
|
+
## Known follow-ups (unchanged from 0.25.1)
|
|
75
|
+
|
|
76
|
+
- The classic (no `oats-local.yaml`) spawn path still runs the pre-v2 compose.
|
|
77
|
+
- The readiness quartet (`readinessApi: 1`) is still produced by the 0.24 tier
|
|
78
|
+
observers.
|
|
79
|
+
- `oats-local.yaml` `transport:` needs a schema addition before it can be
|
|
80
|
+
written.
|
package/docs/schedules.md
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
# Schedules
|
|
2
2
|
|
|
3
3
|
A schedule launches an agent, runs an oats command, or wakes an existing
|
|
4
|
-
instance on a cron. Definitions belong to a scope
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
instance on a cron. Definitions belong to a scope and are committable; every
|
|
5
|
+
`oats schedule` command run anywhere inside that scope, including from an
|
|
6
|
+
instance home, reads and writes the same file. On a **workspace deployment**
|
|
7
|
+
(0.25, [workspaces.md](workspaces.md)) the scope is the deployment directory
|
|
8
|
+
— the one holding `oats-local.yaml` and the `agents/` root (the kernel derives
|
|
9
|
+
it as the directory above the agents root; a leftover `oats-config.yaml` that
|
|
10
|
+
declares `team:` would still win, so remove it); scheduled spawns there
|
|
11
|
+
materialize exactly like `oats spawn`. On a classic 0.24 deployment the scope
|
|
12
|
+
is the team workspace (the config level that declares the team, else the
|
|
13
|
+
outermost `oats-config.yaml` level). Execution belongs to the host that holds
|
|
14
|
+
the scope, so a schedule on a registered server keeps running while your laptop
|
|
15
|
+
sleeps.
|
|
10
16
|
|
|
11
17
|
There is no daemon. One host timer (a launchd user agent on macOS, a systemd
|
|
12
18
|
user timer on Linux) runs `oats schedule tick --host` once a minute; the tick
|