@awebai/oats 0.25.8 → 0.26.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 (185) hide show
  1. package/README.md +8 -6
  2. package/bin/oats.mjs +576 -1714
  3. package/capabilities/oats-authoring/oats-package.json +2 -2
  4. package/capabilities/oats-authoring/oats.json +2 -2
  5. package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +46 -25
  6. package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +13 -6
  7. package/capabilities/oats-aweb/bin/oats-aweb.mjs +334 -72
  8. package/capabilities/oats-aweb/injects/aweb.md +7 -2
  9. package/capabilities/oats-aweb/lib/binding-wire.mjs +107 -5
  10. package/capabilities/oats-aweb/lib/captured-native.mjs +1 -1
  11. package/capabilities/oats-aweb/lib/grant-custody.mjs +38 -0
  12. package/capabilities/oats-aweb/oats.json +14 -4
  13. package/capabilities/oats-jira/bin/oats-jira.mjs +4 -4
  14. package/capabilities/oats-jira/oats.json +2 -2
  15. package/capabilities/oats-jira/skills/jira-tasks/SKILL.md +6 -3
  16. package/capabilities/oats-linear/bin/oats-linear-hook.mjs +6 -4
  17. package/capabilities/oats-linear/oats.json +2 -2
  18. package/capabilities/oats-linear/skills/linear-tasks/SKILL.md +6 -0
  19. package/capabilities/oats-okf/bin/oats-okf.mjs +1 -1
  20. package/capabilities/oats-okf/lib/binding-wire.mjs +1 -1
  21. package/capabilities/oats-okf/lib/migration.mjs +2 -2
  22. package/capabilities/oats-okf/lib/sources.mjs +5 -4
  23. package/capabilities/oats-okf/lib/stores.mjs +40 -9
  24. package/capabilities/oats-okf/lib/worker.mjs +3 -3
  25. package/capabilities/oats-okf/oats.json +1 -1
  26. package/capabilities/oats-review/oats.json +3 -2
  27. package/docs/capabilities.md +218 -47
  28. package/docs/capability-manifest.schema.json +13 -4
  29. package/docs/configuration.md +17 -5
  30. package/docs/conventions.md +16 -26
  31. package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +1 -1
  32. package/docs/design/2026-09-13-knowledge-and-memory-direction.md +3 -3
  33. package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +2 -2
  34. package/docs/design/2026-09-15-portable-souls-handoff.md +2 -2
  35. package/docs/design/2026-09-15-portable-souls-implementation.md +1 -1
  36. package/docs/design/2026-09-20-redesign-program-board.md +2 -2
  37. package/docs/design/2026-09-23-workspace-module-contracts.md +1 -1
  38. package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +1 -1
  39. package/docs/design/2026-09-24-desktop-phase-f-boundary.md +54 -10
  40. package/docs/design/2026-09-24-phase-d-plan.md +77 -0
  41. package/docs/design/2026-09-25-teams-contract.md +226 -0
  42. package/docs/design/README.md +3 -3
  43. package/docs/design/launch-configurations.md +20 -16
  44. package/docs/design/operations-contract.md +27 -10
  45. package/docs/desktop-cli-api.md +546 -264
  46. package/docs/desktop-instance-start.md +1 -1
  47. package/docs/desktop.md +7 -13
  48. package/docs/execution-targets.md +16 -18
  49. package/docs/first-team.md +14 -17
  50. package/docs/implementation.md +28 -59
  51. package/docs/integrations.md +88 -32
  52. package/docs/knowledge-capability-authoring.md +1 -1
  53. package/docs/knowledge-reference/package-craft.md +10 -8
  54. package/docs/knowledge-theory.md +1 -1
  55. package/docs/knowledge.md +10 -11
  56. package/docs/layers.md +16 -17
  57. package/docs/oats-local.schema.json +29 -1
  58. package/docs/oats-membership.schema.json +5 -3
  59. package/docs/oats-package.schema.json +2 -2
  60. package/docs/oats-workspace.schema.json +1 -1
  61. package/docs/{official-marketplace.md → official-catalog.md} +15 -16
  62. package/docs/packages.md +75 -52
  63. package/docs/release-notes/v0.22.0.md +1 -1
  64. package/docs/release-notes/v0.23.1.md +1 -1
  65. package/docs/release-notes/v0.25.9.md +23 -0
  66. package/docs/release-notes/v0.26.0.md +670 -0
  67. package/docs/schedules.md +48 -126
  68. package/docs/soul.schema.json +11 -4
  69. package/docs/souls-and-instances.md +56 -43
  70. package/docs/workspaces.md +80 -58
  71. package/injects/instance-boundary.md +1 -1
  72. package/injects/work-attached.md +1 -1
  73. package/injects/work-workspace.md +2 -2
  74. package/lib/{portable-files.mjs → bounded-read.mjs} +6 -6
  75. package/lib/{portable-values.mjs → canonical-json.mjs} +3 -12
  76. package/lib/capability-contract.mjs +110 -0
  77. package/lib/config-data.mjs +2 -2
  78. package/lib/core.mjs +700 -4824
  79. package/lib/digest.mjs +12 -0
  80. package/lib/instance-inspect.mjs +396 -0
  81. package/lib/instance-lifecycle.mjs +3 -4
  82. package/lib/instance-resolution.mjs +212 -26
  83. package/lib/instruction-composition.mjs +0 -20
  84. package/lib/materialize.mjs +6 -4
  85. package/lib/operator-dispatch.mjs +33 -13
  86. package/lib/packages.mjs +25 -190
  87. package/lib/provider-binding.mjs +4 -2
  88. package/lib/provider-reasons.mjs +3 -68
  89. package/lib/resolve.mjs +204 -68
  90. package/lib/schedule.mjs +97 -272
  91. package/lib/servers.mjs +13 -13
  92. package/lib/{portable-shape.mjs → shape.mjs} +4 -3
  93. package/lib/tree-copy.mjs +44 -0
  94. package/lib/workspace.mjs +125 -20
  95. package/package-catalog.json +7 -7
  96. package/package.json +1 -1
  97. package/skills/integration-authoring/SKILL.md +48 -40
  98. package/skills/oats-getting-started/SKILL.md +105 -110
  99. package/skills/oats-support/SKILL.md +2 -2
  100. package/skills/soul-craft/SKILL.md +13 -6
  101. package/bin/oats-pi-sdk-host.mjs +0 -17
  102. package/docs/2026-09-03-architecture-proposal.md +0 -642
  103. package/docs/artifact-approvals.schema.json +0 -7
  104. package/docs/captured-invocation-context.schema.json +0 -7
  105. package/docs/captured-resolution.schema.json +0 -7
  106. package/docs/design/package-engine-contract.md +0 -813
  107. package/docs/design/package-runtime-api.md +0 -588
  108. package/docs/desktop-succession.md +0 -57
  109. package/docs/execution-capsule.schema.json +0 -108
  110. package/docs/first-team-demo.md +0 -92
  111. package/docs/knowledge-migration.md +0 -147
  112. package/docs/migration-from-oas.md +0 -103
  113. package/docs/oats-config.schema.json +0 -172
  114. package/docs/oats-lock-v3.schema.json +0 -7
  115. package/docs/oats-lock.schema.json +0 -175
  116. package/docs/operating-team-migration.md +0 -470
  117. package/docs/portable.schema.json +0 -2512
  118. package/docs/provider-check-input.schema.json +0 -7
  119. package/docs/rebuild-to-v2.md +0 -511
  120. package/docs/workspace-adoption.md +0 -74
  121. package/injects/framework-workspace.md +0 -7
  122. package/injects/local-soul.md +0 -19
  123. package/injects/oats-portable.md +0 -20
  124. package/injects/oats.md +0 -11
  125. package/injects/portable-instance-boundary.md +0 -39
  126. package/injects/portable-work-directory.md +0 -29
  127. package/lib/artifact-approvals.mjs +0 -120
  128. package/lib/artifact-tree.mjs +0 -141
  129. package/lib/capability-artifacts.mjs +0 -179
  130. package/lib/capability-execution.mjs +0 -15
  131. package/lib/capability-inputs.mjs +0 -39
  132. package/lib/capability-provenance.mjs +0 -231
  133. package/lib/captured-action-shape.mjs +0 -21
  134. package/lib/captured-admission-shape.mjs +0 -20
  135. package/lib/captured-binding-file.mjs +0 -36
  136. package/lib/captured-dispatch.mjs +0 -66
  137. package/lib/captured-instance-index.mjs +0 -277
  138. package/lib/captured-invocation-context.mjs +0 -130
  139. package/lib/captured-launch-request.mjs +0 -66
  140. package/lib/captured-operation-process.mjs +0 -15
  141. package/lib/captured-pi-custody.mjs +0 -29
  142. package/lib/captured-pi-host.mjs +0 -167
  143. package/lib/captured-pi-outcome.mjs +0 -172
  144. package/lib/captured-resolutions.mjs +0 -275
  145. package/lib/captured-scaffold.mjs +0 -87
  146. package/lib/captured-selector.mjs +0 -28
  147. package/lib/captured-session-backend.mjs +0 -52
  148. package/lib/captured-source-receipt-file.mjs +0 -72
  149. package/lib/helper-injection-policy.mjs +0 -104
  150. package/lib/legacy-lock-codec.mjs +0 -106
  151. package/lib/manifest-settings.mjs +0 -84
  152. package/lib/package-closure.mjs +0 -48
  153. package/lib/package-materialization.mjs +0 -83
  154. package/lib/pi-sdk-host.mjs +0 -229
  155. package/lib/portable-artifacts.mjs +0 -115
  156. package/lib/portable-choices.mjs +0 -82
  157. package/lib/portable-composition.mjs +0 -136
  158. package/lib/portable-digest.mjs +0 -105
  159. package/lib/portable-identity.mjs +0 -40
  160. package/lib/portable-lock.mjs +0 -117
  161. package/lib/portable-onboarding-request.mjs +0 -49
  162. package/lib/portable-onboarding.mjs +0 -256
  163. package/lib/portable-package-preparation.mjs +0 -188
  164. package/lib/portable-policy.mjs +0 -44
  165. package/lib/portable-soul.mjs +0 -42
  166. package/lib/portable-state.mjs +0 -80
  167. package/lib/prepare-composition.mjs +0 -170
  168. package/lib/prepared-bindings.mjs +0 -92
  169. package/lib/prepared-resources.mjs +0 -127
  170. package/lib/provider-binding-broker.mjs +0 -65
  171. package/lib/provider-binding-wire.mjs +0 -116
  172. package/lib/readiness.mjs +0 -225
  173. package/lib/repository-observation.mjs +0 -226
  174. package/lib/resolution-shape.mjs +0 -393
  175. package/lib/schedule-capsule.mjs +0 -206
  176. package/lib/soul-constraints.mjs +0 -40
  177. package/lib/source-projection.mjs +0 -84
  178. package/lib/source-spec.mjs +0 -189
  179. package/lib/workspace-definition.mjs +0 -126
  180. package/lib/workspace-discovery.mjs +0 -146
  181. package/skills/oats/SKILL.md +0 -162
  182. package/skills/oats-config/SKILL.md +0 -164
  183. package/skills/oats-packages/SKILL.md +0 -184
  184. package/skills/oats-portable/SKILL.md +0 -115
  185. package/skills/oats-portable-artifacts/SKILL.md +0 -63
@@ -25,7 +25,7 @@ Legend: ✅ on main/published · 🔄 in flight (PR/branch) · 🟡 preserved, n
25
25
  - Open: strict-Pi "enriched profile" remains unqualified (documented, not hidden).
26
26
 
27
27
  ## S2 — Workspace adoption of the OATS repos
28
- - ✅ **PR23 merged f6d5a89b**: `oats-workspace.yaml` (7 members, `imports: []`), `oats.yaml` (exports souls/oats-expert, oats-package, capabilities/oats-authoring), transitional `souls/oats-expert/` edition, `docs/workspace-adoption.md`, layout tests.
28
+ - ✅ **PR23 merged f6d5a89b**: `oats-workspace.yaml` (7 members, `imports: []`), `oats.yaml` (exports souls/oats-expert, oats-package, capabilities/oats-authoring), transitional `souls/oats-expert/` edition, the workspace adoption guide (removed in 0.26.0), layout tests.
29
29
  - ✅ **PR24 merged da38e5a9**: deletion of `skills/oats-portable-setup` + `oats inspect --request` read-only seam (ACCEPTED as the public inspection route); full gate 1621/0.
30
30
  - ✅ Member `oats.yaml` merged to main: oats-okf #3 (fec78a20), oats-aweb #1 (069ea2f6), oats-authoring #1 (54183a6a), oats-jira #1 (2f855daf).
31
31
  - ✅ oats-dev#1 (main 6e164ee3) and oats-linear#1 (main a2121e48) merged after Juan granted write access — M's exact commits 0434f4ef / 8c183c37.
@@ -117,7 +117,7 @@ Fresh directory and deployment, `@awebai/oats@0.24.4`, policy sources v2.1.2 / v
117
117
 
118
118
  ## S5 — Official marketplace
119
119
  - ✅ Mechanism exists (`package-catalog.json`, `officialPackageCatalog()`); decision names it the official list.
120
- - ✅ **D4 merged PR26 (786490ae)**: `docs/official-marketplace.md`, `package-catalog.json` policy pointer (inert to the reader), README/packages/capabilities links, D3 sketch in adoption guide. ⬜ entries for `oats.core`/`oats.setup` at D1 release. Desktop view → S8.
120
+ - ✅ **D4 merged PR26 (786490ae)**: the official list policy (now `docs/official-catalog.md`), `package-catalog.json` policy pointer (inert to the reader), README/packages/capabilities links, D3 sketch in adoption guide. ⬜ entries for `oats.core`/`oats.setup` at D1 release. Desktop view → S8.
121
121
 
122
122
  ## S6 — Five expert souls in the oats repo
123
123
  - ✅ **PR30 merged (40a579dc)** + maintainer follow-up **caa341f3**: all five declare `oats.core: {source: repo:oats-package}`, oats.okf@v2.1.1; `oats.yaml` exports all five; `oats-workspace.yaml` imports all five at caa341f3 (375b9f42). Live inspection against published main resolves them.
@@ -462,7 +462,7 @@ stays advertised because the verb still serves classic homes
462
462
  ### 0.25.2 operator-rebuild round (2026-09-24) — appended, not edited in place
463
463
 
464
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
465
+ following the 0.25 rebuild guide literally (removed in 0.26.0). **The guide is a contract the
466
466
  kernel must honour**: where the guide claimed behaviour the kernel lacked, the
467
467
  kernel changes; where the guide described keys no provider consumes, the guide
468
468
  changes. Findings R1–R10; kernel side in 0.25.2 (`docs/release-notes/v0.25.2.md`).
@@ -29,7 +29,7 @@ Each is one PR (or two small ones), reviewed by me, on `main`, as canonical code
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
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 |
32
+ | **W10** | **Docs** | **the rebuild guide (removed in 0.26.0; 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
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
 
@@ -87,15 +87,26 @@ the purpose field becomes a name field that shows `<soul>-<purpose>` live and
87
87
  then the kernel's final name from the preview. A no-prefix toggle maps to
88
88
  `spawn --name <slug>`, gated on the `spawn-name` feature; `--purpose` stays the
89
89
  default. **Runtime and model** are always visible, with their resolved value and
90
- its source. **Work** comes from the soul's `work:` mode, and a `checkout` soul is
91
- offered "Use a worktree instead?" (`--work worktree`). There is **no**
92
- modules/capabilities list and no attach-knowledge / child-spawn / open-PR
93
- toggles, because those behaviours come from capabilities. There is **no**
94
- preview button either. The preview runs in the background to fill the real
95
- defaults, and apply still binds with `--expect-decision` (`E_DECISION_STALE`
96
- re-previews). A collapsed **Advanced** section holds harness permissions, launch
97
- config, relation, messaging identity (`--provider <cap> identity.mode=…`,
98
- decision 27), branch/base overrides and the execution server. Every modal gets a darker backdrop.
90
+ its source. **Relationship** sits in the main form, shown by default: None /
91
+ Child of / Sibling of / Parent of. There is **no** modules/capabilities list and
92
+ no attach-knowledge / child-spawn / open-PR toggles, because those behaviours
93
+ come from capabilities. There is **no** preview button either. The preview runs
94
+ in the background to fill the real defaults, and apply still binds with
95
+ `--expect-decision` (`E_DECISION_STALE` re-previews). A collapsed section named
96
+ **Developer settings** holds:
97
+ - **work**: base | branch and the worktree path. A `checkout` soul is offered
98
+ "Use a worktree instead?" (`--work worktree`); the work mode itself comes
99
+ from the soul's `work:`.
100
+ - harness permissions
101
+ - launch config
102
+ - session backend
103
+ - Run on (the execution server)
104
+ - wake-up
105
+ - messaging identity (`--provider <cap> identity.mode=…`, decision 27)
106
+
107
+ (Second human redirect, same day: relationship moved into the main form, work
108
+ moved into the collapsed section, and the section was renamed from "Advanced".)
109
+ Every modal gets a darker backdrop.
99
110
 
100
111
  **F4 — Instance card and roster on v2 facts.** The served-identity line (`acts
101
112
  as <address> via grant, expires <t>` / `alias <a> on <team>`); module rows with
@@ -108,6 +119,38 @@ action (`oats retire`) and `--force` behind a confirm.
108
119
  **F5 — Redesign frames** (the original Phase 3 deliverable, excl. 05/06),
109
120
  implemented on top of F1–F4 rather than on the 0.24 model.
110
121
 
122
+ **F7 — Side panels redesigned, teams everywhere (human, 2026-09-25). This
123
+ supersedes the frames and the text above wherever they differ.** One Desktop PR:
124
+ - **Side panels** in the spawn modal's design language (frame 01a):
125
+ - an identity header and compact cards;
126
+ - only useful facts (no "Not reported" rows), and empty sections hidden;
127
+ - relative dates, and shortened paths with Copy;
128
+ - errors as one plain sentence, with the code behind Details;
129
+ - discoverable cross-links between an instance and its soul.
130
+ - **Terminal-side context panel tabs, in order:** Instance · Soul · Git & GitHub.
131
+ - **Teams, per surface** (teams contract `docs/design/2026-09-25-teams-contract.md`):
132
+ - **Soul** (Workspace inspector): the soul's labels, primary marked, each
133
+ joinable or "not mapped by this workspace". Read-only, from
134
+ `inspect --soul` `teams`.
135
+ - **Instance** (Workspace inspector and the terminal-side Instance tab): the
136
+ live panel (#178). Personal is always on; joined teams have Leave, and
137
+ joinable ones have Join.
138
+ - **Spawn modal, main form** (not Developer settings), a Teams row:
139
+ - "Personal team — always", fixed;
140
+ - each joinable label as an unchecked checkbox, sent as
141
+ `--provider <messaging> join=<a,b>`;
142
+ - unmapped labels greyed with the reason;
143
+ - the line "By default it's only in your personal team. Tick the teams it
144
+ should also join."
145
+
146
+ The row is shown iff the preview carries `teams` AND the messaging
147
+ module's preview row lists `join` in `declares` (feature
148
+ `settings-declared`; the Desktop gates on declared facts, never on
149
+ versions).
150
+ - **Second human redirect (QA, same day):** unmapped labels are not shown
151
+ anywhere (no greyed rows, no "not mapped" text), and Teams in the spawn
152
+ modal is one row styled like Relationship.
153
+
111
154
  **F6 — Version and doctor surface.** `oats version --json` and `oats doctor
112
155
  --json` in an About/Health pane; `ACCEPT_RANGE` and the three pins move to
113
156
  `>=0.25.6`; a kernel below the floor is refused with the upgrade command shown.
@@ -120,7 +163,8 @@ spawns children (its call; the lead reviews each PR).
120
163
  Read, in this order, in the checked-out main:
121
164
  1. `docs/workspaces.md` — the v2 model end to end (deployment vs workspace,
122
165
  members, packages, payloads, `byTeam`, hosting).
123
- 2. `docs/rebuild-to-v2.md` — how an operator builds a deployment (this is the
166
+ 2. the 0.25 rebuild guide (removed in 0.26.0; `docs/configuration.md` and
167
+ `oats onboard` now) — how an operator builds a deployment (this is the
124
168
  flow F2 wraps).
125
169
  3. `docs/desktop-cli-api.md` — every JSON surface, with examples; note
126
170
  `features[]`, `decision.effective.providers`, `instances[].identity`.
@@ -42,6 +42,26 @@ pushes. Agreed by both on 2026-09-24:
42
42
  - `main` on these repositories carries no branch or tag protection: the parity
43
43
  gate and the cross-review are the only things between a merge and `main`.
44
44
 
45
+ ## Human decision (2026-09-24): no package approval
46
+
47
+ **Package approval is removed from the kernel and from the Desktop.** People
48
+ install a package only when they trust it. Declaring it in the workspace's
49
+ `packages:` IS the trust decision, so there's no second, per-version approval
50
+ step. This supersedes the "executables approved once per version" rule in
51
+ `docs/workspaces.md` (§ Packages, lock, approval, catalog) and everything built
52
+ on it:
53
+ - `oats sync` exit 2 for pending approvals and `approvalNeeded`
54
+ - `--approve <id>@<version>` and the interactive prompt
55
+ - the lock's `approved` record
56
+ - `E_PACKAGE_UNAPPROVED` at spawn and dispatch
57
+ - the Desktop F2 approval flow (`E_APPROVAL_STALE`)
58
+ - the planned pinned `--approve …=<digest>`
59
+
60
+ What stays: the lock still pins each package to the exact commit and integrity,
61
+ and restore still refuses drift (`E_PACKAGE_INTEGRITY`). Reproducibility is not
62
+ approval. It's a breaking contract change, so it ships in a minor release, and
63
+ the Desktop requires that kernel.
64
+
45
65
  ## Slices, in order
46
66
 
47
67
  ### D1 — Knowledge centralisation (IN PROGRESS)
@@ -147,6 +167,51 @@ Kernel (lead): pass the soul's team label and the workspace identity so the
147
167
  provider derives the personal team deterministically; an unmapped team means
148
168
  "personal". The onboarding skill's manual invite-then-join step is the 1.12.0
149
169
  path and is rewritten when the provider ships R1/R2.
170
+ **Teams, re-stated as THE priority (human, 2026-09-25; amends R1/R2):**
171
+ "As long as we don't have this working seamlessly, people won't understand
172
+ things." The two most important messaging behaviours, above every other
173
+ messaging item:
174
+ - **(R1) A personal team by default.** A user's agents in a workspace get
175
+ that person's personal team for the workspace with no setup step. This is
176
+ the default whenever the workspace maps nothing else.
177
+ - **Defaults and joining (human, later the same day; refines R1/R2′):**
178
+ - By default an instance is in the person's personal team ONLY, even when
179
+ its soul names teams.
180
+ - Joining a wider team is explicit: a spawn choice, or at any point of the
181
+ instance's life one simple command, run by the human, another agent, or
182
+ the instance when told to. It covers only the soul's labels the workspace
183
+ maps.
184
+ - The Desktop offers these controls.
185
+ - Personal teams are per WORKSPACE: one personal team spanning several
186
+ workspaces is wrong, and aweb's per-workspace get-or-create (abjj) is
187
+ urgent. Nobody uses OATS in production yet, which is what keeps this a
188
+ fix and not a migration.
189
+ - Contract: `docs/design/2026-09-25-teams-contract.md`.
190
+ - **(R2′) Wider teams, at spawn AND during an instance's life.** When a soul
191
+ belongs to one or more wider teams, its instance is also included in each
192
+ team the soul specifies, both at spawn and at any later point of its life,
193
+ not only at spawn. The teams are exactly those the workspace defines
194
+ (`messaging.byTeam` / the workspace's team labels); no team outside the
195
+ workspace's definition, and none silently missing. R2′ supersedes R2's
196
+ "is spawned … joins": joining is a lifetime operation, and a soul may name
197
+ several teams.
198
+ Both are oats.aweb's (Antares) with aweb primitives. **The kernel's share is
199
+ the lead's:**
200
+ - today `soul.team` is ONE label (docs/soul.schema.json), and the messaging
201
+ payload merges `byTeam[soul.team]` for that one label. "Team/teams" needs a
202
+ soul to name several team labels, and the payload to carry each mapped
203
+ team's entry (not one merged view), so the provider can join all of them;
204
+ - the workspace identity and the person's identity reach the provider, so
205
+ the personal team derives deterministically;
206
+ - a lifecycle entry point for "join now" during an instance's life (for
207
+ example, a provider operation or hook run against a live home when the
208
+ workspace's teams or the soul's team labels change at a new commit).
209
+ The contract shape is decided with Antares and recorded as a Decision before
210
+ code. **Human, same day:** the primitives must be seamlessly integrated into
211
+ the aweb messaging capability (acceptance is the oats.aweb experience: no
212
+ manual `aw team …` anywhere we ship, and live instances follow the
213
+ workspace's teams), and kernel changes are in scope, so the provider is not
214
+ bent around today's kernel.
150
215
  **Naming (lead, 2026-09-24):** the `oats-` prefix on all six —
151
216
  it matches the repository names and the roster's `oats-kernel-`/`oats-desktop-`/
152
217
  `oats-operator-expert`, and it keeps instance aliases from colliding with the
@@ -191,6 +256,18 @@ the harvester delivers to that node as a PR the owning expert reviews.
191
256
  - **Legacy souls** (`agents/*` and their knowledge bundles) are NOT part of D4:
192
257
  they go when the live instances linking them retire (human rule).
193
258
 
259
+ **Human direction 2026-09-25: runtime → harness** ("throughout the app and cli and everywhere"). It ships in 0.26.0 as one kernel PR after (e):
260
+ - every kernel-owned name is renamed with no alias (`--harness`, `launch-configs.<n>.harness`, `instance.json.harness`, JSON fields, feature `harness`);
261
+ - two released-contract aliases stay: the provider env sets both `OATS_HARNESS` and `OATS_RUNTIME`, and manifests may say `requires[].runtime` or `requires[].harness`;
262
+ - the Desktop switches on the `harness` feature.
263
+
264
+ **Human direction 2026-09-25 (~17:20Z), amending the above: release 0.26.0 WITHOUT the harness rename; harness ships as a separate, later release.**
265
+ - 0.26.0 ships main as it stands once the in-flight PRs land: (e), K, the teamsOf check, `layers.<layer>.from`, and Desktop F7. It keeps the `runtime` names everywhere.
266
+ - The harness rename (the kernel PR, with the alias addendum: env `OATS_RUNTIME`, stdin `launch.runtime`, manifest `agents[].runtime`/`requires[].runtime`, `spawn --runtime`) ships in the **next minor, 0.27.0**. It is a breaking CLI/JSON change (`--runtime` refused on session/launch-config, `version.runtimes` dropped, launch-configs field renamed), so it isn't a patch. It ships together with the Desktop's switch on feature `harness`, and the Desktop's accepted kernel range widens to include 0.27.
267
+ - Neither the kernel harness PR nor the Desktop switch merges to main before the v0.26.0 tag.
268
+
269
+ Also: "core capabilities" is the human vocabulary for the knowledge/messaging/tasks capabilities in prose; wire names are unchanged.
270
+
194
271
  ### D5 — Catalog update and 0.26.0
195
272
 
196
273
  Catalog pins for the new package versions; **widen Desktop `ACCEPT_RANGE` and
@@ -0,0 +1,226 @@
1
+ # Teams contract: several team labels per soul, per-team messaging, live reconciliation
2
+
3
+ Status: AGREED 2026-09-25 by both co-leads (provider verbs confirmed in e205c93a). First drafted by the lead (kernel lane), with the messaging
4
+ co-lead's provider plan. It serves the human priority of 2026-09-25, recorded
5
+ in `2026-09-24-phase-d-plan.md` under "Teams, re-stated as THE priority".
6
+ This document is the kernel half; the provider half (oats.aweb) is the
7
+ messaging lane's. Co-lead review (9a18a381): agreed, with three additions,
8
+ folded in below.
9
+
10
+ ## The model (human, 2026-09-25)
11
+
12
+ - **Default: the personal team only.** Every instance is in its person's
13
+ personal team for THIS workspace. A soul's `team` labels do NOT put it
14
+ in those teams by default.
15
+ - **Joining is explicit.** A wider team is joined by an explicit action:
16
+ - at spawn, a spawn choice;
17
+ - or at any point of the instance's life, one simple command, run by the
18
+ human, by another agent, or by the instance itself when told to.
19
+ - **Only what the soul and workspace allow.** The teams an instance MAY join
20
+ are exactly its soul's labels that the workspace maps. Nothing else is
21
+ offered or accepted.
22
+ - **Leaving** is the same kind of command. When the workspace removes a
23
+ mapping or the soul drops a label, the joined membership for it is left.
24
+ - **The Desktop has controls for it:** at spawn (which eligible teams to
25
+ join) and on a live instance (join/leave, and the joined vs eligible
26
+ teams).
27
+ - **Personal teams are per WORKSPACE.** One personal team spanning several
28
+ workspaces is wrong. Until aweb ships the per-workspace get-or-create, the
29
+ person's single default team is an explicitly temporary stand-in.
30
+
31
+ ## Problem
32
+
33
+ - A v2 soul names ONE team label (`soul.yaml` `team`, else the repository's
34
+ default from `oats-membership.yaml`).
35
+ - The resolver merges `workspace.messaging ⊕ byTeam[<that label>]` into one
36
+ messaging payload, and applies `defaults.byTeam[<that label>].capabilities`
37
+ to the soul's composition.
38
+ - An instance can therefore be placed in one team only, and only at spawn.
39
+ - The human's bar: a person's agents are in their personal team by default,
40
+ AND in every wider team their soul belongs to, at spawn and during the
41
+ instance's life, and those teams are exactly the ones the workspace defines.
42
+
43
+ ## Decision
44
+
45
+ 1. **Several labels.**
46
+ - `soul.yaml` `team` accepts a label or a non-empty list of distinct
47
+ labels: `team: dev` or `team: [dev, reviewers]`.
48
+ - `oats-membership.yaml`'s repository default takes the same shape.
49
+ - The FIRST label is the **primary**. A single string is a one-element
50
+ list, so existing souls don't change.
51
+ 2. **Composition (`defaults.byTeam[*].capabilities`).**
52
+ - Applied for every label, in soul order, after `defaults.capabilities`
53
+ and before the soul's own `capabilities`. The soul's own entries still
54
+ win.
55
+ - Two labels that give the same capability different entries is
56
+ `E_TEAM_CONFLICT`, naming both labels. There's no silent
57
+ last-writer-wins. Identical entries from two labels are not a
58
+ conflict.
59
+ 3. **Messaging payload.**
60
+ - **Amended (K, co-lead ruling on the 1.14.0 review):** the messaging
61
+ provider's merged settings are `base ⊕ soul ⊕ host ⊕ spawn`, and **no
62
+ `byTeam[<label>]` is merged into them**, the primary's included. Each
63
+ label's `base ⊕ byTeam[label]` lives only in its `teams` entry (below).
64
+ So `settings.team` / `OATS_TEAM_ID` mean "the personal team, if the
65
+ host, soul or spawn set one"; empty means the provider's own default
66
+ (for oats.aweb, the root's active team). Reason: the instance is
67
+ personal-by-default (the human's model), and the primary label is just
68
+ the first eligible team. Merging its payload made the provider mint the
69
+ primary identity into the mapped team, and it couldn't tell a host-set
70
+ personal team from the workspace's mapped one.
71
+ - New and kernel-owned: `teams`, an ordered list of
72
+ `{ label, mapped: boolean, payload }`.
73
+ - `payload` is `base ⊕ byTeam[label]` when the workspace maps the label.
74
+ - It's `base` alone, with `mapped: false`, when the label isn't mapped.
75
+ - Each entry also carries the resolved team id as `team` (null when the
76
+ label isn't mapped), so a provider never digs it out of `payload`.
77
+ "Personal" is the provider's to resolve; the kernel says nothing about
78
+ it.
79
+ - These are the **eligible** teams. Joining them is explicit (see "The
80
+ model"), not automatic.
81
+ - It's delivered beside the settings, in the environment (`OATS_TEAMS` JSON),
82
+ never inside the provider's own settings object, so it can't collide
83
+ with a provider key or its manifest's settings validation.
84
+ - `teams` is present when the soul has a label; a soul with no label gets
85
+ `[]`, meaning "personal only".
86
+ 4. **Environment.**
87
+ - `OATS_TEAM_LABEL` / `OATS_TEAM_ID` stay the primary's.
88
+ - New: `OATS_TEAM_LABELS` (all labels, comma-joined, in order) and
89
+ `OATS_TEAMS` (the JSON above).
90
+ - New: `OATS_TEAMS_SOURCE`, which is `live` or `recorded` (decision 6). It's empty
91
+ when `OATS_TEAMS` is empty (unknown).
92
+ - **The environment is the only channel.** No stdin wire gains keys: the
93
+ binding check's request stays exactly the released wire, because released
94
+ providers (oats.aweb 1.13.1) key it strictly and refuse unknown keys with
95
+ `invalid-binding`. A provider check reads the teams from the same env
96
+ variables its hooks get. (Correction after #179, co-lead finding.)
97
+ - Workspace identity is unchanged: `OATS_WORKSPACE_KEY` / `OATS_WORKSPACE_NAME`.
98
+ - The person's identity stays the provider's (its messaging root from host
99
+ settings); the kernel passes the deployment, as today.
100
+ 5. **Discovery.**
101
+ - A label the workspace doesn't map is a discovery **warning**
102
+ (`unmapped-team-label`), not an error, so the provider can fall back to
103
+ the personal team and say so.
104
+ - There's one warning per unmapped label, naming its souls
105
+ (`{ code, label, souls, paths, message }`), not one per soul and label.
106
+ A workspace with no `messaging.byTeam` must not print a line per soul.
107
+ - A label that isn't in the workspace's `teams:` list at all stays the
108
+ error it is today.
109
+ 6. **Live resolution for a home.**
110
+ - A home's teams are **live messaging state, not frozen composition**. The
111
+ modules and skills an instance got at spawn don't change under it; which
112
+ teams it belongs to follows the workspace.
113
+ - For home-context provider operations and the launch hook, the kernel
114
+ computes `teams` from the soul's labels at the soul commit the
115
+ deployment currently resolves (the lock), and the workspace's current
116
+ `messaging`.
117
+ - That lets the provider offer a team the workspace added as eligible,
118
+ and leave one it removed, without a respawn.
119
+ - The recorded spawn-time `teams` stays in `instance.json.teams` as
120
+ evidence: beside `providers`, never inside that capability-keyed map.
121
+ - **Live or recorded.** When the workspace can't be read now (the host is
122
+ offline, or the soul is no longer listed), the kernel falls back to the
123
+ recorded set and says so: `OATS_TEAMS_SOURCE=recorded`, or
124
+ `teamsSource: "recorded"` in inspect.
125
+ - **A provider leaves a joined team only on a `live` answer.** On
126
+ `recorded` or unknown it keeps every membership and may warn
127
+ (`teams-unverified`).
128
+ - Reason: a team mapped after spawn and joined live is absent from the
129
+ record, and an offline host must never cost an instance a membership.
130
+ - **Cost.** Live teams are computed only where they're consumed:
131
+ - session start/restart;
132
+ - the messaging module's own home-context commands and operations;
133
+ - `inspect` / `readiness --home`.
134
+
135
+ The live read covers the workspace host and the soul's repo, not a full
136
+ workspace discovery. Every other in-home capability command uses the
137
+ record and costs what it did before.
138
+ - **0.26.0 limitation:** a scheduled wake's start uses the spawn-time teams.
139
+ The scheduler is synchronous; only operator starts are live.
140
+ - **Retire** works from the provider's own recorded membership list,
141
+ never from the live eligible set. A mapping removed after the spawn
142
+ still has its membership revoked at retire.
143
+ 7. **Explicit join, spawn choice, Desktop.**
144
+ - The join/leave/list verbs are the provider's (oats.aweb 1.14.0), run
145
+ inside a home or with `--home <abs>`, all idempotent, all with `--json`:
146
+ - `oats aweb teams` answers
147
+ `{ personal: {team}, primary, eligible: [{label, team, joined}], joined: [{label, team, since, identityHome, receive}], unmapped: [label], at }`, where `receive` is `native` or `poll`;
148
+ - `oats aweb join <label>[,<label>]` and
149
+ `oats aweb leave <label>[,<label>]` answer the same document. The
150
+ personal team can't be left (`E_TEAM_PERSONAL`).
151
+ - The same verbs are declared as home-context operations
152
+ `messaging:teams|join|leave`, so the Desktop uses `oats operation run`
153
+ and needs no new kernel surface.
154
+ All three are `kind: action` (the default): a `view` must answer
155
+ `{documents:[…]}` (markdown/text for reading), and the teams document
156
+ is structured JSON. `messaging:teams` is read-only by its own
157
+ contract, which its description says. Join and leave declare one
158
+ required arg, `labels` (flag `--labels`, comma-separated).
159
+ - Identity model: one local identity per joined team, under the home as
160
+ `.aweb-identity-<label>`. The personal-team identity is the primary one,
161
+ wired to the harness. There are no global identities by default.
162
+ - **Sending and receiving as a joined team (oats.aweb 1.14.0):**
163
+ - Sending is complete: `aw --identity-home <identityHome> mail|chat …`,
164
+ the one form the aweb inject teaches.
165
+ - Receiving is by POLL: the channel plugin, the pi extension and a wake
166
+ registration each listen on one identity home. The inject says to
167
+ check a joined team's inbox at task boundaries, and readiness and
168
+ `receive: poll` say so.
169
+ - Native receive for joined teams needs one of two aweb primitives:
170
+ wake/channel on several identity homes per instance home, or global
171
+ instance identities with address release (one identity, many teams,
172
+ one channel). This is a named gap against the "seamless" bar,
173
+ tracked as an aweb ask.
174
+ - Each is idempotent, and refuses a label the instance isn't eligible for
175
+ (`E_TEAM_NOT_ELIGIBLE`, naming the eligible labels).
176
+ - **Declared setting keys (added after #179):** the spawn preview's
177
+ `modules[]` rows and `inspect`'s `capabilities[]` rows carry
178
+ `declares: [<setting key>…]` (names only; feature `settings-declared`).
179
+ That's how a Desktop sees that the messaging manifest declares
180
+ `settings.join` without reading manifests.
181
+ - `inspect --soul` on a soul whose labels conflict (`E_TEAM_CONFLICT`)
182
+ refuses. The error details name the capability and both labels; no
183
+ `teams` is answered, because the soul can't be spawned until the
184
+ workspace resolves it.
185
+ - At spawn, the kernel carries the operator's choice to the provider as a
186
+ spawn provider setting (`--provider <messaging> join=<label>[,<label>]`).
187
+ That needs no new kernel flag, and the spawn preview shows it.
188
+ - The launch hook re-checks joined memberships against the live eligible
189
+ set: it leaves what's no longer eligible (only when
190
+ `OATS_TEAMS_SOURCE=live`, decision 6) and never joins on its own.
191
+ - **The kernel puts `teams` (eligible, the OATS_TEAMS entries) in the
192
+ spawn preview and in `inspect --home`**, so a Desktop can offer the
193
+ choice before anything is minted. The Desktop reads that, plus joined memberships from the provider's inspect or
194
+ operation, and drives the same verbs.
195
+ - Later kernel item: `oats sync` reports the souls whose labels or
196
+ mappings changed since the previous lock.
197
+
198
+ ## Compatibility
199
+
200
+ - A single-label soul sees byte-identical **composition** (modules, skills,
201
+ injects). Its messaging **payload** changes: the mapped label's `byTeam`
202
+ entry is no longer merged into the provider's settings; it's only in
203
+ `OATS_TEAMS` (amendment K). With 0.26.0 + oats.aweb 1.13.1, the primary
204
+ identity therefore mints into the personal (root-active) team, as the
205
+ human's model wants.
206
+ - oats.aweb 1.13.1 ignores `OATS_TEAMS` and keeps working, because no stdin
207
+ wire changes (its binding decoder refuses unknown request keys).
208
+ - oats.aweb 1.14.0 reads them.
209
+ - The schema change goes into 0.26.0, the release that already breaks the
210
+ file formats.
211
+
212
+ ## Tests
213
+
214
+ - Several labels compose in order; a conflict → `E_TEAM_CONFLICT`. A
215
+ capability the soul names itself is exempt, since the soul's entry wins over
216
+ every label.
217
+ - A mapped primary's `byTeam` payload is absent from the provider's merged
218
+ settings and present in `OATS_TEAMS[0].payload` (amendment K).
219
+ - `OATS_TEAMS` shape: mapped and unmapped labels, and no label → `[]`.
220
+ - Home-context `teams` follows a new workspace commit (a mapping added and
221
+ removed) while the home's modules stay frozen.
222
+ - Discovery warns once per unmapped label.
223
+ - A home whose workspace can't be read gets the record with
224
+ `OATS_TEAMS_SOURCE=recorded`; a non-messaging in-home command runs no live
225
+ team read.
226
+ - `teams` entries carry `team`; identical cross-label entries don't conflict.
@@ -7,12 +7,12 @@ Dated design documents record how decisions were reached and what each implement
7
7
  - **[Workspace module contracts (2026-09-23)](2026-09-23-workspace-module-contracts.md) — NORMATIVE for implementation**: `lib/remote.mjs`, `lib/workspace.mjs`, `lib/resolve.mjs`, `lib/packages.mjs` (lock v3), `lib/materialize.mjs`, the CLI verbs and DTOs, the error codes, the Northwind fixture.
8
8
  - [Simplified workspace model — worked example (2026-09-23)](2026-09-23-simplified-workspace-model.md) — ACCEPTED: one workspace per org, membership = trust, `from:` as location, nothing installed, full per-instance copy, teams as labels, harnesses start normally. The Decision concept is `agents/oats-expert/soul/knowledge/decisions/workspace-model-v2.md`.
9
9
  - [Implementation plan (2026-09-23)](2026-09-23-workspace-v2-implementation-plan.md) — phases A/B/C, what each deletes.
10
- - Operator-facing: [rebuild guide](../rebuild-to-v2.md) (0.24.x → v2; no converter), [Desktop CLI API — workspace model](../desktop-cli-api.md#workspace-model-workspaceapi-2).
10
+ - Operator-facing: [workspace model](../workspaces.md), [Desktop CLI API — workspace model](../desktop-cli-api.md#workspace-model-workspaceapi-2).
11
11
  - Open threads (tracked here until closed): the `workspace` work mode still derives its `./work` boundary from the classic `team:` scope; the readiness `enrolled` producer still reads the 0.24 `oats.yaml` backlink; launch configurations / yolo / work-mode setup are still read from a classic config chain.
12
12
 
13
13
  ## Superseded by the workspace model
14
14
 
15
- Everything below this line that describes per-soul `source:` provenance, `oats.yaml` exports/imports, the installed-capability tier (`.agents/capabilities/installed/`), `oats-config.yaml` scopes, `oats init`/`use`/`install`/`restore`/`trust`/`migrate`, lock v1/v2 or ambient-skill exclusion at launch is **history**. In particular [package-engine-contract.md](package-engine-contract.md) and [package-runtime-api.md](package-runtime-api.md) describe the removed acquisition/materialization engine; the package tier is now [packages.md](../packages.md) + module contract §4. Capability **manifests**, hooks, the operations contract, provider binding wire/codecs and the knowledge/messaging capability contracts are unchanged.
15
+ Everything below this line that describes per-soul `source:` provenance, `oats.yaml` exports/imports, the installed-capability tier (`.agents/capabilities/installed/`), `oats-config.yaml` scopes, `oats init`/`use`/`install`/`restore`/`trust`/`migrate`, lock v1/v2 or ambient-skill exclusion at launch is **history**. In particular `package-engine-contract.md` and `package-runtime-api.md` (both deleted in 0.26) described the removed acquisition/materialization engine; the package tier is now [packages.md](../packages.md) + module contract §4. Capability **manifests**, hooks, the operations contract, provider binding wire/codecs and the knowledge/messaging capability contracts are unchanged.
16
16
 
17
17
  ## Earlier plan (0.24)
18
18
 
@@ -38,7 +38,7 @@ Everything below this line that describes per-soul `source:` provenance, `oats.y
38
38
 
39
39
  ## Capabilities and providers
40
40
 
41
- - [Package engine contract](package-engine-contract.md) · [package-runtime API](package-runtime-api.md) — **superseded** (installed tier removed; see [packages](../packages.md)) · [operations contract](operations-contract.md) · [launch configurations](launch-configurations.md).
41
+ - Package engine contract (`package-engine-contract.md`, removed in 0.26) · package-runtime API (`package-runtime-api.md`, removed in 0.26) — **superseded** (installed tier removed; see [packages](../packages.md)) · [operations contract](operations-contract.md) · [launch configurations](launch-configurations.md).
42
42
  - [Provider binding wire v1](2026-09-16-provider-binding-wire.md) · [provider binding codecs](2026-09-16-provider-binding-codecs.md) · [capability helper/input contract](2026-09-17-capability-helper-input-contract.md).
43
43
  - [Knowledge capability contract](2026-09-16-knowledge-capability-contract.md) · [messaging capability contract](2026-09-16-messaging-capability-contract.md).
44
44
 
@@ -1,12 +1,16 @@
1
1
  # Launch configurations and launch recipes
2
2
 
3
- A **launch configuration** is a named way to start a harness, declared per
4
- scope under `launch-configs:` in `oats-config.yaml` (see
5
- docs/configuration.md): runtime, an executable, literal arguments,
6
- environment (literals or `{fromEnv}` references), model, yolo. It is
7
- independent of any soul; a soul may name one as its default
8
- (`launch-config:` in soul.yaml, `oats soul set --launch-config`), and a
9
- spawn, start or restart selects one by name.
3
+ A **launch configuration** is a named way to start a harness, declared by
4
+ the host under `launch-configs:` in the deployment's `oats-local.yaml` (see
5
+ docs/configuration.md; 0.26.0, lead decision 2 — earlier kernels read it
6
+ from a scope's `oats-config.yaml`): runtime, an executable, literal
7
+ arguments, environment (literals or `{fromEnv}` references), model, yolo.
8
+ It is independent of any soul, and a spawn, start or restart selects one by
9
+ name (`--launch-config`, or the Desktop's per-launch choice). A launch
10
+ configuration is a spawn-time host choice, never a soul field: `launch-config:`
11
+ is not a field of a workspace-model soul.yaml (docs/soul.schema.json; discovery
12
+ refuses it), so a v2 soul cannot name one, not even as a default. (A classic
13
+ 0.25 soul.yaml could name a preferred entry; 0.26.0 reads no such field.)
10
14
 
11
15
  A **launch recipe** is what a start is made of, recorded in the instance's
12
16
  `instance.json` under `launch` beside the rendered `command`:
@@ -15,9 +19,9 @@ A **launch recipe** is what a start is made of, recorded in the instance's
15
19
  {
16
20
  "version": 1,
17
21
  "runtime": "claude",
18
- "launchConfig": "personal", "launchConfigSource": "/scope",
19
- "executable": "/scope/tools/claude-wrapper.sh",
20
- "executableDeclared": "./tools/claude-wrapper.sh", "executableResolvedFrom": "relative to /scope",
22
+ "launchConfig": "personal", "launchConfigSource": "/deployment",
23
+ "executable": "/deployment/tools/claude-wrapper.sh",
24
+ "executableDeclared": "./tools/claude-wrapper.sh", "executableResolvedFrom": "relative to /deployment",
21
25
  "args": ["--settings", "/abs/settings.json"],
22
26
  "env": { "KEY": { "fromEnv": "SRC" }, "LIT": "plain" },
23
27
  "model": "claude-opus-5", "yolo": true,
@@ -55,16 +59,16 @@ shows them: `list` and `preview` redact every environment value.
55
59
  that disagrees with the configuration's runtime is refused
56
60
  (`E_LAUNCH_CONFIG_MISMATCH`) before anything happens; the same runtime may
57
61
  be repeated; `--model` and `--yolo` override the configuration's fields.
58
- - Without `--launch-config`: a spawn takes the soul's `launch-config` default
59
- or none; an existing home keeps its recorded configuration, except that
62
+ - Without `--launch-config`: a spawn uses no configuration (the runtime's
63
+ defaults; a soul names none); an existing home keeps its recorded configuration, except that
60
64
  `--runtime` alone deliberately leaves it behind and renders the new
61
65
  runtime's defaults (no old executable or args are carried).
62
66
  - Model: explicit, else the configuration's, else on an existing home the
63
67
  recorded model when the runtime is unchanged, else the runtime's native
64
- default; a spawn without either resolves the soul's preference for the
65
- runtime. A model never crosses runtimes.
68
+ default; a spawn without either uses the runtime's native default (a
69
+ workspace-model soul declares no model). A model never crosses runtimes.
66
70
  - Executable: the configuration's (bare name on PATH; a path resolved against
67
- the declaring scope when relative) or the runtime's default (claude through
71
+ the deployment directory when relative) or the runtime's default (claude through
68
72
  `oats-claude-config`). It must be a regular executable file; it is never
69
73
  run to probe it. Capability runtime-package requirements are checked with
70
74
  the runtime's default binary, as at spawn.
@@ -87,7 +91,7 @@ re-run by a start or restart.
87
91
  Read-only; nothing is locked or started. `--home ABS` describes an existing
88
92
  home under a selection (`selection.source`: `frozen` when nothing was
89
93
  selected, `config` when re-resolved, `frozen-command` for a home that
90
- predates recipes, whose selection needs the restart conversion);
94
+ predates recipes, where a selection answers `E_LAUNCH_LEGACY`: re-spawn it);
91
95
  `--soul NAME [--dir SCOPE] [--agents-root ABS]` describes a new instance.
92
96
  Answer: `{context, selected, selection:{source, launchConfig, runtime,
93
97
  model, yolo}, runtime, model, modelSource, yolo, launchConfig,
@@ -97,15 +97,16 @@ are listed. `use none --layer l` is a level statement and takes no soul or
97
97
  type. A layer bound to another capability at a level is never overwritten
98
98
  (`E_LAYER_BOUND` with the exact remedy).
99
99
 
100
+ *(0.24/0.25 only — `oats soul set` was removed in 0.26.0: a soul is edited in its member repository, then `oats sync`. Kept for history.)*
100
101
  `oats soul set <name> [--dir] [--agents-root] [--runtime] [--model |
101
102
  --no-model] [--yolo | --no-yolo] [--backend] [--description |
102
- --no-description] [--instructions-file <path>] --json` edits only the given
103
- `soul.yaml` lines and replaces `AGENTS.md`; packaged souls are refused
104
- (`E_SOUL_READONLY`). The receipt carries before/after and sha256s.
103
+ --no-description] [--instructions-file <path>] --json` edited only the given
104
+ `soul.yaml` lines and replaced `AGENTS.md`; packaged souls were refused
105
+ (`E_SOUL_READONLY`). The receipt carried before/after and sha256s.
105
106
 
106
107
  ## Remote
107
108
 
108
- `inspect`, `operation`, `use` and `soul` route with `--server <id>` through
109
+ `inspect` and `operation` route with `--server <id>` through
109
110
  the saved route. The gate is the destination's `features` list containing
110
111
  `operations` and its `operationsApi: 1` (`E_REMOTE_INCOMPATIBLE` before
111
112
  anything is sent): `features` describes what a kernel can do locally, which
@@ -113,12 +114,28 @@ is what runs on the host. The probe's `remote` list describes what a CLI
113
114
  can ROUTE to a server and is what a GUI checks on the local CLI before
114
115
  offering remote actions; it is not a gate on the destination. An explicit `--dir` is the exact member context and
115
116
  travels as is; `--home` is its own context; otherwise the registered
116
- workspace is the scope. Soul instructions travel as bytes on the ssh stdin
117
- (`--instructions-stdin` on the host), never as a local path.
117
+ workspace is the scope.
118
118
 
119
119
  ## Replaceability
120
120
 
121
- `test/inspect.test.mjs`, `test/operation.test.mjs` and
122
- `test/operations-routing.test.mjs` use an owned alternative knowledge
123
- provider (namespace `notes`, one `MEMORY.md`, operations `harvest` and
124
- `inspect`) and never mention the official provider.
121
+ `test/operation.test.mjs` runs the real CLI on the Northwind fixture and
122
+ replaces the provider in a spawned home's module copy (what `operation run
123
+ --home` executes) with a recording provider of its own: the runner is generic
124
+ and assumes nothing about the official provider's operations.
125
+
126
+ ## Workspace model (0.26.0)
127
+
128
+ On a workspace deployment, and for a home whose `instance.json` records
129
+ `modules`, `oats inspect`, `oats readiness` and `oats operation run` read the
130
+ workspace model's own records instead of the config chain:
131
+ - the subject is `--home` (instance.json plus its module copies) or `--soul`
132
+ (the soul resolved as its spawn would be), never a scope;
133
+ - `operationsApi` is 2 (also on the run result), and each soul row carries
134
+ `soulsApi: 2`;
135
+ - there is no trust gate (`E_CAPABILITY_BLOCKED` is gone);
136
+ - no `scope`, `chain`, `activation`, `snapshot` or `currentConfig` block.
137
+
138
+ The payloads are specified in
139
+ [desktop-cli-api.md](../desktop-cli-api.md#inspect-readiness-and-operation-run-on-the-workspace-model-operationsapi-2-soulsapi-2-readinessapi-2-oats-0260).
140
+ The sections above describe the classic path, which is removed with the classic
141
+ config chain.