@awebai/oats 0.24.1 → 0.24.3

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.
@@ -0,0 +1,21 @@
1
+ # OATS v0.24.2 — `oats onboard`, official capabilities in a soul's definition, aweb 1.11.0 pairing
2
+
3
+ Kernel/Pi/Desktop **0.24.2**. Publication is not deployment: every operator still installs, approves and qualifies locally.
4
+
5
+ ## What changed
6
+
7
+ - **`oats onboard [--dir <deployment>] [--workspace <git:source[@rev]>] [--force-existing] [--json]`** — bootstrap a deployment with an **`oats-setup-expert`** soul that holds `oats.core` and `oats.setup`. Acquires the official `oats.framework` package through the existing engine (resources-only guard before commit, no catalog auto-trust), activates exactly those two capabilities for that soul, writes its definition with both pinned at the acquired immutable commit, and **prints** the spawn command — it never launches a model. Classic local bootstrap, not captured preparation. `oats setup` (record capture setup) is unchanged.
8
+ - **Soul creation declares `oats.core` explicitly.** `oats create` and new local souls write `requires.capabilities.oats.core` resolved from the official catalog; `--no-oats-core` opts out; nothing is invented when the catalog has no entry. When a soul declares `oats.core` or `oats.setup`, the kernel composes **neither** the legacy kernel skills (`oats`, `oats-config`, `oats-packages`) nor the `kernel:oats` injection — the capability's skills are the operational curriculum. Souls without it keep legacy behavior; `oats doctor` prints an informational line.
9
+ - **Official capabilities.** `oats.framework` **1.1.1** (tag `oats-framework/v1.1.1`) exports `oats.core` (`oats-operate`, `oats-souls`, "you run on OATS" briefing), `oats.setup` (`oats-config`, `oats-packages`, `oats-workspace-setup` — now documenting the operator request shape) and `oats.knowledge-theory`. Listed in the official catalog with capability aliases.
10
+ - **Retained inspection projects `launchSelection`** (`{runtime, model}` or `null`) for the primary from the verified retained record; provider binding codecs receive the same-kernel `OATS_CLI_BIN` after the environment scrub. These are what a provider `check` needs to be truthful about the primary's profile.
11
+ - **aweb 1.11.0 pairing.** The catalog and all framework soul editions pin `oats.aweb@v1.11.0`, which adds the portable binding interface (floor `>=0.24.2`). Its `check` qualifies HOME-route operational custody only: explicit private team, `delivery: session`, an input-capable Claude/Codex primary. A strict-Pi print primary reports `needs-configuration`; the requirement is never dropped.
12
+ - **Six framework soul editions** exported and imported by `oats-workspace.yaml`: the five knowledge-owning experts (`oats-expert`, `oats-kernel-expert`, `oats-desktop-expert`, `market-research-expert`, `oats-assistant`) at `caa341f3` and `oats-setup-expert` at `0aad753c`. All six declare `oats.core` from the repository's own payload.
13
+ - Baseline hygiene: golden fixtures use a genuinely empty catalog; the package check lazy-loads dev validators so syntax-only lanes work without `node_modules`.
14
+
15
+ ## Second-operator gate
16
+
17
+ An independent operator on a fresh machine adopted the shared definition from public sources through `inspect` → `prepare` → approvals with 0.24.1: the whole graph (including `oats.core`/`oats.setup`) resolved and materialized; preparation stopped at aweb 1.10.3 as declared. Five usability seams were recorded (request-file mismatch between `inspect`/`prepare`, raw ENOENT on an absent deployment, `trust --dir` rejecting lock v3, `--artifact-set` missing from help, unattributed provider problems). **They are not fixed in this cut**; see the [program board](../design/2026-09-20-redesign-program-board.md).
18
+
19
+ ## Not in this cut
20
+
21
+ Desktop marketplace view / soul creation UI, legacy `agents/` roster and in-soul knowledge decommission (after the fresh-reader proof against the public `oats-knowledge`), Pi session-input support.
@@ -0,0 +1,13 @@
1
+ # OATS v0.24.3 — second-operator `prepare` seams
2
+
3
+ Kernel/Pi/Desktop **0.24.3**. Fixes the five usability seams an independent second operator hit adopting the shared workspace definition on 0.24.1/0.24.2 (see the [program board](../design/2026-09-20-redesign-program-board.md)). No contract, schema or authority change.
4
+
5
+ - **Attributed provider problems.** Every preparation problem now carries `slot`, `capability` and its `origins`, with kernel-fixed text that names the missing item (for example `oats.okf knowledge normalize binding could not be prepared` / `oats.aweb@1.10.3 declares no binding interface; messaging cannot be prepared`). One unqualified slot no longer masks another slot's diagnostics: every slot that has a binding interface is normalized, and the single resolver reports each invalid choice under its slot. Provider free text still never crosses the wire.
6
+ - **`oats trust <capability> --dir <deployment>` on a lock v3 deployment** returns a typed problem with the exact working command (`oats trust <cap> --deployment <abs> --artifact-set <sha256-…>`) instead of `unsupported lockfileVersion 3`. No auto-approval.
7
+ - **Help** documents `--artifact-set` and how `prepare`'s `selections[].artifactSet` / `approvalRequired[]` pair with it.
8
+ - **`prepare` on an absent deployment path** returns a typed explicit-provisioning hold, not a raw `ENOENT` with a host path.
9
+ - **One request file for both commands.** `prepare --request` accepts the explicit `workTarget` that `inspect --request` requires (explicit beats cwd; it never implies captured placement or current config), and `oats inspect --request <f> --emit-prepare-request <out>` writes the converted preparation request through the existing builder.
10
+
11
+ The second operator's recorded requests and results are preserved verbatim as regression fixtures (`test/fixtures/second-operator/`). Their valid-vs-bogus `stores.oats` pair remains identical by design: both refused on missing OKF runtime settings (`bindings-file`, `state-dir`), which the messages now say.
12
+
13
+ Setup guidance for those settings lands in the next `oats.framework` payload.
@@ -54,6 +54,31 @@ in a soul bundle; see [knowledge](knowledge.md) for its prepared version scope.
54
54
  Future integrations may add expert-specific artifacts such as rule files or
55
55
  runtime-specific guidance, while keeping `AGENTS.md` canonical.
56
56
 
57
+ ## OATS operational knowledge is a capability
58
+
59
+ New souls declare `requires.capabilities.oats.core` with a Git source resolved
60
+ from the official package catalog. `oats create` and new local-soul scaffolds
61
+ write that requirement; `--no-oats-core` explicitly omits it. If the catalog has
62
+ no published revision yet, creation reports `needs-configuration` (also in JSON
63
+ `notes`) and leaves the requirement absent rather than inventing a source.
64
+ Declaring a capability is not acquiring, activating or approving it: those remain
65
+ normal deployment/preparation steps. The Desktop server currently has no
66
+ soul-creation endpoint; `oats soul set` edits existing definitions only.
67
+
68
+ The dependency is visible and removable in `soul.yaml`. Updating an existing
69
+ local soul preserves its requirements—including a deliberate removal—rather
70
+ than applying the creation default again. For the one-release transition,
71
+ a declaration of **either `oats.core` or `oats.setup`** suppresses the entire
72
+ legacy kernel operational skill list (`oats`, `oats-config`, `oats-packages`)
73
+ and `kernel:oats` injection. This also prevents setup's moved skill names from
74
+ colliding with the kernel copies; no skill override is needed for this case.
75
+ Without either declaration, legacy composition is unchanged.
76
+ `oats doctor --soul <name>` reports an absent `oats.core` as an informational
77
+ deprecation notice, not an error. Instance-boundary, work-mode and
78
+ configuration-declared briefings remain kernel-owned. Existing captured records
79
+ keep their retained resources; this does not migrate them or retire the kernel
80
+ skill files yet.
81
+
57
82
  ## Instance anatomy
58
83
 
59
84
  An instance has a lifecycle, but need not be short-lived. It is the identity of
@@ -5,12 +5,30 @@ source-complete exports and declares its own reciprocal membership. `oats-dev`
5
5
  remains a development-capability repository, including `oats.review`; membership
6
6
  neither activates that package nor replaces its existing configuration templates.
7
7
 
8
- This is phase 1: a shared repository graph and a transitional portable edition of
9
- **the existing oats-expert**. It is not the five-role rebuild, the curated knowledge
10
- cutover, a shared live runtime, a private-team enrollment or Desktop feature parity.
8
+ This guide covers the shared repository graph and portable expert source editions,
9
+ beginning with **the existing oats-expert**. Source publication is not a live
10
+ five-role roster or curated knowledge cutover, a shared runtime, private-team
11
+ enrollment or Desktop feature parity.
11
12
  The [phase plan](design/2026-09-20-workspace-and-portable-adoption-plan.md) and
12
13
  [knowledge model](knowledge-theory.md) retain those separate boundaries.
13
14
 
15
+ ## The five framework experts
16
+
17
+ The indexed source editions are `oats-expert`, `oats-kernel-expert`,
18
+ `oats-desktop-expert`, `market-research-expert` and `oats-assistant`. Each declares
19
+ `owns` for its own same-named node and `reads` for the other four through logical
20
+ store `oats`, preserving the reviewed owner UUIDs. These are knowledge-routing and
21
+ context declarations, not access grants or proof of accepted knowledge. The parallel
22
+ `souls/` editions neither replace the legacy `agents/` roster nor adopt the curated
23
+ KB or change live instances; actual adoption still needs compatible providers and
24
+ explicit bindings. All five now explicitly declare `oats.core` under
25
+ `requires.capabilities`, with `source: repo:oats-package`. This retains the complete
26
+ package from the same reviewed source revision.
27
+
28
+ The dependency is visible and removable in the authored definition, not a change
29
+ to existing captured records. An edited edition needs its own reviewed publication
30
+ and import update; changing today's source does not rewrite an older pinned import.
31
+
14
32
  ## Shared versus local
15
33
 
16
34
  | Git-shared declaration | Operator-local input or evidence |
@@ -22,17 +40,18 @@ The [phase plan](design/2026-09-20-workspace-and-portable-adoption-plan.md) and
22
40
  | A reviewed source revision | Exact executable approval, current readiness and deployment acceptance |
23
41
 
24
42
  No machine paths, credentials, private team identifiers, accepted-store locator or
25
- owner registry belongs in the public workspace. The uninitialized phase-2 knowledge
26
- repository is not advertised as a ready knowledge export. Preserve the parked
43
+ owner registry belongs in the public workspace. Knowledge publication and acceptance
44
+ (S7) remain separate; no ready knowledge export is advertised. Preserve the parked
27
45
  roster/curation and every old home, lock, source, pending job, history and worktree.
28
46
 
29
47
  ## Planned onboarding: OATS Soul Setup (D3)
30
48
 
31
- **Not shipped in OATS 0.24.** The planned onboarding flow creates and instantiates
49
+ **The D3 onboarding flow remains pending.** It will create and instantiate
32
50
  `oats-setup-expert`, declaring both `oats.core` and `oats.setup` from the
33
- [official marketplace](official-marketplace.md). Their package releases and this
34
- onboarding flow are future work, not existing catalog entries or a new command
35
- introduced by this guide.
51
+ [official marketplace](official-marketplace.md). Those capabilities are now
52
+ published in `oats.framework` 1.1.0 and listed; the separate setup-expert edition
53
+ and onboarding entry point do not become available merely by listing the package.
54
+ This flow was not shipped in the 0.24.0 or 0.24.1 kernel releases.
36
55
 
37
56
  - The setup expert will help the operator adopt repositories, select capabilities
38
57
  and carry out the normal prepare/approve/scaffold/start steps. It bypasses no
@@ -41,27 +60,29 @@ introduced by this guide.
41
60
  and its source explicitly. The operator can remove or replace that dependency
42
61
  by editing the authored definition, not a captured record; the kernel will not
43
62
  silently reinsert an absent one.
44
- - The CLI/Desktop entry point remains separately implemented and reviewed. Do not
45
- invent a workspace init/adopt command, create a setup soul from this sketch, or
46
- treat a planned package as installed. Existing instances and retained resources
47
- are not rewritten by the plan.
63
+ - The CLI/Desktop entry point still requires separate implementation and review.
64
+ Do not invent a workspace init/adopt command, create a setup soul from this
65
+ sketch, or treat a listed package as installed. Existing instances and retained
66
+ resources are not rewritten by the plan.
48
67
 
49
- ## What this first source commit establishes
68
+ ## Stage two: published experts and pinned imports
50
69
 
51
70
  - `oats-workspace.yaml` explicitly admits `oats` and the six intended capability
52
71
  repositories: `oats-dev`, `oats-okf`, `oats-aweb`, `oats-authoring`, `oats-jira`
53
72
  and `oats-linear`. It activates no additional capability; tasks default to none.
54
- - `oats.yaml` advertises `souls/oats-expert` and the actual framework package roots
55
- `oats-package` and `capabilities/oats-authoring`, not the npm root as a fictitious
56
- OATS distribution. Its workspace backlink names the same framework repository.
57
- - `souls/oats-expert/` is parallel to, not a replacement for, the live `agents/`
58
- source. It contains canonical instructions, `CLAUDE.md -> AGENTS.md`, and the
59
- existing reviewed PR/release procedure closure. No durable KB is copied into it.
60
- - The role preserves its knowledge owner, owned node and four cross-read interests.
73
+ - `oats.yaml` exports all five `souls/<name>` editions above and the actual package
74
+ roots `oats-package` (`oats.framework`) and `capabilities/oats-authoring`, not
75
+ the npm root as a fictitious OATS distribution. Its workspace backlink names
76
+ the same framework repository.
77
+ - The editions are parallel to, not replacements for, the live `agents/` roster.
78
+ Each contains canonical instructions, `CLAUDE.md -> AGENTS.md`, and its reviewed
79
+ private procedures where applicable. No durable KB is copied into them; legacy
80
+ roster cutover remains deferred until S7 knowledge publication.
81
+ - Each edition preserves its knowledge owner, owned node and four cross-reads.
61
82
  Store `oats` requires the explicit `stores.oats` binding; no publisher writer,
62
83
  production store or grants are supplied. An acceptance fixture is parent-owned
63
84
  and cannot be counted as production knowledge adoption.
64
- - Knowledge **oats.okf@2.1.1** and messaging **oats.aweb@1.10.3** are explicit hard
85
+ - Knowledge **oats.okf@2.1.1** and messaging **oats.aweb@1.11.0** are explicit hard
65
86
  requirements, not optional defaults. They are published starting revisions,
66
87
  **not proof that their combined bindings/runtime profile is ready**. The provider
67
88
  owner supplies that evidence and any subsequently reviewed compatible revision.
@@ -69,38 +90,41 @@ introduced by this guide.
69
90
 
70
91
  At these starting pins, the provider boundary is concrete:
71
92
 
72
- - Published OKF2.1.0 already supports `inherit: stores.oats`, normalized to
93
+ - Published OKF2.1.1 supports `inherit: stores.oats`, normalized to
73
94
  `/bindings/knowledge/stores/oats`. The explicit `destination: oats` preserves
74
95
  routing; omitting it would instead require `write.default`. No new schema,
75
96
  owner or production locator is needed for this declaration.
76
- - Released aweb1.10.3 (`24efa6f9`) has **no mandatory portable binding interface**,
77
- so it currently blocks this pilot's portable preparation. Candidate
78
- [aweb PR2](https://github.com/awebai/oats-aweb/pull/2), `165b20e7`, adds codecs;
79
- it is not a reviewed/published successor or native lifecycle qualification.
80
- Human/native-principal, private-context, admin/grant and admitted-lifecycle
81
- requirements remain provider/integration-owner work.
82
- - Published OKF2.1.0's captured worker profile retains its strict Pi,
83
- explicit-model and sole-OKF limitation. Candidate
84
- [OKF PR4](https://github.com/awebai/oats-okf/pull/4), `7cff887c`, preserves retained
85
- Claude/Codex/null-model intent and the approved helper capability closure;
86
- review is pending, not published2.1.0 behavior. Pi plus messaging is still
87
- unqualified. Do not silently switch runtimes, force a model, or drop capabilities.
97
+ - aweb 1.10.3 (`24efa6f9`) has no portable binding interface; **aweb 1.11.0**
98
+ (`v1.11.0`, OATS >=0.24.2) adds it and the five editions now pin it. Its `check`
99
+ qualifies only an input-capable Claude/Codex primary with an explicit private team
100
+ and `delivery: session`; strict-Pi print reports `needs-configuration`. Status is on
101
+ the [program board](design/2026-09-20-redesign-program-board.md). Qualification
102
+ is HOME-route operational custody only: not human/native-principal delegation,
103
+ private grants, broker delivery or model consumption.
104
+ - Published OKF2.1.1 accepts retained Claude/Codex helpers with the complete approved
105
+ capability closure and native-default model intent. Strict Pi still requires an
106
+ explicit model and the sole-OKF profile; Pi plus messaging remains unqualified.
107
+ This provider release alone is not combined-profile acceptance. Do not silently
108
+ switch runtimes, force a model, or drop capabilities.
88
109
 
89
110
  These are explicit readiness holds, not reasons to weaken the source. Parent must
90
111
  select reviewed compatible provider revisions and update the source pin deliberately
91
112
  before claiming an operational pilot; metadata-only repository indexes change none
92
113
  of these runtime facts.
93
114
 
94
- The workspace intentionally starts with **`imports: []`**. A source cannot pin a
95
- future commit containing itself. This first commit is publishable source metadata,
96
- not an already usable/adopted pilot graph or a phase-1 exit verdict.
115
+ Stage one used an empty imports list until source publication. Stage two is now
116
+ committed: all five imports pin **`caa341f34009e37006567419a983d5a743037a79`**, the
117
+ published edition revision containing explicit core requirements and the package.
118
+ The later workspace commit `375b9f42` added those imports. Live source inspection
119
+ against published main resolved all five as `ready-for-preparation`; this is
120
+ metadata readiness, not provider binding, approval, enrollment or a running pilot.
97
121
 
98
- ## Publish in this order
122
+ ## Preserve source-before-import publication order
99
123
 
100
- 1. Review and publish this source/export commit to the framework repository. Save
101
- the **actual reviewed immutable source commit** containing the complete soul.
102
- Do not put an invented SHA, a mutable branch or an unreviewed local candidate
103
- in the workspace import and describe it as the accepted source.
124
+ 1. Publish complete, reviewed source editions before pinning them. The current
125
+ source is `caa341f34009e37006567419a983d5a743037a79`; future revisions must likewise
126
+ exist before their workspace import update. Never use an invented SHA, a mutable
127
+ branch or an unreviewed local candidate as the accepted source.
104
128
  2. In each of the six repositories, review a root `oats.yaml` against its actual
105
129
  source head and actual `oats-package/oats-package.json`. The declaration is:
106
130
 
@@ -114,21 +138,25 @@ not an already usable/adopted pilot graph or a phase-1 exit verdict.
114
138
  ```
115
139
 
116
140
  Preserve payloads, versions, old tags and legacy templates. This does not
117
- activate oats.dev, messaging or either optional task integration.
118
- 3. In a subsequent reviewed framework commit, replace the empty imports list with
119
- an import of the source commit from step 1:
141
+ activate oats.dev, messaging or either optional task integration. Indexes are
142
+ published on `main` in all six capability repositories (oats-okf, oats-aweb,
143
+ oats-authoring, oats-jira, oats-dev, oats-linear); their publication proceeded
144
+ separately from the framework's own source imports.
145
+ 3. A subsequent workspace commit pins the published source, never itself or a
146
+ future commit. The current first import is:
120
147
 
121
148
  ```yaml
122
149
  imports:
123
150
  - source: git:github.com/awebai/oats
124
151
  soul: souls/oats-expert
125
- revision: <actual-reviewed-published-source-commit>
152
+ revision: caa341f34009e37006567419a983d5a743037a79
126
153
  alias: oats-expert
127
154
  ```
128
155
 
129
- The placeholder is explanatory text, never a value to commit. Update the
130
- staged-import test with that real publication evidence at this step. Do not
131
- change the stable export path or owner merely because the workspace advances.
156
+ The [actual workspace](../oats-workspace.yaml) contains all five imports at that
157
+ same revision; this excerpt is not a replacement for the full list. The layout
158
+ test now checks stage-two imports and source-document declarations. Do not
159
+ change stable export paths or owners merely because the workspace advances.
132
160
  4. Qualify reciprocal admission at the now-published observations. A missing
133
161
  backlink, a fork's copied file or a stale workspace observation is not membership.
134
162
  Cross-repository indexes may land separately; until both sides exist, report the
@@ -149,19 +177,18 @@ may consume it without membership in the OATS development workspace.
149
177
 
150
178
  ## Inspect source metadata before preparation
151
179
 
152
- The public source inspector is implemented in **PR24, commit
153
- `bc598c484fd097fc5707fd4a33b7877ca00e5da3`**. Use this section only after the
154
- integration owner supplies a reviewed CLI containing that implementation. It is
155
- **not a command supported by the original 0.24.0 release**: that older inspect
156
- route can ignore the request flag and consult ambient classic configuration.
157
- Do not infer availability from the version floor of a capability.
180
+ The public source inspector ships in [OATS 0.24.1](release-notes/v0.24.1.md),
181
+ following PR24 integration. Use an installed CLI containing that implementation.
182
+ It is **not supported by the original 0.24.0 release**: that older inspect route
183
+ can ignore the request flag and consult ambient classic configuration. Do not
184
+ infer command availability from a capability's version floor or today's catalog.
158
185
 
159
186
  ```sh
160
187
  oats inspect --request /absolute/inspection.json --json
161
188
  ```
162
189
 
163
- After the real workspace import from publication step 3 exists, the authored
164
- inspection input may use the same repository for workspace, member and source:
190
+ With the stage-two imports published, the authored inspection input can use the
191
+ same repository for workspace, member and source:
165
192
 
166
193
  ```json
167
194
  {
@@ -222,7 +249,7 @@ Before preparing, the integration lead must supply:
222
249
  - The published workspace/source observations and an explicit fresh physical
223
250
  deployment/home placement. Do not copy old locks, retained records or identities.
224
251
  - An operator-owned nonsecret request with `workspace` (its source and origin),
225
- `source: "oats-expert"` after the real import is published, and `mode: "directory"`.
252
+ `source: "oats-expert"` (or another published expert alias), and `mode: "directory"`.
226
253
  Standalone callers instead give the complete `{source,soul,revision,alias}`
227
254
  reference and an explicit standalone context; they do not inherit this workspace.
228
255
  - Explicit provider-specific settings and bindings. OKF preparation needs selected
@@ -236,7 +263,7 @@ Before preparing, the integration lead must supply:
236
263
  `operator.policy.messaging` selection: capability, matching selected source and
237
264
  `settings: {delivery: session}`. Retain host requirements, session `ifInstalled`
238
265
  minimums and any selected authoring requirements. Selecting session delivery neither
239
- adds the missing1.10.3 binding adapter nor supplies captured wake/input authority.
266
+ makes a strict-Pi print primary input-capable nor supplies captured wake authority.
240
267
  - A qualified primary/helper runtime/model/resource profile. Capture the intended
241
268
  helper selection in `helperLaunches["oats.okf:memory-harvest"]`, not the legacy
242
269
  `souls.memory-harvest` configuration. Do not replace retained intent to fit an easier
@@ -262,10 +289,12 @@ Do not mix other preparation flags into request-file mode. A needs-configuration
262
289
  approval result is not a ready instance. A scaffold materializes resources and may
263
290
  run approved hooks; it is not a message exchange or model session. Actual dispatch,
264
291
  continuation, native capture, messaging and learning require the integration owner's
265
- qualified profile and receipts. Captured wake/input and public captured retirement
266
- remain unsupported; a stopped-home observation is not delivery or retirement authority.
267
- A session-delivered messaging profile therefore cannot pass on start-only evidence.
268
- Do not route it through legacy input/retire or remove the messaging requirement.
292
+ qualified profile and receipts. OATS 0.24.1 adds captured custody checks to the
293
+ existing HOME-only session inspect/input route; it does not supply the missing
294
+ messaging adapter or qualify every lifecycle route. Verify wake/input/retirement
295
+ support for the exact route and runtime. A stopped-home observation is not delivery
296
+ or retirement authority, and a messaging profile cannot pass on start-only evidence.
297
+ Do not bypass custody with a legacy fallback or remove the messaging requirement.
269
298
  Consult the current installed public help and the provider's supported commands;
270
299
  this guide introduces no new CLI grammar. The source inspector above is a separate
271
300
  implementation dependency, not a change to the existing prepare request contract.
@@ -282,4 +311,4 @@ indexes are not evidence that the six real repositories are already published.
282
311
  Source and metadata checks do not enroll users, initialize the phase-2 store, register
283
312
  production writers or qualify private messaging. Parent alone coordinates publication,
284
313
  local operator approval and actual adoption. Full Desktop parity follows usable
285
- infrastructure adoption, not merely seven YAML files passing validation.
314
+ infrastructure adoption, not merely source metadata passing validation.
@@ -9,6 +9,10 @@ configuration cascade, package lock, similarly named capability, or source path.
9
9
  Load **oats-portable** before invoking or reasoning about captured OATS commands.
10
10
  Load **oats-portable-artifacts** for exact retained inspection and approval. Do not load the legacy **oats**,
11
11
  **oats-config**, or **oats-packages** procedures to fill a captured input.
12
+ If this retained composition includes **oats.core**, its **oats-operate** and
13
+ **oats-souls** skills are capability resources, not implicit kernel additions.
14
+ Use only the resources actually included; a missing capability is not permission
15
+ to fetch or substitute a current version.
12
16
 
13
17
  Captured start/restart use exact retained launch inputs and supported native
14
18
  endpoints. Captured wake/retire, unqualified runtime contributions and non-directory
package/lib/core.mjs CHANGED
@@ -51,6 +51,7 @@ import { buildCapturedInvocationContext, withCapturedInvocationContextFile } fro
51
51
  import { readCapturedInstanceIndex, readCapturedInstanceMetadata, readCapturedInstanceAuthority, assertCapturedInstanceCustody,
52
52
  recordCapturedCustodyFailure, markCapturedNativeScaffold, failCapturedNativeIntent, observeCapturedNativeTarget, setCapturedInstanceStatus, admitCapturedInstanceAction, beginCapturedIntent, settleCapturedIntent } from "./captured-instance-index.mjs";
53
53
  import { validateExecutionBinding } from "./schedule-capsule.mjs";
54
+ import { parsePortableSource } from "./source-spec.mjs";
54
55
  import { validateIntentRef } from "./captured-admission-shape.mjs";
55
56
  export { readCapturedInstanceIndex, readCapturedInstanceMetadata, beginCapturedIntent, settleCapturedIntent };
56
57
  export { buildCapturedInvocationContext, withCapturedInvocationContextFile };
@@ -61,7 +62,7 @@ import { validateCapturedLaunchRequest } from "./captured-launch-request.mjs";
61
62
  import { validateCapabilityInputDeclarations } from "./capability-inputs.mjs";
62
63
  import { validateCapturedSessionBackend, validateCapturedSessionTarget, assertCapturedSessionPlacement } from "./captured-session-backend.mjs";
63
64
  import { createRepositoryTransaction } from "./repository-observation.mjs";
64
- import { inspectPortableOnboarding as inspectOnboarding, describePortableOnboarding } from "./portable-onboarding.mjs";
65
+ import { inspectPortableOnboarding as inspectOnboarding, describePortableOnboarding, buildFreshPreparationRequest, inspectWorkTarget } from "./portable-onboarding.mjs";
65
66
  import { readLock3 } from "./portable-lock.mjs";
66
67
  import { portableScope, portableStateDirectory } from "./portable-state.mjs";
67
68
  import { objectAt } from "./portable-shape.mjs";
@@ -112,6 +113,18 @@ export const OATS_VERSION = JSON.parse(readFileSync(join(PKG_ROOT, "package.json
112
113
  /** Skills shipped with the kernel. Only oats-getting-started is ambient; spawn composes selected skills locally. */
113
114
  export const PACKAGED_SKILLS_DIR = join(PKG_ROOT, "skills");
114
115
 
116
+ function soulRequirements(soulDir) {
117
+ const file = soulDir && join(soulDir, "soul.yaml");
118
+ return file && existsSync(file) ? withConfigFile(file, () => parseYamlNested(readFileSync(file, "utf8"))).requires : undefined;
119
+ }
120
+ function declaredOperationalCapabilities(soulDir) {
121
+ const capabilities = soulRequirements(soulDir)?.capabilities ?? {};
122
+ return ["oats.core", "oats.setup"].filter(id => Object.hasOwn(capabilities, id));
123
+ }
124
+ function legacyOperationalSkills(soulDir) {
125
+ return declaredOperationalCapabilities(soulDir).length ? [] : ["oats", "oats-config", "oats-packages"].map(name => ({ id: "kernel", path: join(PACKAGED_SKILLS_DIR, name) }));
126
+ }
127
+
115
128
  // ---------- shell helpers ----------
116
129
  function sh(cmdline) { return execSync(cmdline, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim(); }
117
130
  function shTry(cmdline) { try { return sh(cmdline); } catch { return undefined; } }
@@ -1198,13 +1211,17 @@ export function approveAvailableCapability(deployment, artifactSet, capability,
1198
1211
  /** Public read-only source/workspace inspection through the existing facade.
1199
1212
  * Owns only transient repository scratch, never deployment state, provisioning,
1200
1213
  * provider code, approvals or a serialized mutation witness. */
1201
- export function inspectPortableOnboarding(input, { repositoryOptions = {} } = {}) {
1214
+ export function inspectPortableOnboarding(input, { repositoryOptions = {}, includePrepareRequest = false } = {}) {
1202
1215
  canonicalJson(input);
1216
+ if (typeof includePrepareRequest !== "boolean") throw oatsError("invalid-declaration", "request conversion requires an explicit boolean option");
1203
1217
  const directory = mkdtempSync(join(realpathSync(tmpdir()), "oats-source-inspection-")), owned = lstatSync(directory);
1204
1218
  let repositories, primary;
1205
1219
  try {
1206
1220
  repositories = createRepositoryTransaction({ ...repositoryOptions, directory, accessContextKey: repositoryOptions.accessContextKey ?? "native" });
1207
- return describePortableOnboarding(inspectOnboarding(input, { repositories }));
1221
+ const inspection = inspectOnboarding(input, { repositories }), result = describePortableOnboarding(inspection);
1222
+ // Opt-in data export, not a serialized custody witness or executable grant.
1223
+ // Default metadata continues to omit unclassified adoption/provider values.
1224
+ return includePrepareRequest ? { ...result, prepareRequest: buildFreshPreparationRequest(inspection).preparation } : result;
1208
1225
  } catch (error) { primary = error; throw error; }
1209
1226
  finally {
1210
1227
  try {
@@ -1226,21 +1243,28 @@ export function inspectPortableOnboarding(input, { repositoryOptions = {} } = {}
1226
1243
  * a fabricated complete record. No lifecycle side effects run here. */
1227
1244
  export function prepareCapturedComposition(input, { repositoryOptions = {} } = {}) {
1228
1245
  canonicalJson(input);
1229
- objectAt(input, ["deployment", "source", "origin", "workspace", "member", "operator", "mode", "allowLocalPaths", "standaloneContextKey", "launch", "helperLaunches"], ["deployment", "source"]);
1246
+ objectAt(input, ["deployment", "workTarget", "source", "origin", "workspace", "member", "operator", "mode", "allowLocalPaths", "standaloneContextKey", "launch", "helperLaunches"], ["deployment", "source"]);
1230
1247
  if (input.launch !== undefined) validateCapturedLaunchRequest(input.launch, validateLaunchConfig);
1231
1248
  if (input.helperLaunches !== undefined) {
1232
1249
  objectAt(input.helperLaunches, null, []);
1233
1250
  for (const request of Object.values(input.helperLaunches)) validateCapturedLaunchRequest(request, validateLaunchConfig);
1234
1251
  }
1235
1252
  if (input.mode !== undefined && !WORK_MODES.includes(input.mode)) throw oatsError("invalid-declaration", "invalid preparation work mode");
1236
- const deployment = portableScope(input.deployment);
1253
+ const { workTarget, ...preparationInput } = input;
1254
+ const target = Object.hasOwn(input, "workTarget") ? inspectWorkTarget(workTarget) : null;
1255
+ let deployment;
1256
+ try { deployment = portableScope(input.deployment); }
1257
+ catch (error) {
1258
+ if (error.code === "ENOENT") throw oatsError("needs-configuration", "deployment directory is absent; provision the explicitly selected directory and inspect again before preparation");
1259
+ throw error;
1260
+ }
1237
1261
  const previous = readLock3(deployment); // Old state refuses before scratch/fetch.
1238
1262
  const directory = mkdtempSync(join(portableStateDirectory(deployment, true), ".prepare-")), owned = lstatSync(directory);
1239
1263
  const origin = input.origin ?? { kind: "operator", document: { kind: "operator", id: "oats-prepare" }, pointer: "/source" };
1240
1264
  let repositories, primary, catalog;
1241
1265
  try {
1242
1266
  repositories = createRepositoryTransaction({ ...repositoryOptions, directory, accessContextKey: repositoryOptions.accessContextKey ?? "native" });
1243
- return prepareComposition({ ...input, deployment, origin, directory }, { repositories, previous, kernel: {
1267
+ const result = prepareComposition({ ...preparationInput, deployment, origin, directory }, { repositories, previous, kernel: {
1244
1268
  loadPackageManifestAt, capabilityCompatibility, assertPlatformInvariantLocks, materializeCapability,
1245
1269
  manifest: loadRetainedManifest,
1246
1270
  binding: runCapturedProviderBinding,
@@ -1253,6 +1277,8 @@ export function prepareCapturedComposition(input, { repositoryOptions = {} } = {
1253
1277
  settings: assertCapabilitySettingValues, skills: skillEntriesIn, hooks: manifestHookDeclarations,
1254
1278
  validateLaunchConfig, runtimeRequirements: applicableRequirements }),
1255
1279
  } });
1280
+ // Explicit work context never selects source/config, cwd, or H/work placement.
1281
+ return target ? { ...result, workTarget: target } : result;
1256
1282
  } catch (error) { primary = error; throw error; }
1257
1283
  finally {
1258
1284
  try {
@@ -4083,6 +4109,10 @@ export function composeInstanceAgentsMd(soulDir, contextDir, soulName, workMode,
4083
4109
  if (!existsSync(agentsMd)) throw new Error(`canonical soul instructions missing: ${agentsMd}`);
4084
4110
  const resolved = resolveOatsConfig(contextDir, soulName);
4085
4111
  const wanted = [];
4112
+ const declaredOperations = declaredOperationalCapabilities(soulDir);
4113
+ const oatsCoreDeclared = declaredOperations.includes("oats.core");
4114
+ if (declaredOperations.length) resolved.kernelInjection = { inject: undefined,
4115
+ provenance: `declared ${declaredOperations.join(", ")} ${declaredOperations.length === 1 ? "capability" : "capabilities"}` };
4086
4116
  const kernelInject = resolved.kernelInjection?.inject;
4087
4117
  if (kernelInject && existsSync(kernelInject)) wanted.push(["kernel:oats", kernelInject]);
4088
4118
  if (kind === "local") {
@@ -4104,7 +4134,7 @@ export function composeInstanceAgentsMd(soulDir, contextDir, soulName, workMode,
4104
4134
  }
4105
4135
  for (const inj of resolved.injects) wanted.push([`config:${inj.source}`, inj.file]);
4106
4136
  const blocks = wanted.map(([source, file]) => ({ source, file, content: readFileSync(file, "utf8").trim() }));
4107
- return { text: renderInstructionText(readFileSync(agentsMd, "utf8"), blocks), blocks, resolved };
4137
+ return { text: renderInstructionText(readFileSync(agentsMd, "utf8"), blocks), blocks, resolved, oatsCoreDeclared };
4108
4138
  }
4109
4139
 
4110
4140
  /** The skill entries a tree contributes — THE discovery rule, shared by preflight
@@ -4162,7 +4192,7 @@ export function planInstanceResources({ resolved, soulDir, agent, contextDir, co
4162
4192
  }
4163
4193
  };
4164
4194
 
4165
- for (const path of [join(PACKAGED_SKILLS_DIR, "oats"), join(PACKAGED_SKILLS_DIR, "oats-config"), join(PACKAGED_SKILLS_DIR, "oats-packages")]) {
4195
+ for (const { path } of legacyOperationalSkills(soulDir)) {
4166
4196
  add({ type: "skill-tree", source: "kernel", declared: basename(path), path: existsSync(path) ? path : undefined });
4167
4197
  }
4168
4198
  const soulSkills = soulDir && join(soulDir, "skills");
@@ -5110,15 +5140,31 @@ export function resolveYolo(value) {
5110
5140
  throw new Error("yolo must be true or false");
5111
5141
  }
5112
5142
 
5113
- export function writeSoul(root, { name, kind, repo, work, runtime, model, yolo, description, type, instructions }) {
5143
+ /** Creation chooses a declared source, never acquisition/activation or a made-up
5144
+ * future release. Existing definitions (including deliberate removal) are kept. */
5145
+ function defaultOatsCoreRequirement() {
5146
+ const mapping = officialCapabilityPackage("oats.core");
5147
+ const entry = mapping.available && mapping.migratedCapability === "oats.core" && officialPackageCatalog()[mapping.package];
5148
+ if (entry && typeof entry.url === "string" && typeof entry.ref === "string" && entry.ref) {
5149
+ const parsed = parsePortableSource(`git:${entry.url}@${entry.ref}#${entry.path ?? DEFAULT_PACKAGE_PATH}`);
5150
+ return { requires: { capabilities: { "oats.core": { source: `${parsed.source}#${parsed.path}` } } }, notes: [] };
5151
+ }
5152
+ return { requires: undefined, notes: [{ code: "needs-configuration", message: "oats.core has no published revision in the official package catalog; no source was invented. Add its explicit requirement after the catalog entry is available, or use --no-oats-core to opt out at creation." }] };
5153
+ }
5154
+
5155
+ export function writeSoul(root, { name, kind, repo, work, runtime, model, yolo, description, type, instructions, oatsCore = true }) {
5114
5156
  yolo = resolveYolo(yolo);
5157
+ if (typeof oatsCore !== "boolean") throw oatsError("E_BAD_ARGS", "oatsCore must be a boolean");
5115
5158
  const agentDir = agentDirOf(root, name, kind);
5116
- const soulDir = soulOf(agentDir);
5159
+ const soulDir = soulOf(agentDir), soulFile = join(soulDir, "soul.yaml");
5160
+ const { requires, notes } = existsSync(soulFile)
5161
+ ? { requires: soulRequirements(soulDir), notes: [] }
5162
+ : oatsCore ? defaultOatsCoreRequirement() : { requires: undefined, notes: [] };
5117
5163
  mkdirSync(soulDir, { recursive: true });
5118
5164
  mkdirSync(join(agentDir, "instances"), { recursive: true });
5119
- writeFileSync(join(soulDir, "soul.yaml"), yamlFlat({
5165
+ writeFileSync(soulFile, yamlFlat({
5120
5166
  name, kind, description, type, repo, work: work || "checkout", runtime: runtime || "pi", model, yolo,
5121
- }));
5167
+ }) + (requires === undefined ? "" : `requires: ${JSON.stringify(requires)}\n`));
5122
5168
  const agentsMd = join(soulDir, "AGENTS.md");
5123
5169
  if (instructions !== undefined || !existsSync(agentsMd)) {
5124
5170
  writeFileSync(agentsMd, instructions ?? defaultSoulAgentsMd(name, description));
@@ -5132,7 +5178,7 @@ export function writeSoul(root, { name, kind, repo, work, runtime, model, yolo,
5132
5178
  home: soulDir, instance: name, agentName: name, soulDir,
5133
5179
  contextDir: ctx, workspaceDir: workspaceOf(root), rootDir: root, resolved,
5134
5180
  });
5135
- return { agentDir, soulDir };
5181
+ return { agentDir, soulDir, ...(notes.length ? { notes } : {}) };
5136
5182
  }
5137
5183
  function defaultSoulAgentsMd(name, description) {
5138
5184
  return `# ${name}
@@ -5154,8 +5200,8 @@ export function createAgent(root, o) {
5154
5200
  // kind: "local" → a FULL soul (memory, skills, instances) under the scope's
5155
5201
  // local-agents/ — uncommitted by contract; otherwise a committed persistent soul.
5156
5202
  const kind = o.local || o.kind === "local" ? "local" : "persistent";
5157
- const { agentDir } = writeSoul(root, { ...o, name, kind });
5158
- return { agent: name, kind, soul: soulOf(agentDir) };
5203
+ const { agentDir, notes } = writeSoul(root, { ...o, name, kind });
5204
+ return { agent: name, kind, soul: soulOf(agentDir), ...(notes ? { notes } : {}) };
5159
5205
  }
5160
5206
 
5161
5207
  /** Upsert a local agent soul (from raw instructions or a Claude-style def file).
@@ -5182,13 +5228,13 @@ export function upsertLocalAgent(root, o) {
5182
5228
  const existing = findAgent(root, name);
5183
5229
  if (existing && existing.kind !== "local") throw new Error(`"${name}" is a persistent agent — spawn it instead`);
5184
5230
  if (!existing && instructions === undefined) throw new Error(`local agent "${name}" needs instructions (none on disk yet)`);
5185
- writeSoul(root, {
5186
- name, kind: "local",
5231
+ const { notes } = writeSoul(root, {
5232
+ name, kind: "local", oatsCore: o.oatsCore,
5187
5233
  repo: repo ?? existing?.repo, work: work ?? existing?.work,
5188
5234
  runtime: runtime ?? existing?.runtime, model: model ?? existing?.model, yolo: yolo ?? existing?.yolo,
5189
5235
  description: description ?? existing?.description, instructions,
5190
5236
  });
5191
- return findAgent(root, name);
5237
+ return { ...findAgent(root, name), ...(notes ? { notes } : {}) };
5192
5238
  }
5193
5239
  /** Back-compat alias: older installed capabilities (oats-okf ≤1.3.x) call this. */
5194
5240
  export const upsertTmpAgent = upsertLocalAgent;
@@ -6136,7 +6182,7 @@ export function spawnInstance(root, agent, o = {}) {
6136
6182
  symlinkSync("AGENTS.md", join(home, "CLAUDE.md"));
6137
6183
 
6138
6184
  // Runtime-neutral exact skill materialization. No harness receives ambient workspace/package skills.
6139
- const sources = [{ id: "kernel", path: join(PACKAGED_SKILLS_DIR, "oats") }, { id: "kernel", path: join(PACKAGED_SKILLS_DIR, "oats-config") }, { id: "kernel", path: join(PACKAGED_SKILLS_DIR, "oats-packages") }];
6185
+ const sources = legacyOperationalSkills(soulDir);
6140
6186
  const soulSkills = join(soulDir, "skills");
6141
6187
  if (existsSync(soulSkills)) sources.push({ id: "soul", path: soulSkills });
6142
6188
  for (const cap of resolvedCfg.capabilities) for (const path of cap.skills || []) sources.push({ id: cap.id, path });
@@ -108,7 +108,7 @@ export function preflightFreshDeployment({ deployment, maxEntries = 4096 }) {
108
108
  return result;
109
109
  }
110
110
 
111
- function workTarget(path) {
111
+ export function inspectWorkTarget(path) {
112
112
  const canonical = canonicalExistingDirectory(path, "work target"), marker = present(join(canonical, ".git"));
113
113
  return { path: canonical, state: "existing", git: marker ? { present: true, kind: marker.isDirectory() ? "directory" : marker.isFile() ? "file" : "other" } : { present: false, kind: null } };
114
114
  }
@@ -146,7 +146,7 @@ export function inspectPortableOnboarding(input, { repositories } = {}) {
146
146
  validateOrigin(input.origin);
147
147
  const standalone = explicitContext(input);
148
148
  const deployment = preflightFreshDeployment({ deployment: input.deployment });
149
- const target = workTarget(input.workTarget), discovery = createWorkspaceDiscovery(repositories);
149
+ const target = inspectWorkTarget(input.workTarget), discovery = createWorkspaceDiscovery(repositories);
150
150
  const witness = { deployment: preflightWitnesses.get(deployment), work: directoryIdentity(target.path) };
151
151
  const workspace = Object.hasOwn(input, "workspace") ? discovery.readWorkspace(input.workspace) : null;
152
152
  const selected = typeof input.source === "string" ? sourceOrigin(workspace, input.source) : { reference: input.source, origin: input.origin };
@@ -236,7 +236,7 @@ export function buildFreshPreparationRequest(inspection, options = {}) {
236
236
  const { operator, mode, allowLocalPaths = false } = options;
237
237
  if (typeof allowLocalPaths !== "boolean") throw oatsError("invalid-declaration", "allowLocalPaths must be boolean");
238
238
  if (mode !== undefined && (typeof mode !== "string" || !mode.trim())) throw oatsError("invalid-declaration", "preparation mode must be non-empty text");
239
- const input = { deployment: inspection.deployment.deployment.path,
239
+ const input = { deployment: inspection.deployment.deployment.path, workTarget: inspection.workTarget.path,
240
240
  source: inspection.source.reference,
241
241
  origin: inspection.source.revision.provenance[0], allowLocalPaths,
242
242
  ...(inspection.workspace ? { workspace: inspection.workspace.request } : { standaloneContextKey: inspection.context.key }),