@awebai/oats 0.27.2 → 0.29.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 (75) hide show
  1. package/bin/oats.mjs +445 -96
  2. package/capabilities/oats-okf/bin/oats-okf.mjs +55 -30
  3. package/capabilities/oats-okf/injects/okf.md +36 -28
  4. package/capabilities/oats-okf/lib/binding-wire.mjs +4 -1
  5. package/capabilities/oats-okf/lib/config.mjs +6 -1
  6. package/capabilities/oats-okf/lib/consult.mjs +496 -0
  7. package/capabilities/oats-okf/lib/harvest-status.mjs +88 -0
  8. package/capabilities/oats-okf/lib/harvest-switch.mjs +81 -0
  9. package/capabilities/oats-okf/lib/inspection.mjs +11 -3
  10. package/capabilities/oats-okf/lib/io.mjs +9 -2
  11. package/capabilities/oats-okf/lib/okf-validate.mjs +123 -0
  12. package/capabilities/oats-okf/lib/sources.mjs +42 -55
  13. package/capabilities/oats-okf/lib/stores.mjs +19 -11
  14. package/capabilities/oats-okf/lib/worker.mjs +90 -8
  15. package/capabilities/oats-okf/oats.json +24 -9
  16. package/capabilities/oats-okf/skills/okf-consultation/SKILL.md +144 -0
  17. package/capabilities/oats-okf/skills/okf-consultation/references/consult.md +86 -0
  18. package/capabilities/oats-okf/skills/okf-instance-knowledge/SKILL.md +104 -0
  19. package/capabilities/oats-okf-harvest/bin/okf-harvest.mjs +140 -0
  20. package/capabilities/oats-okf-harvest/injects/harvester.md +12 -0
  21. package/capabilities/oats-okf-harvest/oats.json +26 -0
  22. package/capabilities/oats-okf-harvest/skills/knowledge-harvest/SKILL.md +168 -0
  23. package/capabilities/oats-okf-harvest/skills/knowledge-theory/SKILL.md +192 -0
  24. package/capabilities/{oats-okf/skills/okf → oats-okf-harvest/skills/okf-authoring}/SKILL.md +15 -22
  25. package/capabilities/oats-okf-maintenance/bin/okf-maintenance.mjs +149 -0
  26. package/capabilities/oats-okf-maintenance/injects/maintainer.md +12 -0
  27. package/capabilities/oats-okf-maintenance/lib/provenance.mjs +45 -0
  28. package/capabilities/oats-okf-maintenance/oats.json +21 -0
  29. package/capabilities/oats-okf-maintenance/skills/knowledge-review/SKILL.md +144 -0
  30. package/capabilities/oats-okf-maintenance/skills/knowledge-theory/SKILL.md +192 -0
  31. package/capabilities/oats-okf-maintenance/skills/okf-authoring/SKILL.md +151 -0
  32. package/capabilities/oats-okf-maintenance/skills/okf-authoring/scripts/okf-validate.mjs +123 -0
  33. package/capabilities/oats-okf-maintenance/skills/okf-trigger-setup/SKILL.md +146 -0
  34. package/capabilities/oats-review/injects/review.md +3 -2
  35. package/capabilities/oats-review/oats.json +3 -4
  36. package/docs/capabilities.md +41 -9
  37. package/docs/capability-manifest.schema.json +0 -7
  38. package/docs/design/2026-09-24-phase-d-plan.md +11 -0
  39. package/docs/design/2026-09-26-desktop-design-brief-architecture.md +241 -0
  40. package/docs/design/2026-09-26-okf-knowledge-operations.md +389 -0
  41. package/docs/desktop-cli-api.md +342 -10
  42. package/docs/implementation.md +1 -1
  43. package/docs/knowledge-capability-authoring.md +8 -2
  44. package/docs/knowledge-reference/package-craft.md +8 -5
  45. package/docs/knowledge.md +101 -0
  46. package/docs/oats-local.schema.json +33 -2
  47. package/docs/oats-package.schema.json +39 -0
  48. package/docs/official-catalog.md +7 -4
  49. package/docs/packages.md +76 -6
  50. package/docs/release-lane.md +1 -1
  51. package/docs/release-notes/v0.28.0.md +144 -0
  52. package/docs/release-notes/v0.29.0.md +240 -0
  53. package/docs/schedules.md +230 -4
  54. package/docs/souls-and-instances.md +11 -9
  55. package/docs/workspaces.md +18 -3
  56. package/lib/automations.mjs +369 -0
  57. package/lib/core.mjs +87 -158
  58. package/lib/instance-inspect.mjs +16 -8
  59. package/lib/instance-resolution.mjs +90 -197
  60. package/lib/materialize.mjs +18 -7
  61. package/lib/operator-dispatch.mjs +1 -2
  62. package/lib/packages.mjs +107 -6
  63. package/lib/remote.mjs +21 -1
  64. package/lib/resolve.mjs +71 -9
  65. package/lib/schedule.mjs +228 -45
  66. package/lib/triggers.mjs +678 -0
  67. package/lib/workspace.mjs +81 -4
  68. package/package-catalog.json +6 -4
  69. package/package.json +1 -1
  70. package/capabilities/oats-okf/agents/memory-harvest/AGENTS.md +0 -21
  71. package/capabilities/oats-okf/agents/memory-harvest/soul.yaml +0 -5
  72. package/capabilities/oats-okf/skills/memory-harvest/SKILL.md +0 -285
  73. package/capabilities/oats-review/agents/reviewer/AGENTS.md +0 -53
  74. package/capabilities/oats-review/agents/reviewer/soul.yaml +0 -6
  75. /package/capabilities/{oats-okf/skills/okf → oats-okf-harvest/skills/okf-authoring}/scripts/okf-validate.mjs +0 -0
package/docs/schedules.md CHANGED
@@ -10,7 +10,7 @@ is `E_LOCAL_MISSING`. Scheduled spawns materialize exactly like `oats spawn`.
10
10
  Execution belongs to the host that holds the scope, so a schedule on a
11
11
  registered server keeps running while your laptop sleeps.
12
12
 
13
- There is no daemon. One host timer (a launchd user agent on macOS, a systemd
13
+ [Triggers](#triggers) are evaluated by the same tick. There is no daemon. One host timer (a launchd user agent on macOS, a systemd
14
14
  user timer on Linux) runs `oats schedule tick --host` once a minute; the tick
15
15
  is a short-lived process that evaluates only the current minute, launches
16
16
  what is due through the same `spawn`, `session start` and `session input`
@@ -39,7 +39,7 @@ see [Captured definitions](#captured-definitions-removed-in-026).
39
39
  ## Kinds
40
40
 
41
41
  - **spawn** `{id, enabled, cron, tz, kind: "spawn", agent, agentsRoot?,
42
- repo?, backend?, purpose?, task, harness?, model?, yolo?, wake?}` — every
42
+ repo?, backend?, purpose?, task, launchConfig?, harness?, model?, yolo?, wake?}` — every
43
43
  due minute launches one disposable instance of `agent` with the same
44
44
  options `oats spawn` takes. `agentsRoot` names the exact agents root that
45
45
  holds the soul (it must lie inside the workspace and defaults to the
@@ -89,6 +89,220 @@ required IANA zone; both are evaluated by the croner library. `--wake-every
89
89
  N` at spawn time means `*/N * * * *`: every 7 fires at :00, :07, ... :56 and
90
90
  then :00 again, so 1, 5, 10, 15 and 30 give an even cadence.
91
91
 
92
+ ## Triggers
93
+
94
+ A **trigger** (OATS 0.28.0, feature `triggers`) is an event-driven schedule:
95
+ "when EVENT matches, spawn a NEW instance of SOUL with TASK, in TEAMS". It is
96
+ stored in the same `oats-schedules.json` as a job of `kind: "trigger"`,
97
+ managed with `oats trigger …` (never `oats schedule …`, which neither lists nor
98
+ edits one), and evaluated by the same host tick (`oats schedule tick --host`,
99
+ and `oats schedule tick` for one scope). There is no daemon and no webhook: it
100
+ runs only on the host that holds the scope, with **that host's own
101
+ credentials**; a definition carries none.
102
+
103
+ **Credentials reach the tick through the host timer, not your shell.** The
104
+ timer (a user LaunchAgent on macOS, a `systemd --user` unit on Linux) runs the
105
+ tick with its own environment, which sets only `PATH` and `OATS_HOME_DIR`. `gh`
106
+ logged in with the keyring or its config file under your HOME works there. A
107
+ `GH_TOKEN` or `GITHUB_TOKEN` exported in your shell does not reach it.
108
+ `oats trigger test` reports where `gh`'s credential comes from (`gh.credentialSource`:
109
+ `keyring`, `config`, `env:<VAR>`) and warns when the timer cannot reach it.
110
+
111
+ ```json
112
+ { "id": "okf-harvest-review", "enabled": true, "kind": "trigger",
113
+ "on": { "source": "github.pull_request", "repo": "github.com/acme/knowledge",
114
+ "events": ["opened", "reopened", "ready_for_review"],
115
+ "labels": ["okf-harvest"], "base": "main", "poll": "2m" },
116
+ "spawn": { "soul": "oats.okf/knowledge-maintainer", "purpose": "review-pr-{number}",
117
+ "task": "Review knowledge-base PR {repo}#{number}. Load knowledge-review first.",
118
+ "teams": ["okf"], "harness": "claude", "model": "opus" },
119
+ "concurrency": { "max": 2, "perKey": 1 } }
120
+ ```
121
+
122
+ - **Source** `github.pull_request` (the only one in v1): the tick polls the
123
+ repository's open pull requests with the host's `gh` (`gh api repos/<owner>/<repo>/pulls`,
124
+ `state=open`, most recently updated first) every `poll` (default `2m`, at
125
+ least `1m`). `labels` (all must be present) and `base` filter them. A repo is
126
+ `github.com/<owner>/<repo>`; another host is passed to `gh` as `--hostname`.
127
+ - **Events** are inferred poll over poll: `opened` (a PR first seen, not a
128
+ draft; the first poll sees every open PR), `reopened` (seen closed, open
129
+ again), `ready_for_review` (was a draft), `labeled` (now carries the filter
130
+ labels it lacked; without a filter, any new label), `synchronize` (a new
131
+ head commit).
132
+ - **Dedup and retry.** Each event has a key
133
+ `<trigger>:<repo>#<number>:<event>:<stamp>` (`created_at` for `opened`, the
134
+ head SHA for `synchronize`, `updated_at` otherwise). A key is recorded as
135
+ fired **only after a successful spawn**; until then the event stays pending
136
+ and is retried at every poll, and dropped when its PR closes.
137
+ - **At least once, not exactly once.** The fired key is written after the
138
+ spawn returns. If the tick dies in between (a crash, a kill, the host going
139
+ down), the spawned instance exists but the key does not, and the next poll
140
+ spawns the event again. Concurrency still applies to that retry: with the
141
+ default `perKey: 1` the first instance is live, so the event is `held` rather
142
+ than spawned twice, and it fires once that instance retires. A trigger's soul
143
+ should therefore tolerate a second run on the same PR event (a review that
144
+ finds its own earlier review, for example).
145
+ - **Concurrency.** `max` (default 1) bounds the live instances of the trigger,
146
+ `perKey` (default 1) those of one PR; both are counted from the homes'
147
+ `instance.json.trigger` records, so a retired instance frees its slot. An
148
+ event over the bound stays pending (`held`).
149
+ - **The spawn** is `oats spawn` (the same path as a scheduled spawn). `soul` is
150
+ bare or qualified (`<package>/<soul>`). `purpose` (default
151
+ `{trigger}-{number}`, must render to a slug) and `task` are templated from
152
+ **only** `{repo} {number} {url} {event} {headSha} {trigger}`: a pull
153
+ request's title and body are untrusted and never reach the task (a template
154
+ naming any other field is refused). `teams` becomes the messaging
155
+ capability's `join=` setting (as `--provider <messaging cap> join=<labels>`).
156
+ `launchConfig` (a launch configuration in the running host's
157
+ `oats-local.yaml` `launch-configs`), `harness`, `model`, `yolo`, `backend`
158
+ are as for schedules; a package template may expose any of them as a
159
+ parameter (`"path": "spawn.launchConfig"`).
160
+ - **The event reaches the instance** as `OATS_TRIGGER_EVENT_FILE`
161
+ (`<home>/.oats/trigger-event.json`: `{ trigger, source, repo, number, url,
162
+ event, headSha, labels, observedAt, key }`), given to the spawn hooks and the
163
+ harness; `instance.json.trigger` records `{ id, key, source, repo, number,
164
+ url, event, headSha, observedAt, eventFile }`. The task ends with a short
165
+ block naming the event file.
166
+ - **State** lives in `<scope>/.agents/schedules/triggers.json` (last poll, the
167
+ PRs seen, pending events, fired keys, the last error).
168
+
169
+ ```sh
170
+ oats trigger add --file trigger.json # or:
171
+ oats trigger add --from oats.okf:harvest-review --set repo=github.com/acme/knowledge [--id <id>]
172
+ oats trigger list | show <id> | enable <id> | disable <id> | remove <id>
173
+ oats trigger test <id> # dry run: gh auth + credential source, repo + permissions (push/maintain/admin), the soul resolves,
174
+ # its messaging capability, the teams declared, what WOULD fire now; spawns nothing
175
+ oats trigger status [<id>] # last poll, next due, pending, fired keys (time, instance), live vs max, last error
176
+ ```
177
+
178
+ All take `--dir` and `--json` (`triggerApi: 1`). `remove` leaves the instances
179
+ it spawned running. `oats schedule list` does not list triggers, but it counts
180
+ them (`triggers: { count, command: "oats trigger list" }`, and a line in text
181
+ mode). Errors: `E_TRIGGER_INVALID { field }`, `E_TRIGGER_EXISTS`,
182
+ `E_TRIGGER_UNKNOWN`, `E_BAD_ARGS`.
183
+
184
+ **Package trigger templates.** A package may declare `triggers: [{ id, file }]`
185
+ in `oats-package.json`. Each file is `{ parameters: { <name>: { path,
186
+ required?, default?, description? } }, definition: { …a trigger… } }`.
187
+ `oats trigger add --from <package>:<id>` reads it at the locked commit;
188
+ `--set <name>=<value>` fills a parameter at its dotted `path` (a list value is
189
+ comma-separated); a required parameter without a value is `E_BAD_ARGS
190
+ { missing }` naming it. The trigger records `template: { package, version,
191
+ commit, template }`.
192
+
193
+ ## Workspace triggers and schedules
194
+
195
+ (OATS 0.29.0, feature `automations`.) A trigger or a schedule is defined at one of
196
+ two levels:
197
+
198
+ - **in the workspace**: a file committed in a confirmed member repository, shared
199
+ through Git and named `<member>/<id>`. This is the default for anything a team
200
+ relies on.
201
+ - **locally**: in the deployment's `oats-schedules.json` (`oats trigger add`,
202
+ `oats schedule add`), machine-private and named `local/<id>`.
203
+
204
+ The two kinds stay separate at every step. Each has its own folder, its own file
205
+ kind, its own ids, its own commands, its own list and its own opt-out.
206
+
207
+ | | trigger | schedule |
208
+ | --- | --- | --- |
209
+ | canonical folder (at the member's root) | `oats-triggers/` | `oats-schedules/` |
210
+ | file name anywhere in the member | `*.oats-trigger.yaml` | `*.oats-schedule.yaml` |
211
+ | `kind:` | `oats-trigger` | `oats-schedule` |
212
+ | body | `from:` + `set:` (a package template), or `on`, `spawn`, `concurrency` as above | `run: spawn \| command`, `cron`, `tz`, `agent`, `task`, `purpose`, `launchConfig`, `harness`, `model`, `yolo`, `backend`, `wake`, `argv`, `cwd` |
213
+ | commands | `oats trigger …` | `oats schedule …` |
214
+ | opt-out on this host | `triggers.disabled` | `schedules.disabled` |
215
+
216
+ Every `.yaml`/`.yml` under a canonical folder is a candidate, and so is a file with
217
+ the kind's suffix anywhere in the member (`.yml` works too), e.g.
218
+ `services/billing/nightly.oats-schedule.yaml` beside the code it concerns.
219
+ `oats-package/`, `.git/` and `node_modules/` are never scanned.
220
+
221
+ ```yaml
222
+ # <member>/oats-triggers/okf-harvest-review.yaml
223
+ kind: oats-trigger
224
+ schemaVersion: 1
225
+ description: Review every harvest PR on the knowledge base
226
+ from: oats.okf:harvest-review # a package template at the locked commit, then its parameters
227
+ set: { repo: github.com/acme/knowledge }
228
+ runsOn: kb-bot-server # the host.name that runs it
229
+ owner: github.com/acme-kb-bot # the GitHub account it acts as
230
+ ```
231
+
232
+ ```yaml
233
+ # <member>/services/billing/nightly.oats-schedule.yaml
234
+ kind: oats-schedule
235
+ schemaVersion: 1
236
+ run: spawn
237
+ cron: "0 7 * * *"
238
+ tz: Europe/Madrid
239
+ agent: digest-writer # resolved like `oats spawn <soul>` (member or package soul)
240
+ task: Write the nightly digest.
241
+ runsOn: ana-laptop
242
+ owner: github.com/ana
243
+ ```
244
+
245
+ - **The file describes itself.** It carries `kind` and `schemaVersion: 1`. A
246
+ candidate of the wrong kind (a schedule in `oats-triggers/`) or without one is an
247
+ `E_AUTOMATION_SCHEMA` problem, never silently skipped.
248
+ - **The id** is `id:`, else the filename stem. The same id twice in one member for
249
+ one kind is `E_AUTOMATION_DUPLICATE`, naming both paths; the second file is not
250
+ listed. A trigger and a schedule may share an id: they are different things.
251
+ - **A workspace schedule is `run: spawn` or `run: command`.** A `command`'s `cwd` is
252
+ relative to the deployment. `wake` and `operation` target an instance home on one
253
+ machine, so they stay local.
254
+
255
+ **Who runs it.** A host runs a workspace trigger or schedule only when both of these
256
+ hold:
257
+
258
+ 1. its `runsOn` is this host's `host.name` in `oats-local.yaml`;
259
+ 2. the host's authenticated `gh` account (`gh api user`, asked once per tick) is its
260
+ `owner`.
261
+
262
+ Otherwise the item is listed with a reason:
263
+
264
+ - `assigned-elsewhere`: another host runs it;
265
+ - `owner-mismatch`: this host is named, but its `gh` is logged in as someone else or
266
+ not at all. Nothing runs, the tick reports it, and `oats trigger test` says so;
267
+ - `host-unnamed`: this host has no `host.name`.
268
+
269
+ So exactly one machine runs it, and consent is explicit: the machine's operator
270
+ named the host and is logged in as the account.
271
+
272
+ **Opting out** without a commit: `oats trigger disable <member>/<id>` writes
273
+ `triggers.disabled`, and `oats schedule disable <member>/<id>` writes
274
+ `schedules.disabled`, in `oats-local.yaml`. `enable` removes the entry. A
275
+ workspace definition is never edited or removed here (`update` and `remove` are
276
+ `E_AUTOMATION_WORKSPACE`): change the file in Git.
277
+
278
+ **Refresh.**
279
+
280
+ - `oats sync` discovers the workspace triggers and schedules of the confirmed
281
+ members into a snapshot, `.agents/automations/snapshot.json` in the deployment,
282
+ with one list per kind. It takes one tree listing per member commit.
283
+ - The host tick reads that snapshot. It refreshes it (`oats automations refresh`)
284
+ when the snapshot is more than ten minutes old, at most once per interval. When
285
+ the refresh fails, the last good snapshot keeps serving.
286
+ - A change in Git therefore reaches the named host within about ten minutes.
287
+ - The run state (dedup keys, last poll, last run) stays per host and local. A
288
+ workspace schedule's job lock and state are keyed `<member>~<id>`.
289
+ - A trigger template (`from:`) is instantiated when the snapshot is taken, at the
290
+ commit the host's lock pins.
291
+
292
+ **Writing one.** Add `--workspace <member> --runs-on <host> --owner <host>/<login>`
293
+ to `oats trigger add` or `oats schedule add`:
294
+
295
+ - Run inside a checkout of that member, it writes `oats-triggers/<id>.yaml` or
296
+ `oats-schedules/<id>.yaml` there, for you to commit and push.
297
+ - Anywhere else, it prints the file.
298
+ - Either way, the file is read back and validated first.
299
+ - `oats trigger test <member>/<id>` checks the placement, and everything else it
300
+ checked before, on this host.
301
+
302
+ Everything above still holds: the soul must resolve here, templates name only the
303
+ whitelisted fields, a PR's text is never interpolated, and no definition carries a
304
+ credential.
305
+
92
306
  ## Captured definitions (removed in 0.26)
93
307
 
94
308
  0.24–0.25 could save captured command definitions: `definitionVersion`,
@@ -119,6 +333,7 @@ oats schedule add <id> --file spec.json --dir <workspace> --json
119
333
  oats schedule update <id> --file spec.json
120
334
  oats schedule list | show <id> | enable <id> | disable <id> | remove <id>
121
335
  oats schedule run <id> # now, under the same lock and bound
336
+ oats schedule test <id> # dry run: where it runs, whether its soul resolves, when it is next due; spawns nothing
122
337
  oats schedule tick --dry-run # what would run this minute, launching nothing
123
338
  oats schedule reconcile <id> [--clear] # resolve an attempt whose result was never recorded
124
339
  oats schedule host install # register this scope and install the ONE host timer (idempotent while active)
@@ -129,8 +344,9 @@ oats spawn <agent> ... --wake-every 15 --wake-message "Anything new?" # or --w
129
344
  Every subcommand takes `--server <id>` instead of `--dir`: it then runs on
130
345
  that host, in its registered workspace, because schedules are host-owned.
131
346
 
132
- `list --json` answers `{schedules: [{id, ...definition, nextRun, lastRun,
133
- running}], scheduler: {installed, active, lastTick, maxConcurrent, ...}}`.
347
+ `list --json` answers `{schedules: [{id, ...definition, nextDue, lastRun,
348
+ running}], scheduler: {installed, active, lastTick, maxConcurrent, ...}}`
349
+ (`oats trigger list --json` carries the same `scheduler`).
134
350
  `active` is what the OS reports about the timer, not whether a file exists.
135
351
 
136
352
  ## What a run reports
@@ -194,6 +410,16 @@ the instance is neither hidden nor spawned again.
194
410
 
195
411
  ## OKF v2 source jobs
196
412
 
413
+ > **oats.okf 4.0.0:** a source and its job exist only where harvest is
414
+ > effectively on (`harvest: on|off`, default off), and the job spawns the
415
+ > harvester package soul. The review of its PR runs as a **trigger**, run by
416
+ > the same host tick: a workspace file (`oats-triggers/okf-harvest-review.yaml`
417
+ > in a member repo, `kind: oats-trigger`, with `runsOn` and `owner`), or a
418
+ > local one in this file. See
419
+ > [knowledge.md](knowledge.md#knowledge-operations), [Triggers](#triggers) and
420
+ > [Workspace triggers and schedules](#workspace-triggers-and-schedules); the
421
+ > trigger commands are `oats trigger …`.
422
+
197
423
  The [prepared OKF v2 runtime](knowledge.md) registers **one command job per
198
424
  source**, not a fleet sweep or a home-bound operation job. It runs from stable
199
425
  deployment context with argv equivalent to:
@@ -196,9 +196,13 @@ refused (`E_INSTANCE_NAME_TAKEN`).
196
196
 
197
197
  From a deployment (where `oats-local.yaml` is), a spawn: reads the local file →
198
198
  discovers the workspace over its remotes and confirms membership → finds the
199
- soul among the confirmed members (or `external:`; an ambiguous bare name is
200
- `E_SOUL_AMBIGUOUS` — say `<repo>/<soul>`) → fetches the soul's source into
201
- `<agents-root>/<soul>/souls/<commit12>/` at its commit (the home links that
199
+ soul among the confirmed members, `external:` souls and the locked packages'
200
+ souls (an ambiguous bare name is `E_SOUL_AMBIGUOUS`, naming each qualified
201
+ form: `<member>/<soul>` or `<package>/<soul>`; a soul listed in
202
+ `oats-local.yaml` `souls.disabled` is `E_SOUL_DISABLED`) → fetches the soul's
203
+ source into `<agents-root>/<soul>/souls/<commit12>/` at its commit (a package
204
+ soul at the locked commit, verified against the lock's digest — see
205
+ [package souls](packages.md#package-souls); the home links that
202
206
  directory; `<agents-root>/<soul>/soul` points at the current one) → resolves
203
207
  every capability by
204
208
  `from:` (member = latest, package = locked) → creates the home →
@@ -512,14 +516,12 @@ Default layout:
512
516
  docs-expert/ # a workspace soul, defined in a member repository's
513
517
  souls/<commit12>/ # souls/docs-expert/ and copied here per commit
514
518
  instances/
515
- memory-harvest/ # a capability-defined agent: only instances/, no soul
516
- instances/
517
519
  ```
518
520
 
519
- A capability-defined agent (declared by a package or member module, such as
520
- the OKF harvester) homes under the agents root exactly like a soul; its
521
- directory holds only `instances/`. A name that is both a workspace soul and a
522
- capability agent is ambiguous (`E_SOUL_AMBIGUOUS`).
521
+ Every agent is a soul — a member soul, or a package soul homed under
522
+ `agents/<package>--<soul>/`. A capability-defined agent (a manifest's
523
+ `agents:`) was removed in 0.29.0; a home an earlier kernel left for one (its
524
+ directory holds only `instances/`) is still listed and retirable.
523
525
 
524
526
  There are no local souls. A soul is a member repository's `souls/<name>`
525
527
  (`soul.yaml` + `AGENTS.md`); author it there and run `oats sync`. OATS 0.25
@@ -59,8 +59,8 @@ members: # repo refs, NO @revision (E_WORKSPAC
59
59
  - git:github.com/acme/tools # a member that ALSO publishes a package (see below)
60
60
 
61
61
  packages: # the ONLY versioned things
62
- oats.framework: v1.1.3 # bare version → resolves through the official catalog
63
- oats.okf: v2.1.5
62
+ oats.framework: v1.3.0 # bare version → resolves through the official catalog
63
+ oats.okf: v4.0.0
64
64
  acme.tools: git:github.com/acme/tools@v0.4.0 # outside the catalog → git:<repo>@<tag|OID>; still a package
65
65
 
66
66
  teams: # labels, declared once so they cannot drift
@@ -168,9 +168,18 @@ settings: # host-owned values the manifests ask
168
168
  bindings-file: /Users/ana/.oats/okf-bindings.json
169
169
  state-dir: /Users/ana/.oats/okf
170
170
  souls:
171
- disabled: [data-analyst] # not run on this machine
171
+ disabled: [data-analyst] # not run on this machine (E_SOUL_DISABLED); oats.okf/knowledge-harvester names a package soul
172
+ host:
173
+ name: ana-laptop # this machine's name: runs the workspace triggers/schedules whose runsOn names it
174
+ triggers:
175
+ disabled: [knowledge/okf-harvest-review] # workspace triggers this host does not run (oats trigger disable)
176
+ schedules:
177
+ disabled: [platform/nightly-digest] # workspace schedules this host does not run (oats schedule disable)
172
178
  ```
173
179
 
180
+ `host`, `triggers.disabled` and `schedules.disabled` (0.29.0) are machine facts:
181
+ see [schedules.md#workspace-triggers-and-schedules](schedules.md#workspace-triggers-and-schedules).
182
+
174
183
  See [configuration.md](configuration.md). `oats-config.yaml` no longer exists.
175
184
 
176
185
  ### `oats-lock.json` — lock v3
@@ -230,6 +239,12 @@ read; the soul gets no member-tier capabilities of its own repo; it is
230
239
  "source-complete" (its skills travel with it) and the workspace's defaults fill
231
240
  its slots. An `external[].team` overrides the soul's own `team`.
232
241
 
242
+ **Package souls.** A package may ship souls (`souls:` in `oats-package.json`,
243
+ 0.28.0): they are listed from the lock for each package the workspace declares,
244
+ named `<package>/<soul>` (a bare name when unique), resolved like any soul
245
+ (`from: here` = their own package at the locked commit) and trusted as the
246
+ package is. See [packages](packages.md#package-souls).
247
+
233
248
  ## Member tier vs package tier — the non-collapse rule
234
249
 
235
250
  A repository may be a **member** (it completed the handshake; its `souls/*` and