@awebai/oats 0.25.9 → 0.27.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 (183) hide show
  1. package/README.md +8 -6
  2. package/bin/oats.mjs +648 -1755
  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 +279 -93
  8. package/capabilities/oats-aweb/injects/aweb.md +7 -2
  9. package/capabilities/oats-aweb/lib/binding-wire.mjs +89 -13
  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 +8 -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-review/oats.json +3 -2
  20. package/docs/capabilities.md +229 -58
  21. package/docs/capability-manifest.schema.json +29 -9
  22. package/docs/configuration.md +17 -5
  23. package/docs/conventions.md +18 -28
  24. package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +1 -1
  25. package/docs/design/2026-09-13-knowledge-and-memory-direction.md +3 -3
  26. package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +2 -2
  27. package/docs/design/2026-09-15-portable-souls-handoff.md +2 -2
  28. package/docs/design/2026-09-15-portable-souls-implementation.md +1 -1
  29. package/docs/design/2026-09-20-redesign-program-board.md +2 -2
  30. package/docs/design/2026-09-23-workspace-module-contracts.md +1 -1
  31. package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +1 -1
  32. package/docs/design/2026-09-24-desktop-phase-f-boundary.md +34 -1
  33. package/docs/design/2026-09-24-phase-d-plan.md +57 -0
  34. package/docs/design/2026-09-25-teams-contract.md +226 -0
  35. package/docs/design/README.md +3 -3
  36. package/docs/design/launch-configurations.md +20 -16
  37. package/docs/design/operations-contract.md +27 -10
  38. package/docs/desktop-cli-api.md +604 -271
  39. package/docs/desktop-instance-start.md +3 -3
  40. package/docs/desktop.md +7 -13
  41. package/docs/execution-targets.md +16 -18
  42. package/docs/first-team.md +15 -18
  43. package/docs/implementation.md +31 -62
  44. package/docs/integrations.md +64 -33
  45. package/docs/knowledge-capability-authoring.md +1 -1
  46. package/docs/knowledge-reference/package-craft.md +10 -8
  47. package/docs/knowledge-theory.md +1 -1
  48. package/docs/knowledge.md +10 -11
  49. package/docs/layers.md +16 -17
  50. package/docs/oats-local.schema.json +30 -1
  51. package/docs/oats-membership.schema.json +5 -3
  52. package/docs/oats-package.schema.json +2 -2
  53. package/docs/oats-workspace.schema.json +1 -1
  54. package/docs/{official-marketplace.md → official-catalog.md} +15 -16
  55. package/docs/packages.md +76 -53
  56. package/docs/release-notes/v0.22.0.md +1 -1
  57. package/docs/release-notes/v0.23.1.md +1 -1
  58. package/docs/release-notes/v0.26.0.md +670 -0
  59. package/docs/release-notes/v0.27.0.md +100 -0
  60. package/docs/schedules.md +54 -132
  61. package/docs/servers.md +4 -4
  62. package/docs/soul.schema.json +11 -4
  63. package/docs/souls-and-instances.md +60 -47
  64. package/docs/workspaces.md +80 -58
  65. package/injects/instance-boundary.md +2 -2
  66. package/injects/work-attached.md +1 -1
  67. package/injects/work-workspace.md +2 -2
  68. package/lib/{portable-files.mjs → bounded-read.mjs} +6 -6
  69. package/lib/{portable-values.mjs → canonical-json.mjs} +3 -12
  70. package/lib/capability-contract.mjs +110 -0
  71. package/lib/config-data.mjs +2 -2
  72. package/lib/core.mjs +947 -5023
  73. package/lib/deprecation.mjs +24 -0
  74. package/lib/digest.mjs +12 -0
  75. package/lib/instance-inspect.mjs +397 -0
  76. package/lib/instance-lifecycle.mjs +3 -4
  77. package/lib/instance-resolution.mjs +212 -26
  78. package/lib/instruction-composition.mjs +0 -20
  79. package/lib/materialize.mjs +6 -4
  80. package/lib/operator-dispatch.mjs +33 -13
  81. package/lib/packages.mjs +25 -190
  82. package/lib/process-group.mjs +1 -1
  83. package/lib/provider-binding.mjs +4 -2
  84. package/lib/provider-reasons.mjs +3 -68
  85. package/lib/remote.mjs +1 -1
  86. package/lib/resolve.mjs +204 -68
  87. package/lib/schedule.mjs +136 -292
  88. package/lib/servers.mjs +70 -38
  89. package/lib/{portable-shape.mjs → shape.mjs} +4 -3
  90. package/lib/tree-copy.mjs +44 -0
  91. package/lib/workspace.mjs +132 -20
  92. package/package-catalog.json +6 -6
  93. package/package.json +1 -1
  94. package/packages/record/lib/session-roots.mjs +8 -6
  95. package/skills/integration-authoring/SKILL.md +48 -40
  96. package/skills/oats-getting-started/SKILL.md +105 -110
  97. package/skills/oats-support/SKILL.md +2 -2
  98. package/skills/soul-craft/SKILL.md +13 -6
  99. package/bin/oats-pi-sdk-host.mjs +0 -17
  100. package/docs/2026-09-03-architecture-proposal.md +0 -642
  101. package/docs/artifact-approvals.schema.json +0 -7
  102. package/docs/captured-invocation-context.schema.json +0 -7
  103. package/docs/captured-resolution.schema.json +0 -7
  104. package/docs/design/package-engine-contract.md +0 -813
  105. package/docs/design/package-runtime-api.md +0 -588
  106. package/docs/desktop-succession.md +0 -57
  107. package/docs/execution-capsule.schema.json +0 -108
  108. package/docs/first-team-demo.md +0 -92
  109. package/docs/knowledge-migration.md +0 -147
  110. package/docs/migration-from-oas.md +0 -103
  111. package/docs/oats-config.schema.json +0 -172
  112. package/docs/oats-lock-v3.schema.json +0 -7
  113. package/docs/oats-lock.schema.json +0 -175
  114. package/docs/operating-team-migration.md +0 -470
  115. package/docs/portable.schema.json +0 -2512
  116. package/docs/provider-check-input.schema.json +0 -7
  117. package/docs/rebuild-to-v2.md +0 -511
  118. package/docs/workspace-adoption.md +0 -74
  119. package/injects/framework-workspace.md +0 -7
  120. package/injects/local-soul.md +0 -19
  121. package/injects/oats-portable.md +0 -20
  122. package/injects/oats.md +0 -11
  123. package/injects/portable-instance-boundary.md +0 -39
  124. package/injects/portable-work-directory.md +0 -29
  125. package/lib/artifact-approvals.mjs +0 -120
  126. package/lib/artifact-tree.mjs +0 -141
  127. package/lib/capability-artifacts.mjs +0 -179
  128. package/lib/capability-execution.mjs +0 -15
  129. package/lib/capability-inputs.mjs +0 -39
  130. package/lib/capability-provenance.mjs +0 -231
  131. package/lib/captured-action-shape.mjs +0 -21
  132. package/lib/captured-admission-shape.mjs +0 -20
  133. package/lib/captured-binding-file.mjs +0 -36
  134. package/lib/captured-dispatch.mjs +0 -66
  135. package/lib/captured-instance-index.mjs +0 -277
  136. package/lib/captured-invocation-context.mjs +0 -130
  137. package/lib/captured-launch-request.mjs +0 -66
  138. package/lib/captured-operation-process.mjs +0 -15
  139. package/lib/captured-pi-custody.mjs +0 -29
  140. package/lib/captured-pi-host.mjs +0 -167
  141. package/lib/captured-pi-outcome.mjs +0 -172
  142. package/lib/captured-resolutions.mjs +0 -275
  143. package/lib/captured-scaffold.mjs +0 -87
  144. package/lib/captured-selector.mjs +0 -28
  145. package/lib/captured-session-backend.mjs +0 -52
  146. package/lib/captured-source-receipt-file.mjs +0 -72
  147. package/lib/helper-injection-policy.mjs +0 -104
  148. package/lib/legacy-lock-codec.mjs +0 -106
  149. package/lib/manifest-settings.mjs +0 -84
  150. package/lib/package-closure.mjs +0 -48
  151. package/lib/package-materialization.mjs +0 -83
  152. package/lib/pi-sdk-host.mjs +0 -229
  153. package/lib/portable-artifacts.mjs +0 -115
  154. package/lib/portable-choices.mjs +0 -82
  155. package/lib/portable-composition.mjs +0 -136
  156. package/lib/portable-digest.mjs +0 -105
  157. package/lib/portable-identity.mjs +0 -40
  158. package/lib/portable-lock.mjs +0 -117
  159. package/lib/portable-onboarding-request.mjs +0 -49
  160. package/lib/portable-onboarding.mjs +0 -256
  161. package/lib/portable-package-preparation.mjs +0 -188
  162. package/lib/portable-policy.mjs +0 -44
  163. package/lib/portable-soul.mjs +0 -42
  164. package/lib/portable-state.mjs +0 -80
  165. package/lib/prepare-composition.mjs +0 -170
  166. package/lib/prepared-bindings.mjs +0 -92
  167. package/lib/prepared-resources.mjs +0 -127
  168. package/lib/provider-binding-broker.mjs +0 -65
  169. package/lib/provider-binding-wire.mjs +0 -116
  170. package/lib/readiness.mjs +0 -225
  171. package/lib/repository-observation.mjs +0 -226
  172. package/lib/resolution-shape.mjs +0 -393
  173. package/lib/schedule-capsule.mjs +0 -206
  174. package/lib/soul-constraints.mjs +0 -40
  175. package/lib/source-projection.mjs +0 -84
  176. package/lib/source-spec.mjs +0 -189
  177. package/lib/workspace-definition.mjs +0 -126
  178. package/lib/workspace-discovery.mjs +0 -146
  179. package/skills/oats/SKILL.md +0 -162
  180. package/skills/oats-config/SKILL.md +0 -164
  181. package/skills/oats-packages/SKILL.md +0 -184
  182. package/skills/oats-portable/SKILL.md +0 -115
  183. package/skills/oats-portable-artifacts/SKILL.md +0 -63
@@ -1,17 +1,19 @@
1
1
  # Capability packages
2
2
 
3
3
  A **capability** is OATS's reusable unit of behaviour. It can contribute
4
- skills, instance instructions, requirements, namespaced commands, and approved
4
+ skills, instance instructions, requirements, namespaced commands, and declared
5
5
  lifecycle hooks. A soul — not the capability — decides which souls receive it,
6
6
  by naming it with where it comes from (`from:`; see [workspaces](workspaces.md)).
7
7
 
8
- The [official marketplace policy](official-marketplace.md) defines the reviewed
8
+ The [official catalog policy](official-catalog.md) defines the reviewed
9
9
  package list and its acceptance criteria. Finding an official package does not
10
- pin, approve or give it to any soul; those remain explicit, separate choices.
10
+ declare it or give it to any soul; declaring it in `packages:` is the
11
+ workspace's trust decision, and giving it to a soul is a separate choice.
11
12
 
12
- An **integration** is a capability that implements one exclusive fundamental
13
- layer: `knowledge`, `messaging`, or `tasks`. General capabilities claim no
14
- layer and compose additively.
13
+ A **core capability** fills one of the three positions a soul has: knowledge,
14
+ messaging or tasks, at most one of each per soul. The manifest's `layer` field
15
+ names which core capability it is. Other capabilities claim no `layer` and
16
+ compose additively.
15
17
 
16
18
  ## Mental model
17
19
 
@@ -20,8 +22,8 @@ A capability lives in one of two kinds of source:
20
22
  1. a **member repo** of the workspace, at `capabilities/<name>/oats.json` —
21
23
  unversioned, always the member's latest state, trusted by membership;
22
24
  2. a **package** (`oats-package/` in a repo, pinned by version in the
23
- workspace's `packages:`, locked and approved once per version —
24
- [packages.md](packages.md)).
25
+ workspace's `packages:` — the declaration is the trust — and locked to a
26
+ commit and integrity; [packages.md](packages.md)).
25
27
 
26
28
  A soul says `capabilities: { <name>: { from: here | <repo key> | package } }`
27
29
  (or `off`); the workspace supplies defaults. At spawn every resolved
@@ -45,7 +47,7 @@ A self-contained package has an `oats.json`:
45
47
  "requires": [
46
48
  { "command": "team-chat", "why": "send and receive messages" },
47
49
  {
48
- "runtime": "pi",
50
+ "harness": "pi",
49
51
  "package": "npm:team-chat-pi",
50
52
  "why": "real-time push events in pi sessions"
51
53
  }
@@ -67,8 +69,16 @@ A self-contained package has an `oats.json`:
67
69
  namespace.
68
70
  - `command` is an optional, unique CLI namespace. The example exposes
69
71
  `oats team-chat auth`.
70
- - `layer` is optional and may name exactly one fundamental layer. Two active
71
- packages cannot implement the same layer for one soul.
72
+ - `compatibility.oats` is the kernel range the capability runs on. The kernel
73
+ refuses to compose a capability whose range does not admit it
74
+ (`E_CAPABILITY_INCOMPATIBLE`, naming capability, range and kernel) wherever a
75
+ soul or capability agent resolves (spawn, `spawn --preview`, `inspect
76
+ --soul`, operator commands). `oats inspect` shows each module's
77
+ `compatibility: { ok, range, kernel }`; a home whose spawned module no longer
78
+ admits the running kernel reports a `capability-incompatible` problem.
79
+ - `layer` is optional; when present it names which core capability this is
80
+ (`knowledge`, `messaging` or `tasks`). A soul has at most one capability per
81
+ slot.
72
82
  - `skills` entries can be skill directories or roots containing skills.
73
83
  - `inject` is optional instance instruction Markdown.
74
84
  - Only `soul-scaffold`, `spawn`, and `retire` hooks are accepted. A hook is a
@@ -79,15 +89,16 @@ A self-contained package has an `oats.json`:
79
89
  woken by mail. Every other hook stays best-effort and only warns, so advisory
80
90
  work never becomes a spawn blocker. `retire` and `soul-scaffold` cannot be
81
91
  required: they run outside a spawn transaction, so there is no moment to
82
- enforce them.
92
+ enforce them. The kernel no longer runs `soul-scaffold` (it ran when
93
+ `oats create` wrote a soul); a manifest may still declare it.
83
94
  - A capability declaring a **required** spawn hook should declare a `retire` hook
84
95
  too. Without one, OATS has no way to undo what the spawn hook did and no way to
85
96
  know whether it did anything, so a failure quarantines the home rather than
86
97
  rolling it back — the operator cleans up by hand and removes it with `--force`.
87
- - A required hook must also be **able** to run: a package capability whose
88
- version is not approved in the lock is refused at resolution
89
- (`E_PACKAGE_UNAPPROVED`, remedy `oats sync`), so a required hook never
90
- silently fails to configure an instance.
98
+ - A required hook must also be **able** to run: a package capability the lock
99
+ does not pin is refused at resolution (`E_PACKAGE_MISSING`, remedy `oats
100
+ sync`), and drifted content is `E_PACKAGE_INTEGRITY`, so a required hook
101
+ never silently fails to configure an instance.
91
102
  - When a required hook fails and its compensation cannot finish, the instance
92
103
  home is **retained**, not deleted — it holds the credentials and metadata a
93
104
  retry needs, and removing it would turn a transient cleanup failure into
@@ -123,27 +134,29 @@ A self-contained package has an `oats.json`:
123
134
  the operator to clean up by hand.
124
135
  - `requires` declares what must exist before the capability works. Two kinds:
125
136
  - a **host command** (`command`), satisfied by a binary on `PATH`;
126
- - a **runtime package** (`runtime` + `package`, optionally `marketplace`),
127
- satisfied by that runtime's own package manager — `npm:@scope/name` for pi,
137
+ - a **harness package** (`harness` + `package`, optionally `marketplace`),
138
+ satisfied by that harness's own package manager — `npm:@scope/name` for pi,
128
139
  `plugin@marketplace` for Claude Code. It is raised only for deployments that use the named
129
- runtime — a Claude-only deployment is never asked to install a pi package —
130
- and is verified in the runtime's package list, never on `PATH`. A version
140
+ harness — a Claude-only deployment is never asked to install a pi package —
141
+ and is verified in the harness's package list, never on `PATH`. A version
131
142
  selector is allowed and ignored for identity, so `@latest` and a pinned
132
143
  version are one requirement.
133
- A runtime package is **verified at spawn, never installed there**: installing
134
- would mutate the operator's runtime configuration without asking, in the
144
+ A harness package is **verified at spawn, never installed there**: installing
145
+ would mutate the operator's harness configuration without asking, in the
135
146
  middle of a spawn. A missing, uninstalled or disabled package fails the spawn
136
147
  with the consent command that fixes it.
137
148
  - OATS never installs a host requirement silently. A missing host command is
138
149
  the operator's to install; `oats doctor` reports it. Consent to install is
139
- separate from package approval.
140
- - `environment` lists the exact launch variables executable trust approves;
150
+ separate from declaring the package.
151
+ - `environment` lists the exact launch variables the capability may set;
141
152
  spawn hook output must be a subset and use the capability vendor prefix.
142
153
  - Target names never appear in a package manifest.
143
154
 
144
155
  `capability` is the only manifest identity field; it may also carry
145
- `private: true` (usable only by souls of its own repo) and `team: <label>`
146
- (a workspace team label). The machine-readable contract is
156
+ `private: true` (a **repo-owned** capability: listed, but usable only by souls
157
+ of its own repo) and `team: <label>`
158
+ (the one workspace team label it is listed under; without it, the primary of
159
+ its repository's default). The machine-readable contract is
147
160
  [`capability-manifest.schema.json`](capability-manifest.schema.json).
148
161
 
149
162
  ## Who gets a capability
@@ -171,15 +184,73 @@ knowledge:
171
184
  ```
172
185
 
173
186
  Composition order: `defaults.<slot>` ⊕ `defaults.capabilities` ⊕
174
- `defaults.byTeam[<soul team>]` ⊕ `soul.capabilities` — later wins, `off`
175
- removes, a soul `<slot>: none` drops the workspace's slot default. A resolved
187
+ `defaults.byTeam[<label>]` for each of the soul's team labels, in order ⊕
188
+ `soul.capabilities` — later wins, `off` removes, a soul `<slot>: none` drops
189
+ the workspace's slot default. Two labels that give one capability different
190
+ entries are `E_TEAM_CONFLICT`, naming both labels; identical entries are fine,
191
+ and a capability the soul names itself settles it (the soul's entry wins). A resolved
176
192
  capability whose manifest says `layer: X` fills slot X; two for one slot are
177
- `E_SLOT_CONFLICT`. Provider settings come from three homes — the soul's slot
178
- payload, `oats-local.yaml` `settings.<cap>`, and `oats spawn --provider` — and
179
- are deep-merged in that order. There are no agent types, no `global`, no
193
+ `E_SLOT_CONFLICT`. Provider settings start from the manifest's own declared
194
+ defaults (`settings.<key>.default`, the lowest layer), then take the workspace's
195
+ `messaging` payload (its base: no `byTeam` entry is merged, per teams
196
+ amendment K) for the messaging slot, the soul's
197
+ slot payload, `oats-local.yaml` `settings.<cap>`, and `oats spawn --provider`,
198
+ deep-merged in that order. There are no agent types, no `global`, no
180
199
  per-deployment activation or exclusion maps.
181
200
 
182
- ## Exact runtime composition
201
+ ### Several team labels
202
+
203
+ A soul's `team` (or its repository's default in `oats-membership.yaml`) is a
204
+ label or a non-empty list of distinct labels; the first is the **primary**:
205
+
206
+ ```yaml
207
+ # souls/release-manager/soul.yaml
208
+ schemaVersion: 2
209
+ name: release-manager
210
+ description: Cuts and ships releases.
211
+ work: worktree
212
+ team: [engineering, reviewers]
213
+ ```
214
+
215
+ - `OATS_TEAM_LABEL` is the primary label. The merged messaging payload takes
216
+ **no** label's `byTeam` entry, the primary's included (teams amendment K), so
217
+ `OATS_TEAM_ID` (the payload's `team`) is the personal team a host, soul or
218
+ spawn set; empty means the provider's default.
219
+ - Every label is an **eligible team**: the kernel hands the messaging provider
220
+ `teams`, one `{ label, team, mapped, payload }` per label in order. `payload`
221
+ is `workspace.messaging` ⊕ `byTeam[<label>]` when the workspace maps the
222
+ label (`team` is then its team id), else the base alone with `mapped: false`
223
+ and `team: null`. A soul with no label gets `[]` (personal only).
224
+ - `teams` travels **beside** a provider's settings, never inside them:
225
+ `OATS_TEAMS` (the JSON), `OATS_TEAM_LABELS` (comma-joined) and
226
+ `OATS_TEAMS_SOURCE` in the environment of every hook, home command and
227
+ provider check. The environment is their only channel: a check's stdin
228
+ request stays the released binding wire, which providers decode strictly.
229
+ The variables are empty (not `[]`) when a home's teams are unknown.
230
+ - `OATS_TEAMS_SOURCE` is `live` (read from the workspace now, or a fresh
231
+ resolution) or `recorded` (the spawn-time list). **A provider leaves a joined
232
+ team only on a `live` list**: a recorded one lacks every team mapped since
233
+ the spawn, so acting on it could drop a valid membership.
234
+ - Joining an eligible team is the provider's explicit act (a spawn choice or a
235
+ command at any time); the kernel never joins anything.
236
+ - For an existing home the teams are **live** where they are acted on: its
237
+ launch hook (`oats session start|restart`), its messaging module's commands
238
+ and `messaging:` operations (`oats operation run --home`), and `oats inspect
239
+ --home` read the soul's labels and the workspace's `messaging` as they stand
240
+ now — two repository reads (the workspace host, the soul's own repo), never a
241
+ discovery; `oats readiness --home` takes them from the discovery it already
242
+ runs. The home's modules and skills stay as spawned. Every other capability
243
+ command and operation gets the teams the spawn recorded in `instance.json`
244
+ (`teams`), marked `recorded`, at no remote cost; so does any read where the
245
+ workspace cannot be reached.
246
+ - **Known limitation (0.26.0):** a scheduled wake's session start uses the
247
+ recorded teams (`OATS_TEAMS_SOURCE=recorded`), so its launch hook leaves
248
+ nothing; the next operator start or messaging command is live.
249
+ - A label in the workspace's `teams:` but not in `messaging.byTeam` is a
250
+ discovery warning (`unmapped-team-label`, one per label naming its souls); a
251
+ label not in `teams:` at all is the `E_TEAM_UNKNOWN` problem.
252
+
253
+ ## Exact harness composition
183
254
 
184
255
  Every spawned instance receives:
185
256
 
@@ -223,10 +294,10 @@ A **package** is the versioned tier: a directory with an `oats-package.json`
223
294
  that enumerates one or more capabilities (schema
224
295
  [`oats-package.schema.json`](oats-package.schema.json)). It is pinned once in
225
296
  the workspace's `packages:`, resolved to an exact commit + integrity by
226
- `oats sync` into `oats-lock.json` (lockfileVersion 3), and its executables are
227
- approved once per version. A soul names a package capability with
228
- `from: package`. Everything about declaring, syncing, locking, approving and
229
- publishing packages is in [packages.md](packages.md). There is no installed
297
+ `oats sync` into `oats-lock.json` (lockfileVersion 3); declaring it is the
298
+ workspace's decision to trust it. A soul names a package capability with
299
+ `from: package`. Everything about declaring, syncing, locking and publishing
300
+ packages is in [packages.md](packages.md). There is no installed
230
301
  copy at a deployment and no `oats install`/`trust`/`update`/`remove`.
231
302
 
232
303
  ## Member capabilities
@@ -236,8 +307,9 @@ by every soul in the workspace (`oats capabilities` lists it with origin
236
307
  `member <repo key> @ <commit>`) and is named with `from: <repo key>` — or
237
308
  `from: here` by souls of the same repo. It is trusted by **membership**: the
238
309
  repo's access control is the boundary and its latest default-branch state is
239
- what is copied. `private: true` in the manifest keeps it usable only from its
240
- own repo. A member's `oats-package/` is **not** a member capability: it is
310
+ what is copied. `private: true` in the manifest makes it **repo-owned**: still
311
+ listed (`private: true`, marked "(repo-owned)"), but usable only from its own
312
+ repo (`E_CAPABILITY_PRIVATE` elsewhere). A member's `oats-package/` is **not** a member capability: it is
241
313
  reported as `publishes` and consumed only as a package.
242
314
 
243
315
  ## Capability-defined agents
@@ -255,15 +327,19 @@ instance's modules (or the soul's resolved set). Workspace commands (`sync`,
255
327
  `package`, `workspace status`, `capabilities`, `souls`, `doctor`) are always
256
328
  available.
257
329
 
330
+ A manifest's `helperInjection` and a hook's `inputs` are **ignored since 0.26**:
331
+ they served the captured path (removed in 0.26), are still accepted so that
332
+ existing manifests load, and change nothing.
333
+
258
334
  Hooks receive `OATS_EVENT`, `OATS_CAPABILITY`, `OATS_LAYER`, `OATS_INSTANCE`,
259
335
  `OATS_HOME`, `OATS_AGENT`, `OATS_SOUL`, `OATS_CONTEXT`, `OATS_WORKSPACE`,
260
336
  `OATS_ROOT`, `OATS_LEVEL`, `OATS_SETTINGS`, and `OATS_META`. A final JSON line may
261
- return `meta`, `brief`, `warning`, or runtime-specific `launch` arguments. A
337
+ return `meta`, `brief`, `warning`, or harness-specific `launch` arguments. A
262
338
  **spawn hook only** may also return an `env` object for the launched process;
263
339
  returning `env` from retire or soul-scaffold is an explicit contract error.
264
340
 
265
341
  A **launch hook** runs at every start and restart of a home for each provider
266
- captured at spawn (under its captured settings). Its `launch` arguments and
342
+ recorded at spawn (under its recorded settings). Its `launch` arguments and
267
343
  `env` replace that provider's previous contribution whole. Its `meta`, when
268
344
  returned, replaces that provider's entry in `instance.json.capabilityMeta`
269
345
  after the start succeeds — the same record the spawn hook wrote and the retire
@@ -285,22 +361,24 @@ hyphen to `_` would let `aweb-evil.*` collide with names already inside
285
361
  A manifest's `settings.<key>` may carry `hostOnly: true` (decision 27). Such a
286
362
  key is a fact about the machine — a custody directory, a state root — and the
287
363
  resolver accepts it only from the deployment's own `oats-local.yaml`
288
- `settings.<capability>`; a committed workspace or soul file or a `--provider`
289
- flag carrying it is refused (`E_WORKSPACE_SCHEMA`, reason `host-only-key`).
364
+ `settings.<capability>`; a committed workspace or soul file (every
365
+ `messaging.byTeam` entry of a label the soul carries included) or a
366
+ `--provider` flag carrying it is refused (`E_WORKSPACE_SCHEMA`, reason
367
+ `host-only-key`).
290
368
  Declare it for any key whose value points at something a committed file must
291
369
  never be able to choose.
292
370
 
293
371
  A hook may return only names in its manifest's exact `environment` declaration.
294
372
  For package capabilities that declaration is part of the integrity-locked tree
295
- and of what the per-version approval showed; for member capabilities it is
296
- part of what membership trusts. Undeclared output is fatal. This positive
297
- authority is the contract boundary — adding a new launch variable requires a
298
- visible manifest change (and, for a package, a new approved version).
373
+ the workspace declared; for member capabilities it is part of what membership
374
+ trusts. Undeclared output is fatal. This positive authority is the contract
375
+ boundary — adding a new launch variable requires a visible manifest change
376
+ (and, for a package, a new version, reviewed as a new pin).
299
377
 
300
378
  `OATS_*`, `PI_AGENT_*`, kernel launch variables, and known shell/bootstrap/loader
301
379
  names are also rejected as defense in depth. The denylist includes current Node,
302
380
  JVM, .NET, Python, Perl, Ruby, Lua, PHP, ELF, and dyld surfaces, but is explicitly
303
- not the authority boundary: runtime bootstrap names are open-ended, so the
381
+ not the authority boundary: harness bootstrap names are open-ended, so the
304
382
  manifest declaration and trust review enforce what an artifact may contribute.
305
383
  Two capabilities claiming the same name is an error even when their values
306
384
  match.
@@ -316,7 +394,7 @@ without a retire hook uses the standard retryable quarantine instead. Ordinary
316
394
  advisory hook execution failure itself contributes no environment.
317
395
 
318
396
  The environment prefix applies to the initial Pi or Claude process. `--no-launch`
319
- validates command preparation but has no runtime consumer. The fallback shell
397
+ validates command preparation but launches no harness. The fallback shell
320
398
  after that process exits does not inherit command-scoped assignments, and OATS
321
399
  has no restart command or replay policy yet. The generated command is persisted
322
400
  as before; hooks must contribute locators, selectors, or broker endpoints—not
@@ -327,12 +405,9 @@ this mechanism must never copy or expose that global identity's root keys to the
327
405
  worker process. Session-scoped execution credentials need a separate lifecycle
328
406
  and must not be encoded into this persisted spawn command.
329
407
 
330
- Spawn/scaffold order is by capability name; retirement reverses successful
408
+ Spawn order is by capability name; retirement reverses successful
331
409
  spawn order. Hooks run from the instance's own copy
332
- (`<home>/.oats/modules/<cap>/`). Scaffold hooks cannot modify or delete
333
- canonical or another capability's files. OATS records ownership, restores the
334
- pre-hook snapshot, and raises a conflict instead of accepting destructive or
335
- last-writer-wins behavior.
410
+ (`<home>/.oats/modules/<cap>/`).
336
411
 
337
412
  ## Official packages
338
413
 
@@ -340,14 +415,14 @@ last-writer-wins behavior.
340
415
  |---|---|---|---|
341
416
  | `oats.core` | additive | day-to-day OATS operation for an instance | `oats.framework` |
342
417
  | `oats.setup` | additive | whole-architecture knowledge for an onboarding expert | `oats.framework` |
343
- | `oats.okf` | knowledge integration | External owned OKF bases, durable notes/record custody, independent judgment and inspection | `oats.okf` |
344
- | `oats.aweb` | messaging integration | aweb identity lifecycle and messaging skills | `oats.aweb` |
345
- | `oats.jira` | tasks integration | Jira task protocol via `acli` | `oats.jira` |
346
- | `oats.linear` | tasks integration | Linear GraphQL task commands and workflow | `oats.linear` |
418
+ | `oats.okf` | knowledge core capability | External owned OKF bases, durable notes/record custody, independent judgment and inspection | `oats.okf` |
419
+ | `oats.aweb` | messaging core capability | aweb identity lifecycle and messaging skills | `oats.aweb` |
420
+ | `oats.jira` | tasks core capability | Jira task protocol via `acli` | `oats.jira` |
421
+ | `oats.linear` | tasks core capability | Linear GraphQL task commands and workflow | `oats.linear` |
347
422
  | `oats.authoring` | additive | capability, skill, and soul authoring guidance | `oats.authoring` |
348
423
 
349
424
  Each is pinned by a bare version in `packages:` and resolved through the
350
- [official catalog](official-marketplace.md); each package repo is also a member
425
+ [official catalog](official-catalog.md); each package repo is also a member
351
426
  of the OATS workspace carrying its expert soul (`okf-expert`, `aweb-expert`, …).
352
427
  The framework's own souls say `oats.okf: { from: package }` — membership never
353
428
  turns a package into a latest-state capability.
@@ -358,3 +433,99 @@ A manifest may declare `operations` (named actions or views delegating to
358
433
  its own commands) that a GUI or a schedule invokes through `oats operation
359
434
  run <layer>:<name>`; `oats inspect --json` reports them with availability.
360
435
  See [docs/design/operations-contract.md](design/operations-contract.md).
436
+
437
+ ## Readiness check (`binding.check`)
438
+
439
+ A slot provider (knowledge, messaging, tasks) that declares `binding` in its
440
+ manifest is asked by `oats readiness` whether it is ready for the subject. The
441
+ subject is an instance home (`--home`) or a soul (`--soul`). The command named
442
+ by `binding.check` receives one request and answers once. The kernel relays
443
+ that answer to consumers as it came: readiness `providers` items. For the
444
+ consumer side, see [desktop-cli-api.md](desktop-cli-api.md#oats-readiness---home---soul---dir---policy---json-readinessapi-2).
445
+ This check reads configuration only; it binds nothing and does not change a
446
+ spawn's fail-closed hooks.
447
+
448
+ ```json
449
+ "commands": { "binding-check": "bin/my-provider.mjs binding-check" },
450
+ "binding": { "version": 1, "normalize": "binding-normalize", "bind": "binding-bind", "check": "binding-check" }
451
+ ```
452
+
453
+ **Invocation.** The kernel runs the command's script with `node`, from the
454
+ module directory. That is the home's module copy for `--home`, or the
455
+ deployment's verified module store for `--soul`. The script must resolve inside
456
+ the module and be a regular file. The command's words after the script are
457
+ passed as arguments; no shell is involved.
458
+
459
+ **Request** — one JSON document on stdin:
460
+
461
+ ```json
462
+ {"schemaVersion":1,"phase":"check","slot":"messaging","capability":"my.provider",
463
+ "settings":{"team":"acme:eng","root":"/srv/aw"},
464
+ "input":{"context":{"kind":"workspace","workspace":"github.com/acme/agents","deployment":"/srv/acme",
465
+ "soul":"release-manager","team":"engineering","instance":"release-manager-1",
466
+ "home":"/srv/acme/agents/release-manager/instances/release-manager-1"},
467
+ "action":{"kind":"readiness"}}}
468
+ ```
469
+
470
+ - `slot` is the manifest's `layer`.
471
+ - `settings` is the merged provider payload: the one the spawn recorded for a
472
+ home, or the one the resolution computes for a soul.
473
+ - The request has exactly these keys; a provider may decode it strictly. The
474
+ soul's eligible teams (see *Several team labels*) are not on stdin: the check
475
+ reads them from `OATS_TEAMS` / `OATS_TEAMS_SOURCE` / `OATS_TEAM_LABELS`.
476
+ - `context.team` is the soul's primary team label, or `null`.
477
+ - `instance` and `home` are `null` for a soul subject.
478
+
479
+ **Environment:**
480
+ - Every ambient `OATS_*`, `OAS_*` and `PI_*` variable is removed. Other
481
+ variables pass through.
482
+ - The kernel sets:
483
+ - `OATS_CAPABILITY` and `OATS_SETTINGS` (the payload as JSON);
484
+ - `OATS_CLI_BIN`;
485
+ - `OATS_WORKSPACE` (the deployment);
486
+ - the team variables `OATS_TEAM_ID` (the messaging payload's `team`: the
487
+ personal team if one is set; empty = the provider's default),
488
+ `OATS_TEAM_SCOPE`, `OATS_TEAM_LABEL`, `OATS_TEAM_NAME`,
489
+ `OATS_TEAM_LABELS`, `OATS_TEAMS`, `OATS_TEAMS_SOURCE`, `OATS_WORKSPACE_NAME` and
490
+ `OATS_WORKSPACE_KEY`;
491
+ - `OATS_AGENT` (the soul);
492
+ - `OATS_SOUL` when the soul directory is known;
493
+ - for a home, `OATS_INSTANCE` and `OATS_INSTANCE_HOME`.
494
+ - A home's `OATS_WORKSPACE_NAME` is `""` until spawn records the workspace
495
+ name.
496
+
497
+ **Answer** — exit 0, and exactly one JSON document on stdout (whitespace
498
+ around it is fine; progress text is not):
499
+
500
+ ```json
501
+ {"schemaVersion":1,"phase":"check","slot":"messaging","capability":"my.provider","ok":true,
502
+ "result":{"status":"ready","problems":[],"warnings":[{"code":"e2ee-disabled","message":"end-to-end encryption is off for this team"}]}}
503
+ ```
504
+
505
+ - **The envelope** has exactly these keys. `schemaVersion`, `phase`, `slot`
506
+ and `capability` echo the request.
507
+ - **A refusal** is `{…, "ok": false, "error": {"code", "message"?}}`.
508
+ Readiness shows it as `unknown`, with your code.
509
+ - **`status`** is one of four values, and maps to the readiness item as
510
+ follows:
511
+
512
+ | `status` | readiness item | use it when |
513
+ |---|---|---|
514
+ | `ready` | `pass` | nothing is missing; `problems` must be `[]` |
515
+ | `needs-configuration` | `fail` | a setting or host resource is missing |
516
+ | `authorization-required` | `fail` | the operator must log in or grant access |
517
+ | `unavailable` | `unknown` | you cannot tell right now (a service is down) |
518
+
519
+ - **`problems`** is a list of `{code, message}` strings. The codes are yours;
520
+ they are not matched against `binding.reasons`. The first message becomes
521
+ the item's reason.
522
+ - **`warnings`** is optional: `{code, message}` strings, relayed as they come.
523
+ A warning never changes the status or the readiness summary.
524
+ - **Anything else is `unknown`** (`provider-unavailable`): a nonzero exit, two
525
+ documents, unknown keys, a wrong echo, or `ready` with problems.
526
+
527
+ **Time.** Each check gets at most 30 s and is killed after that. All provider
528
+ checks in one readiness read share 60 s, and checks the budget does not reach
529
+ are not run (`unknown`, `time-budget-exhausted`). Answer from configuration
530
+ and local state. A provider that must call a remote service should bound that
531
+ call well inside the 30 s.
@@ -55,6 +55,15 @@
55
55
  "type": "string",
56
56
  "minLength": 1
57
57
  },
58
+ "private": {
59
+ "type": "boolean",
60
+ "description": "Workspace discovery: true makes this member capability repo-owned — listed (private: true) but usable only by souls of its own repository (E_CAPABILITY_PRIVATE elsewhere)."
61
+ },
62
+ "team": {
63
+ "type": "string",
64
+ "pattern": "^[a-z0-9][a-z0-9._-]*$",
65
+ "description": "Workspace discovery: the team label for this capability; else the repository's default from oats-membership.yaml."
66
+ },
58
67
  "layer": {
59
68
  "enum": [
60
69
  "knowledge",
@@ -108,17 +117,28 @@
108
117
  "additionalProperties": false
109
118
  },
110
119
  {
111
- "title": "runtime package requirement",
112
- "description": "A package that must be installed into a specific runtime's own package manager. Raised only for deployments using that runtime, satisfied by the runtime's package list rather than by PATH, and installed only with explicit consent.",
120
+ "title": "harness package requirement",
121
+ "description": "A package that must be installed into a specific harness's own package manager. Raised only for deployments using that harness, satisfied by the harness's package list rather than by PATH, and installed only with explicit consent. The harness is named by `harness` (0.27.0) or by `runtime`, its pre-0.27 name that released manifests use: exactly one of the two.",
113
122
  "type": "object",
114
123
  "required": [
115
- "runtime",
116
124
  "package",
117
125
  "why"
118
126
  ],
127
+ "oneOf": [
128
+ { "required": ["harness"], "not": { "required": ["runtime"] } },
129
+ { "required": ["runtime"], "not": { "required": ["harness"] } }
130
+ ],
119
131
  "properties": {
132
+ "harness": {
133
+ "type": "string",
134
+ "enum": [
135
+ "pi",
136
+ "claude"
137
+ ]
138
+ },
120
139
  "runtime": {
121
140
  "type": "string",
141
+ "description": "The pre-0.27 name of `harness`, accepted for released manifests.",
122
142
  "enum": [
123
143
  "pi",
124
144
  "claude"
@@ -126,7 +146,7 @@
126
146
  },
127
147
  "package": {
128
148
  "type": "string",
129
- "description": "Source spec in that runtime's own naming: \"npm:@scope/name\" for pi, \"plugin@marketplace\" for Claude."
149
+ "description": "Source spec in that harness's own naming: \"npm:@scope/name\" for pi, \"plugin@marketplace\" for Claude."
130
150
  },
131
151
  "why": {
132
152
  "type": "string"
@@ -146,7 +166,7 @@
146
166
  },
147
167
  "minVersion": {
148
168
  "type": "string",
149
- "description": "Lowest acceptable installed version of the package, read from the package.json under the install directory the runtime's listing names; an older or absent manifest fails the requirement with the install remedy.",
169
+ "description": "Lowest acceptable installed version of the package, read from the package.json under the install directory the harness's listing names; an older or absent manifest fails the requirement with the install remedy.",
150
170
  "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+"
151
171
  },
152
172
  "ifInstalled": {
@@ -170,7 +190,7 @@
170
190
  }
171
191
  },
172
192
  "binding": {
173
- "description": "Versioned provider-owned normalize/bind/check phases, each naming an existing command in this manifest. Runtime validation enforces fundamental-slot ownership and command references; presence is not approval or provider readiness.",
193
+ "description": "Versioned provider-owned normalize/bind/check phases, each naming an existing command in this manifest. Runtime validation enforces that the phases belong to a core capability (a manifest with `layer`) and command references; presence is not approval or provider readiness.",
174
194
  "type": "object",
175
195
  "required": ["version", "normalize", "bind", "check"],
176
196
  "additionalProperties": false,
@@ -247,11 +267,11 @@
247
267
  "items": {
248
268
  "type": "string"
249
269
  },
250
- "description": "Package-relative soul directories (soul.yaml + AGENTS.md) \u2014 capability-defined agents; they resolve like local souls where the capability is active, souls stay read-only in the package, instances home under the scope's local-agents/."
270
+ "description": "Package-relative soul directories (soul.yaml + AGENTS.md) \u2014 capability-defined agents; they resolve where the capability is active, souls stay read-only in the package, instances home under <deployment>/agents/<agent>/instances/."
251
271
  },
252
272
  "settings": {
253
273
  "type": "object",
254
- "description": "Declared capability settings: name to { default, values?, description }. Documentation for `oats use --settings`; undeclared settings are still accepted.",
274
+ "description": "Declared capability settings: name to { default, values?, description }. Documentation for the values a soul, oats-local.yaml or `oats spawn --provider` supplies; undeclared settings are still accepted.",
255
275
  "additionalProperties": {
256
276
  "type": "object",
257
277
  "properties": {
@@ -274,7 +294,7 @@
274
294
  },
275
295
  "environmentNamespaces": {
276
296
  "type": "array",
277
- "description": "Additional environment-name prefixes this capability may declare besides its vendor's own (e.g. AWEB_ for the official oats.aweb integration). Each is an uppercase prefix ending in an underscore; the reserved core (OATS_, PI_AGENT_) and process bootstrap namespaces cannot be claimed. Disclosed at trust time with the environment list.",
297
+ "description": "Additional environment-name prefixes this capability may declare besides its vendor's own (e.g. AWEB_ for the official oats.aweb messaging capability). Each is an uppercase prefix ending in an underscore; the reserved core (OATS_, PI_AGENT_) and process bootstrap namespaces cannot be claimed. Disclosed at trust time with the environment list.",
278
298
  "items": {
279
299
  "type": "string",
280
300
  "pattern": "^[A-Z][A-Z0-9]*_$"
@@ -13,7 +13,8 @@ derived from workspace defaults plus each soul's `capabilities:` — and its
13
13
  `souls:` blocks are gone — per-instance provider content moved to
14
14
  `oats spawn … --provider`. There is no `oats init`, no `oats use`, no config
15
15
  scope chain, no adopted config templates. A 0.24.x deployment is rebuilt, not
16
- converted: [rebuild-to-v2.md](rebuild-to-v2.md).
16
+ converted: write `oats-local.yaml` with `oats onboard` and move what the old
17
+ file declared into `oats-workspace.yaml` and each soul's `soul.yaml`.
17
18
 
18
19
  ## The file
19
20
 
@@ -33,6 +34,16 @@ settings: # optional — host-owned values pe
33
34
 
34
35
  souls: # optional — souls this machine does not run
35
36
  disabled: [data-analyst]
37
+
38
+ launch-configs: # optional — named ways this host starts a harness
39
+ personal:
40
+ harness: claude
41
+ executable: "./bin/claude-wrapper.sh" # relative → against this deployment directory
42
+ args: ["--verbose"]
43
+ env:
44
+ CLAUDE_CONFIG_DIR: { fromEnv: PERSONAL_CLAUDE_DIR }
45
+ model: opus
46
+ yolo: false
36
47
  ```
37
48
 
38
49
  Schema: [`oats-local.schema.json`](oats-local.schema.json). Unknown keys are
@@ -44,6 +55,7 @@ refused (`E_WORKSPACE_SCHEMA`).
44
55
  | `clones` | `<canonical repo key>: <absolute path>` — where a member's clone lives when it is not at `<deployment>/<member name>/`. Only a soul's **work target** (`work: worktree \| checkout`) needs a clone. Lookup order: `spawn --repo`, then this map (keys normalised through `parseRepoRef`, so any ref spelling of the same repo matches), then `<deployment>/<member name>` (a member named `agents` → `<deployment>/agents-repo`, since `agents/` is the instance root); none → `E_CLONE_MISSING`; a directory whose `origin` is another repo → `E_CLONE_MISMATCH`. |
45
56
  | `settings.<cap>.<key>` | Host-owned provider values the capability's manifest asks for — absolute paths, state roots, delivery modes. The workspace file **refuses** absolute paths; this is where they go. Merged into the capability's provider payload after the soul's own payload and before any `--provider` flag (see [three homes](workspaces.md#provider-payloads-have-three-homes)). |
46
57
  | `souls.disabled` | Soul names not run on this machine; reported by `oats sync` ("disabled here"). |
58
+ | `launch-configs.<name>` | A named way to start a harness on this host (0.26.0; lead decision 2 — a spawn-time host choice, never a soul field): `harness` (`pi` \| `claude` \| `codex`, required; named `runtime` before 0.27.0, which is still read with a `deprecated-runtime-name` warning), `executable` (a bare name looked up on `PATH`, or a path — relative to this deployment directory), `args` (literal, no shell), `env` (a literal string, non-secret by contract and always redacted, or `{ fromEnv: NAME }` resolved on the host at start), `model`, `yolo`. Selected with `--launch-config <name>` on `oats spawn` and `oats session start \| restart`; explicit flags override its fields. Written by `oats launch-config set <name> --file <json>` / `remove <name>`, which rewrite only this block. Earlier kernels read `launch-configs:` from a scope's `oats-config.yaml`; 0.26.0 refuses it there with a message naming this move. |
47
59
 
48
60
  ## Where it sits and how it is found
49
61
 
@@ -76,8 +88,8 @@ nothing else.
76
88
  activation or targeting.
77
89
  - **Versions** — `packages:` in the workspace file; exact commits in
78
90
  `oats-lock.json`.
79
- - **Trust** — membership for members; per-version approval in the lock for
80
- packages. No per-operator trust list.
91
+ - **Trust** — membership for members; the declaration in the workspace's
92
+ `packages:` for packages (no approval step). No per-operator trust list.
81
93
  - **Per-instance provider facts** (a retained messaging seat, a one-off state
82
94
  root) — `oats spawn <soul> --provider <cap> key=value`, recorded in
83
95
  `instance.json.providers`.
@@ -86,8 +98,8 @@ nothing else.
86
98
  ## Inspecting the effective configuration
87
99
 
88
100
  ```bash
89
- oats workspace status # membership table, locked packages, approval state, external souls
90
- oats sync # confirm, resolve, approve, report the diff
101
+ oats workspace status # membership table, locked packages, external souls
102
+ oats sync # confirm, resolve, lock, report the diff
91
103
  oats capabilities | oats souls # everything a soul may name, with origin and team
92
104
  oats spawn <soul> --preview # the exact modules (from/commit/changedSince), team, resolution revision
93
105
  oats doctor # this deployment's oats-local.yaml and lock, plus kernel diagnostics