@awebai/oats 0.25.1 → 0.25.3

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.
@@ -28,9 +28,9 @@ Each is one PR (or two small ones), reviewed by me, on `main`, as canonical code
28
28
  | **W7** | **CLI switch-over** | `oats sync`, `oats spawn` (preview/apply unchanged in shape, now fed by W4/W6), `oats capabilities` / `oats souls` with origin + team, `oats workspace status`; `oats status` shows `modules … @ commit` + `member moved since`; `oats version --json` advertises `workspaceApi: 2` + feature `workspace-v2`; **remove the v1 readers** (`oats.yaml`, per-soul `source:`, `oats-config.yaml` activation) — a v1 file at a v2 path errors naming the schema | lead | M |
29
29
  | **W8** | **Delete v1** | Remove `portable-*`, `source-spec`, `capability-provenance` (v1 parts), `prepared-resources`, migration stores/evidence, the installed tier in `packages.mjs`, classic config activation, their tests and docs; `core.mjs` loses everything that only served v1 | lead | L (mostly deletion) |
30
30
  | **W9** | **Framework repos as the first workspace (decisions 18–21)** | `oats-workspace.yaml` v2 in `oats` (drop the six imports; `packages:` pins oats.framework/okf/aweb/jira/linear/authoring/dev **as packages**; `teams:` global/engineering; `defaults`); `oats-membership.yaml` in ALL seven repos; the six soul editions rewritten (`oats.okf: {from: package}` etc. even though the repos are members); **a new expert soul in every package repo** — `okf-expert`, `aweb-expert`, `jira-expert`, `linear-expert`, `authoring-expert`, `dev-expert` (v2 `souls/<name>/`, team global, knows and evolves that capability); `package-catalog.json` kept as the official marketplace (bare-version `packages:` entries resolve through it) | lead (member-repo PRs to their owners; the expert souls' AGENTS.md drafted by me, reviewed by the package owner) | L |
31
- | **W9b** | **`oats.core` and `oats.setup` rewritten for the new architecture (human, 2026-09-23)** | The two official capabilities' skills and injects are rewritten to teach the *new* model, not patched: **`oats.core`** (`oats-operate`, `oats-souls`, the "you run on OATS" inject) — how an agent works inside an instance under this architecture: its home layout (`.oats/modules/`, `.agents/skills/`, `instance.json` modules/providers), the verbs it will actually use (`status`, `spawn --preview/--provider`, `retire`, `session`, `instance events`, `capabilities`, `souls`, `workspace status`), what a workspace/member/package/team is *from the agent's seat*, drift ("member moved since"), how to find and read other souls. **`oats.setup`** (`oats-config`/`oats-packages` → renamed to what they now are: `oats-workspace`, `oats-packages`, `oats-onboarding`) — the whole architecture and its best practices: workspace ↔ repo handshake and why, `oats-workspace.yaml` / `oats-membership.yaml` / `soul.yaml` v2 / `oats-local.yaml` field by field, teams as labels, member-tier vs package-tier and the non-collapse rule, `packages:` + lock v3 + per-version approval, the official catalog vs `git:` refs, `sync`/`package add`, the `<name>-workspace/` convention and clone-then-spawn, private items, external souls, the standalone case, provider payload homes (soul/machine/spawn), what is deliberately NOT versioned and why. Every skill is validated against the shipped CLI (`oats <verb> --help` snapshot test) so they cannot drift from the commands. | lead (swarm + review) | M |
32
- | **W10** | **Docs** | **`docs/rebuild-to-v2.md` — the rebuild guide (ships with the schemas; states that 0.24.x keeps spawning 0.24.x deployments)**; `docs/workspaces.md` rewritten around v2; `souls-and-instances.md`, `packages.md`, `configuration.md` (mostly deleted), `first-team.md` → the `<name>-workspace/` convention; DTO doc § Workspace v2; release notes | lead | M |
33
- | **W11** | **Onboarding skill** | `oats-setup-expert` / `oats.setup`: teach `<name>-workspace/`, `oats sync`, clone-then-spawn; **the hosting rule for mixed public/private organisations (decision 26): ask up front whether any member is private; if so the workspace file is hosted in a private repo that is NOT a public member (a dedicated private `workspace` repo is the honest shape), public contributors get the standalone case with `oats.core` by default (decision 25); `oats onboard` prints the same rule in its next-steps**; the `oats-operate` skill updated for the new verbs | lead | S |
31
+ | **W9b** | **`oats.core` and `oats.setup` rewritten for the new architecture (human, 2026-09-23)** | The two official capabilities' skills and injects are rewritten to teach the *new* model, not patched: **`oats.core`** (`oats-operate`, `oats-souls`, the "you run on OATS" inject) — how an agent works inside an instance under this architecture: its home layout (`.oats/modules/`, `.agents/skills/`, `instance.json` modules/providers), the verbs it will actually use (`status`, `spawn --preview/--provider`, `retire`, `session`, `instance events`, `capabilities`, `souls`, `workspace status`), what a workspace/member/package/team is *from the agent's seat*, drift ("member moved since"), how to find and read other souls. **`oats.setup`** (`oats-config`/`oats-packages` → renamed to what they now are: `oats-workspace`, `oats-packages`, `oats-onboarding`) — the whole architecture and its best practices: workspace ↔ repo handshake and why, `oats-workspace.yaml` / `oats-membership.yaml` / `soul.yaml` v2 / `oats-local.yaml` field by field, teams as labels, member-tier vs package-tier and the non-collapse rule, `packages:` + lock v3 + per-version approval, the official catalog vs `git:` refs, `sync`/`package add`, the deployment directory (the operator's; no naming convention) and clone-then-spawn, private items, external souls, the standalone case, provider payload homes (soul/machine/spawn), what is deliberately NOT versioned and why. Every skill is validated against the shipped CLI (`oats <verb> --help` snapshot test) so they cannot drift from the commands. | lead (swarm + review) | M |
32
+ | **W10** | **Docs** | **`docs/rebuild-to-v2.md` — the rebuild guide (ships with the schemas; states that 0.24.x keeps spawning 0.24.x deployments)**; `docs/workspaces.md` rewritten around v2; `souls-and-instances.md`, `packages.md`, `configuration.md` (mostly deleted), `first-team.md` → the operator's deployment directory; DTO doc § Workspace v2; release notes | lead | M |
33
+ | **W11** | **Onboarding skill** | `oats-setup-expert` / `oats.setup`: ASK for the deployment directory (an existing folder with the operator's clones is the usual case — no named convention, decision 9), `oats sync`, clone-then-spawn; **the hosting rule for mixed public/private organisations (decision 26): ask up front whether any member is private; if so the workspace file is hosted in a private repo that is NOT a public member (a dedicated private `workspace` repo is the honest shape), public contributors get the standalone case with `oats.core` by default (decision 25); `oats onboard` prints the same rule in its next-steps**; the `oats-operate` skill updated for the new verbs | lead | S |
34
34
  | **W12** | **Desktop follow-through** | New kernel DTOs (from W7) consumed the usual way — engineer reads the merged head, files pins, wires: Capabilities/Souls origin + team columns; "installed" removed as a state; spawn preview shows modules (from/commit/hash) + team; Workspaces surface shows membership status | Desktop engineer, after W7 | M |
35
35
 
36
36
  **Team review (Antares, 2026-09-23) folded in:** rebuild guide (W10), **second review (two aweb teams, stores, soul layout, standalone, public/private hosting → decisions 23–26: `messaging.byTeam`, stores = repo + provider `root`, `souls/` only, `oats.core` standalone default, private host rule taught by onboarding),** `--provider` instance payload (W6/W7), drift display (W7), duplicate-name rule (W6), 0.24.x-keeps-working stated everywhere.
@@ -748,7 +748,7 @@ Every `commit` is a full 40-hex OID; every digest is `sha256-<hex>`; every
748
748
  `oats onboard [<dir>] --workspace <repo ref> [--json]`
749
749
 
750
750
  The **bootstrap** of a deployment (decision 9): realizes a workspace on this
751
- machine in the taught `<name>-workspace/` layout. It writes
751
+ machine in the directory the operator chooses (any existing folder). It writes
752
752
  `<dir>/oats-local.yaml` (`{ schemaVersion: 2, workspace: <ref> }`), creates
753
753
  `<dir>/agents/` (the instance homes), then runs exactly the `oats sync` body
754
754
  over the directory just written — discover over the remotes, confirm
package/docs/desktop.md CHANGED
@@ -86,7 +86,7 @@ The app starts on the directory it was launched with (its own folder by
86
86
  default). To view a deployment, open the workspace switcher in the sidebar
87
87
  and choose **Add workspace → Browse**, then point it at an OATS deployment —
88
88
  a directory containing `agents/` (under the 0.25 workspace model that is the
89
- `<name>-workspace/` directory holding `oats-local.yaml` and `agents/`;
89
+ deployment directory (the operator's choice) holding `oats-local.yaml` and `agents/`;
90
90
  under 0.24, an `agents/` root, a `local-agents/` root for machine-local souls,
91
91
  or a team scope whose `oats-config.yaml` declares `team:`). *The Desktop's own
92
92
  multi-repo roster ("team scopes show every member repo's agents under one
@@ -56,15 +56,15 @@ one soul and, optionally, one capability. Every soul gets `oats.core` from the
56
56
  the resulting layout).
57
57
 
58
58
  ```bash
59
- oats onboard ~/acme-workspace --workspace git:github.com/acme/agents
59
+ oats onboard ~/acme --workspace git:github.com/acme/agents # any directory — an existing one with your clones is fine
60
60
  ```
61
61
 
62
62
  ```
63
- ~/acme-workspace/ # the taught "<name>-workspace" convention
63
+ ~/acme/ # the directory you chose; these three entries are what the kernel needs
64
64
  ├── oats-local.yaml # { schemaVersion: 2, workspace: git:github.com/acme/agents }
65
65
  ├── oats-lock.json # lockfileVersion 3: commit + integrity + approval per package
66
66
  ├── agents/ # instance homes
67
- └── <member>/ # clones of the members you work IN (printed as next steps)
67
+ └── <member>/ # clones of the members you work IN — here or anywhere named in oats-local.yaml clones:
68
68
  ```
69
69
 
70
70
  Read the report it prints: every member row must be `✓↔` (confirmed) — fix
package/docs/knowledge.md CHANGED
@@ -66,9 +66,11 @@ packages:
66
66
  defaults:
67
67
  knowledge: { oats.okf: { from: package } }
68
68
 
69
- # souls/domain-expert/soul.yaml
69
+ # souls/domain-expert/soul.yaml — nothing under knowledge: for oats.okf; the default fills the slot.
70
+ # What the soul owns/reads is souls/domain-expert/okf.json (below), not a soul.yaml payload.
71
+ # A soul-true binding setting is the one thing the payload may carry, e.g.:
70
72
  knowledge:
71
- owns: domain-expert
73
+ harvest-runtime: claude
72
74
 
73
75
  # oats-local.yaml (this machine)
74
76
  settings:
@@ -79,11 +81,14 @@ settings:
79
81
 
80
82
  ```bash
81
83
  oats sync # resolves v2.1.3 to a commit, asks executable approval once
82
- oats spawn domain-expert --preview --json # the exact oats.okf module (package, version, commit)
84
+ oats spawn domain-expert --preview --json # the exact oats.okf module (package, version, commit) + settings.oats.okf (the merged payload)
83
85
  ```
84
86
 
85
- Pinning activates nothing by itself: the soul's `knowledge:` payload and the
86
- machine's `settings.oats.okf` must be bindable. The lock stays exact until the
87
+ Pinning activates nothing by itself: the soul's `okf.json` must exist and the
88
+ merged payload (soul `knowledge:` ⊕ `settings.oats.okf` ⊕ `--provider`) must be
89
+ bindable — it may carry **only** the four settings below (`bindings-file`,
90
+ `state-dir`, `harvest-runtime`, `harvest-model`); `owns`/`reads`/`root` on the
91
+ soul payload are refused by 2.1.3, not read. The lock stays exact until the
87
92
  workspace bumps `packages.oats.okf`; v1 operators must plan migration before
88
93
  that bump. Executable changes come with a new version and a new approval. A
89
94
  service worker need not itself fill the knowledge slot (`knowledge: none`).
@@ -129,8 +134,10 @@ directory, not the current working directory:
129
134
  source homes/worktrees; keep the bindings file outside state and bases. Local
130
135
  Git locators must not be disposable linked worktrees. Directory lock/journal
131
136
  artifacts also must not overlap state, sources or another base.
132
- - Settings are `bindings-file`, `harvest-runtime` (`pi`, `claude`, `codex`,
133
- default `pi`), and optional `harvest-model`. Choose an installed, authenticated
137
+ - Settings are `bindings-file`, `state-dir` (both required, absolute host
138
+ paths), `harvest-runtime` (`pi`, `claude`, `codex`, default `pi`), and
139
+ optional `harvest-model` — the complete list a 2.1.3 payload may carry.
140
+ Choose an installed, authenticated
134
141
  worker runtime independently of the source; omitted models use that runtime's
135
142
  configured default. V1 record-window settings are not v2 settings.
136
143
 
@@ -48,6 +48,8 @@ capabilities plus `oats.core`), so a public soul stays usable.
48
48
  Two teams that need two different messaging identities (an open-source team
49
49
  and a hosted-operations team, say) stay in ONE workspace: `team:` is a label,
50
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.
51
53
 
52
54
  ## 2. Write `oats-workspace.yaml` v2 in the host repo
53
55
 
@@ -141,7 +143,7 @@ they enumerate `souls/*/soul.yaml`. A soul left under `agents/` is invisible to
141
143
  | `stores.inherit` | delete (stores are declared once in the workspace) |
142
144
  | `imports` | delete |
143
145
  | `kind`, `type`, `repo`, `runtime`, `model`, `launch-config` | delete — model/runtime/launch config are spawn-time choices; `team:` replaces `type:` as the grouping |
144
- | `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`** |
145
147
  | — | `compatibility: { <cap>: ">=x.y" }` if you want a floor |
146
148
 
147
149
  ```yaml
@@ -152,9 +154,8 @@ work: worktree
152
154
  team: engineering
153
155
  capabilities:
154
156
  acme-release-tooling: { from: here }
155
- knowledge:
156
- owns: release-manager
157
- 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.
158
159
  messaging:
159
160
  channels: [acme-eng]
160
161
  ```
@@ -164,6 +165,48 @@ are required. Capabilities the repo exports live at
164
165
  `capabilities/<name>/oats.json` — the manifest is unchanged; you may add
165
166
  `private: true` / `team:`.
166
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
+
167
210
  **Carry `team:` on every soul, or on its repo's membership.** A soul's team is
168
211
  `soul.yaml#team`, else `oats-membership.yaml#team`, else *unassigned*
169
212
  (`null`). Labels never gate anything, but the kernel addresses provider payload
@@ -175,25 +218,27 @@ outside every team-addressed payload; nothing refuses it. Label the membership
175
218
  when a whole repo belongs to one team, and the soul when it does not.
176
219
 
177
220
  **Per-soul memory-harvest opt-out:** not available in OKF 2.1.3 — an OKF 2.1.4
178
- item. The 2.1.3 `knowledge:` payload admits `owner`, `owns`, `reads` and
179
- `stores` (plus the kernel-rendered `runtime`/`execution`); there is no key that
180
- keeps a soul registered for reads while excluding it from harvest. A soul that
181
- must not be harvested today says `knowledge: none` (no OKF at all for that
182
- soul) or `oats.okf: off`; do not invent a key — the binding refuses unknown
183
- payload keys.
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.
184
227
 
185
228
  ## 5. Write `oats-local.yaml` on each machine
186
229
 
187
230
  ```
188
- ~/acme-workspace/ # the taught convention: "<name>-workspace"
231
+ ~/acme/ # the directory YOU choose — an existing folder with your clones is the usual case
189
232
  ├── oats-local.yaml
190
- ├── agents/ # instance homes
191
- └── platform/ # member clones, only where someone works IN them
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:)
192
235
  ```
193
236
 
194
237
  ```yaml
195
238
  schemaVersion: 2
196
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
197
242
  settings: # what used to be `settings:` under capabilities.layers.* in oats-config.yaml
198
243
  oats.okf:
199
244
  bindings-file: /Users/ana/.oats/okf-bindings.json # required by the OKF binding: absolute host path
@@ -205,6 +250,27 @@ souls:
205
250
  disabled: [data-analyst]
206
251
  ```
207
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
+
208
274
  `settings.<cap>` is merged into that capability's payload after the soul's
209
275
  slot payload and before `spawn --provider` (decision 14); the keys are the
210
276
  capability's own (`oats.json#settings`). For **`oats.okf` 2.1.3** the binding
@@ -220,8 +286,12 @@ here but see §8 for why it belongs at spawn.
220
286
  Move host paths from `oats-config.yaml` `settings:` here; the `souls:` blocks of
221
287
  `oats-config.yaml` become `--provider` flags at spawn (step 8). Delete
222
288
  `oats-config.yaml`; it is not read. Do not commit `oats-local.yaml`.
223
- (`oats onboard <dir> --workspace <repo ref>` writes a minimal `oats-local.yaml`
224
- 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.)
225
295
 
226
296
  ## 6. `oats sync`
227
297
 
@@ -231,11 +301,17 @@ From the deployment directory:
231
301
  oats sync
232
302
  ```
233
303
 
234
- It confirms every member (fix any `no-backlink` / `backlink-elsewhere` /
235
- `cannot-read` row before going on), resolves `packages:` to commits, writes
236
- `oats-lock.json` (lockfileVersion 3) and asks for executable approval once per
237
- package version. The 0.24 lock is not read; delete it (`E_LOCK_SCHEMA` names
238
- it if you leave it in the way).
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`).
239
315
 
240
316
  ## 7. Approve packages
241
317
 
@@ -243,10 +319,23 @@ Approval is **per package version, once, in the lock** — no `oats trust`, no
243
319
  per-capability approval, no per-operator trust list. `oats sync` on a terminal
244
320
  prints every executable (`commands.*` and `hooks.*.command` targets of every
245
321
  capability the package provides) and asks `approve <id> <version>? [y/N]`.
246
- Declined or non-interactive → exit `2`, the lock records the entry
247
- unapproved, and spawns of souls using it are refused (`E_PACKAGE_UNAPPROVED`)
248
- until you run `oats sync` in a terminal and say yes. Member capabilities need no
249
- 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).
250
339
 
251
340
  ## 7b. OKF 2: start a FRESH `state-dir` — do not re-point the old one
252
341
 
@@ -309,16 +398,83 @@ machine-level setting would give the seat to EVERY instance of every messaging
309
398
  soul on that machine, and a seat can be held once. The Desktop's
310
399
  confirmed apply carries the same map.
311
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
+
312
446
  ## 9. Spawn, and check drift
313
447
 
314
448
  ```bash
315
449
  oats souls # every non-private soul of every confirmed member, with origin and team
316
450
  oats capabilities # every capability, member (origin: member <key> @ <commit>) or package (package <id> v<ver>)
317
- 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)
318
453
  oats spawn <soul> --purpose x
319
- oats status # per instance: modules … [member moved since (now @ …)] / [capability no longer present]
454
+ oats status # per instance: soul: <name> from <member> @ <c7> [member moved since …]
455
+ # modules … [member moved since (now @ …)] / [capability no longer present]
320
456
  ```
321
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
+
322
478
  ## What disappears
323
479
 
324
480
  | Gone | Replaced by |
@@ -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.
@@ -0,0 +1,19 @@
1
+ # OATS v0.25.3 — `OATS_SOUL_ID`
2
+
3
+ Kernel/Pi **0.25.3**. Tag `v0.25.3` → the commit carrying these notes. No API change;
4
+ `features[]` unchanged; one additive hook environment variable and one additive
5
+ `instance.json` field.
6
+
7
+ ## Kernel
8
+
9
+ - **`OATS_SOUL_ID`** — a soul's stable identity for capability hooks (`spawn`,
10
+ `retire`, `launch`): `<repo key>#<soul name>` for a workspace soul, the realpath of
11
+ `agents/<name>/soul` for a classic soul. Recorded as `instance.json.workspace.soul.id`.
12
+ Fixes the consequence of 0.25.1's per-commit soul cache found by the first outsider
13
+ rebuild: OKF 2.1.3 pins a soul's owner to `realpath(home/soul)`, which now changes with
14
+ every member commit → the next spawn of the same soul was refused `E_OWNER`. The provider
15
+ half (owners keyed by `OATS_SOUL_ID`, one-time migration of path pins) ships in OKF 2.1.4.
16
+ - **`OATS_SOUL` is the content the home links** — for a workspace soul the per-commit
17
+ directory, never the swappable `agents/<name>/soul` pointer (spawn and retire hooks agree).
18
+
19
+ Contract: `docs/design/2026-09-23-workspace-module-contracts.md` § "0.25.3 — OATS_SOUL_ID".