@awebai/oats 0.22.0 → 0.22.1

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 (41) hide show
  1. package/README.md +30 -47
  2. package/bin/oats.mjs +11 -6
  3. package/capabilities/oats-authoring/LICENSE +21 -0
  4. package/capabilities/oats-authoring/oats-package.json +11 -0
  5. package/capabilities/oats-authoring/oats.json +4 -4
  6. package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +63 -0
  7. package/capabilities/oats-authoring/skills/skill-craft/SKILL.md +109 -0
  8. package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +109 -0
  9. package/capabilities/oats-aweb/oats.json +6 -6
  10. package/capabilities/oats-aweb/skills/LICENSE +21 -0
  11. package/capabilities/oats-aweb/skills/VENDORED.md +26 -0
  12. package/capabilities/oats-aweb/skills/aweb-identity/SKILL.md +201 -0
  13. package/capabilities/oats-aweb/skills/aweb-messaging/SKILL.md +161 -0
  14. package/capabilities/oats-aweb/skills/aweb-messaging/references/messaging-scenarios.md +61 -0
  15. package/capabilities/oats-aweb/skills/aweb-team-membership/SKILL.md +328 -0
  16. package/capabilities/oats-aweb/skills/aweb-team-membership/references/team-membership-reference.md +74 -0
  17. package/capabilities/oats-jira/oats.json +1 -1
  18. package/capabilities/oats-linear/oats.json +1 -1
  19. package/capabilities/oats-okf/agents/memory-harvest/soul.yaml +6 -0
  20. package/capabilities/oats-okf/bin/oats-okf.mjs +82 -51
  21. package/capabilities/oats-okf/oats.json +4 -1
  22. package/capabilities/oats-review/oats.json +1 -1
  23. package/docs/2026-09-03-architecture-proposal.md +642 -0
  24. package/docs/first-team-demo.md +87 -0
  25. package/docs/first-team.md +179 -0
  26. package/docs/integrations.md +83 -65
  27. package/docs/layers.md +356 -80
  28. package/docs/migration-from-oas.md +80 -116
  29. package/docs/release-notes/v0.22.1.md +106 -0
  30. package/lib/core.mjs +255 -363
  31. package/package.json +1 -1
  32. package/packages/record/README.md +76 -16
  33. package/packages/record/docs/turn-record-sot.md +1 -1
  34. package/packages/record/lib/store.mjs +207 -43
  35. package/capabilities/oats-aweb/package.json +0 -20
  36. package/capabilities/oats-jira/package.json +0 -25
  37. package/capabilities/oats-linear/README.md +0 -234
  38. package/capabilities/oats-linear/package.json +0 -29
  39. package/capabilities/oats-linear/test/oats-linear.test.mjs +0 -168
  40. package/capabilities/oats-okf/package.json +0 -22
  41. /package/capabilities/oats-okf/agents/{memory-harvest.md → memory-harvest/AGENTS.md} +0 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "capability": "oats.review",
3
3
  "version": "1.2.0",
4
- "compatibility": { "oats": ">=0.16.0" },
4
+ "compatibility": { "oats": ">=0.19.0" },
5
5
  "description": "Post-commit review discipline: a fresh reviewer agent (defined by this capability) reviews each new commit's diff, reports its verdict to its spawner over the deployment's messaging layer (or in its transcript when there is none), and retires — plus the shared developer delivery discipline.",
6
6
  "requires": [],
7
7
  "agents": ["agents/reviewer"],
@@ -0,0 +1,642 @@
1
+ # OATS architecture proposal: components, contracts, and what may be replaced
2
+
3
+ Status: proposal, for review. Not shipped behavior.
4
+
5
+ Date: 2026-09-03
6
+
7
+ Decision owner: Pepe
8
+
9
+ The component model in this document is Pepe's and Juan's, as
10
+ stated on 2026-09-03. The contract analysis, simplifications, and
11
+ consequences are the oats workspace's first-principles reading of
12
+ that model. Where this document and a shipped doc disagree about
13
+ *current* behavior, the shipped doc is right; where they disagree
14
+ about *direction*, this document is the proposal.
15
+
16
+ The turn record is one capture implementation in this model,
17
+ not its center. The clothes model survives only as an optional
18
+ realization artifact. `docs/layers.md` remains a correct
19
+ description of the shipped five layers; this document
20
+ generalizes it.
21
+
22
+ ## Goal
23
+
24
+ An architecture that expands and adapts: new runtimes, new places
25
+ to run them, new knowledge formats, new task trackers, new
26
+ communication systems, new ways to store souls, new soul types,
27
+ each added without changing OATS.
28
+
29
+ The single design rule that produces that property:
30
+
31
+ > Every main component is replaceable by another that offers the
32
+ > same contract, with the exception of OATS itself. OATS knows
33
+ > only contracts.
34
+
35
+ Nothing in the model mentions a repository. A repository is one
36
+ place a soul can be stored and one thing an instance can work
37
+ on. Both are behind contracts. A soul could equally be
38
+ instantiated in a provider of autonomous agents that has no
39
+ filesystem at all.
40
+
41
+ A constraint that holds across every seam (Pepe, 2026-09-04): it
42
+ must remain possible to run everything filesystem-based and
43
+ fully local, meaning with no dependency on a hosted service that
44
+ the operator cannot replace. Real communication needs a server;
45
+ a self-hosted aweb server on localhost satisfies the constraint,
46
+ and that is how the reference deployment runs today. What OATS or
47
+ aweb must hold on *their* servers, and the traffic that must pass
48
+ through them, is to be minimized. Each contract below is read
49
+ with that constraint, and the organizational-sovereignty strategy
50
+ makes the same demand from the other side.
51
+
52
+ An acceptance criterion follows from it (Juan, 2026-09-05): a
53
+ default installation after the migration produces the same
54
+ composed instructions, launch commands, home layout, instance
55
+ metadata, and retirement results as v0.22.0. Everything the new
56
+ architecture adds is additive and off by default, and the golden
57
+ fixtures (step 1) are the proof: a refactor step lands only if
58
+ they are byte-unchanged, and a deliberate change is a re-approved
59
+ golden.
60
+
61
+ ## The model
62
+
63
+ ### Soul
64
+
65
+ A soul has:
66
+
67
+ - `AGENTS.md`: the operating definition, runtime-neutral by
68
+ construction;
69
+ - optional **finetuning**;
70
+ - optional **clothes**;
71
+ - its **capabilities**.
72
+
73
+ Finetuning and clothes are optimizations that may or may not pay off. They
74
+ are bracketed on purpose (see "Realization artifacts" below).
75
+
76
+ ### Capability
77
+
78
+ A capability is a set of scripts, skills, and docs. Skills come
79
+ from capabilities. An OATS installation needs capabilities for
80
+ these contracts, possibly scoped by soul type:
81
+
82
+ - **Knowledge access**;
83
+ - **Task management**;
84
+ - **Communication**;
85
+ - optionally, **capture** of ephemeral state such as notes or conversation
86
+ history, which is also the input from which clothes may be derived.
87
+
88
+ The user selects which capability fills each contract, usually after
89
+ installing it from a package.
90
+
91
+ ### Harvester
92
+
93
+ A harvester knows how to convert ephemeral state into organizational
94
+ knowledge, in the format the knowledge-access capability reads.
95
+
96
+ ### Package
97
+
98
+ A package groups cohesive souls and capability sets.
99
+
100
+ ### OATS
101
+
102
+ OATS knows how to:
103
+
104
+ - create instances for any given (soul, runtime);
105
+ - run the runtimes in supported platforms;
106
+ - install the task, knowledge, and communication capabilities;
107
+ - instantiate and run the harvester;
108
+ - install souls and capabilities from packages;
109
+ - manage all of the above with a clear GUI.
110
+
111
+ ## The architecture is its seams
112
+
113
+ A component list says what exists. The architecture is the set of
114
+ contracts between them. The table below names each seam, what
115
+ must be able to vary across it, and the two implementations that
116
+ would prove the contract is real. Where only one implementation
117
+ can be named, the contract is still a description of that one;
118
+ those rows are the work.
119
+
120
+ | Seam | What varies behind it | Implementation today | Second implementation |
121
+ |------------------------|--------------------------------------------------------|-----------------------------------------|-----------------------------------|
122
+ | Soul format | how a specialist is defined | `soul.yaml` + `AGENTS.md` + skills | same format, different store |
123
+ | Soul type | which capabilities and knowledge scope a soul may have | config agent types | (formalize; see below) |
124
+ | Capability manifest | what a capability contributes and requires | `oats.json` | exists, stable |
125
+ | Knowledge contract | format read by instances and written by harvest | `oats.okf` | plain-markdown or wiki capability |
126
+ | Task contract | where shared work state lives | `oats.jira`, `oats.linear` | beads, GitHub Issues |
127
+ | Communication contract | how instances are reached and reach others | `oats.aweb` | Slack, A2A gateway |
128
+ | Capture contract | format of ephemeral state | OKF `notes/` + turn record | either alone |
129
+ | Runtime provider | how a realization bundle becomes a running agent | Pi or Claude Code in local tmux | a hosted agent provider |
130
+ | Soul store | where souls are kept and versioned | git repository under `agents/` | package; provider registry |
131
+ | Work target | what an instance operates on | worktree, checkout, attached, workspace | none (a mail-only agent) |
132
+ | Package | acquisition, version, integrity, trust | git-acquired, hash-locked | local path (exists) |
133
+ | Lifecycle events | when capabilities may act | scaffold, spawn, retire | + harvest |
134
+
135
+ The "Second implementation" column names an implementation that
136
+ would *prove* the contract, not a planned replacement. Today's
137
+ defaults stay the defaults: OKF for knowledge, aweb for
138
+ communication, the git repository for the soul store, the four
139
+ work modes for the work target. `none` is an additional work
140
+ target, not a substitute for any mode.
141
+
142
+ Two seams on this table have one real implementation: runtime
143
+ provider and soul store. They are where expandability is
144
+ currently a promise rather than a property.
145
+
146
+ ## The contracts
147
+
148
+ ### Soul format
149
+
150
+ A soul declares *which contracts it needs*, never which
151
+ implementation fills them. `AGENTS.md` speaks of "your task
152
+ layer" and "your knowledge"; the capability's injected block says
153
+ what that is in this installation. This is what lets one packaged
154
+ soul run in an installation bound to Jira and another bound to
155
+ Linear, and it is already how injection works.
156
+
157
+ A soul is runtime-neutral as an artifact. Anything
158
+ runtime-specific derived from it is a realization artifact, not
159
+ soul content.
160
+
161
+ ### Soul type
162
+
163
+ A soul type is the policy unit. It decides which capabilities a
164
+ soul of that type receives, what knowledge it may read, and
165
+ whether it may write knowledge. Authority lives here, not in a
166
+ permission system.
167
+
168
+ Consequences:
169
+
170
+ - a reviewer is a soul type, not a component;
171
+ - a harvester is a soul type permitted to write knowledge;
172
+ - the GUI's natural organizing axis is type;
173
+ - a type carries the soul's communication **reach**: whom its
174
+ instances may address, and who may address them.
175
+
176
+ The last point is a requirement from Pepe (2026-09-04). In a
177
+ shared team, every engineer's agents can live in one team, and
178
+ each engineer decides whether their agents may talk to agents
179
+ owned by other engineers or only to their own; a further type may
180
+ talk to agents outside the organization. The type expresses the
181
+ policy; the communication capability enforces it. Spawning into
182
+ the correct team is deployment configuration (today the `team:`
183
+ block that the messaging integration reads), and the flow for
184
+ choosing it must be explicit, never inferred from where a command
185
+ was run.
186
+
187
+ The policy is one field, `reach`, whose values form a ladder in
188
+ which each level includes everything below it (names proposed by
189
+ the OAS side, 2026-09-04):
190
+
191
+ ```text
192
+ reach: owner # only agents owned by the same human
193
+ reach: team # any agent in the deployment's team
194
+ reach: org # any team in the same organization
195
+ reach: external # agents outside the organization
196
+ ```
197
+
198
+ The ladder mirrors aweb's addressing tiers: a plain alias reaches
199
+ the team, a team-qualified alias the organization, a namespace
200
+ address the outside. `owner` maps to a contacts-only inbound mode
201
+ that aweb does not ship yet (`aweb-abhx`; see the open questions).
202
+ "No communication at all" is not a fifth level; that is the
203
+ messaging slot set to `none`, a layer selection rather than a
204
+ policy. `reach` governs both directions by default, with the
205
+ inbound side enforced by the communication capability; if a case
206
+ ever needs asymmetric policy it splits into `reach.out` and
207
+ `reach.in` then, not now.
208
+
209
+ Today this is the config's agent-type targeting. Formalizing it
210
+ means naming the type in the soul, letting packages ship types,
211
+ and letting the knowledge contract read the type for scoping.
212
+
213
+ ### Capability manifest and lifecycle events
214
+
215
+ A capability declares: its slot, if it fills one; what it
216
+ contributes (skills, instruction blocks, commands, environment);
217
+ which lifecycle events it hooks; and what it requires from the
218
+ host. The event list is the API that makes capabilities
219
+ composable and should change rarely. Proposed list: `scaffold`,
220
+ `spawn`, `retire`, `harvest`.
221
+
222
+ ### The slot contracts
223
+
224
+ These carry the architecture and should be written as contracts,
225
+ not as descriptions of the default packages.
226
+
227
+ **Knowledge access** has two sides. *Read*: an instance can find
228
+ and consult organizational knowledge within its type's
229
+ scope. *Write*: a permitted soul can promote into it. The format
230
+ is the capability's; the two sides are the contract. Custody
231
+ scoping (soul-shared, workspace overlay, repository overlay)
232
+ belongs to this contract so that repository-specific knowledge
233
+ cannot leak to a cross-repository soul.
234
+
235
+ What the write side accepts is doctrine, and Pepe's position
236
+ (2026-09-04) sharpens `docs/knowledge-theory.md`. The line is
237
+ decision versus description. A description of how the code fits
238
+ together goes stale and competes with the code; that is the slop
239
+ automatic memory accumulates, and the write side rejects it. A
240
+ decision, what was chosen, what was rejected, and why, cannot be
241
+ derived from code; without the record an agent will refactor
242
+ toward the rejected alternative, so the write side accepts it.
243
+ Also accepted: inspiration genealogy ("took this from X, rejected
244
+ Y because Z", the strongest case for design souls), process
245
+ lessons, and maintained, timestamped, superseded-on-change slow
246
+ state about an area. Rejected: task residue (PR numbers,
247
+ half-done plans, point-in-time environment facts), which dies
248
+ with the instance. One home per decision; split-brain comes from
249
+ copies. For non-coding specialists (reviewers, coordinators,
250
+ domain experts) none of their knowledge is re-derivable from a
251
+ repository: those souls are almost pure knowledge, and the
252
+ code-is-truth critique does not apply to them. The harvester's
253
+ promotion judgment encodes this.
254
+
255
+ **Task management**: where shared work state lives, and how an
256
+ instance claims, updates, blocks, and completes work. Verdicts
257
+ and review outcomes are task records, which is how verification
258
+ enters the model without a component.
259
+
260
+ **Communication**: how an instance becomes reachable and reaches
261
+ others. The contract must state whether the address can outlive
262
+ the instance. For a durable specialist the answer should be yes,
263
+ realized however the implementation chooses (aweb: a soul-level
264
+ identity served through per-instance grants). Without this, a
265
+ hosted provider cannot host "the specialist you can always write
266
+ to." The contract also carries the fully-local constraint: two
267
+ instances on one machine must be able to exchange messages
268
+ through a server the operator runs, with no hosted dependency.
269
+ The implementation enforces the soul type's `reach` in both
270
+ directions (for aweb: addressing tiers outbound, inbound mode and
271
+ contacts inbound).
272
+
273
+ **Capture**: the format of ephemeral state. Notes are the agent's
274
+ own report of what mattered; a captured session is ground
275
+ truth. Both satisfy the contract; the harvester may consume
276
+ either or both. Clothes are one optional consumer of the same
277
+ contract.
278
+
279
+ ### Runtime provider
280
+
281
+ OATS hands a **realization bundle** to a provider and gets back
282
+ an instance handle:
283
+
284
+ ```text
285
+ bundle
286
+ instructions (composed AGENTS.md)
287
+ skills (the exact materialized set)
288
+ capability scripts and environment
289
+ task
290
+ realization artifacts for this runtime, if any (finetune ref, compiled clothes)
291
+
292
+ handle
293
+ observe (status, the session where capture can find it)
294
+ message / steer, where the provider supports it
295
+ stop
296
+ ```
297
+
298
+ Whether the bundle becomes Pi in tmux, Claude Code in a
299
+ container, or a hosted agent with no filesystem is the provider's
300
+ business. Define this contract from what Pi and Claude Code
301
+ actually need today, and keep it that small. The 2026-08-15
302
+ implementation plan failed by abstracting execution targets
303
+ beyond that before a second provider existed.
304
+
305
+ #### Worked example: today's Pi launch as bundle and handle
306
+
307
+ Nothing below is new behavior; it is what `spawnInstance` does
308
+ now, named by the contract's parts.
309
+
310
+ The **bundle** OATS assembles under the instance home:
311
+
312
+ - instructions: the composed `AGENTS.md` (soul text, kernel
313
+ block, home/work boundary, work-mode block, capability blocks);
314
+ - skills: the exact materialized tree at `.agents/skills/`,
315
+ with `.claude/skills` linking to it;
316
+ - capability environment and launch arguments: what the spawn
317
+ hooks returned, for example `AWEB_IDENTITY_HOME` and the pi or
318
+ claude launch flags the aweb capability contributes;
319
+ - task: `TASK.md`;
320
+ - a resolved model preference;
321
+ - realization artifacts: none today. This slot is where a
322
+ provider-specific derivative would live if one existed, for
323
+ example a compiled native session file (what `oats experimental
324
+ spawn` produces) or a finetune reference, cached per (soul,
325
+ runtime). It is empty by default and the contract works with it
326
+ empty.
327
+
328
+ The **Pi provider** turns that into a process: it resolves the
329
+ `pi` binary, verifies the runtime packages active capabilities
330
+ require (the aweb pi package, for instance), and builds the
331
+ command `pi --no-skills --skill <home>/.agents/skills
332
+ --no-context-files --no-prompt-templates --append-system-prompt
333
+ <home>/AGENTS.md --approve --name <instance> --model <m> @TASK.md
334
+ <hook args>` with `OATS_INSTANCE` and `OATS_INSTANCE_HOME` in the
335
+ environment. The **Claude Code provider** builds `claude --model
336
+ <m> <hook args> -- "$(cat TASK.md)"` and relies on the
337
+ `CLAUDE.md` and `.claude/skills` links instead of flags. Same
338
+ bundle, two providers.
339
+
340
+ The **platform** is tmux: create the session if absent, open a
341
+ window named after the instance with the home as its working
342
+ directory, run the command, drop to a shell when it exits.
343
+
344
+ The **handle** is what comes back and is recorded in
345
+ `instance.json`: today the `tmux` object (session, window,
346
+ socket) and the persisted command line. Read through the
347
+ contract: *observe* is "does the window exist" plus the native
348
+ transcript location capture reads (`~/.pi/agent/sessions/...`);
349
+ *steer* is attaching to the pane or sending keys; *stop* is
350
+ killing the window, which retire does. A hosted provider would
351
+ return a different handle with the same three verbs.
352
+
353
+ Two things follow. Runtime and platform are separable, which is
354
+ why the migration plan extracts them in two steps and why the
355
+ provider must not be entangled with tmux. And the work-mode
356
+ setup script (`work-modes.worktree.setup`, run inside each fresh
357
+ worktree) belongs to the work target, not to the provider.
358
+
359
+ ### Soul store and work target
360
+
361
+ These are separate parameters of instantiation. Today one
362
+ filesystem location under `agents/` decides three things at once:
363
+ who the soul is, where its knowledge is kept, and what its
364
+ instances work on. The 2026-07-30 product boundary review named
365
+ this as the foundational gap. Separating the two seams is what
366
+ removes the repository from the architecture.
367
+
368
+ An instance is therefore:
369
+
370
+ ```text
371
+ instance = (soul, runtime provider, work target?, task)
372
+ ```
373
+
374
+ Any of the four may be substituted; the work target may be
375
+ absent. The four work modes stay exactly as they are; the
376
+ distinction between an isolated worktree and working directly on
377
+ a checked-out branch matters and is kept. `none` is added for
378
+ instances that operate on nothing, such as a mail-only agent; it
379
+ replaces no mode. The per-mode environment setup script remains
380
+ part of the work-target contract.
381
+
382
+ ### Package
383
+
384
+ Acquisition, exact version, integrity, dependency closure,
385
+ executable trust. Exists; a package may now also ship soul
386
+ types.
387
+
388
+ ## Three simplifications
389
+
390
+ **The harvester is a soul.** Its input is the capture contract
391
+ and its output is the knowledge contract's write side. Only the
392
+ knowledge capability knows the knowledge format, so the harvester
393
+ belongs to the knowledge package. OATS does not run a special
394
+ harvester; it spawns a soul of a type permitted to write
395
+ knowledge, on the `harvest` lifecycle event. This is already how
396
+ the OKF `memory-harvest` agent runs. A new knowledge format
397
+ brings its own judge.
398
+
399
+ **Soul type is the policy unit.** Stated above; repeated here
400
+ because it removes two would-be components (a reviewer, a
401
+ permission model).
402
+
403
+ **Contracts and bootstrap skills in OATS; implementations in packages.**
404
+ OATS ships the slot contracts and the skill that
405
+ teaches an agent the shape of each. Implementations are
406
+ packages. OATS may bundle a minimal default per slot so a fresh
407
+ installation is a complete product with nothing acquired. This
408
+ answers "meta-capabilities or OATS basic skills": both, split by
409
+ that line, and it keeps OATS the only irreplaceable component
410
+ while ensuring it never knows a tracker, a mail system, or a
411
+ knowledge format.
412
+
413
+ ## Realization artifacts: finetuning and clothes
414
+
415
+ Both are runtime-specific by nature: a finetune is per provider,
416
+ a compiled session is per harness. Inside the soul they would
417
+ break "any runtime." Attached to a (soul, runtime) realization
418
+ and cached there, they cost nothing and can be dropped if they
419
+ never pay off. That is the property wanted for a bet whose
420
+ outcome is unknown.
421
+
422
+ Two different things have been called clothes and should be named
423
+ apart:
424
+
425
+ - **curated exemplars** shipped and reviewed with a soul, a
426
+ committed artifact needing no selection machinery;
427
+ - **spawn-time selection** from captured history, the
428
+ experimental `oats experimental dress` path.
429
+
430
+ Only the first is a soul-adjacent artifact. The second stays
431
+ experimental until its experiment reads out, and this
432
+ architecture does not depend on it.
433
+
434
+ ## Where the turn record fits
435
+
436
+ The turn record is one implementation of the capture contract,
437
+ and a good one: passive, verbatim, content-addressed,
438
+ searchable. `recall` is a tool over it. `dress` is an optional
439
+ consumer. `attend` belongs to the communication contract's
440
+ implementation, not to OATS. The specification and vectors stay
441
+ where they are.
442
+
443
+ What changes is status: the bookshelf's turn-record documents
444
+ should be re-marked from "accepted architecture direction" to
445
+ "capture substrate and experiment." The repository README should
446
+ lead with the soul, capability, and package model and present the
447
+ record as the default capture.
448
+
449
+ ## Consequences for the current implementation
450
+
451
+ Already matching the proposal, no change: souls and `AGENTS.md`;
452
+ capability manifests and hooks; packages and locks; the OKF
453
+ harvester as a capability-defined agent; exclusive slots for
454
+ knowledge, messaging, tasks; the Desktop as the management
455
+ surface.
456
+
457
+ Changes, none of which rewrites anything:
458
+
459
+ 1. Write the four slot contracts as contracts, replacing the
460
+ package-shaped descriptions in `docs/layers.md` and
461
+ `docs/integrations.md`.
462
+ 2. Formalize soul type: named in `soul.yaml`, shippable in
463
+ packages, read by the knowledge contract for scoping.
464
+ 3. Add `harvest` as a lifecycle event; route `oats okf harvest`
465
+ through it.
466
+ 4. Make capture a slot; `packages/record` becomes its default
467
+ implementation. Capture means session capture: OKF `notes/`
468
+ are the knowledge capability's own instance memory, not a
469
+ capture implementation, and the harvester reads both. Feed
470
+ the harvester from the capture contract (epic `aweb-abfz`).
471
+ 5. Define the runtime-provider contract from the current Pi and
472
+ Claude Code launch paths, and put both behind it. No third
473
+ provider until a real one asks.
474
+ 6. Separate soul store from work target in instantiation. First
475
+ step: an instance may declare its work target independently of
476
+ where its soul is stored, which the `workspace` mode already
477
+ half-does.
478
+ 7. Add a realization cache per (soul, runtime) for finetune
479
+ references and compiled exemplars, empty by default.
480
+
481
+ ## The replaceability test
482
+
483
+ For each seam, name two implementations that both satisfy the
484
+ contract and show a soul running unchanged behind each. Where
485
+ that cannot be done, the contract is unfinished. The two seams
486
+ that fail the test today are runtime provider and soul store;
487
+ they should be the first contracts written, and they should be
488
+ written narrowly.
489
+
490
+ ## Migration plan
491
+
492
+ The goal of the migration is the functionality OATS has today,
493
+ under the seams above. Each step is one reviewable landing that
494
+ leaves the test suite green and the output fixtures identical,
495
+ except where a step deliberately adds a surface.
496
+
497
+ ### Where the seams sit in the code today
498
+
499
+ The kernel is about 10k lines in `lib/core.mjs` and
500
+ `bin/oats.mjs`, with the package engine partly in
501
+ `lib/packages.mjs`.
502
+
503
+ | Seam | Where it lives today | State |
504
+ |---|---|---|
505
+ | Slot contracts | `LAYERS` plus `resolveOatsConfig` | data-driven; capture is one entry away |
506
+ | Soul type | `agent-types` in config, `type:` in `soul.yaml`, `soulTypeOf` | exists; not exported to hooks, not shippable by packages |
507
+ | Capability manifest, hooks | `runLifecycleHooks`, the manifest schema | stable; three events |
508
+ | Harvester | `memory-harvest` capability agent spawned via `oats spawn --json` | already a soul; triggered by an instance command, not an event |
509
+ | Runtime provider | about 150 lines inside `spawnInstance` plus the model, binary, and runtime-package helpers | entangled with tmux and the home layout |
510
+ | Platform | tmux calls in spawn, retire, status, and the Desktop | one implementation, no interface |
511
+ | Soul store | `findAgent`, `listAgents`, `teamAgentRoots`, `findCapabilityAgent` | three lookups, all filesystem |
512
+ | Work target | `resolveWorkMode` plus the mode branches in spawn, retire, baseline, quarantine | four modes; retire safety depends on them |
513
+ | Package | `lib/packages.mjs` and the engine half of core | the most tested seam; not touched |
514
+
515
+ Two things make "same functionality" checkable. The test suite
516
+ is behavioral, and the externally meaningful outputs are few:
517
+ the composed `AGENTS.md`, `instance.json`, `TASK.md`, the launch
518
+ command line, and the retirement outcome. Frozen as golden
519
+ fixtures first, every extraction below is verified against
520
+ bytes rather than against intent.
521
+
522
+ ### The sequence
523
+
524
+ 1. **Golden fixtures.** For Pi and Claude Code, each work mode,
525
+ knowledge none and okf, messaging none and a stub: snapshot
526
+ the home layout, the composed instructions, instance metadata
527
+ minus timestamps, the launch command, and the retire
528
+ result. This is the contract for everything after it.
529
+ 2. **Contracts on paper.** Rewrite `docs/layers.md` and
530
+ `docs/integrations.md` as the four slot contracts, the
531
+ lifecycle events including `harvest`, runtime provider, soul
532
+ store, and work target. Runs in parallel with step 1.
533
+ 3. **Split core by responsibility, no behavior change.** Config
534
+ and scopes, package engine, souls, composition, hooks,
535
+ instances. `lib/core.mjs` stays as the re-export facade so
536
+ the Desktop and the tests keep their import. A pure move,
537
+ checked by the fixtures.
538
+ 4. **Extract runtime providers.** A registry with one module
539
+ each for Pi and Claude Code: binary resolution, model
540
+ preference, runtime-package verification, the launch command
541
+ from a realization bundle, and the posture record. Spawn asks
542
+ the provider and no longer branches on the runtime name.
543
+ 5. **Extract the platform.** A tmux module with launch, alive,
544
+ stop, and list. Spawn, retire, status, and the Desktop's tmux
545
+ target reads go through it. The `tmux` field in
546
+ `instance.json` stays; a `handle` carrying a kind is added
547
+ beside it, never instead of it, so the Desktop keeps working.
548
+ 6. **Harvest as a lifecycle event.** Accept `harvest` in
549
+ manifests, add `oats harvest` to run the active capabilities'
550
+ harvest hooks, have oats-okf declare its hook, and update the
551
+ injected instruction. `oats okf harvest` keeps working. This
552
+ spans repositories and touches the frozen package-runtime
553
+ boundary, so it goes through the coordinator to Pepe.
554
+ 7. **Soul type export.** Pass the type to hooks and dispatched
555
+ commands (`OATS_SOUL_TYPE`), let packages declare agent
556
+ types, show it in doctor. Knowledge scoping by type then
557
+ becomes okf's work, reading the type it is given. Same
558
+ boundary rule as step 6.
559
+ 8. **Capture as a slot.** Add `capture` to the slot list, wrap
560
+ the record package as a bundled framework-trusted capability
561
+ whose spawn hook records the instance-to-session mapping, and
562
+ keep `oats setup`, `oats capture`, and `oats recall` as
563
+ kernel aliases resolving to the active capture
564
+ capability. The record-fed harvester (`aweb-abfz`) becomes
565
+ okf work over this contract.
566
+ 9. **Soul store interface.** Unify the three finders behind one
567
+ list of stores with the filesystem as the single
568
+ implementation; the instance home root becomes the store's
569
+ answer. This is what a provider registry or a Library later
570
+ plugs into.
571
+ 10. **Extract work targets and add `none`.** One module per
572
+ mode, with retirement baseline and inspection moving
573
+ alongside. Last, because retire safety and quarantine sit on
574
+ it; the retirement fixtures from step 1 exist for this step.
575
+ 11. **Docs and release.** The README leads with the model and
576
+ presents the record as the default capture; the bookshelf
577
+ turn-record documents are re-marked; a release whose only
578
+ user-visible additions are `oats harvest`, the capture slot,
579
+ and the `none` work mode.
580
+
581
+ ### Deliberately not in the sequence
582
+
583
+ - a second runtime provider, until a real one asks;
584
+ - a realization cache for finetunes or exemplars, until
585
+ something consumes one;
586
+ - minimal bundled defaults per slot;
587
+ - any change to the package engine.
588
+
589
+ The point of the sequence is that each seam ends with one
590
+ implementation behind a real interface and a fixture proving
591
+ it, so the second implementation is a new module rather than a
592
+ refactor.
593
+
594
+ ### Size and parallelism
595
+
596
+ Three to four weeks single-threaded. After step 3, the pairs
597
+ (4, 5), (6, 7), and step 8 are independent and can run on
598
+ separate agents. Steps 6 and 7 need the maintainer's ruling on
599
+ the package-runtime boundary before they start.
600
+
601
+ ## Open questions
602
+
603
+ - Does the communication contract *require* a soul-level durable
604
+ address, or only permit one?
605
+ - Is capture a fourth exclusive slot, or an additive capability
606
+ that several packages may provide at once?
607
+ - What does a provider registry need from the soul-store contract
608
+ that a git repository does not?
609
+ - What triggers `harvest`: commit, retire, schedule, or the
610
+ capture capability reporting new state?
611
+ - How does a runtime provider without a filesystem receive
612
+ instructions and skills? This decides whether the bundle is
613
+ files or a document.
614
+ - How do config agent types and soul types unify without breaking
615
+ existing configs?
616
+ - Fully local, answered by the aweb coordinator (2026-09-04,
617
+ task `aweb-abhw`): no hop-free path exists; every delivery goes
618
+ through an aweb server, and the self-hosted stack on localhost
619
+ with the reserved `local` namespace is the intended answer. The
620
+ durable mailbox is the point, so hop-free exchange is not on the
621
+ roadmap. The honest gap is weight (aweb plus AWID plus
622
+ PostgreSQL plus Redis); the open decision is whether a
623
+ lightweight single-process local server is needed.
624
+ - `reach: owner`, answered by the aweb coordinator (2026-09-04,
625
+ task `aweb-abhx`, proposal pending Juan's go-ahead): team
626
+ membership is delivery authority, so owner-only reach cannot be
627
+ enforced inside a shared team with today's inbound modes. The
628
+ proposal is `inbound_mode=contacts_only`, exact active contacts
629
+ only with team membership ignored; each engineer adds their own
630
+ agents as mutual contacts and `owner` maps to it directly. No
631
+ per-owner scope in the team model is needed. Until it lands,
632
+ `owner` reads as "contacts_only, pending aweb-abhx".
633
+
634
+ ## Related
635
+
636
+ - [Layers](layers.md), [Capabilities](capabilities.md),
637
+ [Souls and instances](souls-and-instances.md),
638
+ [Knowledge theory](knowledge-theory.md)
639
+ - Bookshelf: `product/2026-07-30-oas-aweb-product-boundary-review.md`,
640
+ `oats/2026-08-18-turn-record-and-tools.md`,
641
+ `oats/2026-08-18-oats-runtime-agents-and-clothes.md`,
642
+ `strategy/2026-09-01-aweb-adoption-and-organizational-sovereignty.md`