@awebai/oats 0.22.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 (112) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +380 -0
  3. package/bin/oats.mjs +3294 -0
  4. package/capabilities/oats-authoring/oats.json +12 -0
  5. package/capabilities/oats-aweb/bin/oats-aweb.mjs +297 -0
  6. package/capabilities/oats-aweb/injects/aweb.md +55 -0
  7. package/capabilities/oats-aweb/oats.json +47 -0
  8. package/capabilities/oats-aweb/package.json +20 -0
  9. package/capabilities/oats-jira/bin/oats-jira.mjs +40 -0
  10. package/capabilities/oats-jira/injects/jira.md +10 -0
  11. package/capabilities/oats-jira/oats.json +22 -0
  12. package/capabilities/oats-jira/package.json +25 -0
  13. package/capabilities/oats-jira/skills/jira-tasks/SKILL.md +176 -0
  14. package/capabilities/oats-linear/README.md +234 -0
  15. package/capabilities/oats-linear/bin/oats-linear-hook.mjs +32 -0
  16. package/capabilities/oats-linear/bin/oats-linear.mjs +344 -0
  17. package/capabilities/oats-linear/injects/linear.md +8 -0
  18. package/capabilities/oats-linear/oats.json +24 -0
  19. package/capabilities/oats-linear/package.json +29 -0
  20. package/capabilities/oats-linear/skills/linear-tasks/SKILL.md +217 -0
  21. package/capabilities/oats-linear/test/oats-linear.test.mjs +168 -0
  22. package/capabilities/oats-okf/agents/memory-harvest.md +25 -0
  23. package/capabilities/oats-okf/bin/oats-okf.mjs +283 -0
  24. package/capabilities/oats-okf/injects/okf.md +62 -0
  25. package/capabilities/oats-okf/oats.json +20 -0
  26. package/capabilities/oats-okf/package.json +22 -0
  27. package/capabilities/oats-okf/skills/memory-harvest/SKILL.md +100 -0
  28. package/capabilities/oats-okf/skills/okf/SKILL.md +107 -0
  29. package/capabilities/oats-okf/skills/okf/scripts/okf-validate.mjs +123 -0
  30. package/capabilities/oats-review/agents/reviewer/AGENTS.md +53 -0
  31. package/capabilities/oats-review/agents/reviewer/soul.yaml +6 -0
  32. package/capabilities/oats-review/injects/review.md +68 -0
  33. package/capabilities/oats-review/oats.json +10 -0
  34. package/capabilities/oats-review/skills/code-review/SKILL.md +44 -0
  35. package/capabilities/oats-review/skills/security-review/SKILL.md +59 -0
  36. package/docs/capabilities.md +505 -0
  37. package/docs/capability-manifest.schema.json +223 -0
  38. package/docs/configuration.md +482 -0
  39. package/docs/conventions.md +73 -0
  40. package/docs/design/desktop-ux-plan.md +362 -0
  41. package/docs/design/package-engine-contract.md +813 -0
  42. package/docs/design/package-runtime-api.md +414 -0
  43. package/docs/desktop-cli-api.md +89 -0
  44. package/docs/desktop-succession.md +51 -0
  45. package/docs/desktop.md +187 -0
  46. package/docs/implementation.md +282 -0
  47. package/docs/integrations.md +123 -0
  48. package/docs/knowledge-theory.md +97 -0
  49. package/docs/knowledge.md +139 -0
  50. package/docs/layers.md +110 -0
  51. package/docs/migration-from-oas.md +122 -0
  52. package/docs/oats-config.schema.json +140 -0
  53. package/docs/oats-lock.schema.json +175 -0
  54. package/docs/oats-package.schema.json +129 -0
  55. package/docs/packages.md +442 -0
  56. package/docs/release-lane.md +132 -0
  57. package/docs/release-notes/v0.18.2.md +79 -0
  58. package/docs/release-notes/v0.18.3.md +63 -0
  59. package/docs/release-notes/v0.18.4.md +65 -0
  60. package/docs/release-notes/v0.18.5.md +66 -0
  61. package/docs/release-notes/v0.18.6.md +87 -0
  62. package/docs/release-notes/v0.19.0.md +186 -0
  63. package/docs/release-notes/v0.19.1.md +24 -0
  64. package/docs/release-notes/v0.19.2.md +32 -0
  65. package/docs/release-notes/v0.19.3.md +16 -0
  66. package/docs/release-notes/v0.19.4.md +12 -0
  67. package/docs/release-notes/v0.20.0.md +89 -0
  68. package/docs/release-notes/v0.22.0.md +180 -0
  69. package/docs/souls-and-instances.md +344 -0
  70. package/injects/framework-workspace.md +7 -0
  71. package/injects/instance-boundary.md +43 -0
  72. package/injects/local-soul.md +19 -0
  73. package/injects/oats.md +11 -0
  74. package/injects/work-attached.md +16 -0
  75. package/injects/work-checkout.md +12 -0
  76. package/injects/work-workspace.md +22 -0
  77. package/injects/work-worktree.md +13 -0
  78. package/lib/core.mjs +6792 -0
  79. package/lib/packages.mjs +1375 -0
  80. package/lib/tmux-config.mjs +35 -0
  81. package/package-catalog.json +44 -0
  82. package/package.json +55 -0
  83. package/packages/record/README.md +151 -0
  84. package/packages/record/bin/capture.mjs +256 -0
  85. package/packages/record/bin/recall.mjs +102 -0
  86. package/packages/record/bin/setup.mjs +281 -0
  87. package/packages/record/bin/turn-record.mjs +47 -0
  88. package/packages/record/docs/turn-record-sot.md +350 -0
  89. package/packages/record/lib/canonical.mjs +151 -0
  90. package/packages/record/lib/capture-aw.mjs +179 -0
  91. package/packages/record/lib/capture-cc.mjs +322 -0
  92. package/packages/record/lib/formats.mjs +296 -0
  93. package/packages/record/lib/ignore.mjs +155 -0
  94. package/packages/record/lib/index-db.mjs +524 -0
  95. package/packages/record/lib/project-aweb.mjs +251 -0
  96. package/packages/record/lib/segments.mjs +186 -0
  97. package/packages/record/lib/store.mjs +447 -0
  98. package/packages/record/lib/tags.mjs +114 -0
  99. package/packages/record/package.json +45 -0
  100. package/packages/record/test/vectors/README.md +34 -0
  101. package/packages/record/test/vectors/aweb-projection-v1.json +231 -0
  102. package/packages/record/test/vectors/journal-merge-v1.json +261 -0
  103. package/packages/record/test/vectors/turn-id-v1.json +152 -0
  104. package/packages/record/test/vectors/validate.mjs +391 -0
  105. package/skills/integration-authoring/SKILL.md +76 -0
  106. package/skills/oats/SKILL.md +151 -0
  107. package/skills/oats-config/SKILL.md +162 -0
  108. package/skills/oats-getting-started/SKILL.md +159 -0
  109. package/skills/oats-packages/SKILL.md +180 -0
  110. package/skills/oats-support/SKILL.md +79 -0
  111. package/skills/skill-craft/SKILL.md +109 -0
  112. package/skills/soul-craft/SKILL.md +109 -0
@@ -0,0 +1,97 @@
1
+ # What belongs in a soul, and what belongs in an instance
2
+
3
+ The knowledge layer's *format* is pluggable in OATS. The ideas below are not.
4
+ They come from asking what memory means for an agent that outlives its
5
+ sessions, and they apply whatever format or tooling you bind — OKF, plain
6
+ markdown, or something else entirely.
7
+
8
+ ## The derivation
9
+
10
+ A **soul** is identity across incarnations. So soul knowledge must be
11
+ **incarnation-invariant** — true and binding for any future instance, on any
12
+ model, in any session. "Durable AND would change what a future instance
13
+ does" is the promotion bar, and it is really an invariance test.
14
+
15
+ An **instance** is one incarnation's work. So instance memory is
16
+ **indexical** — saturated with *I, here, now*. This branch. This blocker.
17
+ This half-done plan. Indexical content cannot live in the soul, because its
18
+ referents die when the instance does.
19
+
20
+ That one distinction decides most placement questions on its own.
21
+
22
+ ## Harvest is de-indexicalization
23
+
24
+ Moving knowledge from instance to soul is not copying files. It is
25
+ rephrasing an insight so it survives its author. "The build broke until I
26
+ cleared the cache" is indexical. "The build caches stale schemas — clear
27
+ `.cache/schemas` after model changes" is invariant. Consolidation succeeds
28
+ when the *I, here, now* is gone and the claim still holds.
29
+
30
+ A useful corollary for any implementation: an instance file's format rigor
31
+ should be proportional to its odds of surviving the instance. Working state
32
+ can be loose. Promotion candidates should be written in soul genre from
33
+ birth.
34
+
35
+ ## Capture and judgment are different jobs
36
+
37
+ Instances should **capture without judging**. Writing an insight down is
38
+ cheap and best done in the moment, by the one who had it. Judging whether it
39
+ clears the promotion bar is expensive and best done deliberately, by
40
+ something that is not in the middle of a task.
41
+
42
+ Splitting the two has a second benefit. Agents that self-censor against a
43
+ half-remembered bar write less. Agents told "capture everything non-obvious,
44
+ judgment is someone else's job" write more, and the judge applies one
45
+ consistent standard across all of them.
46
+
47
+ ## Knowledge consolidates through stages
48
+
49
+ A fresh observation is not yet a lesson. It matures:
50
+
51
+ | Stage | Example | Lives |
52
+ |---|---|---|
53
+ | Working state | "next: fix the failing test" | instance, rewritten freely |
54
+ | Observation | "PATCH with nulls seems to be a no-op" | instance, captured as-is |
55
+ | Lesson | "the API drops nulls — send empty strings" | soul, once verified |
56
+ | Procedure | "steps to clear stuck queues" | soul, as a playbook or skill |
57
+
58
+ The judge's routing question at the last step is one of shape. Facts that
59
+ future instances should **know** become knowledge. Steps they should **run
60
+ the same way** become skills. A correction to existing steps maintains the
61
+ skill it corrects.
62
+
63
+ ## Decisions carry jurisdiction by location
64
+
65
+ "We will retry twice, then escalate" can bind one task, or bind every future
66
+ incarnation. The words are the same. The difference is where it is recorded
67
+ — task decisions live and die with the instance's working state, soul
68
+ decisions bind everyone after. Placement, not phrasing, carries the
69
+ authority.
70
+
71
+ ## Souls hold slow state, not just timeless truth
72
+
73
+ Not everything in a soul is eternal. A roadmap, a deployment's current
74
+ shape, an open question — these are present-tense facts that tick at project
75
+ speed rather than task speed. Durability is a spectrum. Task-fast state
76
+ belongs to the instance. Project-slow state belongs to the soul and should
77
+ carry dates, because it rots. Timeless lessons belong to the soul and mostly
78
+ do not.
79
+
80
+ ## The knowledge structure is itself specialization
81
+
82
+ Ship a minimal core ontology and let each soul grow the rest. An architect
83
+ soul grows a roadmap section. A developer soul grows a codebase-gotchas
84
+ section. A roadmap section in a developer soul is a smell — project
85
+ direction belongs to whoever stewards the project.
86
+
87
+ The test for growing a section is the promotion bar, one level up. Would
88
+ future instances of *this* soul need to navigate this kind of knowledge? If
89
+ yes, grow it and record why. If no, it is one concept, not a section.
90
+
91
+ ## Where OATS encodes these ideas
92
+
93
+ In the reference implementation the ideas surface as: the okf integration's
94
+ capture protocol (instances write, no judging), the memory-harvest agent
95
+ (the judge, holding the bar and the routing table), and per-soul knowledge
96
+ bundles that grow role-specific sections. Swap the format and the mechanics
97
+ change. The ideas above should not.
@@ -0,0 +1,139 @@
1
+ # Knowledge — layer 2
2
+
3
+ Specialization is accumulated judgment. A specialist remembers what worked,
4
+ what failed, what was decided, and which procedures are worth repeating.
5
+
6
+ OATS treats knowledge as a pluggable layer. The kernel does not choose a
7
+ memory format. The default integration, `oats-okf`, uses markdown OKF bundles
8
+ for soul knowledge and simple files for instance state.
9
+
10
+ > **Status and relationship to the turn record (2026-08-21).** This layer is
11
+ > current and stays. It is the *semantic* memory of an agent team — reviewed,
12
+ > de-indexicalized truths that travel with the soul — and is distinct from the
13
+ > *episodic* memory provided by the turn record (`packages/record`: every
14
+ > session captured verbatim, automatically). Today the harvester's input is
15
+ > `notes/*.md`, the working agent's own in-session notes; the planned upgrade
16
+ > (epic `aweb-abfz`) feeds the harvester from the captured record as well, so
17
+ > lessons reach the soul even when an agent wrote no notes and never ran
18
+ > harvest. The judgment machinery described here — the promotion bar, the
19
+ > capture/judge split, the routing between knowledge and skills — is unchanged
20
+ > by that upgrade; only the input channel widens.
21
+
22
+ ## What the kernel does not own
23
+
24
+ The kernel is memory-agnostic. It provides lifecycle events:
25
+
26
+ - `soul-scaffold`
27
+ - `spawn`
28
+ - `retire`
29
+
30
+ A knowledge integration decides what to do with those events. If config
31
+ resolves `knowledge: none`, the kernel creates no `STATE.md`, no `notes/`, no
32
+ knowledge bundle, and no harvest flow.
33
+
34
+ The ideas behind any of this — what belongs in a soul vs an instance,
35
+ capture vs judgment, consolidation stages — are format-independent and live
36
+ in [knowledge theory](knowledge-theory.md).
37
+
38
+ ## The default: oats-okf
39
+
40
+ With `knowledge: okf`, the integration creates two memory spaces.
41
+
42
+ | Space | Files | Purpose |
43
+ |---|---|---|
44
+ | Soul memory | `soul/knowledge/` | Long-term OKF bundle: lessons, decisions, playbooks, references, role-grown sections. |
45
+ | Instance memory | `STATE.md`, `log.md`, `notes/` | Current task state, dated history, and captured insights. |
46
+
47
+ The instance does not promote its own notes. It captures them, and after
48
+ committing with pending notes it runs `oats okf harvest` (its okf injection
49
+ carries this instruction), which spawns a **memory-harvest** agent. That
50
+ harvester judges the notes and updates the soul; the delivery matches the
51
+ soul's custody — a commit on the instance's branch for repo-resident souls,
52
+ a PR to the soul's home repo for workspace-mode souls, and **direct edits
53
+ with no commit** for local souls (their `local-agents/` home is
54
+ uncommitted by contract). Then it retires.
55
+
56
+ ## Capture and judgment
57
+
58
+ OATS splits memory work into two roles.
59
+
60
+ **The working instance captures.** It keeps `STATE.md` current, appends
61
+ milestones to `log.md`, and writes every non-obvious insight to `notes/`.
62
+ It does not decide whether an insight is "important enough" for the soul.
63
+ Capture should be cheap and in-flow.
64
+
65
+ **The memory-harvest agent judges.** It reads pending notes and applies the
66
+ promotion bar:
67
+
68
+ > Promote only what is durable and would change what a future instance of
69
+ > this soul does.
70
+
71
+ For each note it chooses one outcome:
72
+
73
+ | Outcome | Meaning |
74
+ |---|---|
75
+ | Promote | Move it into the right soul knowledge section, or into a soul skill if it is procedural. |
76
+ | Merge | Fold it into an existing concept or skill. |
77
+ | Drop | Delete it and log why it failed the bar. |
78
+
79
+ This separation keeps working agents from overthinking memory, and gives
80
+ promotion the deliberate attention it deserves.
81
+
82
+ ## Why instance memory and soul memory differ
83
+
84
+ Instance memory is indexical. It talks about this task, this branch, this
85
+ moment, this blocker. That is why it lives in `STATE.md`, `log.md`, and
86
+ `notes/`.
87
+
88
+ Soul memory must be incarnation-invariant. It should remain true for future
89
+ instances, future models, and future sessions. A future instance should be
90
+ able to read a concept and act differently because of it.
91
+
92
+ Harvest is the conversion between the two. Notes are where an instance tries
93
+ to phrase what it learned without "I, here, now". The harvester checks
94
+ whether that conversion succeeded.
95
+
96
+ ## OKF concept types
97
+
98
+ OKF itself does not prescribe one vocabulary. OATS conventions use these
99
+ common types:
100
+
101
+ | Type | Usual home | Meaning |
102
+ |---|---|---|
103
+ | `Instance State` | `STATE.md` | Current working state. Rewritten, not superseded. |
104
+ | `Finding` | `notes/` | A captured observation whose durability is unproven. |
105
+ | `Lesson` | Soul knowledge | A durable behavior-changing conclusion. |
106
+ | `Decision` | `notes/` or soul knowledge | A decision and its rationale. Task-local decisions stay in `STATE.md` or `log.md`. |
107
+ | `Playbook` | Soul knowledge or soul skills | Repeatable steps. Procedure-shaped notes usually become skills. |
108
+ | `Reference` | Soul knowledge | External truth or stable internal reference. |
109
+
110
+ Souls start with core knowledge sections:
111
+
112
+ - `lessons/`
113
+ - `decisions/`
114
+ - `playbooks/`
115
+ - `references/`
116
+
117
+ Souls can grow role-specific sections such as `architecture/`, `codebase/`, or
118
+ `roadmap/`. Add a section when future instances of that soul need to navigate
119
+ that kind of knowledge.
120
+
121
+ ## What a working instance should do
122
+
123
+ As you work:
124
+
125
+ 1. Keep `STATE.md` accurate. A fresh session should be able to resume from
126
+ its `# Next` section.
127
+ 2. Append dated milestones and decisions to `log.md`.
128
+ 3. Write non-obvious insights to `notes/` as one concept per file.
129
+ 4. Before every commit, bring memory up to date.
130
+ 5. Commit, then run `oats okf harvest` to send your notes to the soul.
131
+
132
+ Do not hold back a note because you are unsure it is soul-grade. Capture
133
+ first. The harvester judges.
134
+
135
+ ## Without a knowledge integration
136
+
137
+ `knowledge: none` is valid. The agent gets no OATS memory files, no memory
138
+ briefing, no harvest agent, and no OKF skills. It may still use whatever
139
+ memory conventions the repo or harness already provides.
package/docs/layers.md ADDED
@@ -0,0 +1,110 @@
1
+ # The five layers of agent specialization
2
+
3
+ A capable model in a fresh session is not a specialist. A specialist has
4
+ identity, habits, memory, teammates, and work to track. OATS names those needs
5
+ as five layers.
6
+
7
+ Two layers are the OATS pattern itself: **soul** and **instances**. The kernel
8
+ implements them directly. The other three are formally defined, exclusive
9
+ contracts: **knowledge**, **messaging**, and **tasks**. An integration is the
10
+ capability package selected to bind one contract to a real tool; general
11
+ capability packages remain additive.
12
+
13
+ ## The layers at a glance
14
+
15
+ | Layer | Question it answers | Owner |
16
+ |---|---|---|
17
+ | **Soul** | Who is this agent, and how does it grow? | Kernel |
18
+ | **Knowledge** | Where does learning live, and how is it promoted? | Integration |
19
+ | **Instances** | How does a soul run work in the world? | Kernel |
20
+ | **Messaging** | How can this instance be reached by the team? | Integration |
21
+ | **Tasks** | Where does shared work state live? | Integration |
22
+
23
+ ## 1. Soul — durable expert identity
24
+
25
+ A soul is what an agent *is* when no session is running. It contains the
26
+ agent's operating doc (`AGENTS.md`), its own skills, and any long-term
27
+ knowledge its knowledge layer provides.
28
+
29
+ A soul is committed and reviewed like code. It has no terminal, no network
30
+ identity, and no current task. It is the expert template that compounds over
31
+ many incarnations.
32
+
33
+ The kernel owns this layer. The shape of a soul is the pattern itself:
34
+ `soul.yaml`, canonical files, symlinks, and directory layout. Deployment
35
+ capabilities compose into instances and never redefine or mutate the soul.
36
+
37
+ ## 2. Knowledge — learning that survives sessions
38
+
39
+ Specialization requires memory. The knowledge layer decides where memory
40
+ lives, what kinds exist, and how learning moves from an instance into the
41
+ soul.
42
+
43
+ The kernel is memory-agnostic. It only provides lifecycle events such as
44
+ `soul-scaffold`, `spawn`, and `retire`. A knowledge integration
45
+ uses those events to create memory files, teach the protocol, and run
46
+ promotion.
47
+
48
+ The default `oats-okf` integration creates an OKF bundle in the soul and
49
+ `STATE.md`, `log.md`, and `notes/` in instances. Instances capture what they
50
+ learn, and after committing with pending notes run `oats okf harvest`, which
51
+ spawns a memory-harvest agent that judges notes and promotes
52
+ what belongs in the soul.
53
+
54
+ If config resolves `knowledge: none`, none of this exists. The agent works
55
+ with whatever memory its harness or repo already provides.
56
+
57
+ See [Knowledge](knowledge.md).
58
+
59
+ ## 3. Instances — the soul doing work
60
+
61
+ An instance is one running incarnation of a soul. It has a home directory, a
62
+ work tree, a task briefing, metadata, and possibly episodic memory.
63
+
64
+ Instances are disposable by design. Sessions crash, context windows fill, and
65
+ models change. The soul survives. With a knowledge integration, what matters
66
+ feeds back into the soul as the instance works.
67
+
68
+ The kernel owns this layer: spawn, exact skill/instruction composition,
69
+ retire, work modes, tmux windows, metadata, and lifecycle hook points.
70
+ (There is no resume command; a retired instance is gone — its session
71
+ transcript survives in the turn record if capture is running.)
72
+
73
+ See [Souls and instances](souls-and-instances.md).
74
+
75
+ ## 4. Messaging — reachable team identities
76
+
77
+ Team agents need to reach humans and each other. The messaging layer maps an
78
+ instance name to a communication identity, then cleans that identity up when
79
+ the instance retires.
80
+
81
+ The default integration is `oats-aweb`. It gives each instance an aweb identity
82
+ and teaches agents to use `aw mail` and `aw chat`.
83
+
84
+ Messaging is deliberately narrow. It is about communication only. Task
85
+ coordination belongs to layer 5.
86
+
87
+ ## 5. Tasks — shared work state
88
+
89
+ Work needs a queue and status that outlive any one instance: planned, in
90
+ flight, blocked, done. OATS does not choose the tracker. It only requires that
91
+ agents know where the shared work state lives and how to use it.
92
+
93
+ There is no shipped default task integration. One deployment can bind Jira,
94
+ another Linear, another GitHub Issues. The LFX-style Jira integration is just
95
+ skills plus an injection that points agents at those skills.
96
+
97
+ ## Why the split matters
98
+
99
+ Souls and instances are the stable core. Moving them out would leave nothing
100
+ to specialize.
101
+
102
+ Knowledge, messaging, and tasks vary by team. They should be easy to swap.
103
+ One workspace can run OKF + aweb + Jira. Another can run a team wiki + Slack
104
+ + Linear. A repo can disable messaging with `messaging: none`. The soul stays
105
+ the same kind of object. Only its layer bindings change.
106
+
107
+ Layer exclusivity prevents two task systems or knowledge protocols from
108
+ competing inside one soul context. Capability targeting still allows another
109
+ soul to select a different provider. See [Integrations](integrations.md) and
110
+ [Capability packages](capabilities.md).
@@ -0,0 +1,122 @@
1
+ # Migrating from OAS to OATS
2
+
3
+ Status: **the migration path exists in the kernel** — `oats migrate
4
+ --from-oas`, one transactional conversion per scope (plan steps 1–3 below,
5
+ all landed 2026-08-21). What remains is distribution: end users get the
6
+ command with the v0.22.0 release (steps 4–6). This document keeps the facts
7
+ in one place; the work is tracked as epic `aweb-abfy` in this team's aw
8
+ tasks. Audited 2026-08-21 against a live OAS deployment
9
+ (`@oas-framework/oas@0.20.0-aweb.1`).
10
+
11
+ OATS was published as **OAS** (`@oas-framework/oas`, `@oas-framework/pi`)
12
+ until the 2026-08-13 rename (commit `fa1e646`), which was a deliberate
13
+ clean break: no compatibility shims, and no code in this repo recognizes
14
+ `oas-*` names.
15
+
16
+ ## Do not migrate by hand yet
17
+
18
+ The dangerous property of the original state was that it failed **quietly**:
19
+
20
+ - On a scope with `oas-config.yaml`/`oas-lock.json`, `oats status` works
21
+ (the `agents/` layout is unchanged) — everything looks healthy.
22
+ - But `oats doctor` finds no config, all layers are unresolved, and a spawn
23
+ produces instances **without the knowledge (`oas.okf`) and messaging
24
+ (`oas.aweb`) injections**. The compounding-memory and team-comms value
25
+ disappears with no error.
26
+ - `oats migrate --official --dry-run` on such a scope reported "nothing to
27
+ migrate" and exited 0 — false success.
28
+
29
+ Since plan step 1 landed (below), the silence is closed: `oats doctor` names
30
+ an un-migrated OAS scope with the remedy, and every `oats migrate` form —
31
+ plain or guided, dry run or apply — exits nonzero when `oas-config.yaml` /
32
+ `oas-lock.json` are visible from the scope (detection is by name only; the
33
+ kernel never parses OAS files).
34
+
35
+ ## Migrating: `oats migrate --from-oas`
36
+
37
+ ```bash
38
+ oats migrate --from-oas --dry-run --dir <scope> # full per-scope plan, touches nothing
39
+ oats migrate --from-oas --dir <scope> # convert this scope
40
+ oats migrate --from-oas --recursive --dir <root> # every visible OAS scope
41
+ ```
42
+
43
+ One transaction per scope, two phases under one journal:
44
+
45
+ 1. **Rename phase** — `oas-config.yaml` → `oats-config.yaml` (line
46
+ transform: the `oas:` defaults key becomes `oats:` and capability ids
47
+ with catalog renames become their successors; comments and every other
48
+ byte survive), `oas-lock.json` → `oats-lock.json` verbatim, `oas.json` →
49
+ `oats.json` inside each installed capability dir, and
50
+ `.oas-scaffold-owners.json` → `.oats-scaffold-owners.json` in `agents/`
51
+ and `local-agents/` souls with owner ids mapped to their successors.
52
+ 2. **Chained guided conversion** — the existing v1→v2 official migration
53
+ runs on the renamed lock; the catalog's renaming aliases map each
54
+ `oas.*` entry onto the `oats.*` package that replaces it, re-acquiring
55
+ the artifacts (stale v1 integrity is resolved by re-acquisition, never
56
+ recomputed) and cleaning the superseded `oas`-named artifact dirs.
57
+
58
+ Any failure in either phase restores the scope's original OAS-named bytes.
59
+ A second run finds nothing and exits 0. Executable trust is re-earned after
60
+ conversion (`oats trust` commands are printed), and comments inside your
61
+ config that mention `oas` paths are left untouched — comments are yours.
62
+
63
+ ## The four breaks
64
+
65
+ 1. **File names.** The kernel reads only `oats-config.yaml`
66
+ (`lib/core.mjs:326`) and `oats-lock.json` (`lib/core.mjs:643`). OAS
67
+ scopes have `oas-config.yaml`, `oas-lock.json`, `oas.json` inside each
68
+ installed capability dir, and `.oas-scaffold-owners.json` in souls.
69
+ 2. **The config key.** The schema (`docs/oats-config.schema.json`,
70
+ `additionalProperties: false`) accepts an `oats:` defaults block and
71
+ rejects the `oas:` block an OAS config carries — renaming the file is
72
+ not enough.
73
+ 3. **Capability ids.** `oas.okf` / `oas.aweb` appear in the config, the
74
+ lock, and the `.agents/capabilities/installed/<id>/` directory names.
75
+ 4. **No catalog aliases.** `package-catalog.json` maps only `oats.*` ids;
76
+ a hand-renamed lock naming `marketplace:oas.okf` resolves to
77
+ `available: false` and the guided migration **holds the whole scope**
78
+ (`lib/core.mjs:3251-3262`, `:1416`).
79
+
80
+ ## Why an OAS user cannot even find OATS
81
+
82
+ - `@awebai/oats` and `@awebai/oats-pi` are **not yet published**. The
83
+ v0.22.0 release notes exist and the three manifests are aligned; what
84
+ remains is the tag and the publish (`.github/workflows/release.yml`, or
85
+ the runnerless lane in `scripts/release-lane.mjs`).
86
+ - `@oas-framework/oas` is still live on npm (0.21.2 as of 2026-09-03) and
87
+ **not deprecated**, so the old CLI's `oas update` (which checks that
88
+ package) never learns OATS exists.
89
+ - The old OAS desktop probes for `@oas-framework/oas`; the new desktop
90
+ requires `@awebai/oats >=0.22.0 <0.23.0` — old app and new CLI are
91
+ mutually invisible, so desktop must ship in the same release.
92
+
93
+ ## The plan (epic `aweb-abfy`)
94
+
95
+ 1. **Done.** `oats migrate` / `oats doctor` detect an OAS scope and fail
96
+ **loud** with the exact remedy (never exit 0 on "nothing to migrate" when
97
+ `oas-*` siblings exist). `detectOasScopes` in `lib/core.mjs`,
98
+ `discoverOasScopes` in `lib/packages.mjs`, wired in `bin/oats.mjs`;
99
+ tests in `test/oas-scope-detection.test.mjs`.
100
+ 2. **Done.** Catalog aliases `oas.*` → `oats.*` so legacy locks map instead
101
+ of holding — as *renaming* aliases (`{ "package": "oats.okf",
102
+ "capability": "oats.okf" }`), because the replacement packages export the
103
+ successor ids, never the legacy ones. All seven 0.20 ids are mapped in
104
+ `package-catalog.json`.
105
+ 3. **Done.** `oats migrate --from-oas`: one transactional command covering
106
+ all four breaks (section above), fixture built from the real deployment
107
+ shape, idempotent, byte-identical rollback on failure, with a migrated
108
+ scope reaching green `oats doctor` and spawns composing the knowledge and
109
+ messaging injections again (`test/from-oas-migration.test.mjs`).
110
+ 4. v0.22.0 release: notes and version alignment are **done**; remaining are
111
+ the tag, npm publish of `@awebai/oats` + `@awebai/oats-pi`, and the
112
+ desktop GitHub Release.
113
+ 5. `npm deprecate` the `@oas-framework/*` packages with a pointer here.
114
+ 6. Close the `oats-okf` / `oats-aweb` publication gates so migrated scopes
115
+ can restore their capabilities under the new ids.
116
+
117
+ Until 4–6 are done, OAS users (this includes real daily users) should stay
118
+ on `@oas-framework/oas` — it keeps working and loses nothing by
119
+ waiting. The conversion command exists, but until the v0.22.0 release it is
120
+ only reachable from a repo checkout, and until the satellite publication
121
+ gates close the catalog's package refs are not a supported acquisition
122
+ source for migrated scopes.
@@ -0,0 +1,140 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://oats.dev/schemas/oats-config.schema.json",
4
+ "title": "OATS scoped configuration",
5
+ "$defs": {
6
+ "settings": { "type": "object", "additionalProperties": true },
7
+ "binding": {
8
+ "oneOf": [
9
+ { "type": "boolean" },
10
+ {
11
+ "type": "object",
12
+ "properties": {
13
+ "enabled": { "type": "boolean" },
14
+ "settings": { "$ref": "#/$defs/settings" }
15
+ },
16
+ "additionalProperties": false
17
+ }
18
+ ]
19
+ },
20
+ "injection-override": { "type": "string", "description": "Override an instruction injection: a config-relative path, none, or default. Convention: .agents/injections/…" },
21
+ "capabilityTargets": {
22
+ "type": "object",
23
+ "properties": {
24
+ "from": { "type": "string", "description": "Enforced provenance: installed | owned | path:<dir>." },
25
+ "global": { "$ref": "#/$defs/binding" },
26
+ "agent-types": {
27
+ "oneOf": [
28
+ { "type": "array", "items": { "type": "string" }, "uniqueItems": true },
29
+ { "type": "object", "additionalProperties": { "$ref": "#/$defs/binding" } }
30
+ ]
31
+ },
32
+ "souls": { "type": "object", "additionalProperties": { "$ref": "#/$defs/binding" } },
33
+ "settings": { "$ref": "#/$defs/settings" },
34
+ "injection-override": { "$ref": "#/$defs/injection-override" }
35
+ }
36
+ },
37
+ "layerEntry": {
38
+ "oneOf": [
39
+ { "const": "none" },
40
+ {
41
+ "allOf": [{ "$ref": "#/$defs/capabilityTargets" }],
42
+ "type": "object",
43
+ "required": ["capability"],
44
+ "properties": {
45
+ "capability": { "type": "string" },
46
+ "from": true, "global": true, "agent-types": true, "souls": true, "settings": true, "injection-override": true
47
+ },
48
+ "additionalProperties": false
49
+ }
50
+ ]
51
+ },
52
+ "additiveEntry": {
53
+ "allOf": [{ "$ref": "#/$defs/capabilityTargets" }],
54
+ "type": "object",
55
+ "properties": {
56
+ "from": true, "global": true, "agent-types": true, "souls": true, "settings": true, "injection-override": true
57
+ },
58
+ "additionalProperties": false
59
+ },
60
+ "workMode": {
61
+ "oneOf": [
62
+ { "type": "null" },
63
+ {
64
+ "type": "object",
65
+ "properties": {
66
+ "setup": { "type": "string", "description": "Env-bootstrap command run inside each new worktree after creation (path relative to this config's directory)." }
67
+ },
68
+ "additionalProperties": false
69
+ }
70
+ ]
71
+ }
72
+ },
73
+ "type": "object",
74
+ "properties": {
75
+ "name": { "type": "string" },
76
+ "team": {
77
+ "type": "object",
78
+ "description": "The deployment/team boundary. Closest scope declaring team: wins; used for identity, cross-repo agent discovery (oats status --team), and messaging providers.",
79
+ "properties": {
80
+ "name": { "type": "string" },
81
+ "id": { "type": "string", "description": "Explicit provider team id (e.g. aweb <name>:<namespace>)." }
82
+ },
83
+ "required": ["name"],
84
+ "additionalProperties": false
85
+ },
86
+ "agent-types": {
87
+ "type": "object",
88
+ "description": "Agent families declared by name; membership is `type: <name>` in each soul.yaml.",
89
+ "additionalProperties": {
90
+ "oneOf": [
91
+ { "type": "null" },
92
+ {
93
+ "type": "object",
94
+ "properties": { "description": { "type": "string" } },
95
+ "additionalProperties": false
96
+ }
97
+ ]
98
+ }
99
+ },
100
+ "capabilities": {
101
+ "type": "object",
102
+ "properties": {
103
+ "layers": {
104
+ "type": "object",
105
+ "description": "The three exclusive fundamental slots: a capability entry or an explicit none.",
106
+ "properties": {
107
+ "knowledge": { "$ref": "#/$defs/layerEntry" },
108
+ "messaging": { "$ref": "#/$defs/layerEntry" },
109
+ "tasks": { "$ref": "#/$defs/layerEntry" }
110
+ },
111
+ "additionalProperties": false
112
+ },
113
+ "additive": {
114
+ "type": "object",
115
+ "propertyNames": { "pattern": "^(?:@?[a-z0-9][a-z0-9._-]*[./])[a-z0-9][a-z0-9._/-]*$" },
116
+ "additionalProperties": { "$ref": "#/$defs/additiveEntry" }
117
+ }
118
+ },
119
+ "additionalProperties": false
120
+ },
121
+ "skill-overrides": { "type": "object", "additionalProperties": { "type": "string" } },
122
+ "templates": { "type": "object", "additionalProperties": { "type": "string" }, "description": "Named oats init templates: local config paths or git URLs whose main branch carries an oats-config.yaml." },
123
+ "agents-md-injection": { "oneOf": [{ "type": "string" }, { "type": "object", "additionalProperties": { "type": "string" } }], "description": "Extra unconditional instruction blocks (adds content; does not override packaged defaults)." },
124
+ "oats": {
125
+ "type": "object",
126
+ "properties": { "injection-override": { "$ref": "#/$defs/injection-override" } },
127
+ "additionalProperties": false
128
+ },
129
+ "work-modes": {
130
+ "type": "object",
131
+ "properties": {
132
+ "worktree": { "$ref": "#/$defs/workMode" },
133
+ "checkout": { "$ref": "#/$defs/workMode" },
134
+ "attached": { "$ref": "#/$defs/workMode" }
135
+ },
136
+ "additionalProperties": false
137
+ }
138
+ },
139
+ "additionalProperties": false
140
+ }