@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/conventions.md
CHANGED
|
@@ -31,43 +31,70 @@ instance/.claude/skills -> ../.agents/skills
|
|
|
31
31
|
|
|
32
32
|
Spawn copies kernel + soul-private + active capability skills into real
|
|
33
33
|
instance-local directories there. Directory symlinks are not used because
|
|
34
|
-
harness recursive discovery may not descend through them.
|
|
35
|
-
|
|
34
|
+
harness recursive discovery may not descend through them. Under the workspace
|
|
35
|
+
model (0.25) every capability is copied whole into
|
|
36
|
+
`instance/.oats/modules/<capability>/` and its skills into
|
|
37
|
+
`instance/.agents/skills/<capability>/<skill>/`; nothing is installed at a
|
|
38
|
+
config level. Config-level `.agents/skills` is not an OATS capability source
|
|
36
39
|
or an ambient runtime discovery root.
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
and
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
41
|
+
Harnesses start normally (workspace-model decision 13, and the behaviour of
|
|
42
|
+
every launch a 0.25 kernel performs, classic homes included): Pi starts with
|
|
43
|
+
cwd = the instance home, the composed `AGENTS.md` appended to its system
|
|
44
|
+
prompt, and its own skill, context and extension discovery intact — the
|
|
45
|
+
instance's copied skills are found because they sit under cwd; machine-level
|
|
46
|
+
and repo-level skills resolve exactly as without OATS. *(0.24 kernels started
|
|
47
|
+
Pi with ambient skill and context discovery disabled and the one instance path
|
|
48
|
+
explicit; that exclusion is gone.)* Claude runs provider-native: it reads the
|
|
49
|
+
instance's `.claude/skills` and `CLAUDE.md` symlinks, and the operator's own
|
|
50
|
+
user and project configuration — skills, plugins, settings — stays in effect.
|
|
51
|
+
Neither runtime gets a redirected config home.
|
|
52
|
+
`composition.materialized.runtimePosture` in `instance.json` records what each
|
|
53
|
+
instance actually exposes. `oats-getting-started` is the sole pre-workspace
|
|
54
|
+
ambient bootstrap.
|
|
55
|
+
|
|
56
|
+
Duplicate skill directory names within the OATS-composed set are errors
|
|
57
|
+
(`E_SKILL_DUPLICATE`, naming both capabilities) unless the classic config's
|
|
58
|
+
`skill-overrides` selects a source; between a composed skill and an ambient
|
|
59
|
+
repo/machine skill the harness's own precedence decides (decision 16).
|
|
49
60
|
|
|
50
61
|
## Package locations
|
|
51
62
|
|
|
63
|
+
**Workspace model (0.25, current):** nothing is installed. A capability lives
|
|
64
|
+
where its owner keeps it and is copied whole into each instance at spawn:
|
|
65
|
+
|
|
66
|
+
```text
|
|
67
|
+
<member repo>/capabilities/<name>/oats.json # member-tier capability, latest state (membership is the trust)
|
|
68
|
+
<package repo>/oats-package/oats-package.json # package-tier: versioned via oats-workspace.yaml packages:
|
|
69
|
+
<deployment>/oats-lock.json # lockfileVersion 3: package commit, integrity, per-version approval
|
|
70
|
+
<instance>/.oats/modules/<capability>/ # the copy this instance runs
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Classic 0.24 layout** (still launched by the 0.24 kernel; a 0.25 kernel
|
|
74
|
+
reads none of it as configuration — see [rebuild-to-v2.md](rebuild-to-v2.md)):
|
|
75
|
+
|
|
52
76
|
```text
|
|
53
77
|
<package>/capabilities/<name>/oats.json # the official marketplace (install source, not ambient)
|
|
54
78
|
<level>/.agents/capabilities/installed/<name>/oats.json # acquired (gitignored, restorable)
|
|
55
79
|
<level>/.agents/capabilities/owned/<name>/oats.json # authored at this scope (source; committed where the scope is a repo)
|
|
56
|
-
<level>/oats-lock.json # external source/integrity/trust
|
|
80
|
+
<level>/oats-lock.json # lockfileVersion 2: external source/integrity/trust
|
|
57
81
|
```
|
|
58
82
|
|
|
59
83
|
## Quick map
|
|
60
84
|
|
|
61
|
-
| Thing | Canonical location |
|
|
62
|
-
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
| Soul
|
|
67
|
-
| Soul
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
| Instance
|
|
85
|
+
| Thing | Canonical location (0.25 workspace model) | 0.24 classic |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| Shared declaration | `oats-workspace.yaml` in the host repo; `oats-membership.yaml` in every member | `oats-config.yaml` chain, `oats.yaml` |
|
|
88
|
+
| Per-machine config | `<deployment>/oats-local.yaml` (uncommitted) | `oats-config.yaml` `settings:` |
|
|
89
|
+
| Acquisition lock | `<deployment>/oats-lock.json` (v3) | `<level>/oats-lock.json` (v2) |
|
|
90
|
+
| Soul source | `<member repo>/souls/<name>/` | `agents/<name>/soul/` |
|
|
91
|
+
| Soul operating doc | `souls/<name>/AGENTS.md` | `soul/AGENTS.md` |
|
|
92
|
+
| Soul Claude view | `souls/<name>/CLAUDE.md -> AGENTS.md` | `soul/CLAUDE.md -> AGENTS.md` |
|
|
93
|
+
| Soul-private skills | `souls/<name>/skills/` | `soul/skills/` |
|
|
94
|
+
| Instance operating doc | `instance/AGENTS.md` (generated) | same |
|
|
95
|
+
| Instance skill set | `instance/.agents/skills/` | same |
|
|
96
|
+
| Instance modules | `instance/.oats/modules/<capability>/` | `.agents/capabilities/installed/` (shared) |
|
|
97
|
+
| Instance metadata | `instance/instance.json` (`modules{}`, `providers{}`, `workspace{}`) | `instance/instance.json` |
|
|
71
98
|
|
|
72
99
|
Symlinks prevent compatibility paths from drifting. Generated regular files
|
|
73
100
|
separate canonical portable identity from scope-dependent runtime policy.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Purpose:** the one accurate view of every work stream in the redesign, what is on main, what is in flight, who owns it, and what blocks it. Lead: `oats-expert` (redesign lead). Updated whenever anything merges, is returned, or reality changes. Older per-lane boards are superseded by this file.
|
|
4
4
|
|
|
5
|
-
**Last update:** 2026-09-23
|
|
5
|
+
**Last update:** 2026-09-23 20:00Z · **0.25.0 + 0.25.1 PUBLISHED** (workspace model A–C + team-review fixes) · Phase D plan next · **Desktop engineer paused by the human** (10B-0 uncommitted foundation preserved; resume is the human's call; 0.24.14 to be cut from a maintenance branch off `e5cdaf95` when its PR lands) · parity pipeline ⏸.
|
|
6
6
|
|
|
7
7
|
Legend: ✅ on main/published · 🔄 in flight (PR/branch) · 🟡 preserved, not adopted · ⬜ not started · ⛔ blocked
|
|
8
8
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**Status:** Phase1 implementation authorised by the human, using the existing developers under lead supervision/review. The workspace home is confirmed as `oats`; `oats-dev` remains development capabilities. This does not claim completed conversion or authorise unspecified new contracts, credential operations or live deployment mutations.
|
|
6
6
|
|
|
7
|
-
> **2026-09-23 — direction change, read first.** Phases 1–3 delivered as written (workspace on Git, five souls + central knowledge, all contract-bearing Desktop parity slices, releases 0.24.7–0.24.13). Reviewing the result, the human judged the *declaration model* itself too heavy and, in one sitting, accepted a simplified **workspace model v2**: one workspace per org; reciprocal membership as the only gate and as the trust decision; a soul says `from:` (member repo | `here` | `package`) — a location, never a version; packages are the only versioned thing; **nothing is installed** — every capability is copied whole into the instance at spawn; discovery over Git remotes; teams as labels; harnesses start normally. Record: `agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md`; worked example: [2026-09-23-simplified-workspace-model.md](2026-09-23-simplified-workspace-model.md). **Consequences for this plan:** P1.3's `oats.yaml` export indexes and P1.4's `oats-config.yaml` activation are superseded (→ `oats-membership.yaml`, derived activation); D1/D4 stand (`oats.core` becomes `from: package` by default; the catalog stays as discovery); D2's "explicit `oats.core` on every soul" is met by the workspace default; D3 gains the
|
|
7
|
+
> **2026-09-23 — direction change, read first.** Phases 1–3 delivered as written (workspace on Git, five souls + central knowledge, all contract-bearing Desktop parity slices, releases 0.24.7–0.24.13). Reviewing the result, the human judged the *declaration model* itself too heavy and, in one sitting, accepted a simplified **workspace model v2**: one workspace per org; reciprocal membership as the only gate and as the trust decision; a soul says `from:` (member repo | `here` | `package`) — a location, never a version; packages are the only versioned thing; **nothing is installed** — every capability is copied whole into the instance at spawn; discovery over Git remotes; teams as labels; harnesses start normally. Record: `agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md`; worked example: [2026-09-23-simplified-workspace-model.md](2026-09-23-simplified-workspace-model.md). **Consequences for this plan:** P1.3's `oats.yaml` export indexes and P1.4's `oats-config.yaml` activation are superseded (→ `oats-membership.yaml`, derived activation); D1/D4 stand (`oats.core` becomes `from: package` by default; the catalog stays as discovery); D2's "explicit `oats.core` on every soul" is met by the workspace default; D3 gains the `the operator's deployment directory` convention and clone-then-spawn. **No migration** — clean v2, the framework's own repos are the first workspace. A Phase 4 implementation plan is proposed to the human before any `lib/`/`bin/` change; the Desktop parity pipeline is paused meanwhile except for the in-flight 10B-0 security fix.
|
|
8
8
|
|
|
9
9
|
## Goal and order
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# A simpler multi-repo model for OATS — brainstorm with a worked example
|
|
2
2
|
|
|
3
|
-
**Status:** **ACCEPTED DIRECTION** — every question closed with the human on 2026-09-23; nothing implemented yet. This document is the worked example; the normative record is the Decision concept `agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md` and the adoption plan. Implementation is a clean v2 (no migration), planned separately. **Decided so far (human, 2026-09-23):** (1) reciprocal membership stays a hard gate; (2) workspace-tier trust = membership, nothing more; (3) everything in a member is discoverable by default — a soul or capability that wants to stay internal says `private: true` in its own definition; (4) the repo's half of the handshake is a one-line **`oats-membership.yaml`** (replaces `oats.yaml`); (5) **no migration path** — a clean break to v2; (6) a soul names each capability **with where it comes from** (`from: <member repo>` or `from: package`) — a location, never a version; resolution is a handshake check + lookup, not a search; (7) **full per-instance materialization** — every capability (skills, injects, scripts, hooks) is copied into the instance at spawn; a running instance never changes under itself; (8) **nothing is "installed"** — a package is just a second kind of source, fetched at the pinned version and copied in like a member's capability; the lock records exact commit/integrity and the one-time executable approval per version; (9) **discovery and resolution work against Git remotes, never local clones** — the only thing that needs a clone is a soul's work target; the local layout is the operator's,
|
|
3
|
+
**Status:** **ACCEPTED DIRECTION** — every question closed with the human on 2026-09-23; nothing implemented yet. This document is the worked example; the normative record is the Decision concept `agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md` and the adoption plan. Implementation is a clean v2 (no migration), planned separately. **Decided so far (human, 2026-09-23):** (1) reciprocal membership stays a hard gate; (2) workspace-tier trust = membership, nothing more; (3) everything in a member is discoverable by default — a soul or capability that wants to stay internal says `private: true` in its own definition; (4) the repo's half of the handshake is a one-line **`oats-membership.yaml`** (replaces `oats.yaml`); (5) **no migration path** — a clean break to v2; (6) a soul names each capability **with where it comes from** (`from: <member repo>` or `from: package`) — a location, never a version; resolution is a handshake check + lookup, not a search; (7) **full per-instance materialization** — every capability (skills, injects, scripts, hooks) is copied into the instance at spawn; a running instance never changes under itself; (8) **nothing is "installed"** — a package is just a second kind of source, fetched at the pinned version and copied in like a member's capability; the lock records exact commit/integrity and the one-time executable approval per version; (9) **discovery and resolution work against Git remotes, never local clones** — the only thing that needs a clone is a soul's work target; the local layout is the operator's — onboarding asks for the directory, no named convention; (10) the handshake is *observed* with the operator's own read access to both halves — no access to the workspace repo means no membership from that seat, by design; (11) **members carry no revision** — a member is always its latest state; a team that wants frozen capabilities publishes them as a package and pins that; (12) **one workspace per org, teams are labels** — `team:` on a soul/capability (or a repo default in `oats-membership.yaml`) organises and can supply additive defaults, but never gates, restricts or partitions anything; (13) **harnesses start normally and resolve skills from their own default places** — OATS contributes materialized capability skills into the instance's `.agents/skills/` and stops excluding machine-level or repo-level skills. **All questions closed; ready to be written up as a Decision.**
|
|
4
4
|
**Date:** 2026-09-23
|
|
5
5
|
**Author:** oats-expert, from a conversation with the human about the weight of the current declaration files.
|
|
6
6
|
|
|
@@ -419,10 +419,10 @@ Northwind adopts it through `external:` with a pinned revision. There is deliber
|
|
|
419
419
|
|
|
420
420
|
**The only thing that needs a local clone is a soul's work target** — a soul with `work: worktree | checkout | directory` works *in* a repo, so that repo must be on disk. Spawning a soul whose repo is not yet cloned is therefore a *guided clone into the conventional place, then spawn*: a job for the onboarding skill (`oats-setup-expert`), not the kernel.
|
|
421
421
|
|
|
422
|
-
The
|
|
422
|
+
**The deployment directory is the operator's choice** (decision 9): an existing folder that already holds the member clones is the usual case; onboarding asks for it (`oats onboard <dir>`) and adds the three entries the kernel needs. Shown here as Ana's `~/northwind/`, with the member clones inside it:
|
|
423
423
|
|
|
424
424
|
```
|
|
425
|
-
~/northwind
|
|
425
|
+
~/northwind/ ← the directory Ana chose (any name, any place)
|
|
426
426
|
├── oats-local.yaml ← which workspace this machine realizes + host paths + disabled souls
|
|
427
427
|
├── oats-lock.json ← exact commit + integrity + per-version approval per package
|
|
428
428
|
├── agents/ ← instance homes, each self-contained
|
|
@@ -510,7 +510,7 @@ Today's `capabilities.layers.messaging.{capability, from, global, souls, setting
|
|
|
510
510
|
|---|---|---|
|
|
511
511
|
| True of every instance of the soul | `soul.yaml` → `messaging:` / `knowledge:` | `messaging: { channels: [northwind-eng] }`, `knowledge: { owns: release-manager }` |
|
|
512
512
|
| A fact about this machine | `oats-local.yaml` → `settings.<capability>.<key>` (absolute paths refused in the workspace file) | `settings.oats.okf.state-dir: /Users/ana/.oats/okf` |
|
|
513
|
-
| A fact about **this spawn** | `oats spawn … --provider <cap> key=value` → `instance.json` `providers.<cap>` (the Desktop's confirmed apply carries the same map) | `--provider oats.aweb identity.source
|
|
513
|
+
| A fact about **this spawn** | `oats spawn … --provider <cap> key=value` → `instance.json` `providers.<cap>` (the Desktop's confirmed apply carries the same map) | `--provider oats.aweb identity.source=/abs/path/to/retained/.aw` — one instance takes the retained seat; other instances of the soul mint fresh |
|
|
514
514
|
|
|
515
515
|
The provider's `binding` contract (`normalize → bind → check`) runs over the merged payload exactly as today; the provider still enforces its own rules (e.g. a state root outside the work tree).
|
|
516
516
|
|
|
@@ -565,7 +565,7 @@ OATS is a **skill contributor, not a skill sandbox**. Today the harness is launc
|
|
|
565
565
|
- **Duplicate names** (team review): two *composed* capability skills with the same name are still a spawn error naming both (`skill-overrides` picks one). A composed skill and an ambient repo/machine skill with the same name is **not** an error — the harness's precedence decides; the preview lists composed names so a clash is visible.
|
|
566
566
|
|
|
567
567
|
```
|
|
568
|
-
~/northwind
|
|
568
|
+
~/northwind/agents/release-manager/instances/release-manager-v3/
|
|
569
569
|
├── AGENTS.md ← canonical, composed: soul AGENTS.md + capability injects
|
|
570
570
|
├── CLAUDE.md → AGENTS.md ← relative symlink (unchanged from today)
|
|
571
571
|
├── .agents/skills/ ← canonical; where Pi (and Codex) look
|
|
@@ -307,3 +307,260 @@ Appended, not edited in place; each item names the section it refines. Decision
|
|
|
307
307
|
**§6 `oats package remove <id>` (M15).** BOTH branches — the file tracked by the checkout (edited) and untracked/absent (not edited) — answer `E_PACKAGE_MISSING { id, path? }` when `<id>` is not declared in `packages:`. The tracked receipt is `{ action: "remove", id, value: null, previous: <old value>, edited: true, file }`; the untracked receipt is `{ action: "remove", id, value: null, edited: false, file: null, line: null, hint }` — `previous` is absent and `line` is `null` (a removal has no line to add).
|
|
308
308
|
|
|
309
309
|
**§6 features.** `catalog` is no longer advertised in `oats version --json` `features` (the verb is removed; the official catalog is reached through `packages:` + `sync`, not a command).
|
|
310
|
+
|
|
311
|
+
## Post-0.25.0 clarifications (team review, 2026-09-23)
|
|
312
|
+
|
|
313
|
+
**Capability commands outside an instance home (`oats <ns> <cmd>` from the
|
|
314
|
+
deployment).** Inside an instance home the dispatcher resolves the namespace from
|
|
315
|
+
the home's materialized modules (`instance.json.modules` → `<home>/.oats/modules`);
|
|
316
|
+
that shipped in 0.25.0. Outside a home — the operator acts a knowledge layer needs
|
|
317
|
+
before any instance exists (`oats okf init`, base migration) — the intended rule
|
|
318
|
+
is: **resolve exactly as a spawn of `--soul <name>` would** (`prepareInstance` →
|
|
319
|
+
the soul's Resolution), fetch the namespace's capability into the deployment's
|
|
320
|
+
module store `<deployment>/.oats/modules/<cap>@<commit>/` (the same per-commit
|
|
321
|
+
store capability-defined agents use), and dispatch to that copy with the soul's
|
|
322
|
+
merged payload as `OATS_SETTINGS`. Never "the newest instance's copy" (an
|
|
323
|
+
instance is not an authority for the deployment) and never an unlocked cache
|
|
324
|
+
read (the lock's approval is the gate, as for spawn). `--soul` is required when
|
|
325
|
+
the namespace's capability is not a workspace default. **Status: 0.25.x
|
|
326
|
+
follow-up** — 0.25.0 still answers `E_CAPABILITY_INACTIVE` there (the pre-v2
|
|
327
|
+
chain); the interim is to run the module binary directly with `OATS_SETTINGS`
|
|
328
|
+
and `OATS_CLI_BIN`, as the tarball smoke does.
|
|
329
|
+
|
|
330
|
+
**`work: workspace` is kept.** A coordination soul's `./work` is the deployment
|
|
331
|
+
boundary — the directory holding `oats-local.yaml` (whatever the operator
|
|
332
|
+
named it, member clones beside it or named in `clones:`) — read-only across member
|
|
333
|
+
clones, no branch recorded. The clone map in `oats-local.yaml` (`clones:`) is
|
|
334
|
+
how such a soul finds a member whose clone is elsewhere. **Status: the
|
|
335
|
+
directory link is the intent; 0.25.0's kernel still derives the boundary from
|
|
336
|
+
the classic `team:` scope (`docs/souls-and-instances.md` open thread) — 0.25.x
|
|
337
|
+
follow-up binds it to the `oats-local.yaml` directory.**
|
|
338
|
+
|
|
339
|
+
**`identity.source` (oats.aweb) is the absolute path of the `.aw` directory to
|
|
340
|
+
retain**, given per spawn (`--provider oats.aweb identity.source=/abs/.aw`) or
|
|
341
|
+
per machine (`oats-local.yaml settings.oats.aweb.identity.source`); the kernel
|
|
342
|
+
resolves no symbolic seat names. Absolute paths never enter the workspace file
|
|
343
|
+
(decision 14).
|
|
344
|
+
|
|
345
|
+
**Member capabilities are a code-execution boundary** (decision 2: membership is
|
|
346
|
+
the trust; hooks and scripts of every member's default branch run on every
|
|
347
|
+
operator's machine at spawn). For a mixed public/private organisation the
|
|
348
|
+
recommendation is: **souls only in public members; executable capabilities come
|
|
349
|
+
from packages (approved per version) or from private members.** The onboarding
|
|
350
|
+
skill (Phase E) states this beside the hosting rule (decision 26).
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
### 0.25.1 fix round (team review, 2026-09-23) — appended, not edited in place
|
|
354
|
+
|
|
355
|
+
Each item names the section it refines and the review finding it closes. The
|
|
356
|
+
implementation lands in kernel 0.25.1 (`docs/release-notes/v0.25.1.md`); no
|
|
357
|
+
API integer or feature name changes.
|
|
358
|
+
|
|
359
|
+
**§3 slot `none` (L1).** A soul's `none` for a slot **empties the slot and drops
|
|
360
|
+
any layer-bearing capability the WORKSPACE DEFAULTS contributed for that
|
|
361
|
+
layer** — whether it arrived through `defaults.<slot>`, `defaults.capabilities`
|
|
362
|
+
or `defaults.byTeam[team].capabilities`. A layer-bearing capability **the soul
|
|
363
|
+
itself declares** in its own `capabilities:` alongside `none` for that layer is
|
|
364
|
+
`E_SLOT_CONFLICT { reason: "none" }` (spell `<cap>: off` to remove it). This
|
|
365
|
+
replaces the Phase B reading under which a layered default arriving via
|
|
366
|
+
`defaults.capabilities` was itself a conflict: the workspace's choice is a
|
|
367
|
+
default and `none` is the soul's answer to it; only the soul contradicting
|
|
368
|
+
itself is loud.
|
|
369
|
+
|
|
370
|
+
**§2/§5 per-commit soul cache (M1).** `ensureWorkspaceSoul` fetches a soul's
|
|
371
|
+
source at `(repoKey, commit)` into `<deployment>/agents/<name>/souls/<commit12>/`
|
|
372
|
+
— **immutable once written** (staged, then renamed in; never removed by the
|
|
373
|
+
kernel) — and maintains `<deployment>/agents/<name>/soul` as a **symlink to the
|
|
374
|
+
current commit's directory**, swapped atomically (symlink to a temp name +
|
|
375
|
+
rename over) so classic readers (`findAgent`, `doctor`, the classic spawn path)
|
|
376
|
+
keep seeing "current". A spawned home's `<home>/soul` links **its own commit's
|
|
377
|
+
directory** (the realpath of `souls/<commit12>/`), never the swapped pointer:
|
|
378
|
+
a running instance's soul never changes under it (decision 7), a `--preview`
|
|
379
|
+
may fetch a new commit and swap the pointer without touching any directory an
|
|
380
|
+
instance links, and OKF 2's owner pin (`owners.json` = `realpath(<home>/soul)`)
|
|
381
|
+
stays valid for the instance that registered it. `.oats-soul-source.json`
|
|
382
|
+
remains the stamp of "current". A 0.25.0 layout (`agents/<name>/soul` a real
|
|
383
|
+
directory, no `souls/`) is migrated in place on first use: the directory moves
|
|
384
|
+
to `souls/<commit from the stamp, else unknown>/` and the pointer replaces it.
|
|
385
|
+
A soul symlink whose target lies inside the same `agents/<name>/souls/` is the
|
|
386
|
+
one kernel-owned symlink soul readers accept.
|
|
387
|
+
|
|
388
|
+
**§1 transport (M2).** `parseRepoRef(ref).key` is unchanged — `<host>/<path>`
|
|
389
|
+
is the identity everywhere and every comparison is by key. The **fetch url
|
|
390
|
+
honours the form written**: `git@host:org/repo(.git)` and `ssh://…` fetch over
|
|
391
|
+
SSH as written; `https://…` fetches over HTTPS; the bare scheme
|
|
392
|
+
`git:host/org/repo` fetches over HTTPS by default **unless
|
|
393
|
+
`remoteOptions.transport === "ssh"`** (a per-machine choice; `oats-local.yaml`
|
|
394
|
+
may carry it once the schema admits it — reported by lane 3, not landed here).
|
|
395
|
+
The operator's SSH access is therefore used when the operator wrote an SSH ref,
|
|
396
|
+
and a private repo no longer degrades to `not-found` → standalone through an
|
|
397
|
+
unintended HTTPS probe. When the standalone fallback engages the discovery
|
|
398
|
+
carries `standaloneReason`/`hostFailure { code, reason, url }` so the CLI can
|
|
399
|
+
print *why*.
|
|
400
|
+
|
|
401
|
+
**§3/§4 approval re-verified at spawn (M3).** For a `from: package` module
|
|
402
|
+
`resolveSoul` recomputes `executablesDigest` over the package tree **at the
|
|
403
|
+
locked `entry.commit`** and requires equality with `entry.approved.executables`
|
|
404
|
+
→ else `E_PACKAGE_UNAPPROVED { reason: "digest-mismatch", approved, actual }`.
|
|
405
|
+
The one digest definition is `lib/packages.mjs#executablesDigestAt(remote, ref,
|
|
406
|
+
commit, path, capabilities)`, shared by `sync` and `resolve`; a hand-edited lock
|
|
407
|
+
(same id/version, different commit, copied approval) can no longer materialize
|
|
408
|
+
and run unapproved hooks. Cached per `(id, commit)` within a process.
|
|
409
|
+
|
|
410
|
+
**§1 peeled commit OIDs (M4).** `observeRemote(ref, { at })` accepts an
|
|
411
|
+
annotated tag's OID (or name) but **records the peeled commit** (`<oid>^{commit}`)
|
|
412
|
+
as `commit` — in its result, in the lock, in `fetchRemoteTree`'s errors and in
|
|
413
|
+
every `instance.json` record. A tag OID is never stored where a commit is
|
|
414
|
+
expected.
|
|
415
|
+
|
|
416
|
+
**§1 listing hygiene (L3, L4).** `listRemoteTree` filters by depth **before**
|
|
417
|
+
asserting entry-name safety, so one unsafe deep name does not blank a member's
|
|
418
|
+
souls (unsafe names at the kept depth are still `E_REMOTE_TREE_UNSAFE`). A git
|
|
419
|
+
child killed for `maxBuffer` (`ENOBUFS`) is not reported as `timeout`; an
|
|
420
|
+
unclassified listing failure is wrapped as `E_REMOTE_UNREADABLE { reason:
|
|
421
|
+
"unknown" }` so discovery records a problem row instead of aborting.
|
|
422
|
+
|
|
423
|
+
**§2 `validateWorkspace` absolute paths (L2).** The absolute-path refusal
|
|
424
|
+
applies to **ref/path fields only** — `members[]`, `packages` values, `stores`
|
|
425
|
+
values, `external[].source` / `external[].soul`, `defaults.*.from` — never to
|
|
426
|
+
`teams.<label>.description` or to the opaque `messaging` payload.
|
|
427
|
+
|
|
428
|
+
**§3 revision (L6).** `Resolution.revision = hash(declRevision, payloadRevision)`
|
|
429
|
+
where `declRevision` covers the declarations (member/package commits, the
|
|
430
|
+
composed capability set, skills, injects) and `payloadRevision` covers the
|
|
431
|
+
payload layers (soul slot payloads ⊕ `oats-local.yaml settings` ⊕
|
|
432
|
+
`--provider`). Both are exposed on the Resolution; decision binding keeps using
|
|
433
|
+
`revision`, so a settings-only difference still refuses a stale apply, while
|
|
434
|
+
`spawn --preview` can say **`changed since: declarations | payload | both`**
|
|
435
|
+
instead of a bare `changedSince`.
|
|
436
|
+
|
|
437
|
+
**§6 operator-level dispatch (B3, implements the rule stated above).** Outside a
|
|
438
|
+
home, with `oats-local.yaml` present, `oats <ns> <cmd> … --soul <name>` runs
|
|
439
|
+
`prepareInstance(dir, name)`, picks the module whose `manifest.command === <ns>`,
|
|
440
|
+
ensures its tree in `<deployment>/.oats/modules/<cap>@<commit12>/` (member: the
|
|
441
|
+
member repo at `module.from.commit`, `module.dir`; package: the lock entry) and
|
|
442
|
+
dispatches to that copy with `OATS_SETTINGS = resolution.payloads[cap]` and
|
|
443
|
+
`OATS_CLI_BIN`. `--soul` absent → `E_BAD_ARGS` naming it; a namespace no module
|
|
444
|
+
provides → `E_UNKNOWN_COMMAND`. Trust is the resolution's (membership;
|
|
445
|
+
`E_PACKAGE_UNAPPROVED` for an unapproved package).
|
|
446
|
+
|
|
447
|
+
**§5/§6 `work: workspace` under v2 (B2, implements the rule stated above).**
|
|
448
|
+
With `prepared` present, a `work: workspace` soul's `./work` links the
|
|
449
|
+
deployment directory (`prepared.deployment`, the one holding `oats-local.yaml`);
|
|
450
|
+
no branch is recorded; the "needs a declared boundary" remedy names
|
|
451
|
+
`oats-local.yaml`, not `oats-config.yaml`. The classic root is unchanged.
|
|
452
|
+
|
|
453
|
+
**Decision 13 reach (L7).** "Harnesses start normally" is a property of the
|
|
454
|
+
0.25 **launcher**: every `pi` launch a 0.25 kernel performs — module homes and
|
|
455
|
+
classic 0.24 homes alike — starts pi with cwd = home, the composed `AGENTS.md`
|
|
456
|
+
appended, and pi's own skill/context discovery intact. Consequently `oats
|
|
457
|
+
session recompose` refuses a **module home** (`instance.json.modules` present)
|
|
458
|
+
with `E_UNSUPPORTED_MODE` ("re-spawn"); the `session-recompose` feature name
|
|
459
|
+
stays advertised because the verb still serves classic homes
|
|
460
|
+
(`docs/desktop-cli-api.md`).
|
|
461
|
+
|
|
462
|
+
### 0.25.2 operator-rebuild round (2026-09-24) — appended, not edited in place
|
|
463
|
+
|
|
464
|
+
Source: an operator's first rebuild of a real two-team deployment on 0.25.0,
|
|
465
|
+
following `docs/rebuild-to-v2.md` literally. **The guide is a contract the
|
|
466
|
+
kernel must honour**: where the guide claimed behaviour the kernel lacked, the
|
|
467
|
+
kernel changes; where the guide described keys no provider consumes, the guide
|
|
468
|
+
changes. Findings R1–R10; kernel side in 0.25.2 (`docs/release-notes/v0.25.2.md`).
|
|
469
|
+
No API integer or feature name changes; the only surface additions are
|
|
470
|
+
additive fields (`spawn --preview` `providers` / `settings`, `oats status
|
|
471
|
+
--json instances[].soul`) and the `sync --approve` flag.
|
|
472
|
+
|
|
473
|
+
**§2/§5 member clone lookup (R1).** For a `work: worktree | checkout` soul the
|
|
474
|
+
kernel finds the member clone in this order, first hit wins: (1) `oats spawn
|
|
475
|
+
--repo <abs path>`; (2) `oats-local.yaml` `clones: { <repo key>: <abs path> }`,
|
|
476
|
+
keys normalised through `parseRepoRef(...).key` so any spelling of the same
|
|
477
|
+
repo addresses one entry; (3) the convention `<deployment>/<member name>` where
|
|
478
|
+
`<member name>` is the last segment of the repo key — **a member named `agents`
|
|
479
|
+
is looked for at `<deployment>/agents-repo`** (`<deployment>/agents/` is the
|
|
480
|
+
instance root); (4) none → `E_CLONE_MISSING { repoKey, tried: [...], remedies }`
|
|
481
|
+
naming the three remedies. A directory found by (2) or (3) whose `origin` remote
|
|
482
|
+
resolves to a different repo key → `E_CLONE_MISMATCH { repoKey, path, origin }`
|
|
483
|
+
— the kernel never spawns into a clone that is not the member. This order was
|
|
484
|
+
stated by the guide and `docs/workspaces.md` before 0.25.2 and not implemented;
|
|
485
|
+
it is now normative.
|
|
486
|
+
|
|
487
|
+
**§6 `oats sync` creates `agents/` (R2).** `sync` (and therefore `onboard`,
|
|
488
|
+
which runs the sync body) creates `<deployment>/agents/` when absent. A
|
|
489
|
+
hand-written `oats-local.yaml` needs no `mkdir`.
|
|
490
|
+
|
|
491
|
+
**§5 one "You run on OATS" block (R3).** When `oats.core` resolves as a module
|
|
492
|
+
the composer suppresses the kernel's legacy `oats:kernel:oats` block; the
|
|
493
|
+
module's inject is the one such block. Without `oats.core` (a soul saying `off`)
|
|
494
|
+
the legacy block is composed as before, so no instance is left without the
|
|
495
|
+
briefing.
|
|
496
|
+
|
|
497
|
+
**§5/§6 soul-source drift (R4).** `driftOf` covers `instance.json.workspace.soul`
|
|
498
|
+
as well as `modules`: `oats status` prints `soul: <name> from <member> @ <c7>`
|
|
499
|
+
with `[member moved since …]` when the member's default branch is past the
|
|
500
|
+
recorded commit (`[member unconfirmed]` / `[soul no longer present]` for the
|
|
501
|
+
missing cases); `--json` adds `instances[].soul = { repoKey, commit, current:
|
|
502
|
+
<commit>|null, status: "current"|"moved"|"missing" }`. A moved soul is
|
|
503
|
+
information (decision 17): the instance keeps its own commit directory (M1).
|
|
504
|
+
|
|
505
|
+
**§6 preview payload visibility (R5).** `oats spawn --preview` (text and
|
|
506
|
+
`--json`) reports `providers` — the `--provider <cap> k=v` map exactly as given,
|
|
507
|
+
nested — and `settings.<cap>` — `resolution.payloads[cap]`, the merged payload
|
|
508
|
+
the provider's binding receives (`workspace.messaging` base ⊕ `byTeam[team]` ⊕
|
|
509
|
+
soul slot payload ⊕ `local.settings[cap]` ⊕ `providers[cap]`). Additive fields;
|
|
510
|
+
both empty objects when nothing applies.
|
|
511
|
+
|
|
512
|
+
**§5/§6 `work: workspace` (R6, closed in 0.25.1 as B2).** Documented in the
|
|
513
|
+
guide's §9: a coordination soul's `./work` is the deployment directory.
|
|
514
|
+
|
|
515
|
+
**Provider payload delivery vs provider consumption (R7 — oats.aweb 1.11.2).**
|
|
516
|
+
Decision 23 (`messaging.byTeam`) is **kernel semantics**: the kernel merges and
|
|
517
|
+
delivers; the provider consumes what its binding declares. oats.aweb 1.11.2's
|
|
518
|
+
spawn hook (a) locates the aweb root among `OATS_TEAM_SCOPE`, the home, the
|
|
519
|
+
home's git root, `OATS_CONTEXT` and its git root, and `OATS_WORKSPACE` (under
|
|
520
|
+
v2: the deployment directory) — none of which is a 0.24 team root; and (b)
|
|
521
|
+
resolves the target team from `OATS_TEAM_ID`/`OATS_TEAM_NAME` (the removed
|
|
522
|
+
`oats-config.yaml` `team:` block; empty under v2), else the **active team at
|
|
523
|
+
the root it found** — it does **not** read `team` from `OATS_SETTINGS`. So for
|
|
524
|
+
1.11.2 `byTeam` is delivered and recorded but a no-op; per-label minting is
|
|
525
|
+
obtained only by placing a per-team `.aw` inside each team's member clone
|
|
526
|
+
(gitignored) so it is found through the work repo, or one `.aw` at the
|
|
527
|
+
deployment directory for a single team. The guide states this (§8b) and
|
|
528
|
+
`docs/workspaces.md` states the general rule ("kernel-merged; whether a
|
|
529
|
+
provider honours it is the provider's"). **oats.aweb follow-up**: read `team`
|
|
530
|
+
(and honour `byTeam`'s result) from the payload; accept the deployment
|
|
531
|
+
directory as a first-class root. The kernel does not paper over this with a
|
|
532
|
+
`team:` env shim — the env block is removed with `oats-config.yaml`, and a
|
|
533
|
+
provider contract is the provider's to grow.
|
|
534
|
+
|
|
535
|
+
**OKF 2.1.3 reads `okf.json`, not a soul payload (R8 — corrects §2's `stores`
|
|
536
|
+
comment and decision 24's `root` example).** `oats.okf` 2.1.3's spawn hook
|
|
537
|
+
reads the soul's knowledge declaration from `<soul>/okf.json` (`{ version: 1,
|
|
538
|
+
owner, owns: ["<base>/<node>"], reads: [...] }`, `lib/config.mjs#validateDeclaration`)
|
|
539
|
+
and its settings from `OATS_SETTINGS`, admitting **only** `bindings-file`,
|
|
540
|
+
`state-dir`, `harvest-runtime`, `harvest-model` (`oats.json#settings`) — any
|
|
541
|
+
other key is `E_CONFIG unknown OATS_SETTINGS property`. Where a base lives
|
|
542
|
+
inside a store repository is the **bindings file's** `bases.<alias>.repository`
|
|
543
|
+
+ `root`, not a soul payload key. Therefore: a soul.yaml `knowledge:` payload for
|
|
544
|
+
OKF carries binding settings only (usually nothing — the workspace default
|
|
545
|
+
fills the slot; `none` opts out); `owns`/`reads`/`store`/`root` examples on
|
|
546
|
+
`soul.yaml` are removed from the guide, `workspaces.md`, `souls-and-instances.md`
|
|
547
|
+
and `knowledge.md`; `okf.json` stays in `souls/<name>/` and travels with the
|
|
548
|
+
soul into the per-commit cache (M1). A soul-payload grammar for OKF is an OKF
|
|
549
|
+
follow-up that lands with an `oats.okf` release declaring it in its binding.
|
|
550
|
+
The kernel's part — opaque forwarding of the merged payload — is unchanged and
|
|
551
|
+
correct. §7b's fresh `state-dir` rule is confirmed by the operator's run.
|
|
552
|
+
|
|
553
|
+
**§6 non-interactive approval (R9).** `oats sync --approve <id>@<version>`
|
|
554
|
+
(repeatable) approves exactly the entry the current resolution contains for
|
|
555
|
+
that id and version: the executables digest is always computed by `sync` over
|
|
556
|
+
the fetched tree (`executablesDigestAt`) and recorded — never typed. An
|
|
557
|
+
`--approve` naming an id/version the resolution does not contain → `E_BAD_ARGS`
|
|
558
|
+
(nothing approved); entries not covered stay unapproved (exit `2`). At the
|
|
559
|
+
interactive prompt **Ctrl+D (EOF) is a decline**: exit `2`, entry unapproved —
|
|
560
|
+
never treated as "yes", never a hang.
|
|
561
|
+
|
|
562
|
+
**§6 onboard next steps (R10).** `oats onboard` lists the workspace **host**
|
|
563
|
+
in `next.clone` like any member that lacks a clone at the convention (the host
|
|
564
|
+
is a member; a soul that lives in it may need a work clone). Under an explicit
|
|
565
|
+
`oats-local.yaml` `standalone:` header the next steps say the view is standalone
|
|
566
|
+
and list only that repo.
|
|
@@ -15,7 +15,7 @@ A clean v2 of the kernel's declaration, resolution and launch path: read `oats-w
|
|
|
15
15
|
|
|
16
16
|
## Work packages
|
|
17
17
|
|
|
18
|
-
Each is one PR (or two small ones), reviewed by me, on `main`,
|
|
18
|
+
Each is one PR (or two small ones), reviewed by me, on `main`, as canonical code from the first phase (no seam — see the approach above). Order is dependency order; W1–W3 can proceed in parallel lanes. **Status (2026-09-23): W1–W8 and W10 shipped in OATS 0.25.0 (PR99 `59ae22df`); W8's last part — folding the classic no-`oats-local.yaml` spawn path into the one pipeline and deleting the v1 residue — is a follow-up; W9/W9b/W11/W12 are Phases D–F.**
|
|
19
19
|
|
|
20
20
|
| # | Package | Delivers | Owner | Size |
|
|
21
21
|
|---|---|---|---|---|
|
|
@@ -28,9 +28,9 @@ Each is one PR (or two small ones), reviewed by me, on `main`, behind the v2 sea
|
|
|
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
|
|
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
|
|
33
|
-
| **W11** | **Onboarding skill** | `oats-setup-expert` / `oats.setup`:
|
|
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.
|
|
@@ -52,7 +52,7 @@ Each phase = one developer-swarm workflow (parallel agents, disjoint files, agai
|
|
|
52
52
|
| Risk | Mitigation |
|
|
53
53
|
|---|---|
|
|
54
54
|
| Remote access context is subtle (SSH vs HTTPS vs `gh` token; private repos) | W2 uses git itself (`git ls-remote`, `git archive`/shallow fetch) with the operator's configured credential helpers — no new credential store; typed `cannot read` never guesses |
|
|
55
|
-
| `core.mjs` entanglement makes W8 risky | W8 is deletion behind a green W7;
|
|
55
|
+
| `core.mjs` entanglement makes W8 risky | W8 is deletion behind a green W7; `rg` proof of zero importers per module before each deletion (the v1-residue table from the Phase C review); CI + the Northwind fixture + Desktop suites are the gate |
|
|
56
56
|
| Package approval UX ("asks once") in non-interactive spawns | `oats sync` is where approval is asked; `spawn` refuses `E_PACKAGE_UNAPPROVED` pointing at `sync` — never prompts mid-spawn |
|
|
57
57
|
| Latest-state members drift between preview and apply | The resolution records the member commit; apply re-observes and refuses `E_DECISION_STALE` if it moved — same mechanism as today's decision revision |
|
|
58
58
|
| Desktop relying on "installed" | Removed as a state in W12; until then the Capabilities view keeps working on 0.24.x DTOs (contracts unchanged) |
|
package/docs/desktop-cli-api.md
CHANGED
|
@@ -450,6 +450,26 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
450
450
|
|
|
451
451
|
## Readiness quartet, signatures, enforced policy (`oats readiness`, `readinessApi: 1`, OATS 0.24.8+)
|
|
452
452
|
|
|
453
|
+
> **0.25 status — the producers below are the 0.24 tier.** The readiness DTO
|
|
454
|
+
> (`readinessApi: 1`) still ships unchanged, but its four checks are *produced*
|
|
455
|
+
> by the classic observers: `installed` by `oats list` over the
|
|
456
|
+
> `.agents/capabilities/installed/` tier, `trusted` by the per-artifact approval
|
|
457
|
+
> that `oats trust` wrote, `configured` by `oats-config.yaml` activation, and
|
|
458
|
+
> `enrolled` by the `oats.yaml` backlink. On a **workspace deployment**
|
|
459
|
+
> (`oats-local.yaml` present) none of those sources exists: nothing is installed,
|
|
460
|
+
> approval is per package version in `oats-lock.json` v3 (`oats sync`), activation
|
|
461
|
+
> is derived (workspace defaults ⊕ soul `capabilities:`), and membership is
|
|
462
|
+
> `oats-membership.yaml` observed over the remotes. `oats list` and `oats trust`
|
|
463
|
+
> are removed verbs (`E_UNKNOWN_COMMAND`), so a `remedy` naming them cannot be
|
|
464
|
+
> run. Treat the field names and producer strings as the stable wire shape they
|
|
465
|
+
> are; for the workspace-model facts read `oats spawn <soul> --preview --json`
|
|
466
|
+
> (`modules[]` with from/commit/digest — the "installed" and "configured"
|
|
467
|
+
> truth), `oats sync --json` `approvalNeeded[]` (the "trusted" truth) and
|
|
468
|
+
> `oats workspace status --json` (the "enrolled" truth). Re-basing the quartet on
|
|
469
|
+
> those producers is an open thread of [the workspace model](#workspace-model-workspaceapi-2);
|
|
470
|
+
> when it lands it will be announced as a new feature name, not a silent change of
|
|
471
|
+
> `readinessApi: 1`.
|
|
472
|
+
|
|
453
473
|
`oats readiness [--soul <name>] [--home <abs>] [--verify-signatures] [--policy] [--dir <d>] --json`
|
|
454
474
|
is the first-run readiness view (frame 09) and the Capabilities readiness rows
|
|
455
475
|
(frame 04). Every fact is derived from the **same** data `oats inspect` reports
|
|
@@ -728,7 +748,7 @@ Every `commit` is a full 40-hex OID; every digest is `sha256-<hex>`; every
|
|
|
728
748
|
`oats onboard [<dir>] --workspace <repo ref> [--json]`
|
|
729
749
|
|
|
730
750
|
The **bootstrap** of a deployment (decision 9): realizes a workspace on this
|
|
731
|
-
machine in the
|
|
751
|
+
machine in the directory the operator chooses (any existing folder). It writes
|
|
732
752
|
`<dir>/oats-local.yaml` (`{ schemaVersion: 2, workspace: <ref> }`), creates
|
|
733
753
|
`<dir>/agents/` (the instance homes), then runs exactly the `oats sync` body
|
|
734
754
|
over the directory just written — discover over the remotes, confirm
|
|
@@ -1026,6 +1046,18 @@ refreshes the home in place:
|
|
|
1026
1046
|
schedule; the receipt's `note` says so. Refuses a retiring home
|
|
1027
1047
|
(`E_INSTANCE_RETIRING`), captured incarnations and capability-defined souls
|
|
1028
1048
|
(`E_UNSUPPORTED_MODE`: those are refreshed by a new resolution / package).
|
|
1049
|
+
- **Module homes (0.25+) are `E_UNSUPPORTED_MODE` too.** A home whose
|
|
1050
|
+
`instance.json` carries `modules{}` (spawned on a workspace deployment,
|
|
1051
|
+
`instance-modules`) answers `E_UNSUPPORTED_MODE` ("recompose from
|
|
1052
|
+
materialized modules is not supported yet; re-spawn"): its `AGENTS.md` was
|
|
1053
|
+
composed from the soul at a recorded member commit plus the materialized
|
|
1054
|
+
modules' injects, and the instance never changes under itself (decision 7).
|
|
1055
|
+
The refresh path for such a home is a new spawn (the soul is re-fetched at
|
|
1056
|
+
the member's current commit). `session-recompose` **stays advertised** in
|
|
1057
|
+
`features[]` because the verb still works for classic homes; gate the UI
|
|
1058
|
+
action on the feature AND on the absence of `instance.json.modules`
|
|
1059
|
+
(`oats status --json` `instances[].modules` is non-empty for a module home),
|
|
1060
|
+
and render the typed refusal otherwise.
|
|
1029
1061
|
- Gate on `features.includes("session-recompose")`. It is an **operator
|
|
1030
1062
|
action** (the human or the instance's parent), never something a Desktop
|
|
1031
1063
|
poll or an agent runs on itself.
|
|
@@ -12,10 +12,17 @@ the OATS Desktop app (`packages/desktop/` in the framework repo):
|
|
|
12
12
|
|
|
13
13
|
## Migrating a deployment that used `oats.web`
|
|
14
14
|
|
|
15
|
+
Under the **0.25 workspace model** there is nothing to uninstall: remove
|
|
16
|
+
`oats.web` from `oats-workspace.yaml` `packages:` / `defaults.capabilities`
|
|
17
|
+
and from any `soul.yaml` `capabilities:`, run `oats sync` (the lock v3 entry
|
|
18
|
+
disappears with the declaration), and use the Desktop app (step 3 below).
|
|
19
|
+
|
|
20
|
+
For a **0.24 classic** deployment:
|
|
21
|
+
|
|
15
22
|
1. Remove the `oats.web` entry from `capabilities.additive` in every
|
|
16
23
|
`oats-config.yaml` in your config chain.
|
|
17
|
-
2. Remove the `oats.web` entry from `oats-lock.json` at the same scope(s),
|
|
18
|
-
delete any stale installed copy under `.agents/capabilities/installed/`.
|
|
24
|
+
2. Remove the `oats.web` entry from `oats-lock.json` (v2) at the same scope(s),
|
|
25
|
+
and delete any stale installed copy under `.agents/capabilities/installed/`.
|
|
19
26
|
3. Use the OATS Desktop app instead: `cd packages/desktop && npm install &&
|
|
20
27
|
npm run rebuild && npm start` (see `packages/desktop/README.md`).
|
|
21
28
|
|
package/docs/desktop.md
CHANGED
|
@@ -84,10 +84,15 @@ The probe/mutation contract is specified in
|
|
|
84
84
|
|
|
85
85
|
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
|
-
and choose **Add workspace → Browse**, then point it at an OATS
|
|
88
|
-
a directory containing `agents
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
and choose **Add workspace → Browse**, then point it at an OATS deployment —
|
|
88
|
+
a directory containing `agents/` (under the 0.25 workspace model that is the
|
|
89
|
+
deployment directory (the operator's choice) holding `oats-local.yaml` and `agents/`;
|
|
90
|
+
under 0.24, an `agents/` root, a `local-agents/` root for machine-local souls,
|
|
91
|
+
or a team scope whose `oats-config.yaml` declares `team:`). *The Desktop's own
|
|
92
|
+
multi-repo roster ("team scopes show every member repo's agents under one
|
|
93
|
+
roster") still keys on the 0.24 `oats-config.yaml` `team:` declaration; reading
|
|
94
|
+
the member set from `oats-local.yaml` / `oats workspace status` is the Phase F
|
|
95
|
+
follow-up named in the [0.25.0 notes](release-notes/v0.25.0.md#desktop).*
|
|
91
96
|
Added workspaces are remembered and offered as suggestions next time.
|
|
92
97
|
|
|
93
98
|
Local souls (uncommitted, machine-local agents under `local-agents/`) are
|
|
@@ -231,10 +231,22 @@ and needs equivalent registration glue when switched to session delivery.
|
|
|
231
231
|
|
|
232
232
|
## Shared permission setting
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
the
|
|
234
|
+
The opt-in is per launch or per soul: `oats spawn --yolo` / `--no-yolo`
|
|
235
|
+
(`oats create` accepts the same flags), an optional `yolo` in `soul.yaml`
|
|
236
|
+
(0.24 schema; the v2 `soul.yaml` schema does not carry it — use the spawn flag
|
|
237
|
+
or a launch configuration), and the Desktop's per-launch choice. With no
|
|
238
|
+
setting, native policy is retained.
|
|
239
|
+
|
|
240
|
+
*0.24 classic deployments* may also set `yolo: true` in an `oats-config.yaml`
|
|
241
|
+
to apply it to that scope; the closest scope wins, soul overrides scope, the
|
|
242
|
+
spawn flag overrides both. *Under the workspace model* `oats-config.yaml` is
|
|
243
|
+
not configuration ([configuration.md](configuration.md)); the kernel's
|
|
244
|
+
`composeInstance` still consults the classic chain for the machine-level knobs
|
|
245
|
+
`yolo` and `launch-configs` when such a file happens to sit above the
|
|
246
|
+
deployment, but nothing writes one and the rebuild guide tells you to delete
|
|
247
|
+
it — treat a scope-level `yolo` as a 0.24 feature and prefer the explicit
|
|
248
|
+
spawn flag.
|
|
249
|
+
|
|
238
250
|
Autonomous or unattended execution is not permission to synthesize `yolo: true`.
|
|
239
251
|
Explicit user CLI/UI input or a user-selected configuration is the opt-in; explicit
|
|
240
252
|
`false` remains false.
|