@awebai/oats 0.25.0 → 0.25.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/oats.mjs +68 -21
- package/docs/conventions.md +51 -24
- package/docs/design/2026-09-20-redesign-program-board.md +1 -1
- package/docs/design/2026-09-23-simplified-workspace-model.md +1 -1
- package/docs/design/2026-09-23-workspace-module-contracts.md +151 -0
- package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +2 -2
- package/docs/desktop-cli-api.md +32 -0
- package/docs/desktop-succession.md +9 -2
- package/docs/desktop.md +9 -4
- package/docs/execution-targets.md +16 -4
- package/docs/implementation.md +35 -7
- package/docs/integrations.md +5 -3
- package/docs/knowledge-migration.md +16 -8
- package/docs/knowledge.md +36 -10
- package/docs/migration-from-oas.md +20 -9
- package/docs/rebuild-to-v2.md +119 -5
- package/docs/release-notes/v0.25.1.md +94 -0
- package/docs/schedules.md +12 -6
- package/docs/souls-and-instances.md +2 -2
- package/docs/workspaces.md +8 -1
- package/lib/core.mjs +45 -8
- package/lib/instance-resolution.mjs +96 -21
- 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/implementation.md
CHANGED
|
@@ -64,6 +64,20 @@ their names.
|
|
|
64
64
|
|
|
65
65
|
## Resolution
|
|
66
66
|
|
|
67
|
+
**Workspace model (0.25, current).** `lib/instance-resolution.mjs#prepareInstance(dir, soul)`
|
|
68
|
+
loads `oats-local.yaml`, discovers the workspace over its Git remotes
|
|
69
|
+
(`lib/workspace.mjs#discoverWorkspace`, or the standalone view), finds the
|
|
70
|
+
soul among the confirmed members / external souls, and calls
|
|
71
|
+
`lib/resolve.mjs#resolveSoul` → an immutable Resolution: `modules[]` (each
|
|
72
|
+
`from: member|package` with commit and digest), `slots`, merged provider
|
|
73
|
+
`payloads`, `skills`, `injects`, `revision`. Capability order is
|
|
74
|
+
`defaults.<slot>` ⊕ `defaults.capabilities` ⊕ `defaults.byTeam[team]` ⊕
|
|
75
|
+
`soul.capabilities` (soul wins; `off` removes; a soul's `<slot>: none` empties
|
|
76
|
+
the slot). `lib/materialize.mjs` then copies every module whole into the home.
|
|
77
|
+
The normative contract is
|
|
78
|
+
[docs/design/2026-09-23-workspace-module-contracts.md](design/2026-09-23-workspace-module-contracts.md).
|
|
79
|
+
|
|
80
|
+
**Classic 0.24 (superseded; still carried for homes without `oats-local.yaml`).**
|
|
67
81
|
`configChain(context)` loads `oats-config.yaml` from closest scope outward.
|
|
68
82
|
`resolveCapabilities(context, soulName)`:
|
|
69
83
|
|
|
@@ -196,14 +210,28 @@ never reconciled into committed souls.
|
|
|
196
210
|
|
|
197
211
|
## Acquisition and trust
|
|
198
212
|
|
|
199
|
-
|
|
200
|
-
`
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
**Workspace model (0.25, current).** Nothing is installed. `oats sync`
|
|
214
|
+
(`lib/packages.mjs#resolvePackages`) resolves every `packages:` entry of
|
|
215
|
+
`oats-workspace.yaml` to a commit, computes the package tree's integrity and
|
|
216
|
+
writes `oats-lock.json` **lockfileVersion 3** (`packages.<id>: { source, url,
|
|
217
|
+
path, version, commit, integrity, capabilities[], approved }`). Executable
|
|
218
|
+
approval is **per package version**, recorded in the lock as
|
|
219
|
+
`approved: { executables: sha256-…, at }` after `oats sync` shows the
|
|
220
|
+
executables and the operator says yes; a spawn of a soul using an unapproved
|
|
221
|
+
package is `E_PACKAGE_UNAPPROVED`, and 0.25.1 re-verifies the approved digest
|
|
222
|
+
against the package tree at the locked commit at every spawn. Member-tier
|
|
223
|
+
capabilities need no approval: membership is the trust (decision 2). The
|
|
224
|
+
verbs `oats install|trust|list|restore|use|migrate` are removed
|
|
225
|
+
(`E_UNKNOWN_COMMAND` naming the replacement).
|
|
226
|
+
|
|
227
|
+
**Classic 0.24 (superseded).** External installation copies/clones one exact
|
|
228
|
+
artifact and writes `oats-lock.json` with source, version/commit, and SHA-256
|
|
229
|
+
tree integrity. An existing destination is never pulled silently. Resolution
|
|
230
|
+
rejects changed locked artifacts and unlocked installed/path packages.
|
|
203
231
|
|
|
204
232
|
Executable package hooks, commands, and launch-environment authority are omitted
|
|
205
|
-
until `oats trust <id>` marks the exact locked integrity approved.
|
|
206
|
-
packages are framework-trusted.
|
|
233
|
+
until `oats trust <id>` (0.24) marks the exact locked integrity approved.
|
|
234
|
+
Bundled packages are framework-trusted.
|
|
207
235
|
Packages under a scope's `owned/` subtree are config-owned. Anything under
|
|
208
236
|
`installed/` requires a matching lock entry, so an acquired artifact cannot
|
|
209
237
|
bypass executable trust by its directory location.
|
|
@@ -211,7 +239,7 @@ bypass executable trust by its directory location.
|
|
|
211
239
|
Distribution packages generalize this: a package materializes each capability it
|
|
212
240
|
exports into `.agents/capabilities/installed/<id>/`, each independently
|
|
213
241
|
addressable and independently trusted at its own artifact integrity. There is no
|
|
214
|
-
persistent package store. The `lockfileVersion: 2` lock records package
|
|
242
|
+
persistent package store. The 0.24 `lockfileVersion: 2` lock records package
|
|
215
243
|
provenance (`packages`) and materialized capability identity (`capabilities`)
|
|
216
244
|
separately. See `docs/design/package-engine-contract.md` for the resolver/lock
|
|
217
245
|
API and error taxonomy.
|
package/docs/integrations.md
CHANGED
|
@@ -100,9 +100,11 @@ secrets never belong in OATS config. See
|
|
|
100
100
|
|
|
101
101
|
> **Removed: `oats.web`.** The browser web-panel capability was retired in
|
|
102
102
|
> favor of the OATS Desktop app (`packages/desktop/`), which bundles the same
|
|
103
|
-
> zero-dependency loopback server. If an `oats-
|
|
104
|
-
> `
|
|
105
|
-
>
|
|
103
|
+
> zero-dependency loopback server. If an `oats-workspace.yaml` (`packages:`,
|
|
104
|
+
> `defaults.capabilities`) or a `soul.yaml` still names `oats.web`, remove that
|
|
105
|
+
> entry and `oats sync`; on a 0.24 classic deployment, remove it from
|
|
106
|
+
> `oats-lock.json` / `oats-config.yaml`. Full migration steps:
|
|
107
|
+
> [desktop-succession](desktop-succession.md).
|
|
106
108
|
|
|
107
109
|
## Building an integration
|
|
108
110
|
|
|
@@ -39,17 +39,25 @@ Choose stable base IDs, stable owner IDs, nonoverlapping node paths and durable
|
|
|
39
39
|
permissions. Confirm aliases and owners explicitly, rather than deriving them
|
|
40
40
|
from an instance branch or name.
|
|
41
41
|
|
|
42
|
-
Configure the absolute `bindings-file` for each source soul
|
|
43
|
-
|
|
44
|
-
`
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
and
|
|
42
|
+
Configure the absolute `bindings-file` **and** `state-dir` for each source soul
|
|
43
|
+
— the oats.okf 2.1.x binding requires both as normalized absolute host paths
|
|
44
|
+
(`setting state-dir is required (absolute host path)` is a refusal, not a
|
|
45
|
+
default). Remove obsolete v1 settings such as `record-window-turns` and
|
|
46
|
+
`record-window-bytes`; v2 accepts exactly `bindings-file`, `state-dir`,
|
|
47
|
+
`harvest-runtime` and `harvest-model`. Under the 0.25 workspace model these live
|
|
48
|
+
in `oats-local.yaml` `settings.oats.okf` ([configuration.md](configuration.md));
|
|
49
|
+
a rebuilt deployment gets a **fresh** `state-dir`
|
|
50
|
+
([rebuild-to-v2.md §7b](rebuild-to-v2.md#7b-okf-2-start-a-fresh-state-dir-do-not-re-point-the-old-one)).
|
|
51
|
+
Provision **empty owned nodes** using `oats okf init`. Accept Git initialization
|
|
52
|
+
through a reviewed PR before migration delivery; directory provisioning requires
|
|
53
|
+
explicit confirmation and a genuinely non-Git location.
|
|
48
54
|
|
|
49
55
|
## 3. Stage and deliver each legacy bundle
|
|
50
56
|
|
|
51
|
-
From the
|
|
52
|
-
inherited instance identity, selecting the source soul
|
|
57
|
+
From the deployment directory (the one holding `oats-local.yaml`) in an operator
|
|
58
|
+
shell without inherited instance identity, selecting the source soul with
|
|
59
|
+
`--soul` — the kernel resolves the command exactly as `oats spawn --soul <x>`
|
|
60
|
+
would ([knowledge.md](knowledge.md#inspection-and-operator-commands)):
|
|
53
61
|
|
|
54
62
|
```bash
|
|
55
63
|
oats okf migrate --legacy /absolute/soul/knowledge --base project --node expert --output /absolute/empty-migration-stage --soul domain-expert --json
|
package/docs/knowledge.md
CHANGED
|
@@ -51,11 +51,13 @@ artifact**: npm drops the source worker's `CLAUDE.md -> AGENTS.md` symlink.
|
|
|
51
51
|
Acquire the catalog Git payload; do not install a copied npm mirror as a local
|
|
52
52
|
package or repair missing aliases in installed artifacts.
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
Under the 0.25 workspace model OKF is a **package**: pin it once in the
|
|
55
|
+
workspace file, let `oats sync` lock and approve it, and let every soul that
|
|
56
|
+
fills the knowledge slot say (or inherit) `oats.okf: { from: package }`.
|
|
57
|
+
Operator-level `oats okf` commands run from the deployment directory with
|
|
58
|
+
`--soul <name>` (an explicit `--soul` does not override an invoking instance's
|
|
59
|
+
saved settings — use a clean shell). The pinned version resolves through the
|
|
60
|
+
official catalog:
|
|
59
61
|
|
|
60
62
|
```yaml
|
|
61
63
|
# oats-workspace.yaml
|
|
@@ -72,6 +74,7 @@ knowledge:
|
|
|
72
74
|
settings:
|
|
73
75
|
oats.okf:
|
|
74
76
|
bindings-file: /absolute/config/okf-bindings.json
|
|
77
|
+
state-dir: /absolute/state/okf
|
|
75
78
|
```
|
|
76
79
|
|
|
77
80
|
```bash
|
|
@@ -157,7 +160,9 @@ bindings, owner declarations, base metadata or indexes fail required spawn rathe
|
|
|
157
160
|
than silently bootstrapping empty knowledge.
|
|
158
161
|
|
|
159
162
|
Provisioning is an explicit operator action. Prepare node-map files (the
|
|
160
|
-
`nodes` object above, without its wrapper), then
|
|
163
|
+
`nodes` object above, without its wrapper), then run from the **deployment
|
|
164
|
+
directory** (the one holding `oats-local.yaml`), naming the soul whose
|
|
165
|
+
`knowledge:` payload and `settings.oats.okf` the command should run with:
|
|
161
166
|
|
|
162
167
|
```bash
|
|
163
168
|
# New directory base: refuses an existing destination.
|
|
@@ -166,6 +171,19 @@ oats okf init --base team --nodes /absolute/config/team-nodes.json --confirm --s
|
|
|
166
171
|
oats okf init --base project --nodes /absolute/config/project-nodes.json --output /absolute/new-bundle-stage --soul domain-expert --json
|
|
167
172
|
```
|
|
168
173
|
|
|
174
|
+
These run **before any instance exists**. Outside an instance home the kernel
|
|
175
|
+
resolves `oats okf … --soul <name>` exactly as `oats spawn <name>` would
|
|
176
|
+
(discover → resolve → the soul's `oats.okf` module at its locked, approved
|
|
177
|
+
commit), fetches that module into the deployment's module store
|
|
178
|
+
(`<deployment>/.oats/modules/oats.okf@<commit12>/`) and dispatches to that copy
|
|
179
|
+
with the soul's merged payload as `OATS_SETTINGS`; `--soul` is required
|
|
180
|
+
(`E_BAD_ARGS` names it) unless the namespace's capability is a workspace
|
|
181
|
+
default. It never runs "the newest instance's copy" and never an unapproved
|
|
182
|
+
cache read (`E_PACKAGE_UNAPPROVED` until `oats sync` approves the version).
|
|
183
|
+
*0.25.0 still answers `E_CAPABILITY_INACTIVE` here (the operator-level dispatch
|
|
184
|
+
lands in 0.25.1); the interim is to run the module binary directly with
|
|
185
|
+
`OATS_SETTINGS` and `OATS_CLI_BIN` set, as the tarball smoke does.*
|
|
186
|
+
|
|
169
187
|
Put the Git proposal at the configured root in an operator-owned checkout and
|
|
170
188
|
review/merge it through a PR before spawning working sources. Existing ownership
|
|
171
189
|
changes require an explicit reviewed operator change, not harvest. The standalone
|
|
@@ -189,7 +207,7 @@ Snapshots are immutable by protocol, not live mounts. For current accepted text:
|
|
|
189
207
|
# From the source home:
|
|
190
208
|
oats okf read --base project --path expert/index.md --json
|
|
191
209
|
oats okf refresh --json
|
|
192
|
-
# From the deployment
|
|
210
|
+
# From the deployment directory (oats-local.yaml), even after source retirement — --soul selects the resolution:
|
|
193
211
|
oats okf read --source /absolute/state/sources/UUID/source.json --base project --path expert/index.md --soul domain-expert --json
|
|
194
212
|
oats okf refresh --source /absolute/state/sources/UUID/source.json --soul domain-expert --json
|
|
195
213
|
```
|
|
@@ -289,9 +307,17 @@ spawn and command exit alone are not successful learning.
|
|
|
289
307
|
|
|
290
308
|
## Inspection and operator commands
|
|
291
309
|
|
|
292
|
-
Run home-local commands from that source home
|
|
293
|
-
|
|
294
|
-
|
|
310
|
+
Run home-local commands from that source home: inside an instance the
|
|
311
|
+
dispatcher resolves `okf` from the home's materialized module
|
|
312
|
+
(`instance.json.modules` → `<home>/.oats/modules/oats.okf/`). For cross-source
|
|
313
|
+
or retired-source commands, run from the **deployment directory** (the one
|
|
314
|
+
holding `oats-local.yaml`) in a clean operator shell without another instance's
|
|
315
|
+
`OATS_*`/`PI_*` identity, and select the source soul with `--soul <name>`: the
|
|
316
|
+
kernel resolves that soul as a spawn would and dispatches to the deployment's
|
|
317
|
+
copy of its `oats.okf` module with the soul's merged payload (see
|
|
318
|
+
[Acquire, bind and provision explicitly](#acquire-bind-and-provision-explicitly)).
|
|
319
|
+
No `oats-config.yaml` chain is consulted; a namespace no module of the soul
|
|
320
|
+
provides is `E_UNKNOWN_COMMAND`.
|
|
295
321
|
|
|
296
322
|
```bash
|
|
297
323
|
# Read-only; no capture, refresh, scheduling or worker launch:
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Migrating from OAS to OATS
|
|
2
2
|
|
|
3
|
+
> **0.25 status — this is a 0.22–0.24 procedure.** `oats migrate` and
|
|
4
|
+
> `oats trust` are **removed verbs** in the 0.25 kernel (`E_UNKNOWN_COMMAND`
|
|
5
|
+
> naming the replacement), and 0.25 reads none of the files this page
|
|
6
|
+
> converts to (`oats-config.yaml`, `oats-lock.json` v2, the `installed/` tier).
|
|
7
|
+
> An OAS deployment reaches 0.25 in two steps: run this page's commands with a
|
|
8
|
+
> **0.24.x** kernel (`npm install -g @awebai/oats@0.24`), then rebuild for the
|
|
9
|
+
> workspace model with [rebuild-to-v2.md](rebuild-to-v2.md) — which is a rewrite
|
|
10
|
+
> of three shared files, not a conversion, so an operator comfortable with the
|
|
11
|
+
> v2 declarations may skip straight to it and let the old files go.
|
|
12
|
+
|
|
3
13
|
OATS is the successor to OAS. **OATS 0.22.0 was published on 2026-09-03**:
|
|
4
14
|
the kernel, Pi adapter, and Desktop assets are available. The published
|
|
5
15
|
kernel acquired the official OKF, aweb, authoring, and development packages
|
|
@@ -17,15 +27,15 @@ while its knowledge and messaging configuration remains unmigrated.
|
|
|
17
27
|
> before activation/spawn. The v2 integration is [prepared](release-notes/v0.23.1.md),
|
|
18
28
|
> not a claim that those dependencies or any deployment have already changed.
|
|
19
29
|
|
|
20
|
-
## Upgrade one scope
|
|
30
|
+
## Upgrade one scope (0.24.x kernel)
|
|
21
31
|
|
|
22
32
|
Finish or preserve active work before changing a daily-use deployment.
|
|
23
|
-
Install OATS alongside the old CLI
|
|
24
|
-
scope you intend to convert:
|
|
33
|
+
Install OATS **0.24.x** alongside the old CLI (the 0.25 line has no `oats
|
|
34
|
+
migrate`), then inspect the plan for the exact scope you intend to convert:
|
|
25
35
|
|
|
26
36
|
```bash
|
|
27
|
-
npm install -g @awebai/oats@
|
|
28
|
-
pi install npm:@awebai/oats-pi@
|
|
37
|
+
npm install -g @awebai/oats@0.24
|
|
38
|
+
pi install npm:@awebai/oats-pi@0.24
|
|
29
39
|
oats migrate --from-oas --dry-run --dir /path/to/scope
|
|
30
40
|
```
|
|
31
41
|
|
|
@@ -39,10 +49,11 @@ oats doctor /path/to/scope
|
|
|
39
49
|
```
|
|
40
50
|
|
|
41
51
|
Run the exact `oats trust <capability> --dir <scope>` commands printed by
|
|
42
|
-
migration for the executable capabilities you approve.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
migration for the executable capabilities you approve (0.24: per-artifact
|
|
53
|
+
trust; under 0.25 approval is per package version through `oats sync`). Trust
|
|
54
|
+
does not transfer automatically. Verify the team ID and messaging membership
|
|
55
|
+
with `oats aweb setup --dir /path/to/scope`, then exercise a real task,
|
|
56
|
+
harvest, and retirement as described in [Run your first team](first-team.md).
|
|
46
57
|
|
|
47
58
|
For a multi-repository deployment, start with one scope. The explicit
|
|
48
59
|
`--recursive --dir /path/to/workspace` form converts every discovered OAS
|
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**
|
|
@@ -93,6 +106,29 @@ Delete `oats.yaml`. Its `exports:` lists are gone: every `souls/*/soul.yaml` and
|
|
|
93
106
|
`capabilities/*/oats.json` is discoverable; add `private: true` to the ones that
|
|
94
107
|
should stay internal. The host repo backlinks to itself like any member.
|
|
95
108
|
|
|
109
|
+
## 3b. Move the souls: `agents/<name>/soul/` → `souls/<name>/`
|
|
110
|
+
|
|
111
|
+
In 0.24 a repo's souls lived at `agents/<name>/soul/` beside that soul's
|
|
112
|
+
instances. Under v2 discovery looks **only** at `souls/<name>/soul.yaml`; the
|
|
113
|
+
`agents/` directory belongs to the *deployment* (instance homes and, under the
|
|
114
|
+
kernel's per-commit soul cache, the fetched soul copies — see §7b) and is not
|
|
115
|
+
read as a soul source. Move every soul as a tracked rename so history follows:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
mkdir -p souls
|
|
119
|
+
git mv agents/release-manager/soul souls/release-manager
|
|
120
|
+
# … one line per soul; then
|
|
121
|
+
git rm -r --cached agents 2>/dev/null; echo 'agents/' >> .gitignore # instances were never meant to be tracked
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`souls/<name>/` keeps its `AGENTS.md`, `CLAUDE.md → AGENTS.md` alias, `skills/`,
|
|
125
|
+
`knowledge/` and `soul.yaml` (rewritten in §4); the directory name must equal
|
|
126
|
+
`soul.yaml#name`. Then fix whatever enumerates the old path: repo tests, scripts,
|
|
127
|
+
CI checks and any `oats.yaml`-era `exports:` tooling that globbed
|
|
128
|
+
`agents/*/soul/soul.yaml` (`git grep -n 'agents/.*/soul'` finds them) — under v2
|
|
129
|
+
they enumerate `souls/*/soul.yaml`. A soul left under `agents/` is invisible to
|
|
130
|
+
`oats souls` and to `oats spawn`; nothing warns about it.
|
|
131
|
+
|
|
96
132
|
## 4. Edit every `soul.yaml` to v2
|
|
97
133
|
|
|
98
134
|
| 0.24 | v2 |
|
|
@@ -128,6 +164,24 @@ are required. Capabilities the repo exports live at
|
|
|
128
164
|
`capabilities/<name>/oats.json` — the manifest is unchanged; you may add
|
|
129
165
|
`private: true` / `team:`.
|
|
130
166
|
|
|
167
|
+
**Carry `team:` on every soul, or on its repo's membership.** A soul's team is
|
|
168
|
+
`soul.yaml#team`, else `oats-membership.yaml#team`, else *unassigned*
|
|
169
|
+
(`null`). Labels never gate anything, but the kernel addresses provider payload
|
|
170
|
+
by label: an unlabelled soul receives the messaging **base** payload only —
|
|
171
|
+
`workspace.messaging` minus `byTeam`, no `byTeam.<label>` block, and no
|
|
172
|
+
`defaults.byTeam.<label>` capabilities either. If your 0.24 deployment had one
|
|
173
|
+
messaging identity per team (§1), a soul that loses its label silently lands
|
|
174
|
+
outside every team-addressed payload; nothing refuses it. Label the membership
|
|
175
|
+
when a whole repo belongs to one team, and the soul when it does not.
|
|
176
|
+
|
|
177
|
+
**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.
|
|
184
|
+
|
|
131
185
|
## 5. Write `oats-local.yaml` on each machine
|
|
132
186
|
|
|
133
187
|
```
|
|
@@ -142,12 +196,27 @@ schemaVersion: 2
|
|
|
142
196
|
workspace: git:github.com/acme/agents
|
|
143
197
|
settings: # what used to be `settings:` under capabilities.layers.* in oats-config.yaml
|
|
144
198
|
oats.okf:
|
|
145
|
-
bindings-file: /Users/ana/.oats/okf-bindings.json
|
|
146
|
-
state-dir: /Users/ana/.oats/okf
|
|
199
|
+
bindings-file: /Users/ana/.oats/okf-bindings.json # required by the OKF binding: absolute host path
|
|
200
|
+
state-dir: /Users/ana/.oats/okf-state # required by the OKF binding: absolute host path; FRESH for a rebuilt deployment (§7b)
|
|
201
|
+
harvest-runtime: pi # optional: pi | claude | codex (default pi)
|
|
202
|
+
oats.aweb:
|
|
203
|
+
delivery: channel # channel (default) | session — see capabilities/oats-aweb/oats.json#settings.delivery
|
|
147
204
|
souls:
|
|
148
205
|
disabled: [data-analyst]
|
|
149
206
|
```
|
|
150
207
|
|
|
208
|
+
`settings.<cap>` is merged into that capability's payload after the soul's
|
|
209
|
+
slot payload and before `spawn --provider` (decision 14); the keys are the
|
|
210
|
+
capability's own (`oats.json#settings`). For **`oats.okf` 2.1.3** the binding
|
|
211
|
+
requires both `bindings-file` and `state-dir` as normalized absolute host
|
|
212
|
+
paths (`setting state-dir is required (absolute host path)` is a refusal, not a
|
|
213
|
+
default) and accepts `harvest-runtime` / `harvest-model`. For **`oats.aweb`**
|
|
214
|
+
the one machine-level key is `delivery`: `channel` (the native aweb channel
|
|
215
|
+
packages wake the instance; default) or `session` (delivery is external —
|
|
216
|
+
`AWEB_DELIVERY=session`, the host wake broker registers the instance once it
|
|
217
|
+
exists; requires an `aw` that ships `aw wake`). `identity.source` is also legal
|
|
218
|
+
here but see §8 for why it belongs at spawn.
|
|
219
|
+
|
|
151
220
|
Move host paths from `oats-config.yaml` `settings:` here; the `souls:` blocks of
|
|
152
221
|
`oats-config.yaml` become `--provider` flags at spawn (step 8). Delete
|
|
153
222
|
`oats-config.yaml`; it is not read. Do not commit `oats-local.yaml`.
|
|
@@ -179,6 +248,42 @@ unapproved, and spawns of souls using it are refused (`E_PACKAGE_UNAPPROVED`)
|
|
|
179
248
|
until you run `oats sync` in a terminal and say yes. Member capabilities need no
|
|
180
249
|
approval: membership is the trust.
|
|
181
250
|
|
|
251
|
+
## 7b. OKF 2: start a FRESH `state-dir` — do not re-point the old one
|
|
252
|
+
|
|
253
|
+
OKF 2 pins each knowledge **owner** to a soul by path: at source registration
|
|
254
|
+
(the `oats.okf` spawn hook) it writes `owners.json` in `state-dir` as
|
|
255
|
+
`{ <owner id>: realpath(<home>/soul) }` and refuses a later registration whose
|
|
256
|
+
owner resolves to a different path (`E_OWNER stable owner ID already identifies
|
|
257
|
+
a different soul in this state namespace`).
|
|
258
|
+
|
|
259
|
+
Under v2 that path is no longer your checkout. `oats spawn` fetches the soul
|
|
260
|
+
from its member repo at the confirmed commit into the deployment's
|
|
261
|
+
**per-commit soul cache**, `agents/<name>/souls/<commit12>/` (immutable once
|
|
262
|
+
written; `agents/<name>/soul` is a kernel-swapped pointer to the current one),
|
|
263
|
+
and the instance's `<home>/soul` links **its own commit's directory** — so the
|
|
264
|
+
realpath the hook pins is `<deployment>/agents/<name>/souls/<commit12>`, which
|
|
265
|
+
never equals the 0.24 pin (`<repo>/agents/<name>/soul`) and changes whenever the
|
|
266
|
+
member moves. Two consequences:
|
|
267
|
+
|
|
268
|
+
- **Do not reuse the 0.24 `state-dir`.** Its `owners.json` pins every owner to
|
|
269
|
+
the old path; the first v2 spawn of each soul would be refused with `E_OWNER`.
|
|
270
|
+
Give the rebuilt deployment a fresh `state-dir` (§5) and a fresh
|
|
271
|
+
`bindings-file` if the old one names the old state root. The old `state-dir`
|
|
272
|
+
is **frozen custody**: read-only history (`oats okf inspect --source
|
|
273
|
+
<old-state>/sources/<id>/source.json …` still works against it), never edited,
|
|
274
|
+
never re-pointed at the new soul path. Accepted knowledge is not affected —
|
|
275
|
+
it lives in the bases, not in `state-dir`.
|
|
276
|
+
- **The owner pin is per commit.** OKF 2.1.3 records the realpath at first
|
|
277
|
+
registration and the kernel keeps that commit directory for as long as any
|
|
278
|
+
instance links it, so a running instance's pin stays valid; a *later* spawn of
|
|
279
|
+
the same soul at a newer member commit links a different directory and
|
|
280
|
+
registers under the same owner id → `E_OWNER` again. Until OKF re-bases the
|
|
281
|
+
pin on the owner identity rather than the path (an OKF 2.1.4 item), the
|
|
282
|
+
practical rule is: one `state-dir` per (deployment, soul commit) is safe;
|
|
283
|
+
moving a member that owns knowledge means a fresh `state-dir` for the new
|
|
284
|
+
commit's spawns (the previous one becomes frozen custody, as above). Plan
|
|
285
|
+
knowledge-owning souls' member commits deliberately.
|
|
286
|
+
|
|
182
287
|
## 8. Re-take a retained messaging seat with `spawn --provider`
|
|
183
288
|
|
|
184
289
|
In 0.24, an instance-specific messaging identity (a retained seat) was pinned in
|
|
@@ -186,14 +291,22 @@ In 0.24, an instance-specific messaging identity (a retained seat) was pinned in
|
|
|
186
291
|
**spawn**:
|
|
187
292
|
|
|
188
293
|
```bash
|
|
189
|
-
oats spawn release-manager --purpose seat --provider oats.aweb identity.source
|
|
294
|
+
oats spawn release-manager --purpose seat --provider oats.aweb identity.source=/abs/path/to/retained/.aw
|
|
190
295
|
```
|
|
191
296
|
|
|
192
297
|
`--provider <cap> key=value` is repeatable; dotted keys nest. The payload is
|
|
193
298
|
merged after the soul's `messaging:` and the machine's `settings.oats.aweb`, and
|
|
194
299
|
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
|
-
|
|
300
|
+
the seat while other instances of the soul mint fresh identities.
|
|
301
|
+
|
|
302
|
+
**The value is the path itself.** `oats.aweb` reads `identity.source` as the
|
|
303
|
+
absolute path of the `.aw` directory to retain (it must hold `signing.key`); the
|
|
304
|
+
kernel does not resolve symbolic seat names. Because it is an absolute path it is
|
|
305
|
+
a fact about ONE machine, so its other legal home is `oats-local.yaml`
|
|
306
|
+
(`settings.oats.aweb.identity.source: /abs/path`) — never the workspace file
|
|
307
|
+
(absolute paths are refused there, decision 14). Prefer the spawn form: a
|
|
308
|
+
machine-level setting would give the seat to EVERY instance of every messaging
|
|
309
|
+
soul on that machine, and a seat can be held once. The Desktop's
|
|
197
310
|
confirmed apply carries the same map.
|
|
198
311
|
|
|
199
312
|
## 9. Spawn, and check drift
|
|
@@ -212,6 +325,7 @@ oats status # per instance: modules … [member moved since (n
|
|
|
212
325
|
|---|---|
|
|
213
326
|
| `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
327
|
| `oats.yaml` | `oats-membership.yaml` |
|
|
328
|
+
| `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
329
|
| `.agents/capabilities/installed/` and `owned/` | nothing is installed; `<instance>/.oats/modules/<cap>/` per instance; member capabilities under `<repo>/capabilities/` |
|
|
216
330
|
| `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
331
|
| 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.
|
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
|
|
@@ -176,7 +176,7 @@ instructions and pins model/provider settings; it excludes nothing.
|
|
|
176
176
|
```bash
|
|
177
177
|
oats spawn release-manager --purpose cut-3.2 --task "…" # a soul of a confirmed member
|
|
178
178
|
oats spawn release-manager --preview --json # decide everything, create nothing
|
|
179
|
-
oats spawn release-manager --provider oats.aweb identity.source
|
|
179
|
+
oats spawn release-manager --provider oats.aweb identity.source=/abs/path/to/retained/.aw # instance-level payload
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
From a deployment (where `oats-local.yaml` is), a spawn: reads the local file →
|
|
@@ -208,7 +208,7 @@ Examples of spawn hooks:
|
|
|
208
208
|
registers a durable source plus its per-source schedule definition. Missing
|
|
209
209
|
knowledge is an error, not permission to bootstrap an empty substitute.
|
|
210
210
|
- `oats.aweb` mints a messaging identity — or, with
|
|
211
|
-
`--provider oats.aweb identity.source
|
|
211
|
+
`--provider oats.aweb identity.source=/abs/path/of/the/.aw/to/retain`, re-takes a retained
|
|
212
212
|
one for exactly this instance.
|
|
213
213
|
|
|
214
214
|
### Work
|
package/docs/workspaces.md
CHANGED
|
@@ -346,7 +346,7 @@ store** — it organises and can supply defaults. The messaging provider's paylo
|
|
|
346
346
|
|---|---|---|
|
|
347
347
|
| True of every instance of the soul | `soul.yaml` → `knowledge:` / `messaging:` / `tasks:` | `knowledge: { owns: release-manager }` |
|
|
348
348
|
| A fact about this machine | `oats-local.yaml` → `settings.<cap>.<key>` (absolute paths are refused in the workspace file) | `settings.oats.okf.state-dir: /Users/ana/.oats/okf` |
|
|
349
|
-
| A fact about **this spawn** | `oats spawn … --provider <cap> key=value` (repeatable; dotted keys nest) → `instance.json.providers.<cap>` | `--provider oats.aweb identity.source
|
|
349
|
+
| A fact about **this spawn** | `oats spawn … --provider <cap> key=value` (repeatable; dotted keys nest) → `instance.json.providers.<cap>` | `--provider oats.aweb identity.source=/abs/path/to/retained/.aw` |
|
|
350
350
|
|
|
351
351
|
The merged payload is `workspace.messaging` (messaging slot only; its base
|
|
352
352
|
keys ⊕ `byTeam[<soul's team>]`, with `byTeam` itself stripped) ⊕ soul slot
|
|
@@ -421,6 +421,13 @@ ref>` — the kernel notices it is a member whose workspace it cannot read and
|
|
|
421
421
|
falls back to the standalone view — or `standalone: <repo ref>` to ask for
|
|
422
422
|
that view explicitly).
|
|
423
423
|
|
|
424
|
+
**Executables from public members.** Membership is the trust (decision 2): a
|
|
425
|
+
member capability's hooks and command scripts run on every operator's machine at
|
|
426
|
+
spawn, gated by nothing but the handshake. In a mixed public/private
|
|
427
|
+
organisation keep **souls only** in public members and let executable
|
|
428
|
+
capabilities come from packages (approved per version in the lock) or from
|
|
429
|
+
private members.
|
|
430
|
+
|
|
424
431
|
**Hosting the workspace file when some members are private.** Everyone who
|
|
425
432
|
can read the workspace file sees the member list. So: a public member never
|
|
426
433
|
hosts it when any member is private (it would publish the private repo's
|