@awebai/oats 0.24.13 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/bin/oats.mjs +930 -2820
  2. package/docs/capabilities.md +136 -323
  3. package/docs/configuration.md +68 -533
  4. package/docs/design/2026-09-16-fresh-operator-walkthrough.md +19 -14
  5. package/docs/design/2026-09-16-portable-migration-evidence.md +2 -0
  6. package/docs/design/2026-09-16-portable-onboarding.md +4 -2
  7. package/docs/design/2026-09-20-redesign-program-board.md +1 -1
  8. package/docs/design/2026-09-20-workspace-and-portable-adoption-plan.md +2 -0
  9. package/docs/design/2026-09-23-simplified-workspace-model.md +711 -0
  10. package/docs/design/2026-09-23-workspace-module-contracts.md +309 -0
  11. package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +65 -0
  12. package/docs/design/README.md +20 -8
  13. package/docs/design/operations-contract.md +1 -0
  14. package/docs/design/package-engine-contract.md +1 -1
  15. package/docs/design/package-runtime-api.md +1 -1
  16. package/docs/desktop-cli-api.md +324 -5
  17. package/docs/desktop-succession.md +3 -4
  18. package/docs/first-team.md +107 -224
  19. package/docs/implementation.md +6 -4
  20. package/docs/integrations.md +45 -44
  21. package/docs/knowledge-capability-authoring.md +10 -4
  22. package/docs/knowledge-migration.md +5 -4
  23. package/docs/knowledge-reference/package-craft.md +11 -3
  24. package/docs/knowledge.md +24 -8
  25. package/docs/layers.md +3 -3
  26. package/docs/oats-local.schema.json +50 -0
  27. package/docs/oats-membership.schema.json +23 -0
  28. package/docs/oats-workspace.schema.json +133 -48
  29. package/docs/official-marketplace.md +9 -6
  30. package/docs/packages.md +229 -440
  31. package/docs/rebuild-to-v2.md +233 -0
  32. package/docs/release-notes/v0.25.0.md +99 -0
  33. package/docs/soul.schema.json +41 -68
  34. package/docs/souls-and-instances.md +175 -108
  35. package/docs/workspace-adoption.md +70 -345
  36. package/docs/workspaces.md +429 -119
  37. package/lib/core.mjs +419 -55
  38. package/lib/instance-resolution.mjs +312 -0
  39. package/lib/materialize.mjs +580 -0
  40. package/lib/packages.mjs +501 -1273
  41. package/lib/remote.mjs +639 -0
  42. package/lib/resolve.mjs +576 -0
  43. package/lib/schedule.mjs +90 -16
  44. package/lib/workspace.mjs +635 -0
  45. package/package.json +1 -1
  46. package/lib/portable-migration-artifacts.mjs +0 -135
  47. package/lib/portable-migration-evidence.mjs +0 -305
  48. package/lib/portable-migration-store.mjs +0 -199
  49. package/lib/portable-migration.mjs +0 -104
  50. package/lib/portable-onboarding-acceptance.mjs +0 -66
  51. package/lib/setup-expert-source.mjs +0 -100
@@ -1,154 +1,464 @@
1
- # Workspaces, repositories and portable souls
2
-
3
- A **workspace definition** describes a shared agent setup in Git. A **local deployment** is one operator's realization of it. A **portable soul** declares its role, requirements and software sources independently of either operator's directory layout.
4
-
5
- This is the current OATS architecture. Start here for the model, then use [first-team onboarding](first-team.md) and the version-scoped [configuration](configuration.md) and [packages](packages.md) guides for operations. The [0.24 release notes](release-notes/v0.24.0.md) distinguish shipped foundations from unqualified profiles; an accepted architecture is not proof that every capability is ready.
6
-
7
- ## Four independent things
8
-
9
- | Thing | What it decides | What it does not imply |
10
- |---|---|---|
11
- | Workspace | Intended repository membership, shared defaults, source imports and provider declarations | Installed software, executable approval, messaging enrollment or a shared live session |
12
- | Source repository | The soul/package/store definitions it actually exports | Membership of every consumer in the publisher's workspace |
13
- | Local deployment | Local mappings, retained artifacts/resolutions, operator inputs and execution state | Permission to change source requirements or copy another operator's credentials |
14
- | Work target | Where an instance is assigned to work | Where its soul must be published, where knowledge must live or which team it joins |
15
-
16
- A workspace needs no OATS account, registry or OATS-operated control plane. Git hosting, messaging and model providers retain their own access and authentication requirements.
17
-
18
- ## The three declaration files
19
-
20
- ### `oats-workspace.yaml` — the shared workspace
21
-
22
- The workspace names intended members and may provide defaults, knowledge-store declarations, team aliases, catalogs and external soul imports. Omitted lists admit or activate nothing.
23
-
24
- A workspace is a role, not a requirement for a separate repository. It can live in a dedicated repository or beside project code. For OATS development, the selected home is the `oats` framework repository; `oats-dev` remains a development-capability repository.
25
-
26
- This schematic example uses placeholder sources, not a runnable published team:
1
+ # Workspaces — one workspace per organisation, members are trust, nothing is installed
2
+
3
+ This is the OATS workspace model (v2, the 0.25 line). It replaces the per-soul
4
+ `source:` grammar, the installed-capability tier and `oats-config.yaml`. The
5
+ normative record is the Decision concept
6
+ `agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md`; the module
7
+ contracts the kernel is built against are in
8
+ [design/2026-09-23-workspace-module-contracts.md](design/2026-09-23-workspace-module-contracts.md);
9
+ a full worked example (an imaginary company with three teams) is in
10
+ [design/2026-09-23-simplified-workspace-model.md](design/2026-09-23-simplified-workspace-model.md).
11
+ Moving an existing 0.24.x deployment: [rebuild-to-v2.md](rebuild-to-v2.md).
12
+
13
+ ## The rule
14
+
15
+ **Every capability an instance runs is copied whole into that instance at
16
+ spawn. A capability comes from one of two kinds of source; only one kind is
17
+ versioned.**
18
+
19
+ | Source kind | `from:` | Versioned | Trust |
20
+ |---|---|---|---|
21
+ | Member repo | `<repo key>` or `here` | no — always the member's **latest** default-branch state | membership (the reciprocal handshake) |
22
+ | Package | `package` | yes — the version pinned in the workspace's `packages:`; `oats-lock.json` records the exact commit + integrity | executables approved **once per version**, recorded in the lock |
23
+
24
+ A soul names each capability **with where it comes from — a location, never a
25
+ version**. The workspace's `packages:` says which version; materialization
26
+ *records* the exact state (repo or package, commit, content digest) in the
27
+ instance's `instance.json`. Resolution is a handshake check plus a lookup —
28
+ never a search.
29
+
30
+ Nothing is installed. There is no installed-capability directory, no activation
31
+ step, no `oats install`/`use`/`init`/`restore`. A fetch cache may exist under
32
+ the OS cache directory as invisible plumbing; no file refers to it.
33
+
34
+ ## The files
35
+
36
+ Four declaration files and one lock. Three of them are shared through Git
37
+ (`oats-workspace.yaml`, `oats-membership.yaml`, `soul.yaml`); one is per
38
+ machine (`oats-local.yaml`); the lock (`oats-lock.json`) sits beside
39
+ `oats-local.yaml` and is identical on every machine that synced the same
40
+ workspace commit. Schemas: [`oats-workspace.schema.json`](oats-workspace.schema.json),
41
+ [`oats-membership.schema.json`](oats-membership.schema.json),
42
+ [`soul.schema.json`](soul.schema.json), [`oats-local.schema.json`](oats-local.schema.json),
43
+ [`oats-lock-v3.schema.json`](oats-lock-v3.schema.json). The JSON schemas encode
44
+ shapes; domain rules (declared teams, duplicate members, canonical `from:` keys,
45
+ the two `packages:` value forms) live in the kernel's `validateWorkspace` /
46
+ `validateSoul`, which are the authority.
47
+
48
+ ### `oats-workspace.yaml` — the one shared declaration
49
+
50
+ Lives in the repository that **hosts** the workspace (often a dedicated
51
+ `agents` repo, but any member can host it). One per organisation.
27
52
 
28
53
  ```yaml
29
- schemaVersion: 1
30
- name: example-development
31
- members:
32
- - source: git:github.com/example/service
33
- imports:
34
- - source: git:github.com/example/experts
35
- soul: souls/domain-expert
36
- revision: reviewed-source-ref
37
- alias: domain-expert
54
+ schemaVersion: 2
55
+ name: acme
56
+
57
+ members: # repo refs, NO @revision (E_WORKSPACE_SCHEMA)
58
+ - git:github.com/acme/agents # the host is a member too — it backlinks like any other
59
+ - git:github.com/acme/platform
60
+ - git:github.com/acme/tools # a member that ALSO publishes a package (see below)
61
+
62
+ packages: # the ONLY versioned things
63
+ oats.framework: v1.1.3 # bare version → resolves through the official catalog
64
+ oats.okf: v2.1.3
65
+ acme.tools: git:github.com/acme/tools@v0.4.0 # outside the catalog → git:<repo>@<tag|OID>; still a package
66
+
67
+ teams: # labels, declared once so they cannot drift
68
+ global: { description: Org-wide souls and house capabilities }
69
+ engineering: { description: Platform and release automation }
70
+
71
+ defaults:
72
+ capabilities:
73
+ oats.core: { from: package }
74
+ acme-house-style: { from: github.com/acme/agents } # a CANONICAL repo key: host/path, no scheme, no .git
75
+ knowledge: { oats.okf: { from: package } } # one slot default at most; a soul may say `none`
76
+ messaging: none
77
+ tasks: none
78
+ byTeam:
79
+ engineering:
80
+ capabilities: { acme-release-tooling: { from: github.com/acme/agents } }
81
+
82
+ stores: # knowledge stores, declared once
83
+ org: git:github.com/acme/knowledge
84
+
85
+ messaging: # an opaque provider payload for the messaging slot
86
+ private: per-human
87
+
88
+ external: # souls borrowed from NON-members; revision REQUIRED
89
+ - source: git:github.com/oss-collective/experts@9c4e1f2a9c4e1f2a9c4e1f2a9c4e1f2a9c4e1f2a
90
+ soul: souls/security-reviewer
38
91
  ```
39
92
 
40
- Use actual reviewed source revisions when preparing work. When a repository reference omits its optional revision, discovery observes the hosting provider's intended default branch; it must not guess `main` or silently reuse unrelated local branch state.
93
+ Refused by the schema: absolute filesystem paths as values anywhere (host state
94
+ belongs in `oats-local.yaml`), `@revision` on members, unknown top-level keys.
95
+ A `from:` value is `package`, `here` (souls only) or a **canonical repo key**
96
+ exactly as the kernel spells it (`parseRepoRef(ref).key`: lowercase host,
97
+ `org/repo`, no scheme, no `git:`, no `.git`; `local/<abs-path>` for a
98
+ file/bare-directory remote). Any other spelling is a schema error at
99
+ validation, not a late membership error.
41
100
 
42
- ### `oats.yaml` — a repository's advertised exports
43
-
44
- A repository advertises the souls, package roots and provider-owned knowledge declarations it actually supplies. A member also points back to its workspace:
101
+ ### `oats-membership.yaml` — the backlink, in every member
45
102
 
46
103
  ```yaml
47
- schemaVersion: 1
48
- workspace:
49
- source: git:github.com/example/workspace
50
- exports:
51
- souls:
52
- - path: souls/domain-expert
53
- definition: souls/domain-expert/soul.yaml
54
- packages:
55
- - path: oats-package
104
+ schemaVersion: 2
105
+ workspace: git:github.com/acme/agents # "I am a member of acme"
106
+ team: engineering # optional: default team label for this repo's items
56
107
  ```
57
108
 
58
- Only include exports that exist at the selected revision. A repository need not export every kind. A package export identifies a real directory containing `oats-package.json`; it is not an arbitrary npm package directory.
59
-
60
- `oats-workspace.yaml` and `oats.yaml` may coexist. If the workspace host also participates as a member, it is explicitly admitted and has a matching backlink just like another member.
109
+ Nothing else. It replaces `oats.yaml`; there are no export lists.
61
110
 
62
- ### `soul.yaml` — a source-complete specialist
63
-
64
- A portable soul is an authored definition, not a dependency on whatever happens to be installed on its publisher's machine. It contains canonical `AGENTS.md`, a relative `CLAUDE.md` alias, its reviewed skill/resource closure and a versioned declaration.
65
-
66
- For example, this declaration excerpt requires a particular knowledge capability **and names where it comes from**:
111
+ ### `souls/<name>/soul.yaml` — where each capability comes from
67
112
 
68
113
  ```yaml
69
- schemaVersion: 1
70
- name: domain-expert
71
- requires:
72
- knowledge:
73
- capability: oats.okf
74
- source: git:github.com/awebai/oats-okf@v2.1.3#oats-package
114
+ schemaVersion: 2
115
+ name: release-manager
116
+ description: Cuts, verifies and announces releases.
117
+ work: worktree # worktree | checkout | directory | workspace
118
+ team: engineering # optional; else the repo's default; else "unassigned"
119
+
120
+ capabilities:
121
+ acme-release-tooling: { from: here } # `here` = the repo this soul.yaml lives in
122
+ acme-deploy: { from: package } # provided by acme.tools, pinned in packages:
123
+ acme-house-style: off # removes a workspace default
124
+
125
+ knowledge: # provider payload, opaque to the kernel
126
+ owns: release-manager
127
+ reads: [platform-engineer]
128
+ messaging:
129
+ channels: [acme-eng]
130
+ tasks: none # empties the slot
131
+
132
+ compatibility: # optional FLOORS on package versions — constraints, not sources
133
+ oats.okf: ">=2.1"
75
134
  ```
76
135
 
77
- This illustrates software selection, not complete OKF provisioning: the chosen capability also needs its own valid knowledge declaration, bindings and accepted base.
78
-
79
- - `requires` expresses hard requirements. A fundamental provider can be required by presence or as a concrete capability/source selection.
80
- - `defaults` supplies choices that remain rebindable within those requirements.
81
- - Additive capabilities are named under `requires.capabilities` or `defaults.capabilities`; each concrete selection has a `source`, with optional provider-owned settings.
82
- - `git:` selects versioned software from a repository/package root. `repo:` refers to a contained path in the declaring source repository, not the caller's working directory. `path:` is an explicitly authorised local development choice, not a remotely portable ambient fallback.
83
- - Capability IDs alone do not establish software origin. An old `from: installed` config entry is not a substitute for a portable source declaration.
84
- - A source may contain authored knowledge snapshots or resources, but retained artifacts are not writable knowledge stores. Learning locations and procedures belong to the selected capability.
85
-
86
- See the [soul schema](soul.schema.json) and [declaration contract](design/2026-09-15-portable-declarations.md). Classic fields such as `kind`, `type` and a machine-local `repo` are not portable declaration fields; do not relabel an old file without validating it.
136
+ Beside it: `AGENTS.md` (canonical), `CLAUDE.md → AGENTS.md`, `skills/`. Every
137
+ `souls/*/soul.yaml` in a member is discoverable; one that wants to stay
138
+ internal says `private: true` (spawnable only from its own repo). A soul's
139
+ `name` must equal its directory name; the first of two souls declaring one
140
+ name (by path) is listed, the second is a problem.
87
141
 
88
- ## Membership and adoption are different
142
+ ### `capabilities/<name>/oats.json` — the manifest, unchanged shape
89
143
 
90
- **Repository membership is reciprocal:** the workspace admits the repository and the repository's `oats.yaml` points back to that workspace. Both observations must have compatible identity, access context and revision evidence. A copied backlink, neighbouring folder or URL is not admission.
144
+ The capability manifest is the one file that did not change (see
145
+ [capabilities.md](capabilities.md)). Discovery relies on `capability` (the same
146
+ `^[a-z0-9][a-z0-9._-]*$` grammar every `capabilities:` key uses), `version`,
147
+ `layer`, and may read `private: true` and `team: <label>`. `version` is
148
+ informational for member capabilities — a materialized copy is identified by
149
+ its content digest.
91
150
 
92
- **Source adoption is by reference:** an import identifies `source`, exported `soul` path, `revision` and a local `alias`. It may carry supported adoption choices. Importing does not create an adopter-maintained copy of the soul or automatically follow the publisher's workspace backlink.
151
+ ### `oats-local.yaml` — the only per-machine file
93
152
 
94
- A team can therefore use a public expert without joining its publisher's organization. One source can serve several workspaces or an explicit standalone context, with different legitimate work targets and knowledge bindings.
95
-
96
- Publish source/export revisions before pinning imports to them. Do not use invented future SHAs or require two repositories to contain each other's not-yet-created commit IDs.
97
-
98
- ## How requirements and defaults meet
99
-
100
- The kernel uses one resolver:
101
-
102
- - Workspace defaults establish shared fallback choices.
103
- - The soul's own defaults can specialise them.
104
- - Explicit adoption/operator choices select supported alternatives or supply missing inputs.
105
- - Hard source requirements remain constraints; a conflicting override is an error, not a reason to discard the requirement.
106
-
107
- Provider-owned declarations remain opaque to the kernel until the selected provider interprets them through its contract. There is no portable repository-level capability policy tier silently inherited from the publisher, and no mandatory agent-type hierarchy replacing a soul's own requirements.
108
-
109
- Repository briefing/worktree setup remains work-target behavior with its own supported authority. Merely placing a repository `AGENTS.md` nearby does not guarantee it is composed into every harness's instructions.
153
+ ```yaml
154
+ schemaVersion: 2
155
+ workspace: git:github.com/acme/agents # observed over the remote; need not be cloned
156
+ clones: # optional: where member clones live, if not the convention
157
+ github.com/acme/platform: /Users/ana/src/acme-platform
158
+ settings: # host-owned values the manifests ask for
159
+ oats.okf:
160
+ bindings-file: /Users/ana/.oats/okf-bindings.json
161
+ state-dir: /Users/ana/.oats/okf
162
+ souls:
163
+ disabled: [data-analyst] # not run on this machine
164
+ ```
110
165
 
111
- ## From a definition to a running instance
166
+ See [configuration.md](configuration.md). `oats-config.yaml` no longer exists.
167
+
168
+ ### `oats-lock.json` — lock v3
169
+
170
+ Written by `oats sync`; the only persisted state at the deployment besides
171
+ `oats-local.yaml`. See [packages.md](packages.md).
172
+
173
+ ## Membership and trust
174
+
175
+ **Reciprocal membership is a hard gate.** A repo is a member only when
176
+ `oats-workspace.yaml` lists it **and** the repo's `oats-membership.yaml` names
177
+ the workspace back. One file per side; a copied backlink in a fork, or a folder
178
+ with the right name, is not admission.
179
+
180
+ **Membership is the whole trust decision for member capabilities** — the same
181
+ model as a repo's committed `.agents/skills/`: whoever can push to the repo
182
+ decides what runs, and the branch's latest state is what runs. No per-operator
183
+ trust lists, no per-capability approval for members. Packages come from
184
+ *outside* that boundary and keep a one-time executable approval per version.
185
+
186
+ **The handshake is observed with the operator's own Git read access, in one
187
+ access context.** The kernel reads both halves over the remotes
188
+ (`git ls-remote`, shallow fetches, the operator's own credential helpers,
189
+ never a prompt). A half that cannot be read makes the member *unconfirmed*,
190
+ never a half-success. `oats workspace status` and `oats sync` show each member
191
+ as `confirmed` or the reason it is not:
192
+
193
+ | status | meaning |
194
+ |---|---|
195
+ | `confirmed` | listed and backlinks to this workspace |
196
+ | `not-listed` | the workspace does not list the repo |
197
+ | `no-backlink` | no (or invalid) `oats-membership.yaml` at the member's default branch |
198
+ | `backlink-elsewhere` | the member names a different workspace (a case-only difference is flagged: repo paths are case-sensitive identities) |
199
+ | `cannot-read` | the operator cannot read the member (auth / not-found / network / timeout) |
200
+
201
+ An unconfirmed member contributes nothing but its row: its souls are invisible,
202
+ its capabilities unresolvable (`E_NOT_A_MEMBER` / `E_MEMBERSHIP_UNCONFIRMED`).
203
+ Reading the workspace repo *is* being in the workspace — a workspace's access
204
+ control is Git's.
205
+
206
+ **Private items.** `private: true` on a soul or a capability keeps it out of the
207
+ workspace listing; a private capability is usable only by souls of the same
208
+ repo (`E_CAPABILITY_PRIVATE` otherwise). Owners still see their own private
209
+ items.
210
+
211
+ **External souls.** `external:` adopts a soul by reference from a repo that is
212
+ **not** a member, pinned to a full commit. No handshake is asked for and none is
213
+ read; the soul gets no member-tier capabilities of its own repo; it is
214
+ "source-complete" (its skills travel with it) and the workspace's defaults fill
215
+ its slots. An `external[].team` overrides the soul's own `team`.
216
+
217
+ ## Member tier vs package tier — the non-collapse rule
218
+
219
+ A repository may be a **member** (it completed the handshake; its `souls/*` and
220
+ `capabilities/*` are member-tier: latest state, trusted by membership) **and** a
221
+ **package publisher** (its `oats-package/` is consumed only through
222
+ `packages:`: versioned, locked, approved). The two never collapse:
223
+
224
+ - `from: <repo key>` looks **only** under `<repo>/capabilities/<name>/oats.json`
225
+ at the member's latest state. It never looks inside `oats-package/`. A name
226
+ that exists only inside the repo's package fails with `E_CAPABILITY_MISSING`
227
+ and the hint `provided by package <id>; use from: package`.
228
+ - `from: package` looks **only** in the lock (which package provides the
229
+ capability). It never looks at member capabilities, even when the package's
230
+ repo is a member.
231
+ - Discovery reports a member's `oats-package/` as `publishes: { package,
232
+ version }` on the member row (informational) and does **not** list the
233
+ package's capabilities as member capabilities.
234
+
235
+ So the framework's own souls say `oats.okf: { from: package }` even though
236
+ `oats-okf` is a member of the OATS workspace — and every package repo carries a
237
+ member soul that is the expert in that capability (`okf-expert`, `aweb-expert`,
238
+ …), discoverable at latest state like any member soul.
239
+
240
+ ## Packages, lock, approval, catalog
241
+
242
+ `packages:` values have exactly two forms:
243
+
244
+ - a **bare version** (`v2.1.3`, `2.1.3`, `v1.0.0-rc.1`) — resolved through the
245
+ official catalog (`package-catalog.json` in the `oats` repo; the reviewed
246
+ marketplace, see [official-marketplace.md](official-marketplace.md)). This is
247
+ the only way a package becomes *pinnable by id*.
248
+ - **`git:<repo>@<ref>`** — a direct package ref: `<repo>` is any ref the kernel
249
+ understands (`github.com/org/repo`, `https://…`, `git@host:…`, `/abs/bare.git`,
250
+ `file:///…`), `<ref>` a tag name or a full commit OID. The package is read at
251
+ `oats-package/` inside that repo.
252
+
253
+ Both are packages: versioned, locked, approved. A ref that resolves to a
254
+ **branch** is refused (`E_PACKAGE_INTEGRITY { why: "branch" }`) — versions are
255
+ immutable. A tag that moved (same version string, different commit) fails
256
+ integrity on the next `oats sync` and asks again.
257
+
258
+ `oats sync` confirms membership, resolves every `packages:` entry to a commit +
259
+ content digest, asks (on a terminal) for any missing per-version executable
260
+ approval, writes `oats-lock.json` (lockfileVersion 3) and reports what changed.
261
+ `oats package add <id> <version|git:…@…>` / `oats package remove <id>` edit
262
+ `packages:` in the workspace file when it is tracked by the current checkout,
263
+ else print the line to add — the workspace file is shared through Git. Details:
264
+ [packages.md](packages.md).
265
+
266
+ ## Resolution, spelled out
267
+
268
+ For each `(name, from)` in
269
+ `defaults.<slot>` ⊕ `defaults.capabilities` ⊕ `defaults.byTeam[<soul team>]` ⊕
270
+ `soul.capabilities` (later wins; `off` removes; a soul `<slot>: none` drops
271
+ the workspace's slot default):
112
272
 
113
- 1. Select an explicit workspace or standalone context, source reference, deployment location and work target.
114
- 2. Observe actual source identities/revisions and check requested reciprocal membership.
115
- 3. Resolve requirements, defaults and operator inputs; retain the selected source and software closure.
116
- 4. Review and approve exact executable artifacts before provider code runs.
117
- 5. Obtain honest provider readiness and a retained resolution; missing configuration or unsupported behavior remains visible.
118
- 6. Scaffold and start through the supported captured lifecycle. Preserve the exact source/resources and evidence needed for continuation.
273
+ ```
274
+ from: package → some locked package provides `name` else E_PACKAGE_MISSING (run `oats sync`)
275
+ → that package version is approved else E_PACKAGE_UNAPPROVED
276
+ → read its capability manifest at the locked commit; copy; record package/version/commit/digest
277
+ from: <repo> → <repo> is a CONFIRMED member else E_NOT_A_MEMBER / E_MEMBERSHIP_UNCONFIRMED
278
+ (or `here`) → it has capabilities/<name>/oats.json else E_CAPABILITY_MISSING
279
+ → not private, unless <repo> is the soul's own else E_CAPABILITY_PRIVATE
280
+ → copy from the member's current state; record repo/commit/digest
281
+
282
+ slots a module whose manifest says `layer: X` fills slot X; two → E_SLOT_CONFLICT;
283
+ a slot default must be a capability of that layer; soul `X: none` empties X
284
+ skills duplicate names within the composed set → E_SKILL_DUPLICATE (names both capabilities)
285
+ floors soul.compatibility.<cap> is checked against the package's version → E_COMPATIBILITY
286
+ ```
119
287
 
120
- An existing instance does not silently adopt a new upstream commit, changed workspace default or different curriculum. Updates prepare new choices deliberately; required knowledge refresh and native credential rotation are separate from rewriting its retained software.
288
+ The result is an immutable **resolution** with a `revision` (a hash of
289
+ everything above); the spawn decision binds it, so a member that moved between
290
+ preview and apply is `E_DECISION_STALE`, not a silent drift.
121
291
 
122
- A successful lookup is not execution, an accepted dispatch is not completed work, and a declared knowledge destination is not accepted learning.
292
+ ## Materialization and the instance home
123
293
 
124
- ## What each operator shares or keeps local
294
+ At spawn every resolved capability is **copied whole** into the instance home —
295
+ skills, injects, scripts, hooks — from the remote at the recorded commit.
296
+ Nothing is symlinked, nothing is shared between instances.
125
297
 
126
- Share reviewed definitions, relevant nonsecret configuration/provenance, published source references and accepted knowledge through their chosen Git repositories. Keep credentials, private runtime evidence, instance homes and machine-specific realization local. A messaging roster does not replicate any of these.
298
+ ```
299
+ <agents-root>/<soul>/instances/<instance>/
300
+ ├── AGENTS.md # composed: soul AGENTS.md + kernel/work-mode blocks + each module's inject
301
+ ├── CLAUDE.md → AGENTS.md
302
+ ├── .agents/skills/<capability>/<skill>/SKILL.md # full copies; where pi/codex look
303
+ ├── .claude/skills → ../.agents/skills
304
+ ├── .oats/modules/<capability>/ # the full capability copy: oats.json, bin/, injects/, skills/
305
+ ├── instance.json # modules{}, providers{}, workspace{} recorded here
306
+ ├── soul → <agents-root>/<soul>/soul # read-only reference
307
+ ├── TASK.md
308
+ └── work/
309
+ ```
127
310
 
128
- An adopted package config template is an editable local snapshot, not live inheritance from the package. Updating the kernel or package does not rewrite it, migrate a knowledge base or update a running instance's loaded instructions.
311
+ `instance.json.modules.<cap>` records `from` (`{ kind: "member", repoKey,
312
+ commit }` or `{ kind: "package", package, version, commit, integrity, repoKey }`),
313
+ `commit`, `digest` (sha256 of the copied tree) and `materializedAt`;
314
+ `instance.json.providers.<cap>` records the merged provider payload. A running
315
+ instance never changes under itself: a member moving or `packages:` being
316
+ bumped affects only new spawns. Details and DTOs:
317
+ [souls-and-instances.md](souls-and-instances.md), [desktop-cli-api.md](desktop-cli-api.md).
318
+
319
+ **Drift is shown, not prevented.** `oats status` compares each instance's
320
+ recorded modules with the workspace's current picture: `current`, `moved`
321
+ (member or package now at another commit) or `missing` (capability no longer
322
+ present, member unconfirmed, package no longer locked). `oats spawn --preview`
323
+ lists `changedSince` the newest previous instance of the same soul.
324
+
325
+ **Harnesses start normally.** OATS is a skill contributor, not a skill sandbox:
326
+ cwd = the instance home, the harness's own skill discovery intact
327
+ (`~/.pi/agent/skills`, `.agents/skills` up the tree, `.claude/`, …); machine-
328
+ and repo-level skills resolve exactly as they would without OATS. OATS
329
+ composes instructions (`AGENTS.md`) and pins model/provider settings; it does
330
+ not exclude anything.
331
+
332
+ ## Teams
333
+
334
+ `teams:` declares labels once (`global`, `engineering`, …) so they cannot drift
335
+ into typos. A soul or capability carries `team:`, else its repo's default from
336
+ `oats-membership.yaml`, else `unassigned`. A label not declared in `teams:` is
337
+ `E_TEAM_UNKNOWN` (the item is still listed). `defaults.byTeam.<team>.capabilities`
338
+ adds capabilities additively for souls with that label (`off` removes). **A
339
+ label never gates, restricts, changes trust or partitions the knowledge
340
+ store** — it organises and can supply defaults. The messaging provider's payload
341
+ (private teams, channels) lives under `messaging:`, so "team" means one thing.
342
+
343
+ ## Provider payloads have three homes
344
+
345
+ | What it is | Where | Example |
346
+ |---|---|---|
347
+ | True of every instance of the soul | `soul.yaml` → `knowledge:` / `messaging:` / `tasks:` | `knowledge: { owns: release-manager }` |
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=retained:release-seat` |
129
350
 
130
- ## Compatibility and current readiness
351
+ The merged payload is `workspace.messaging` (messaging slot only; its base
352
+ keys ⊕ `byTeam[<soul's team>]`, with `byTeam` itself stripped) ⊕ soul slot
353
+ payload ⊕ `local.settings[cap]` ⊕ `spawn.providers[cap]` — objects deep-merge,
354
+ later wins on scalars and arrays. The provider's own `binding` contract
355
+ (`normalize → bind → check`) runs over the merged payload exactly as before.
356
+ Two teams, two messaging identities, one workspace:
131
357
 
132
- Classic `oats-config.yaml` scopes, `oats init`, `oats use`, local `agents/` lookup and lock-v2 package restore still have their own supported contracts. They are not renamed portable workspace commands. See [configuration](configuration.md) and [packages](packages.md) for that compatibility surface; do not apply classic lifecycle commands blindly to captured instances.
358
+ ```yaml
359
+ teams: { oss: { description: Open protocol }, cloud: { description: Hosted application } }
360
+ messaging:
361
+ byTeam:
362
+ oss: { team: aweb:example.oss }
363
+ cloud: { team: aweb:example.cloud }
364
+ ```
133
365
 
134
- At the documented0.24 baseline, workspace/declaration/retained-execution foundations are shipped. The released `oats.aweb`1.10.3 package lacks the captured provider-binding interface, so its legacy messaging success does not qualify a new captured profile requiring it. Capability adaptation is implementation work, not a YAML setting that can honestly turn readiness green. Follow current [release scope](release-notes/v0.24.0.md) and the provider's actual version/readiness rather than removing requirements.
366
+ A soul with `team: cloud` hands its messaging provider `{ team: aweb:example.cloud, … }`;
367
+ a label under `byTeam` that is not declared in `teams:` is `E_WORKSPACE_SCHEMA`.
368
+ A store (`stores: { <name>: <repo ref> }`) names a repository; where the base
369
+ lives inside it is the knowledge provider's own binding key (`root` for OKF),
370
+ given in the payload — a repo ref never carries a `#path`.
135
371
 
136
- The project's [adoption plan](design/2026-09-20-workspace-and-portable-adoption-plan.md) puts workspace/source adoption first, centralised knowledge and five experts second, and full Desktop parity afterward.
372
+ `--provider` for a capability the soul does not resolve is `E_CAPABILITY_MISSING`;
373
+ `__proto__`/`constructor`/`prototype` as a key at any depth is refused.
137
374
 
138
- ## How a soul knows OATS (accepted direction, not yet shipped)
375
+ ## Discovery over remotes and the `<name>-workspace/` convention
139
376
 
140
- An agent's knowledge of OATS itself — how to check status, spawn and retire, find other souls — is ordinary capability content, not kernel magic:
377
+ Discovery and resolution work against **Git remotes, never local clones**. The
378
+ kernel fetches `oats-workspace.yaml`, each member's `oats-membership.yaml`,
379
+ every `souls/*/soul.yaml` and `capabilities/*/oats.json`, and every package by
380
+ URL — with the operator's own git configuration (`GIT_TERMINAL_PROMPT=0`, ssh in
381
+ BatchMode: nothing ever prompts). Neither the repo that defines a capability nor
382
+ the repo that hosts the workspace needs to be cloned.
141
383
 
142
- - **`oats.core`** carries day-to-day operation (skills `oats-operate`, `oats-souls`, the "you run on OATS" briefing). Every soul gets it **by default at creation, written explicitly into its definition**; you can remove or replace it.
143
- - **`oats.setup`** carries deployment/workspace configuration and package knowledge ("OATS Soul Setup"). Onboarding a workspace creates and starts an **`oats-setup-expert`** soul with both, which then adopts repositories and creates the team's other souls.
144
- - The **official marketplace** is the reviewed package list in the `oats` repository; a package becomes official through an approved PR to that list, and official packages are discoverable from the CLI and Desktop. Discoverable is not installed; installed is not approved.
384
+ **The only thing that needs a clone is a soul's work target** (`work:
385
+ worktree | checkout`). Spawning a soul whose repo is not yet cloned is a guided
386
+ clone-then-spawn, a job for the onboarding skill, not the kernel.
145
387
 
146
- At the0.24 baseline these skills still ship inside the kernel. Work packages D1–D4 of the [adoption plan](design/2026-09-20-workspace-and-portable-adoption-plan.md) track the move.
388
+ The taught default is one folder named after the workspace:
147
389
 
148
- ## Related references
390
+ ```
391
+ ~/acme-workspace/ ← "<name>-workspace"
392
+ ├── oats-local.yaml ← which workspace this machine realizes + host paths + disabled souls
393
+ ├── oats-lock.json ← exact commit + integrity + per-version approval per package
394
+ ├── agents/ ← instance homes (each self-contained) + fetched soul sources
395
+ ├── platform/ ← clone of github.com/acme/platform (only if someone works IN it)
396
+ └── tools/
397
+ ```
149
398
 
150
- - [Souls and instances](souls-and-instances.md)
151
- - [Capability contracts](layers.md) and [capability authoring/distribution](capabilities.md)
152
- - [Knowledge model](knowledge-theory.md) and [version-scoped operations](knowledge.md)
153
- - [Workspace schema](oats-workspace.schema.json) and [repository export schema](oats-member.schema.json)
154
- - [Design/contract navigation](design/README.md)
399
+ The kernel never depends on this shape: `oats-local.yaml` is found by walking
400
+ up from the current directory (`E_LOCAL_MISSING` otherwise); clones are found
401
+ through `clones:` or the convention. A soul that lives in a member repo is
402
+ fetched into `<agents-root>/<soul>/soul/` at its discovered commit before its
403
+ first spawn (idempotent per commit); its instances then materialize as above.
404
+
405
+ ## The standalone case
406
+
407
+ A repo you can read whose workspace you **cannot** read (a contractor with
408
+ access to `platform` but not to the private `agents` repo) still offers its
409
+ souls: their `from: here` capabilities resolve; `from: <other member>` and
410
+ `from: package` are unresolvable (the version list lives in the workspace
411
+ file); workspace defaults do not apply because they cannot be seen. This is the
412
+ workspace's access control working, not a degraded mode to paper over: make
413
+ the host repo readable (it holds declarations, no secrets) or grant access.
414
+
415
+ Two things keep the standalone spawn useful rather than hollow: `oats.core`
416
+ (the framework's own operational package) is the kernel's default here as
417
+ well, resolved from the official catalog through the operator's own lock and
418
+ approved like any package (a soul may say `oats.core: off`); and the
419
+ operator's `oats-local.yaml` may name the repo directly (`workspace: <member
420
+ ref>` — the kernel notices it is a member whose workspace it cannot read and
421
+ falls back to the standalone view — or `standalone: <repo ref>` to ask for
422
+ that view explicitly).
423
+
424
+ **Hosting the workspace file when some members are private.** Everyone who
425
+ can read the workspace file sees the member list. So: a public member never
426
+ hosts it when any member is private (it would publish the private repo's
427
+ name); the private member hosting it hides the workspace from public
428
+ contributors, who then live in the standalone case above. A dedicated private
429
+ repo (`<org>/workspace`) is the honest shape for a mixed organisation; the
430
+ onboarding skill asks this question first.
431
+
432
+ ## What is deliberately not versioned
433
+
434
+ - **Members.** A member is always its latest state; there is no `@revision`
435
+ on `members:`. A team that wants frozen capabilities publishes them as a
436
+ package and pins that.
437
+ - **Member capabilities' `version` field** — informational; the content digest
438
+ recorded at spawn identifies a copy.
439
+ - **Souls in members.** A soul is spawned from its repo's current state; the
440
+ commit is recorded in `instance.json.workspace.soul`.
441
+ - **The deployment layout** — the operator's; only the convention is taught.
442
+
443
+ What **is** versioned: `packages:` (the workspace's one list), the lock's exact
444
+ commits and digests, and `external:` pins (a stranger's repo is never "latest").
445
+
446
+ ## Removed
447
+
448
+ Per-soul `source: git:…@v#…` lines and the `git:`/`repo:`/`path:` grammar;
449
+ `imports:` of member souls; `exports:` lists; `oats.yaml`; the
450
+ installed-capability tier (`.agents/capabilities/installed/`) and
451
+ `oats-config.yaml` entirely; `oats init` / `use` / `install` / `restore` /
452
+ `trust` / `list` / `catalog` / `remove` / `migrate` / `config` (each answers
453
+ `E_UNKNOWN_COMMAND` naming its replacement); per-soul `stores.<x>.inherit`;
454
+ ambient-skill exclusion at launch. Lock v1/v2 files are `E_LOCK_SCHEMA`.
455
+ There is no converter and no dual-schema reader: a 0.24.x kernel keeps
456
+ spawning 0.24.x deployments; see [rebuild-to-v2.md](rebuild-to-v2.md).
457
+
458
+ ## Related
459
+
460
+ - [Souls and instances](souls-and-instances.md) · [Packages](packages.md) ·
461
+ [Configuration (`oats-local.yaml`)](configuration.md) · [Rebuild guide](rebuild-to-v2.md)
462
+ - [Capability manifests](capabilities.md) · [Contracts](layers.md) ·
463
+ [Desktop CLI API — workspace model](desktop-cli-api.md#workspace-model-workspaceapi-2)
464
+ - [Design navigation](design/README.md)