@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
package/docs/schedules.md CHANGED
@@ -3,16 +3,12 @@
3
3
  A schedule launches an agent, runs an oats command, or wakes an existing
4
4
  instance on a cron. Definitions belong to a scope and are committable; every
5
5
  `oats schedule` command run anywhere inside that scope, including from an
6
- instance home, reads and writes the same file. On a **workspace deployment**
7
- (0.25, [workspaces.md](workspaces.md)) the scope is the deployment directory
8
- — the one holding `oats-local.yaml` and the `agents/` root (the kernel derives
9
- it as the directory above the agents root; a leftover `oats-config.yaml` that
10
- declares `team:` would still win, so remove it); scheduled spawns there
11
- materialize exactly like `oats spawn`. On a classic 0.24 deployment the scope
12
- is the team workspace (the config level that declares the team, else the
13
- outermost `oats-config.yaml` level). Execution belongs to the host that holds
14
- the scope, so a schedule on a registered server keeps running while your laptop
15
- sleeps.
6
+ instance home, reads and writes the same file. The scope is the deployment
7
+ directory ([workspaces.md](workspaces.md)) — the one holding `oats-local.yaml`
8
+ and the `agents/` root, found walking up; with none in reach, `oats schedule`
9
+ is `E_LOCAL_MISSING`. Scheduled spawns materialize exactly like `oats spawn`.
10
+ Execution belongs to the host that holds the scope, so a schedule on a
11
+ registered server keeps running while your laptop sleeps.
16
12
 
17
13
  There is no daemon. One host timer (a launchd user agent on macOS, a systemd
18
14
  user timer on Linux) runs `oats schedule tick --host` once a minute; the tick
@@ -25,15 +21,9 @@ and no queue.
25
21
  ## Files
26
22
 
27
23
  - `<workspace>/oats-schedules.json` — the definitions (`{version: 1|2, jobs:
28
- {<id>: ...}}`). Version 1 is the legacy format. Adding the first explicit
29
- version-2 execution upgrades the whole document to version 2 so an older
30
- scheduler refuses it instead of ignoring capture policy. Existing legacy
31
- entries may remain visibly unmigrated; new entries in a v2 file must declare
32
- their policy. Commit the file if you want the schedule shared with the team.
33
- Automatic wake creation uses this same document-version gate. Remote captured
34
- mutations require advertised numeric schedule API 2 before forwarding; the old
35
- feature-only gate is insufficient. Unclassifiable remote spec files also require
36
- API 2. Legacy inline specs/read operations remain compatible with older hosts.
24
+ {<id>: ...}}`). A new file is version 1. A version-2 file (written by 0.24–0.25
25
+ for captured definitions) is still read; it is never rewritten to version 1.
26
+ Commit the file if you want the schedule shared with the team.
37
27
  - `<workspace>/.agents/schedules/state.json` — last attempted minute and
38
28
  last run per job (gitignored), plus one lock directory per running job.
39
29
  - `~/.oats/schedules/registry.json` — the host registry: which scopes the
@@ -43,9 +33,8 @@ and no queue.
43
33
  with the directory to remove, and the holder removes its own lock on exit
44
34
  and on SIGINT/SIGTERM. Definition edits take a short per-scope lock.
45
35
 
46
- The existing Desktop adapter can inspect/manage supported API-2 schedules, but its
47
- legacy editor refuses captured policy fields rather than dropping them. Full captured
48
- editing UI remains later Desktop work; use the explicit CLI for those definitions.
36
+ Captured (versioned) definitions are refused (the captured/portable path was removed in 0.26):
37
+ see [Captured definitions](#captured-definitions-removed-in-026).
49
38
 
50
39
  ## Kinds
51
40
 
@@ -55,7 +44,10 @@ editing UI remains later Desktop work; use the explicit CLI for those definition
55
44
  options `oats spawn` takes. `agentsRoot` names the exact agents root that
56
45
  holds the soul (it must lie inside the workspace and defaults to the
57
46
  workspace's own root); it is what tells same-named souls in different
58
- member repositories apart. `repo` is the work repository, as `--repo`. The task gets a trailing schedule block naming the job and the
47
+ member repositories apart. `repo` is the work repository, as `--repo`.
48
+ Each run is named `<agent>-<purpose or id>-<YYYYMMDDHHMM>`. Instance names
49
+ are at most 64 characters, so a definition whose run names would be longer
50
+ is refused when it is saved (`E_SCHEDULE_INVALID`, field `purpose` or `id`). The task gets a trailing schedule block naming the job and the
59
51
  minute and ending with `oats retire --self`. An optional `wake` object
60
52
  (`{cron, tz, message}`) attaches a wake schedule to each launched instance;
61
53
  nothing is attached unless you ask.
@@ -66,8 +58,8 @@ editing UI remains later Desktop work; use the explicit CLI for those definition
66
58
  from durable context; the job follows that worker until its home is gone.
67
59
  Command return is not task completion. Avoid binding durable work to a
68
60
  disposable source-home cwd; see [OKF v2 source jobs](#okf-v2-source-jobs).
69
- New exact-record command definitions use the fields described in
70
- [Captured execution and recurrence](#captured-execution-and-recurrence).
61
+ An argv carrying a captured selector (`--deployment`, `--resolution`,
62
+ `--artifact-set`) is refused when saved (`E_SCHEDULE_INVALID`, field `argv`).
71
63
  - **wake** `{id, enabled, cron, tz, kind: "wake", home, message}` — every
72
64
  due minute inspects the instance at `home` through its session receipts.
73
65
  Running: `message` is delivered once with `session input`. Not running
@@ -97,87 +89,28 @@ required IANA zone; both are evaluated by the croner library. `--wake-every
97
89
  N` at spawn time means `*/N * * * *`: every 7 fires at :00, :07, ... :56 and
98
90
  then :00 again, so 1, 5, 10, 15 and 30 give an even cadence.
99
91
 
100
- ## Captured execution and recurrence
101
-
102
- A new captured command definition is explicitly versioned and chooses its
103
- recurrence policy. Its containing schedule document is version 2:
104
-
105
- ```json
106
- {
107
- "id": "retained-job",
108
- "definitionVersion": 2,
109
- "recurrencePolicy": "capture",
110
- "kind": "command",
111
- "cwd": "/absolute/workspace",
112
- "argv": [
113
- "oats", "example-action", "run",
114
- "--deployment", "/absolute/deployment",
115
- "--resolution", "sha256-...",
116
- "--", "--provider-argument", "--json"
117
- ],
118
- "responsibleHuman": null,
119
- "cron": "0 * * * *",
120
- "tz": "UTC"
121
- }
122
- ```
123
-
124
- The admitted-attempt wire is published as
125
- [`execution-capsule.schema.json`](execution-capsule.schema.json). Runtime
126
- validation checks selector/target agreement. A canonical `oats.json.v1` digest
127
- of all fields except `executionId` is the separate content witness;
128
- `executionId` itself is opaque admission identity.
129
-
130
- `capture` requires the explicit deployment/resolution selector pair and
131
- `--json` in the **saved argv**. The scheduler never appends an unrecorded
132
- protocol argument to a captured target. Adding or updating the definition
133
- derives an immutable `execution` template containing that exact target,
134
- resolution, input references and explicitly supplied responsible-human value.
135
- It contains no execution ID; `executionStatus.contentIntegrity` exposes its
136
- canonical content witness. At each due tick the
137
- scheduler verifies the retained action first, then mints a fresh opaque
138
- `executionId`, writes the resulting capsule into the attempt before reserving a
139
- slot, and only then invokes the CLI. Thus two attempts with identical content
140
- have the same capsule digest but remain different intents. Definition edits
141
- affect later admissions only; an unknown attempt, `run`, or reconciliation never
142
- replaces its capsule with the edited definition or today's config/lock. Captured
143
- attempts carry their own `schemaVersion:1`; their launch-slot lock names the same
144
- `executionId`. A mismatching lock or malformed/unknown attempt version stays
145
- unresolved and cannot dispatch. Scheduler launch also scrubs ambient
146
- `OATS_DEPLOYMENT` and `OATS_RESOLUTION`; only the saved argv is authority.
147
-
148
- `prepare-on-tick` is a distinct explicit policy for a genuinely new command
149
- tick. Its `preparation` object maps directly to the generic
150
- `prepareCapturedComposition({deployment,source,workspace?,member?,operator?,mode?})`
151
- input; scheduler code does not parse source/workspace policy itself. Production
152
- uses the core adapter and tests may inject the same contract. Direct-source and
153
- workspace-alias requests are supported; `mode` is a work-mode string, not a nested
154
- launch object. A complete result must preserve the requested deployment and
155
- returned resolution, and contain `executionBinding` and an explicit
156
- `responsibleHuman` (`null` means messaging was actually disabled). The scheduler
157
- then inserts the exact selector pair before `--`, verifies the captured action,
158
- mints and persists the attempt, and dispatches. Missing adapters and incomplete
159
- results return typed `migration-required`/`needs-configuration` with no attempt.
160
- A dry run never invokes preparation or mints intent. There is no current-context
161
- fallback. Captured spawn and
162
- operation definitions remain unavailable until their public captured consumer
163
- adapters exist. A captured wake definition is accepted only when its target
164
- `instance.json` has an exact `executionBinding`; the binding is copied into the
165
- execution template and checked again at admission without consulting source or
166
- configuration. Actual captured wake delivery/start remains blocked with
167
- `migration-required` until the parent-owned lifecycle consumer lands. Legacy
168
- wakes continue through the old lifecycle boundary in this release.
169
-
170
- Definitions without `definitionVersion`/`recurrencePolicy` are legacy v1
171
- definitions. They retain the old release behavior during migration and are not
172
- reported as captured execution. `list`/`show` report their `executionStatus` as
173
- `{kind:"legacy",capture:"unknown",migrationRequired:true}`. A captured definition
174
- reports only `capture:"recorded"` until action admission verifies the retained
175
- record and current exact approval; it does not claim launch readiness. While an
176
- attempt is unresolved or its confirmed launched work still holds a slot,
177
- `executionStatus.intent` separately reports captured, legacy-unknown or invalid
178
- authority, so editing a future definition cannot hide an older admitted capsule.
179
- Partial, malformed or unsupported versioned
180
- definitions/attempts are invalid or blocked, never reinterpreted as legacy.
92
+ ## Captured definitions (removed in 0.26)
93
+
94
+ 0.24–0.25 could save captured command definitions: `definitionVersion`,
95
+ `recurrencePolicy` (`capture` or `prepare-on-tick`), an `execution` template and a
96
+ `preparation` request, run against a captured deployment/resolution. That path
97
+ was removed in 0.26:
98
+
99
+ - `add` and `update` refuse any of those four keys (`E_SCHEDULE_INVALID`, the
100
+ key as `field`), locally and, with `--server`, before anything is forwarded.
101
+ - A stored captured definition is invalid on its own job: every tick reports it
102
+ (`invalid`, "captured schedules are refused (the captured/portable path was removed in 0.26) …") and never runs it;
103
+ the rest of the scope's jobs continue. `list`/`show` report its
104
+ `executionStatus` as `{kind:"invalid", …, reason}`.
105
+ - A captured attempt or job lock left mid-run by 0.25 is reported on that job
106
+ (`executionStatus.intent`, `reconcile` refuses with `E_SCHEDULE_INVALID`) and is
107
+ never run, adopted or released. A held captured lock keeps its launch slot
108
+ until the job is gone: remove it with `oats schedule remove --force <id>`, or
109
+ re-add it without the captured keys.
110
+
111
+ Every other definition is a plain one; `list`/`show` report its `executionStatus`
112
+ as `{kind:"legacy",capture:"unknown",migrationRequired:true}` (a released wire,
113
+ kept as it was).
181
114
 
182
115
  ## Commands
183
116
 
@@ -202,27 +135,23 @@ running}], scheduler: {installed, active, lastTick, maxConcurrent, ...}}`.
202
135
 
203
136
  ## What a run reports
204
137
 
205
- `launched` (spawn or command returned), `blocked` (versioned execution
206
- admission refused before a launch slot or child process), `active` (the instance is running;
138
+ `launched` (spawn or command returned), `active` (the instance is running;
207
139
  a home whose retirement is pending still counts, its runtime may be alive),
208
140
  `ended` (its home is gone), `stopped` (home present, nothing running: needs
209
141
  attention, never removed for you), `launch-failed`, `unknown`, and for wake
210
142
  jobs `delivered`, `started` or `skipped`. The kernel never claims a task
211
143
  succeeded.
212
144
 
213
- `blocked` includes an unavailable exact record/approval or incomplete new-work
214
- preparation, including not-yet-qualified provider bindings. The result preserves the typed
215
- `errorCode`; no attempt capsule or launch lock is created.
145
+ A run recorded by 0.24–0.25 may also read `blocked` (a captured admission that
146
+ refused before a launch slot); 0.26 never produces it.
216
147
 
217
148
  `unknown` means the launch's side effects are unconfirmed: a command timed
218
149
  out or answered no envelope, an envelope named an instance the roster
219
150
  cannot place, or an attempt was never recorded. The job keeps its slot and
220
151
  is skipped until `oats schedule reconcile <id>`. Reconcile adopts only an
221
- attributable receipt: a legacy spawn job's instance is named deterministically
222
- for its minute. A captured command requires the SAME admitted execution ID and
223
- content witness, plus its explicitly recorded home and matching instance metadata;
224
- current-config roster lookup or a previous attempt's name cannot establish it.
225
- Nothing is inferred from file times. Observation validates custody before releasing
152
+ attributable receipt: a spawn job's instance is named deterministically for its
153
+ minute; a command job's, only the instance its answer named. Nothing is inferred
154
+ from file times. Observation validates custody before releasing
226
155
  slots, and unresolved attempts remain held even in the crash gap before a lock
227
156
  exists. Ordinary removal refuses those attempts; force-forget remains explicit. A command whose answer named nothing stays
228
157
  unknown; check the roster and the host by hand, then
@@ -244,9 +173,8 @@ continues.
244
173
 
245
174
  `disable` never stops anything. `update` never touches a running instance,
246
175
  and while a job holds a slot or has an unresolved attempt its complete
247
- execution identity (including a versioned capsule), kind and target cannot
248
- change; cron, tz and enabled can. Legacy definitions retain their narrower
249
- compatibility behavior until migrated. A cold wake persists its slot before
176
+ execution identity, kind and target cannot
177
+ change; cron, tz and enabled can. A cold wake persists its slot before
250
178
  the session start runs and keeps it on any start exception, whatever its code
251
179
  (the kernel can refuse while recording, after the session exists); the next
252
180
  observation releases it once the runtime is proven stopped or absent, one tick
@@ -262,13 +190,7 @@ listed with its skipped reason.
262
190
  saves a wake job `wake-<instance>` bound to the new home after the spawn
263
191
  succeeded. If the spawn succeeds but the save fails, the spawn result still
264
192
  carries the full instance receipt, plus `wakeScheduleError` and a warning;
265
- the instance is neither hidden nor spawned again. When the spawning consumer
266
- supplies both the returned `executionBinding` and `responsibleHuman`,
267
- `saveWakeForHome` creates a version-2 captured wake from the matching binding in
268
- the new home. Supplying only one, or a result binding that differs from the
269
- home, refuses. The current parent-owned spawn caller still needs to pass these
270
- fields when its captured lifecycle path lands; omission retains explicit legacy
271
- behavior rather than inventing a binding.
193
+ the instance is neither hidden nor spawned again.
272
194
 
273
195
  ## OKF v2 source jobs
274
196
 
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://oats.dev/schemas/soul-v2.json",
4
4
  "title": "Soul declaration v2 (soul.yaml)",
5
- "description": "Authored shape only. A soul names each capability WITH WHERE IT COMES FROM (`from: here | <repo key> | package`) — a location, never a version. Provider payloads (knowledge, messaging, tasks) are opaque to the kernel; `none` empties the slot. Membership, privacy and team semantics are enforced by discovery/resolution, not here.",
5
+ "description": "Authored shape only. A soul names each capability WITH WHERE IT COMES FROM (`from: here | <repo key> | package`) — a location, never a version. Core-capability payloads (knowledge, messaging, tasks) are opaque to the kernel; `none` empties the slot. Membership, privacy and team semantics are enforced by discovery/resolution, not here.",
6
6
  "type": "object",
7
7
  "required": ["schemaVersion", "name", "description", "work"],
8
8
  "additionalProperties": false,
@@ -11,8 +11,9 @@
11
11
  "name": { "$ref": "#/$defs/slug" },
12
12
  "description": { "type": "string" },
13
13
  "work": { "enum": ["worktree", "checkout", "directory", "workspace"] },
14
- "team": { "$ref": "#/$defs/label", "description": "Team label; overrides the repo's default from oats-membership.yaml." },
15
- "private": { "type": "boolean", "description": "true → not discoverable in the workspace; usable only from its own repo." },
14
+ "team": { "$ref": "#/$defs/teamLabels", "description": "Team label, or a non-empty list of distinct labels (the first is the primary); overrides the repo's default from oats-membership.yaml. Each label's messaging payload reaches the provider as an eligible team (OATS_TEAMS); joining is the provider's explicit act." },
15
+ "private": { "type": "boolean", "deprecated": true,
16
+ "description": "Ignored since 0.26.0: souls have no private mode. Every soul of a confirmed member is listed and spawnable; discovery warns soul-private-ignored. Accepted so existing files still validate; remove it." },
16
17
  "capabilities": {
17
18
  "type": "object",
18
19
  "propertyNames": { "$ref": "#/$defs/capabilityName" },
@@ -31,6 +32,12 @@
31
32
  "$defs": {
32
33
  "slug": { "type": "string", "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$" },
33
34
  "label": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
35
+ "teamLabels": {
36
+ "anyOf": [
37
+ { "$ref": "#/$defs/label" },
38
+ { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/$defs/label" } }
39
+ ]
40
+ },
34
41
  "capabilityName": { "type": "string", "pattern": "^[a-z0-9][a-z0-9._-]*$" },
35
42
  "repoKey": { "type": "string", "pattern": "^[^\\s@/][^\\s@]*/[^\\s@]+$" },
36
43
  "fromLocation": {
@@ -49,7 +56,7 @@
49
56
  "capabilityChoice": { "anyOf": [{ "$ref": "#/$defs/capabilityRef" }, { "const": "off" }] },
50
57
  "slotPayload": {
51
58
  "anyOf": [{ "const": "none" }, { "type": "object" }],
52
- "description": "Opaque provider payload, or `none` to leave the slot empty."
59
+ "description": "Opaque payload for the slot's core capability, or `none` to leave the slot empty."
53
60
  }
54
61
  }
55
62
  }
@@ -40,7 +40,6 @@ name: release-manager # must equal the directory name
40
40
  description: Cuts, verifies and announces releases.
41
41
  work: worktree # worktree | checkout | directory | workspace
42
42
  team: engineering # optional label; else the repo's default (oats-membership.yaml); else unassigned
43
- private: true # optional: not discoverable; spawnable only from this repo
44
43
 
45
44
  capabilities: # WHERE each capability comes from — a location, never a version
46
45
  acme-release-tooling: { from: here } # here = this soul's own repo
@@ -62,16 +61,19 @@ compatibility: # optional floors on PACKAGE versions
62
61
  | Key | Meaning |
63
62
  |---|---|
64
63
  | `name`, `description`, `work` | Required. `work` is the work mode below. |
65
- | `team` | A label declared in the workspace's `teams:`; may add `defaults.byTeam` capabilities. Never gates or restricts. |
66
- | `private` | `true` keeps the soul out of workspace discovery; its own repo can still spawn it. |
64
+ | `team` | A label, or a list of labels (the first the primary), declared in the workspace's `teams:`; each may add `defaults.byTeam` capabilities and is an eligible messaging team. Never gates or restricts. |
65
+ | `private` | **Ignored since 0.26.0:** souls have no private mode. Every soul of a confirmed member is listed and spawnable; a soul that still carries the field gets a `soul-private-ignored` warning. Remove it. |
67
66
  | `capabilities` | `<cap>: { from: here \| <repo key> \| package }` or `<cap>: off`. Composed over `defaults.<slot>` ⊕ `defaults.capabilities` ⊕ `defaults.byTeam[team]`; the soul wins. |
68
67
  | `knowledge` / `messaging` / `tasks` | The slot's provider payload (true of every instance of the soul), or `none`. Merged with `oats-local.yaml` `settings.<cap>` and `spawn --provider <cap>`; the provider's `binding` contract validates the result — and refuses keys it does not declare. For `oats.okf` 2.1.3 the admitted keys are its settings (`bindings-file`, `state-dir`, `harvest-runtime`, `harvest-model`); the soul's `owns`/`reads` live in `souls/<name>/okf.json`, which OKF reads from the soul directory. |
69
68
  | `compatibility` | `<cap>: <semver range>` checked against the locked package version (`E_COMPATIBILITY`). |
70
69
 
71
70
  Schema: [`soul.schema.json`](soul.schema.json). Not in v2: `kind`, `type`,
72
- `repo`, `runtime`, `model`, `requires`, `source:`, `stores.inherit`. Model and
73
- runtime are spawn-time / launch-configuration choices (`--runtime`, `--model`,
74
- `--launch-config`), not soul identity: a soul is model-agnostic as an artifact.
71
+ `repo`, `runtime`, `model`, `backend`, `yolo`, `launch-config`, `children`,
72
+ `requires`, `source:`, `stores.inherit`. Runtime, model, backend, yolo and the
73
+ launch configuration are spawn-time host choices (`--runtime`, `--model`,
74
+ `--backend`, `--yolo`, `--launch-config`, or a launch configuration in
75
+ `oats-local.yaml`), not soul identity: a soul is model-agnostic as an artifact.
76
+ A child-spawn policy is a spawn flag too (`--no-child-spawns`).
75
77
 
76
78
  A soul never runs by itself. It is incarnated as an instance. Editing a soul
77
79
  is a code change, reviewed in its repo.
@@ -84,9 +86,10 @@ souls — is ordinary capability content: **`oats.core`** (package
84
86
  `defaults.capabilities: { oats.core: { from: package } }`; a soul may say
85
87
  `oats.core: off`. **`oats.setup`** (same package) carries the whole-architecture
86
88
  knowledge an onboarding expert needs. Neither is kernel magic; the kernel still
87
- composes its own instance-boundary and work-mode briefings — and, when
88
- `oats.core` resolves as a module, leaves the "You run on OATS" briefing to the
89
- module's inject (one block, not two; 0.25.2).
89
+ composes its own instance-boundary and work-mode briefings, and the "You run
90
+ on OATS" briefing is `oats.core`'s inject (the kernel ships no copy since 0.26:
91
+ a soul without `oats.core` gets no OATS operating instructions, and
92
+ `oats doctor --soul` says so).
90
93
 
91
94
  ## Instance anatomy
92
95
 
@@ -102,7 +105,6 @@ full copy** of every capability the soul resolved to:
102
105
 
103
106
  ```text
104
107
  <agents-root>/<soul>/instances/<instance>/
105
- soul → ../../soul # the soul, for reference (read-only)
106
108
  AGENTS.md # generated: soul AGENTS.md + kernel/work-mode blocks + each module's inject
107
109
  CLAUDE.md → AGENTS.md
108
110
  .agents/skills/ # canonical skill tree — soul skills + <capability>/<skill>/ full copies
@@ -111,7 +113,7 @@ full copy** of every capability the soul resolved to:
111
113
  .oats/modules/<capability>/ # the whole capability: oats.json, bin/, injects/, skills/ (hooks run from here)
112
114
  work/ # worktree, checkout symlink, attached tree, or private directory
113
115
  TASK.md # briefing and task
114
- instance.json # provenance (below)
116
+ instance.json # provenance (below); `soulDir` = the soul directory hooks get as OATS_SOUL
115
117
  STATE.md, log.md, notes/ # optional, from the knowledge capability
116
118
  ```
117
119
 
@@ -187,6 +189,11 @@ oats spawn release-manager --preview --json # decide ever
187
189
  oats spawn release-manager --provider oats.aweb identity.source=/abs/path/to/retained/.aw # instance-level payload
188
190
  ```
189
191
 
192
+ An instance is named `<soul>-<purpose>` by default, or exactly `--name <slug>`.
193
+ Names are unique per deployment (a workspace-model deployment has one agents
194
+ root): a derived name in use gets `-2`, `-3`…; an explicit `--name` in use is
195
+ refused (`E_INSTANCE_NAME_TAKEN`).
196
+
190
197
  From a deployment (where `oats-local.yaml` is), a spawn: reads the local file →
191
198
  discovers the workspace over its remotes and confirms membership → finds the
192
199
  soul among the confirmed members (or `external:`; an ambiguous bare name is
@@ -194,7 +201,7 @@ soul among the confirmed members (or `external:`; an ambiguous bare name is
194
201
  `<agents-root>/<soul>/souls/<commit12>/` at its commit (the home links that
195
202
  directory; `<agents-root>/<soul>/soul` points at the current one) → resolves
196
203
  every capability by
197
- `from:` (member = latest, package = locked + approved) → creates the home →
204
+ `from:` (member = latest, package = locked) → creates the home →
198
205
  **materializes each module whole** into `.oats/modules/` and copies its skills
199
206
  into `.agents/skills/` (a transaction: any failure leaves nothing behind) →
200
207
  composes `AGENTS.md` → records `modules`/`providers`/`workspace` in
@@ -282,9 +289,9 @@ an agent's environment variables — is operator-origin and appears top-level.
282
289
  Agents spawning sub-agents should pass `--parent "$OATS_INSTANCE"` (or the
283
290
  relation that fits).
284
291
 
285
- If the workspace has a messaging integration such as aweb, spawned instances
292
+ If the workspace has a messaging capability such as aweb, spawned instances
286
293
  can also receive identities and coordinate with each other automatically. The
287
- task layer can provide shared work state while messaging provides conversation.
294
+ tasks capability can provide shared work state while messaging provides conversation.
288
295
 
289
296
  ### Retire
290
297
 
@@ -316,7 +323,7 @@ follow. Every mode sits inside the same home/work boundary, which the generated
316
323
  instructions state first (`injects/instance-boundary.md`):
317
324
 
318
325
  - `<instance-home>` — the gitignored instance directory, `$OATS_INSTANCE_HOME` —
319
- holds the brain (`AGENTS.md`, `soul/`), the task, the provenance
326
+ holds the brain (the composed `AGENTS.md`; there is no soul link), the task, the provenance
320
327
  (`instance.json`) and the episodic state (`STATE.md`, `log.md`, `notes/`), and
321
328
  is where OATS operational/lifecycle commands — and the commands of whatever
322
329
  capabilities are active, `aw` among them when aweb messaging is — are run,
@@ -325,9 +332,11 @@ instructions state first (`injects/instance-boundary.md`):
325
332
  - `<instance-home>/work` — the repository or workspace view — is where
326
333
  repository reading, editing, building, testing, git and commits happen, to the
327
334
  extent the mode below permits.
328
- - The home's `soul` link is to be treated as read-only: writes through it bypass
329
- the branch and review path. Durable soul edits go through tracked paths under
330
- `work/` under the applicable review rules. OKF v2 harvest edits external
335
+ - The home has no soul link: the composed `AGENTS.md` already carries the
336
+ soul's instructions, and `instance.json` `soulDir` records the (read-only,
337
+ per-commit) soul directory every hook and dispatched command receives as
338
+ `OATS_SOUL`. Durable soul edits go through tracked paths under `work/` under
339
+ the applicable review rules. OKF v2 harvest edits external
331
340
  owned knowledge, not canonical soul files or skills.
332
341
 
333
342
  Agents move between the two as the task needs; the boundary is what each
@@ -472,8 +481,7 @@ with **`E_NO_CANONICAL_ROOT`** and creates nothing.
472
481
 
473
482
  ### Deployment prerequisite: the agents directory must be operator-owned
474
483
 
475
- The canonical deployment (the agents root, `local-agents/`, and the instance
476
- homes under them) **must be owned by the operator and not writable by untrusted
484
+ The canonical deployment (the agents root and the instance homes under it) **must be owned by the operator and not writable by untrusted
477
485
  users or processes.** OATS validates resolved destinations and re-checks the home
478
486
  immediately before creating anything in it, but it cannot defeat a concurrent
479
487
  local attacker who already has write access there: Node offers no
@@ -486,33 +494,38 @@ something the kernel can close from inside.
486
494
  Default layout:
487
495
 
488
496
  ```text
489
- <scope>/
490
- agents/ # committed souls
491
- docs-expert/
492
- soul/
497
+ <deployment>/
498
+ oats-local.yaml
499
+ agents/
500
+ docs-expert/ # a workspace soul, defined in a member repository's
501
+ souls/<commit12>/ # souls/docs-expert/ and copied here per commit
493
502
  instances/
494
- local-agents/ # local souls — same shape, never committed
495
- scratch-agent/
496
- soul/
503
+ memory-harvest/ # a capability-defined agent: only instances/, no soul
497
504
  instances/
498
505
  ```
499
506
 
500
- `local-agents/` sits BESIDE `agents/` at the scope level and holds **full local
501
- souls**: complete definitions with instructions, skills, capability declarations
502
- and instances, not committed to the repo. `oats create <name> --local` creates one — the directory
503
- is created on first use, and when the scope is a git repo the kernel adds
504
- `local-agents/` to its `.gitignore` automatically. A scope with only
505
- `local-agents/` is fully operable: people can use OATS with local agents alone.
506
- Ad hoc agents from `oats spawn --instructions-file`/`--def-file` land here too.
507
- Legacy nested `agents/local-agents/` and `agents/tmp-agents/` are still read
508
- for compatibility.
509
-
510
- Instances of a local soul receive a `local-soul` briefing: work and commits
511
- are normal, but soul updates are plain file edits (nothing to commit), and
512
- durability is the machine's — promote the soul to `agents/` when it starts to
513
- matter beyond one machine. That concerns soul artifacts, not a knowledge
514
- provider's custody: a local soul using OKF v2 still reads external bases and
515
- uses PR-only delivery for any Git base.
507
+ A capability-defined agent (declared by a package or member module, such as
508
+ the OKF harvester) homes under the agents root exactly like a soul; its
509
+ directory holds only `instances/`. A name that is both a workspace soul and a
510
+ capability agent is ambiguous (`E_SOUL_AMBIGUOUS`).
511
+
512
+ There are no local souls. A soul is a member repository's `souls/<name>`
513
+ (`soul.yaml` + `AGENTS.md`); author it there and run `oats sync`. OATS 0.25
514
+ and earlier kept local souls and capability-agent homes under
515
+ `<scope>/local-agents/`: this kernel never reads, spawns into or retires from
516
+ that directory; it only detects it. Onboarding refuses into a directory that
517
+ holds one, and `oats status` and `oats doctor` report it once, as the
518
+ `legacy-local-agents` problem naming the instances found there; retire them
519
+ with the 0.25 kernel, or delete the directory once they are stopped.
520
+
521
+ A *captured home* (spawned through 0.24–0.25's captured path, removed in 0.26:
522
+ its `instance.json` records `executionBinding`, `incarnationId` or `captured`) is
523
+ reported by `oats status` and `oats doctor` as the `legacy-captured-home`
524
+ problem. It has no 0.26 runtime: start, restart, inspect/readiness/operation
525
+ `--home` and its in-home commands refuse it (`E_UNSUPPORTED_MODE`). `oats retire`
526
+ still works; it warns once per capability whose retire hook did NOT run, since
527
+ identities and memberships those capabilities created are not revoked — remove
528
+ them with the provider's own tooling. Re-spawn the soul from the deployment.
516
529
 
517
530
  Alternative agents-root layouts are planned but not built. Today the default
518
531
  layout is the only implemented layout.