@awebai/oats 0.25.9 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/bin/oats.mjs +576 -1714
- package/capabilities/oats-authoring/oats-package.json +2 -2
- package/capabilities/oats-authoring/oats.json +2 -2
- package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +46 -25
- package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +13 -6
- package/capabilities/oats-aweb/bin/oats-aweb.mjs +279 -93
- package/capabilities/oats-aweb/injects/aweb.md +7 -2
- package/capabilities/oats-aweb/lib/binding-wire.mjs +89 -13
- package/capabilities/oats-aweb/lib/captured-native.mjs +1 -1
- package/capabilities/oats-aweb/lib/grant-custody.mjs +38 -0
- package/capabilities/oats-aweb/oats.json +8 -4
- package/capabilities/oats-jira/bin/oats-jira.mjs +4 -4
- package/capabilities/oats-jira/oats.json +2 -2
- package/capabilities/oats-jira/skills/jira-tasks/SKILL.md +6 -3
- package/capabilities/oats-linear/bin/oats-linear-hook.mjs +6 -4
- package/capabilities/oats-linear/oats.json +2 -2
- package/capabilities/oats-linear/skills/linear-tasks/SKILL.md +6 -0
- package/capabilities/oats-review/oats.json +3 -2
- package/docs/capabilities.md +218 -47
- package/docs/capability-manifest.schema.json +13 -4
- package/docs/configuration.md +17 -5
- package/docs/conventions.md +16 -26
- package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +1 -1
- package/docs/design/2026-09-13-knowledge-and-memory-direction.md +3 -3
- package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +2 -2
- package/docs/design/2026-09-15-portable-souls-handoff.md +2 -2
- package/docs/design/2026-09-15-portable-souls-implementation.md +1 -1
- package/docs/design/2026-09-20-redesign-program-board.md +2 -2
- package/docs/design/2026-09-23-workspace-module-contracts.md +1 -1
- package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +1 -1
- package/docs/design/2026-09-24-desktop-phase-f-boundary.md +34 -1
- package/docs/design/2026-09-24-phase-d-plan.md +57 -0
- package/docs/design/2026-09-25-teams-contract.md +226 -0
- package/docs/design/README.md +3 -3
- package/docs/design/launch-configurations.md +20 -16
- package/docs/design/operations-contract.md +27 -10
- package/docs/desktop-cli-api.md +537 -261
- package/docs/desktop-instance-start.md +1 -1
- package/docs/desktop.md +7 -13
- package/docs/execution-targets.md +16 -18
- package/docs/first-team.md +14 -17
- package/docs/implementation.md +28 -59
- package/docs/integrations.md +64 -33
- package/docs/knowledge-capability-authoring.md +1 -1
- package/docs/knowledge-reference/package-craft.md +10 -8
- package/docs/knowledge-theory.md +1 -1
- package/docs/knowledge.md +10 -11
- package/docs/layers.md +16 -17
- package/docs/oats-local.schema.json +29 -1
- package/docs/oats-membership.schema.json +5 -3
- package/docs/oats-package.schema.json +2 -2
- package/docs/oats-workspace.schema.json +1 -1
- package/docs/{official-marketplace.md → official-catalog.md} +15 -16
- package/docs/packages.md +75 -52
- package/docs/release-notes/v0.22.0.md +1 -1
- package/docs/release-notes/v0.23.1.md +1 -1
- package/docs/release-notes/v0.26.0.md +670 -0
- package/docs/schedules.md +48 -126
- package/docs/soul.schema.json +11 -4
- package/docs/souls-and-instances.md +56 -43
- package/docs/workspaces.md +80 -58
- package/injects/instance-boundary.md +1 -1
- package/injects/work-attached.md +1 -1
- package/injects/work-workspace.md +2 -2
- package/lib/{portable-files.mjs → bounded-read.mjs} +6 -6
- package/lib/{portable-values.mjs → canonical-json.mjs} +3 -12
- package/lib/capability-contract.mjs +110 -0
- package/lib/config-data.mjs +2 -2
- package/lib/core.mjs +700 -4824
- package/lib/digest.mjs +12 -0
- package/lib/instance-inspect.mjs +396 -0
- package/lib/instance-lifecycle.mjs +3 -4
- package/lib/instance-resolution.mjs +212 -26
- package/lib/instruction-composition.mjs +0 -20
- package/lib/materialize.mjs +6 -4
- package/lib/operator-dispatch.mjs +33 -13
- package/lib/packages.mjs +25 -190
- package/lib/provider-binding.mjs +4 -2
- package/lib/provider-reasons.mjs +3 -68
- package/lib/resolve.mjs +204 -68
- package/lib/schedule.mjs +97 -272
- package/lib/servers.mjs +13 -13
- package/lib/{portable-shape.mjs → shape.mjs} +4 -3
- package/lib/tree-copy.mjs +44 -0
- package/lib/workspace.mjs +125 -20
- package/package-catalog.json +6 -6
- package/package.json +1 -1
- package/skills/integration-authoring/SKILL.md +48 -40
- package/skills/oats-getting-started/SKILL.md +105 -110
- package/skills/oats-support/SKILL.md +2 -2
- package/skills/soul-craft/SKILL.md +13 -6
- package/bin/oats-pi-sdk-host.mjs +0 -17
- package/docs/2026-09-03-architecture-proposal.md +0 -642
- package/docs/artifact-approvals.schema.json +0 -7
- package/docs/captured-invocation-context.schema.json +0 -7
- package/docs/captured-resolution.schema.json +0 -7
- package/docs/design/package-engine-contract.md +0 -813
- package/docs/design/package-runtime-api.md +0 -588
- package/docs/desktop-succession.md +0 -57
- package/docs/execution-capsule.schema.json +0 -108
- package/docs/first-team-demo.md +0 -92
- package/docs/knowledge-migration.md +0 -147
- package/docs/migration-from-oas.md +0 -103
- package/docs/oats-config.schema.json +0 -172
- package/docs/oats-lock-v3.schema.json +0 -7
- package/docs/oats-lock.schema.json +0 -175
- package/docs/operating-team-migration.md +0 -470
- package/docs/portable.schema.json +0 -2512
- package/docs/provider-check-input.schema.json +0 -7
- package/docs/rebuild-to-v2.md +0 -511
- package/docs/workspace-adoption.md +0 -74
- package/injects/framework-workspace.md +0 -7
- package/injects/local-soul.md +0 -19
- package/injects/oats-portable.md +0 -20
- package/injects/oats.md +0 -11
- package/injects/portable-instance-boundary.md +0 -39
- package/injects/portable-work-directory.md +0 -29
- package/lib/artifact-approvals.mjs +0 -120
- package/lib/artifact-tree.mjs +0 -141
- package/lib/capability-artifacts.mjs +0 -179
- package/lib/capability-execution.mjs +0 -15
- package/lib/capability-inputs.mjs +0 -39
- package/lib/capability-provenance.mjs +0 -231
- package/lib/captured-action-shape.mjs +0 -21
- package/lib/captured-admission-shape.mjs +0 -20
- package/lib/captured-binding-file.mjs +0 -36
- package/lib/captured-dispatch.mjs +0 -66
- package/lib/captured-instance-index.mjs +0 -277
- package/lib/captured-invocation-context.mjs +0 -130
- package/lib/captured-launch-request.mjs +0 -66
- package/lib/captured-operation-process.mjs +0 -15
- package/lib/captured-pi-custody.mjs +0 -29
- package/lib/captured-pi-host.mjs +0 -167
- package/lib/captured-pi-outcome.mjs +0 -172
- package/lib/captured-resolutions.mjs +0 -275
- package/lib/captured-scaffold.mjs +0 -87
- package/lib/captured-selector.mjs +0 -28
- package/lib/captured-session-backend.mjs +0 -52
- package/lib/captured-source-receipt-file.mjs +0 -72
- package/lib/helper-injection-policy.mjs +0 -104
- package/lib/legacy-lock-codec.mjs +0 -106
- package/lib/manifest-settings.mjs +0 -84
- package/lib/package-closure.mjs +0 -48
- package/lib/package-materialization.mjs +0 -83
- package/lib/pi-sdk-host.mjs +0 -229
- package/lib/portable-artifacts.mjs +0 -115
- package/lib/portable-choices.mjs +0 -82
- package/lib/portable-composition.mjs +0 -136
- package/lib/portable-digest.mjs +0 -105
- package/lib/portable-identity.mjs +0 -40
- package/lib/portable-lock.mjs +0 -117
- package/lib/portable-onboarding-request.mjs +0 -49
- package/lib/portable-onboarding.mjs +0 -256
- package/lib/portable-package-preparation.mjs +0 -188
- package/lib/portable-policy.mjs +0 -44
- package/lib/portable-soul.mjs +0 -42
- package/lib/portable-state.mjs +0 -80
- package/lib/prepare-composition.mjs +0 -170
- package/lib/prepared-bindings.mjs +0 -92
- package/lib/prepared-resources.mjs +0 -127
- package/lib/provider-binding-broker.mjs +0 -65
- package/lib/provider-binding-wire.mjs +0 -116
- package/lib/readiness.mjs +0 -225
- package/lib/repository-observation.mjs +0 -226
- package/lib/resolution-shape.mjs +0 -393
- package/lib/schedule-capsule.mjs +0 -206
- package/lib/soul-constraints.mjs +0 -40
- package/lib/source-projection.mjs +0 -84
- package/lib/source-spec.mjs +0 -189
- package/lib/workspace-definition.mjs +0 -126
- package/lib/workspace-discovery.mjs +0 -146
- package/skills/oats/SKILL.md +0 -162
- package/skills/oats-config/SKILL.md +0 -164
- package/skills/oats-packages/SKILL.md +0 -184
- package/skills/oats-portable/SKILL.md +0 -115
- package/skills/oats-portable-artifacts/SKILL.md +0 -63
package/docs/desktop-cli-api.md
CHANGED
|
@@ -18,9 +18,11 @@ prints exactly one JSON object on stdout:
|
|
|
18
18
|
```
|
|
19
19
|
|
|
20
20
|
`version` is the installed package's exact semver (e.g. `0.20.0`).
|
|
21
|
-
Desktop
|
|
22
|
-
(
|
|
23
|
-
|
|
21
|
+
The Desktop accepts `desktopApi === 1` and gates on the kernel feature
|
|
22
|
+
`packages-no-approval` (semver range `>=0.25.8 <0.27.0`: the floor admits the
|
|
23
|
+
main-branch kernel before 0.26.0 is tagged; the feature fence is the real gate).
|
|
24
|
+
Earlier bands were `>=0.22.0 <0.26.0` (Desktop 0.25) and `>=0.22.0 <0.24.0`
|
|
25
|
+
(Desktop 0.23). It does not establish complete
|
|
24
26
|
UI, backend, plugin, retirement or recovery parity; capability checks and explicit
|
|
25
27
|
refusals below remain authoritative.
|
|
26
28
|
|
|
@@ -46,49 +48,348 @@ no progress prose (progress goes to stderr):
|
|
|
46
48
|
- success (exit 0): `{"schemaVersion":1,"ok":true,"result":{...}}`
|
|
47
49
|
- failure (nonzero exit): `{"schemaVersion":1,"ok":false,"error":{"code":"...","message":"..."}}`
|
|
48
50
|
|
|
49
|
-
##
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- `
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
51
|
+
## Inspect, readiness and operation run on the workspace model (`operationsApi: 2`, `soulsApi: 2`, `readinessApi: 2`, OATS 0.26.0)
|
|
52
|
+
|
|
53
|
+
On a workspace deployment (an `oats-local.yaml` in reach of `--dir`), and for
|
|
54
|
+
any home whose `instance.json` records `modules`, these three commands read the
|
|
55
|
+
workspace model's own records and **never the classic config chain**. The probe
|
|
56
|
+
integers are the gate; there is no feature string. The probe's integer says
|
|
57
|
+
this kernel CAN answer the v2 shape. **Dispatch on the payload's own integer**.
|
|
58
|
+
There is no v1 shape any more (0.26.0 removed the classic chain's answers):
|
|
59
|
+
with no `oats-local.yaml` in reach and no `--home`, the three commands answer
|
|
60
|
+
`E_LOCAL_MISSING`; a `--home` whose `instance.json` records no `modules`
|
|
61
|
+
(spawned by an earlier kernel) answers `E_UNSUPPORTED_MODE` (re-spawn it from
|
|
62
|
+
the deployment); an unreadable `--home` answers `E_SESSION_UNKNOWN`.
|
|
63
|
+
|
|
64
|
+
**The captured/portable path was removed in 0.26.** A *captured home* (its `instance.json`
|
|
65
|
+
records `executionBinding`, `incarnationId` or `captured`: spawned through
|
|
66
|
+
0.24–0.25's `prepare` / `--deployment --resolution`) answers `E_UNSUPPORTED_MODE`
|
|
67
|
+
(`details: {home, captured: true}`) to these three commands, to `session
|
|
68
|
+
start|restart` and to its in-home commands; `oats retire` still works on it, and
|
|
69
|
+
its result's `warnings[]` names each capability whose retire hook did NOT run
|
|
70
|
+
(what it created is not revoked). `oats status --json` / `oats doctor --json`
|
|
71
|
+
name captured homes once, in `problems[]`, as `legacy-captured-home` `{instances,
|
|
72
|
+
homes, message}`. The captured selectors `--deployment`, `--resolution` and
|
|
73
|
+
`--artifact-set`, and an inherited `OATS_DEPLOYMENT`/`OATS_RESOLUTION`, are refused
|
|
74
|
+
by every command except `version` (`E_UNSUPPORTED_MODE`, `details.selector` or
|
|
75
|
+
`details.inherited`); `oats prepare` and `oats inspect --request` are removed
|
|
76
|
+
verbs (`E_UNKNOWN_COMMAND`, `details: {removed, replacement}`). The version
|
|
77
|
+
document no longer carries `capturedDispatchApi` / `capturedDispatchActions`.
|
|
78
|
+
|
|
79
|
+
| Command | Integer (probe and payload) | 0.25.x value |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `oats inspect --json` | `operationsApi: 2` (top level); each `souls[]` row `soulsApi: 2` | 1 / 1 |
|
|
82
|
+
| `oats readiness --json` | `readinessApi: 2` | 1 |
|
|
83
|
+
| `oats operation run --json` | `operationsApi: 2` on the result | absent |
|
|
84
|
+
|
|
85
|
+
The probe's `soulsApi` follows the inspect soul rows. The `oats souls --json`
|
|
86
|
+
document keeps its own `soulsApi: 1`, because its shape did not change (see
|
|
87
|
+
[`oats souls`](#oats-capabilities---dir---json-capabilitiesapi-1-oats-souls---dir---json-soulsapi-1)).
|
|
88
|
+
|
|
89
|
+
**The subject is an instance or a soul, never a scope.** Pass `--home <abs>`
|
|
90
|
+
or `--soul <name>`. A workspace deployment with neither is `E_BAD_ARGS`. An
|
|
91
|
+
`oats-local.yaml` that exists but cannot be read is reported with its own
|
|
92
|
+
error code, never answered from the classic chain. For
|
|
93
|
+
inspect, the message points to `oats souls` and `oats capabilities`, the
|
|
94
|
+
scope-wide lists.
|
|
95
|
+
- `--home` selects the instance, from its `instance.json` and the module copies
|
|
96
|
+
under `<home>/.oats/modules/`. Everything is as spawned.
|
|
97
|
+
- `--soul` selects the soul, resolved exactly as a spawn of it would be:
|
|
98
|
+
discovery, then soul `capabilities:` plus workspace defaults, then the lock.
|
|
99
|
+
- A v2 home lives at `<deployment>/agents/<soul>/instances/<name>`, and its
|
|
100
|
+
deployment is derived from that path. `<deployment>/oats-local.yaml` must
|
|
101
|
+
exist exactly there (never found by walking up), otherwise
|
|
102
|
+
`E_HOME_MISMATCH`. A v2 spawn ignores an ambient `PI_AGENTS_ROOT` /
|
|
103
|
+
`OATS_ROOT`, so its homes always have this layout.
|
|
104
|
+
- `--dir`, if given with `--home`, must be that home's deployment
|
|
105
|
+
(`E_HOME_MISMATCH`). `--agents-root`, if given, must be
|
|
106
|
+
`<deployment>/agents` (`E_HOME_MISMATCH` with `--home`, `E_SOUL_UNKNOWN`
|
|
107
|
+
with `--soul`).
|
|
108
|
+
|
|
109
|
+
**Gone from every payload:** `scope` (`context`, `chain`, `team`,
|
|
110
|
+
`agentsRoots`), config `levels`, `activation {declaredAt, target, level,
|
|
111
|
+
source}`, `currentConfig`, `snapshot.drift`, `health {trusted, approved,
|
|
112
|
+
locked, installedIntegrity}`, soul `provenance`/`readiness`, and the scope's
|
|
113
|
+
portable `sources`. A capability's origin is its module's `from` (member
|
|
114
|
+
commit, or package version + commit + integrity). Its settings are the merged
|
|
115
|
+
payload the spawn recorded for a home, or the resolution computes for a soul.
|
|
116
|
+
|
|
117
|
+
### `oats inspect (--home <abs> | --soul <name> [--dir <d>]) --json` → `operationsApi: 2`
|
|
79
118
|
|
|
80
119
|
```json
|
|
81
|
-
{"
|
|
82
|
-
"
|
|
120
|
+
{"operationsApi":2,"kernel":"0.26.0",
|
|
121
|
+
"subject":{"kind":"instance","instance":"release-manager-x","home":"/w/agents/release-manager/instances/release-manager-x","soul":"release-manager"},
|
|
122
|
+
"workspace":{"key":"github.com/northwind/agents","name":null,"deployment":"/w","commit":"461b9c24…","standalone":false},
|
|
123
|
+
"souls":[{"soulsApi":2,"name":"release-manager","repoKey":"github.com/northwind/agents","commit":"461b9c24…","team":"engineering",
|
|
124
|
+
"kind":null,"path":null,"description":"Cuts, verifies and announces platform releases.","work":"worktree","runtime":null,"model":null,
|
|
125
|
+
"declarations":{"requires":null,"defaults":null,"knowledge":{"owns":"release-manager","reads":["platform-engineer"]},"teams":null,"resources":null,"children":null,
|
|
126
|
+
"capabilities":{"nw-release-tooling":{"from":"here"},"nw-deploy":{"from":"package"}}},
|
|
127
|
+
"declarationProblems":[],
|
|
128
|
+
"instructions":{"file":"/w/agents/release-manager/souls/461b9c24929c/AGENTS.md","text":"# release-manager\n…","truncated":false}}],
|
|
129
|
+
"layers":{"knowledge":{"id":"oats.okf","from":"workspace"},"messaging":{"id":null,"from":null},"tasks":{"id":null,"from":null}},
|
|
130
|
+
"capabilities":[
|
|
131
|
+
{"id":"nw-house-style","version":"0.0.0-workspace","layer":null,"command":null,
|
|
132
|
+
"from":{"kind":"member","repoKey":"github.com/northwind/agents","commit":"461b9c24…"},
|
|
133
|
+
"dir":"/w/agents/release-manager/instances/release-manager-x/.oats/modules/nw-house-style","settings":{},"declares":[],"compatibility":{"ok":true,"range":">=0.25.0","kernel":"0.26.0"},"missingRequires":[],"operations":[]},
|
|
134
|
+
{"id":"oats.okf","version":"2.1.3","layer":"knowledge","command":"okf",
|
|
135
|
+
"from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"71f53649…","integrity":"sha256-5019…","repoKey":"github.com/awebai/oats-okf"},
|
|
136
|
+
"dir":"/w/agents/release-manager/instances/release-manager-x/.oats/modules/oats.okf",
|
|
137
|
+
"settings":{"owns":"release-manager","reads":["platform-engineer"],"state-dir":"/srv/okf"},"declares":["bindings-file","git-timeout","harvest-model","harvest-runtime","state-dir"],"compatibility":{"ok":true,"range":">=0.24.4","kernel":"0.26.0"},"missingRequires":[],
|
|
138
|
+
"operations":[{"name":"inspect","kind":"view","command":"inspect","context":"home","description":"…","args":[],"argv":["okf","inspect"],"available":true,"reason":null}]}],
|
|
139
|
+
"knowledge":{"provider":"oats.okf","version":"2.1.3","operations":[{"name":"inspect","kind":"view","available":true,"reason":null}]},
|
|
140
|
+
"instance":{"home":"/w/agents/release-manager/instances/release-manager-x","instance":"release-manager-x","agent":"release-manager",
|
|
141
|
+
"runtime":"pi","model":null,"yolo":null,"launched":false,"createdAt":"<iso>","resolution":"7217670b…",
|
|
142
|
+
"soulDir":"/w/agents/release-manager/souls/461b9c24929c",
|
|
143
|
+
"instructions":{"file":"/w/agents/release-manager/instances/release-manager-x/AGENTS.md","text":"…","truncated":false,
|
|
144
|
+
"sources":[{"source":"kernel:instance-boundary","file":"…"},{"source":"capability:oats.okf","file":"…/.oats/modules/oats.okf/injects/okf.md"}]}},
|
|
145
|
+
"identity":null,"problems":[]}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- `subject` is `{kind:"instance", instance, home, soul}` for `--home` (the
|
|
149
|
+
`home` as you passed it) or `{kind:"soul", soul, repoKey, commit, team}` for
|
|
150
|
+
`--soul`.
|
|
151
|
+
- `workspace.deployment` is canonical (realpath). `workspace.name` is
|
|
152
|
+
observed, so it is `null` on `inspect --home`: that command never contacts
|
|
153
|
+
the remotes, and `instance.json` records the workspace `key`, not its name.
|
|
154
|
+
Identify the workspace by `key`.
|
|
155
|
+
- `souls` holds exactly the subject's soul. For a home, it is read from the
|
|
156
|
+
recorded `soulDir` (the per-commit copy the instance incarnates), with
|
|
157
|
+
`path: null`. For a soul, it is the member's current definition, with
|
|
158
|
+
`path` inside the member repository. `kind` is `member` or `external`.
|
|
159
|
+
It is observed from discovery, so it is `null` on `inspect --home`
|
|
160
|
+
(readiness `--home` observes it).
|
|
161
|
+
`declarations` gains `capabilities` (the soul's own `capabilities:`).
|
|
162
|
+
- `layers.<layer>` is `{ id, from }`: the capability filling the slot
|
|
163
|
+
(`null` when empty) and where it came from (feature `layers-from`):
|
|
164
|
+
`"soul"` (the soul's own `capabilities:`), `"workspace"`
|
|
165
|
+
(`defaults.<slot>` or `defaults.capabilities`) or `"team:<label>"`
|
|
166
|
+
(`defaults.byTeam.<label>.capabilities`). `from` is `null` for an empty
|
|
167
|
+
slot. A soul answers from its resolution now. A home answers what its spawn
|
|
168
|
+
recorded, even after the workspace changes. A home spawned before
|
|
169
|
+
`layers-from` recorded nothing, so its `from` is `null`.
|
|
170
|
+
- `capabilities[]` lists the subject's resolved modules, sorted by id:
|
|
171
|
+
- `dir` is the home's module copy, or `null` for a soul (nothing is
|
|
172
|
+
materialized to answer inspect).
|
|
173
|
+
- `settings` is the merged provider payload.
|
|
174
|
+
- `compatibility` is `{ ok, range, kernel }`: the manifest's
|
|
175
|
+
`compatibility.oats` (`null` when none) against the running kernel. A
|
|
176
|
+
soul's resolution refuses an incompatible module (`E_CAPABILITY_INCOMPATIBLE`),
|
|
177
|
+
so `ok: false` appears only for a home, together with a
|
|
178
|
+
`capability-incompatible` entry in `problems`.
|
|
179
|
+
- `declares` lists the setting keys the manifest declares (`settings.<key>`),
|
|
180
|
+
sorted; names only, never descriptions or defaults; `[]` when it declares
|
|
181
|
+
none. Gate on feature `settings-declared` (e.g. offer a Teams choice only
|
|
182
|
+
when the messaging module declares `join`).
|
|
183
|
+
- `missingRequires` lists the manifest `requires` commands absent from PATH.
|
|
184
|
+
- `operations[].available` is `false` with a `reason` when it cannot run
|
|
185
|
+
here: a `context: "home"` operation for a soul subject says `needs a
|
|
186
|
+
running home (--home)`.
|
|
187
|
+
- `instance` is `null` for a soul. For a home, `instructions.sources` names
|
|
188
|
+
each composed inject in order.
|
|
189
|
+
- A soul whose resolution is refused (for example, a package the lock does
|
|
190
|
+
not provide) is an error for inspect (`E_PACKAGE_MISSING`,
|
|
191
|
+
`E_PACKAGE_INTEGRITY`, `E_CAPABILITY_MISSING`, `E_LOCK_SCHEMA`). Readiness
|
|
192
|
+
reports the same condition as a failing item.
|
|
193
|
+
|
|
194
|
+
### `oats readiness (--home <abs> | --soul <name> [--dir <d>]) [--policy] --json` → `readinessApi: 2`
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{"readinessApi":2,
|
|
198
|
+
"subject":{"kind":"soul","soul":"release-manager","repoKey":"github.com/northwind/agents","commit":"461b9c24…","team":"engineering"},
|
|
199
|
+
"selector":{"kind":"soul","soul":"release-manager","agentsRoot":null,"dir":"/w"},"at":"<iso>",
|
|
200
|
+
"checks":{
|
|
201
|
+
"installed":{"status":"pass","items":[
|
|
202
|
+
{"subject":"oats.okf","status":"pass","required":true,"reason":null,"producer":"workspace resolution",
|
|
203
|
+
"evidence":{"from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"71f53649…","integrity":"sha256-5019…"}},"remedy":null,"capability":{"id":"oats.okf"}}]},
|
|
204
|
+
"configured":{"status":"not-applicable","items":[]},
|
|
205
|
+
"member":{"status":"pass","items":[
|
|
206
|
+
{"subject":"member github.com/northwind/agents","status":"pass","required":true,"reason":null,"producer":"workspace discovery",
|
|
207
|
+
"evidence":{"repoKey":"github.com/northwind/agents","workspace":"github.com/northwind/agents","commit":"461b9c24…"},"remedy":null}]},
|
|
208
|
+
"providers":{"status":"fail","items":[
|
|
209
|
+
{"subject":"oats.okf","status":"fail","required":true,"reason":"setting state-dir is required (absolute host path)","producer":"provider binding check",
|
|
210
|
+
"evidence":null,"remedy":null,"capability":{"id":"oats.okf"},
|
|
211
|
+
"result":{"status":"needs-configuration","problems":[{"code":"needs-configuration","message":"setting state-dir is required (absolute host path)"}],"warnings":[]}}]}},
|
|
212
|
+
"summary":{"ready":false,"required":3,"pass":2,"fail":1,"unknown":0,
|
|
213
|
+
"byCapability":[{"capability":{"id":"oats.okf"},"checks":{"installed":"pass","configured":"not-applicable","member":"not-applicable","providers":"fail"},"ownReady":false,"ready":false}],
|
|
214
|
+
"subjectBlockers":[]},
|
|
215
|
+
"notes":["…"]}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
For `--home`, `subject` is `{kind:"instance", instance, home, soul}`, and
|
|
219
|
+
`selector` is `{kind:"home", home, soul, agentsRoot}`. The selector echoes
|
|
220
|
+
your arguments byte-exact, as before. It is now a top-level field, not
|
|
221
|
+
`subject.selector`.
|
|
222
|
+
|
|
223
|
+
The four checks are `installed | configured | member | providers`, each
|
|
224
|
+
`{status, items}` with the item fields as before (`subject, status, required,
|
|
225
|
+
reason, producer, evidence, remedy`, plus `capability {id}` on
|
|
226
|
+
per-capability items). Item and check statuses are `pass | fail | unknown |
|
|
227
|
+
not-applicable`. **`summary.ready`** means every required item passes or is
|
|
228
|
+
not-applicable, and at least one required item exists. `byCapability` and
|
|
229
|
+
`subjectBlockers` keep their 0.24.9 meaning over the four new checks.
|
|
230
|
+
|
|
231
|
+
- **`installed`**:
|
|
232
|
+
- For `--home` (producer `instance modules`): each recorded module, `pass`
|
|
233
|
+
when its copy under `<home>/.oats/modules/<id>/` holds its `oats.json`.
|
|
234
|
+
A missing copy fails, with remedy "spawn a new instance".
|
|
235
|
+
- For `--soul` (producer `workspace resolution`): each resolved module, with
|
|
236
|
+
its `from` as evidence.
|
|
237
|
+
- A resolution refusal is one failing item carrying `code` (`E_PACKAGE_MISSING`,
|
|
238
|
+
`E_PACKAGE_INTEGRITY`, `E_CAPABILITY_MISSING`, `E_LOCK_SCHEMA` or
|
|
239
|
+
`E_REQUIREMENT_INACTIVE`), the kernel's message as `reason`, its details as
|
|
240
|
+
`evidence`, and `remedy: "oats sync (…)"`. That covers a soul whose
|
|
241
|
+
packages are not locked, or whose lock no longer matches. The item's
|
|
242
|
+
`subject` is the soul; it lands in `summary.subjectBlockers`.
|
|
243
|
+
- **`configured`** (producer `capability manifest`): each module's manifest
|
|
244
|
+
`requires` command (`evidence.command`), `pass` on PATH and `fail`
|
|
245
|
+
otherwise, with the manifest's `install` hint as the remedy. It is `not-applicable` when nothing declares a
|
|
246
|
+
requirement. Settings problems are the provider's to say, in `providers`.
|
|
247
|
+
- **`member`** (producer `workspace discovery`): the soul's member
|
|
248
|
+
repository is confirmed in the workspace. It is the host's `members:` with
|
|
249
|
+
the member's `oats-membership.yaml` backlink, as `oats workspace status`
|
|
250
|
+
reports it. The kernel never reads `oats.yaml` here.
|
|
251
|
+
- `fail` when the backlink is not confirmed; the remedy names
|
|
252
|
+
`oats-membership.yaml`.
|
|
253
|
+
- `unknown` when discovery could not be read.
|
|
254
|
+
- `not-applicable` (`required: false`) for an external soul (it has no
|
|
255
|
+
member backlink) and on a standalone view (decision 10, an allowed mode:
|
|
256
|
+
membership is declared there, never confirmed). The reason starts with
|
|
257
|
+
`standalone view (explicit | unreadable-host)`, and
|
|
258
|
+
`evidence.standaloneReason` carries the reason code. A standalone soul can
|
|
259
|
+
therefore read Ready.
|
|
260
|
+
- Never login, never team registration.
|
|
261
|
+
- **`providers`** (producer `provider binding check`): for each module whose
|
|
262
|
+
manifest declares `binding`, the kernel runs the provider's own check
|
|
263
|
+
(`binding.check`). It relays **the provider's answer verbatim** as
|
|
264
|
+
`item.result: {status, problems: [{code, message}], warnings: [{code,
|
|
265
|
+
message}]}`. The status maps to the item:
|
|
266
|
+
|
|
267
|
+
| `result.status` | item `status` |
|
|
268
|
+
|---|---|
|
|
269
|
+
| `ready` | `pass` |
|
|
270
|
+
| `needs-configuration` | `fail` |
|
|
271
|
+
| `authorization-required` | `fail` |
|
|
272
|
+
| `unavailable` | `unknown` |
|
|
273
|
+
|
|
274
|
+
The reason is the first problem's message (`null` on pass).
|
|
275
|
+
`authorization-required` and `unavailable` were added in 0.26.0 (additive):
|
|
276
|
+
treat an unrecognized status as `unknown` and show `result` as sent.
|
|
277
|
+
- `warnings` is always present (`[]` when the provider sends none). It
|
|
278
|
+
**never changes the status** and is not counted in `summary`. A ready
|
|
279
|
+
binding can still say, for example, that end-to-end encryption is off:
|
|
280
|
+
show it next to the pass. A `warnings` that is not an array of `{code,
|
|
281
|
+
message}` strings makes the whole answer `unknown`, the same as a
|
|
282
|
+
malformed `problems`.
|
|
283
|
+
- A provider that cannot answer is `unknown`, with `item.problems` carrying
|
|
284
|
+
its error `{code, message}` and `result: null`. That covers:
|
|
285
|
+
- a refusal (`ok:false`, whose code is relayed);
|
|
286
|
+
- an invalid answer (`provider-unavailable`, see the wire below);
|
|
287
|
+
- a timeout;
|
|
288
|
+
- a module tree that cannot be made available.
|
|
289
|
+
- **One time budget per readiness read**: 60 s for all provider checks
|
|
290
|
+
together, and at most 30 s for each. Checks the budget does not reach are
|
|
291
|
+
not run; they are `unknown` with code `time-budget-exhausted`.
|
|
292
|
+
- For `--home` the check runs from the home's module copy, as the home's
|
|
293
|
+
hooks do. For `--soul` it runs from the module in the deployment's module
|
|
294
|
+
store (the tree `oats <ns> …` dispatch uses). A store tree is used only
|
|
295
|
+
while its content digest matches the digest verified when it was fetched
|
|
296
|
+
at the locked commit. A drifted tree is fetched again, and a fetch that
|
|
297
|
+
does not verify is `E_PACKAGE_INTEGRITY` (the item is `unknown` with that
|
|
298
|
+
code).
|
|
299
|
+
- A module without `binding` has no item; the check is `not-applicable`
|
|
300
|
+
when there are none.
|
|
301
|
+
- This check reads the provider; it does not bind. A spawn's fail-closed
|
|
302
|
+
hooks are unchanged.
|
|
303
|
+
- **Removed:** `trusted` and its `signature` block (declaring a package in
|
|
304
|
+
`packages:` is the trust decision). `--verify-signatures` answers
|
|
305
|
+
`E_BAD_ARGS`. `enrolled` is now `member`.
|
|
306
|
+
|
|
307
|
+
`--policy` is **kept**: it means the same without the chain. With `--home` it
|
|
308
|
+
is the instance's recorded, enforced policy (`instance.json` `policy`, plus
|
|
309
|
+
the recorded work mode). With `--soul` it is the soul's declaration
|
|
310
|
+
(`children.spawn`, `work`), `enforced: false`. The shape is unchanged:
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
"policy":{"childSpawns":{"allowed":true,"enforced":true,"origin":{"kind":"default","detail":"no declaration: children allowed"}},
|
|
314
|
+
"worktrees":{"allowed":false,"mode":"directory","enforced":true,"origin":{"kind":"work-mode","detail":"work: directory"}}}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
**The provider check wire (the request `binding.check` receives).** The
|
|
318
|
+
request is one JSON line on stdin, and the provider answers one envelope line
|
|
319
|
+
on stdout:
|
|
320
|
+
|
|
321
|
+
```json
|
|
322
|
+
{"schemaVersion":1,"phase":"check","slot":"knowledge","capability":"oats.okf","settings":{"…":"the merged payload"},
|
|
323
|
+
"input":{"context":{"kind":"workspace","workspace":"<workspace key>","deployment":"/w","soul":"release-manager","team":"engineering",
|
|
324
|
+
"instance":"release-manager-x","home":"/w/agents/…/release-manager-x"},
|
|
325
|
+
"action":{"kind":"readiness"}}}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
```json
|
|
329
|
+
{"schemaVersion":1,"phase":"check","slot":"knowledge","capability":"oats.okf","ok":true,
|
|
330
|
+
"result":{"status":"ready","problems":[],"warnings":[]}}
|
|
83
331
|
```
|
|
84
332
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
333
|
+
The environment is the provider's module environment:
|
|
334
|
+
- `OATS_CAPABILITY`, `OATS_SETTINGS`, `OATS_CLI_BIN` and `OATS_WORKSPACE`;
|
|
335
|
+
- the team variables (`OATS_TEAM_*`, `OATS_WORKSPACE_NAME`/`_KEY`);
|
|
336
|
+
- `OATS_AGENT` (the soul), and `OATS_SOUL` when the soul directory is known;
|
|
337
|
+
- for a home, `OATS_INSTANCE` and `OATS_INSTANCE_HOME`.
|
|
338
|
+
|
|
339
|
+
For a home, `OATS_WORKSPACE_NAME` is `""` until spawn records the workspace
|
|
340
|
+
name (planned).
|
|
341
|
+
|
|
342
|
+
Ambient `OATS_*`/`PI_*` is removed. For a soul, `instance` and `home` are
|
|
343
|
+
`null`. The answer is decoded by the binding wire's response rules:
|
|
344
|
+
- the process exits 0;
|
|
345
|
+
- stdout is exactly one JSON document within the wire limits;
|
|
346
|
+
- the envelope has exactly `schemaVersion`, `phase`, `slot`, `capability`,
|
|
347
|
+
`ok` and `result` (or `error`), echoing the request's first four;
|
|
348
|
+
- `result` has `status`, `problems` and optionally `warnings`, and nothing else;
|
|
349
|
+
- `ready` carries no problems;
|
|
350
|
+
- problems and warnings are `{code, message}` strings. Their codes are the
|
|
351
|
+
provider's own and are not checked against `binding.reasons`.
|
|
352
|
+
|
|
353
|
+
Anything else is `unknown` (`provider-unavailable`). The check executable must
|
|
354
|
+
resolve (realpath) inside its module directory and be a regular file; otherwise
|
|
355
|
+
the item is `unknown` (`resource-not-found`). The request carries no
|
|
356
|
+
`binding`. A provider whose check
|
|
357
|
+
still requires one answers `invalid-binding`, and readiness reports it as
|
|
358
|
+
`unknown`.
|
|
359
|
+
|
|
360
|
+
### `oats operation run <layer>:<name> (--home <abs> | --soul <name> [--dir <d>]) [--arg k=v …] --json` → `operationsApi: 2`
|
|
361
|
+
|
|
362
|
+
```json
|
|
363
|
+
{"operationsApi":2,"operation":"knowledge:inspect","capability":"oats.okf","version":"2.1.3","argv":["okf","inspect"],
|
|
364
|
+
"cwd":"/w/agents/release-manager/instances/release-manager-x",
|
|
365
|
+
"target":{"home":"/w/agents/release-manager/instances/release-manager-x","instance":"release-manager-x"},
|
|
366
|
+
"result":{"documents":[{"label":"Working state (STATE.md)","kind":"markdown","text":"…"}]}}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
The provider is the module that fills `<layer>`:
|
|
370
|
+
- for `--home`, the home's module copy, with its recorded settings;
|
|
371
|
+
- for `--soul`, the resolved module, materialized into the deployment's module
|
|
372
|
+
store if needed.
|
|
373
|
+
|
|
374
|
+
The rest of the contract is unchanged ([operations contract](design/operations-contract.md)):
|
|
375
|
+
- errors: `E_OPERATION_UNKNOWN`, `E_OPERATION_UNAVAILABLE` (also a
|
|
376
|
+
`context: "home"` operation without `--home`), `E_CAPABILITY_REQUIRES`;
|
|
377
|
+
- the receipt rules and the `E_OPERATION_TIMEOUT` / `E_OPERATION_RESULT`
|
|
378
|
+
unconfirmed outcomes.
|
|
379
|
+
|
|
380
|
+
There is no `E_CAPABILITY_BLOCKED` (no trust gate). `cwd` is the home for a
|
|
381
|
+
`context: "home"` operation, and the deployment otherwise.
|
|
382
|
+
|
|
383
|
+
**Remote.** `--server` routes as before. The destination must advertise
|
|
384
|
+
`operations` with `operationsApi` 1 or 2; a 0.26 CLI routes to either.
|
|
385
|
+
Payload shapes are the destination kernel's.
|
|
386
|
+
|
|
387
|
+
## Souls and sources (`oats inspect --json`, `soulsApi: 1`) — removed in 0.26.0
|
|
388
|
+
|
|
389
|
+
The classic scope document (`souls[].provenance`, `souls[].readiness`, the
|
|
390
|
+
scope's portable `sources`) was removed with the classic config chain.
|
|
391
|
+
`oats inspect` answers only [`soulsApi: 2`](#oats-inspect---home---soul---dir---json-operationsapi-2)
|
|
392
|
+
rows; the soul's declarations are in `oats souls --json`.
|
|
92
393
|
|
|
93
394
|
## Instance Git state (`oats instance git|diff`, `instanceGitApi: 1`, OATS 0.24.7+)
|
|
94
395
|
|
|
@@ -258,8 +559,8 @@ deduplicated per run. Where the run launched or targeted an instance, a
|
|
|
258
559
|
session to open (the existing `oats session` surface); the kernel does not
|
|
259
560
|
copy transcripts. `nextRun`/`lastRun`/`executionStatus` are unchanged. The
|
|
260
561
|
Schedules view (frame 08) renders `recentRuns` as the recent-runs list and the
|
|
261
|
-
transcript pointer as the handoff
|
|
262
|
-
|
|
562
|
+
transcript pointer as the handoff (definition fields are untouched by this
|
|
563
|
+
addition). A stored captured definition (removed in 0.26) lists as `invalid`.
|
|
263
564
|
|
|
264
565
|
### History API 3 (`scheduleHistoryApi: 3`, feature `schedule-read-2`, OATS 0.24.13+) — K8b
|
|
265
566
|
|
|
@@ -332,15 +633,16 @@ executable, runtime packages, child-spawn policy) and returns what the spawn
|
|
|
332
633
|
|
|
333
634
|
```json
|
|
334
635
|
{"spawnPreviewApi":1,"preview":true,"agent":"dev","kind":"persistent","instance":"dev-fix-login","home":"/abs/agents/dev/instances/dev-fix-login",
|
|
335
|
-
"repo":"/abs/repo","work":"worktree","runtime":"claude","model":"opus","modelSource":"
|
|
636
|
+
"repo":"/abs/repo","work":"worktree","runtime":"claude","model":"opus","modelSource":"explicit","launchConfig":null,"yolo":false,"backend":"tmux",
|
|
336
637
|
"branch":"agents/dev-fix-login","base":{"ref":"HEAD","oid":"<oid>"},"worktree":"/abs/agents/dev/instances/dev-fix-login/work",
|
|
337
638
|
"relation":null,"parentInstance":null,"policy":{"childSpawns":{"allowed":true,"origin":{"kind":"default","detail":"…"}}},
|
|
338
639
|
"executable":"/abs/bin/claude","capabilities":["oats.core"],"skills":["oats-operate","oats-souls"],"task":"…"}
|
|
339
640
|
```
|
|
340
641
|
|
|
341
|
-
- **Name / work area**: `instance` is the
|
|
342
|
-
de-duplicated with `-2`, `-3`…)
|
|
343
|
-
|
|
642
|
+
- **Name / work area**: `instance` is the name: by default the derived shape
|
|
643
|
+
`<agent>-<purpose>` (de-duplicated with `-2`, `-3`…), or exactly the
|
|
644
|
+
`--name <slug>` the caller gave (see *Instance names* below); `home` and
|
|
645
|
+
`worktree` are the canonical paths. The renderer never derives paths.
|
|
344
646
|
- **Branch / base** (worktree mode): `branch` defaults to `agents/<instance>`
|
|
345
647
|
(`--branch <name>` overrides; validated); `base` is `--base <ref>` resolved
|
|
346
648
|
to its commit oid (default `HEAD`). `E_BRANCH_EXISTS` and `E_BASE_UNKNOWN`
|
|
@@ -376,15 +678,13 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
376
678
|
import; `--instructions-file`/`--def-file` refused with `E_BAD_ARGS`). Test:
|
|
377
679
|
the deployment tree is byte-identical after a success, a refusal and an
|
|
378
680
|
unknown-soul preview.
|
|
379
|
-
**Workspace deployments (0.
|
|
380
|
-
|
|
381
|
-
(`agents/<soul>/souls/<commit
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
side-effect-free for everything it shows; it must not assume the deployment
|
|
387
|
-
directory's byte-identity across the FIRST preview of a soul or commit.
|
|
681
|
+
**Workspace deployments (0.26.0+)**: this holds for the FIRST preview of a
|
|
682
|
+
soul or commit too. A preview reads the soul from the deployment's per-commit
|
|
683
|
+
cache (`agents/<soul>/souls/<commit>/`) when a spawn already filled it, else
|
|
684
|
+
fetches it to a temporary copy outside the deployment and removes it
|
|
685
|
+
(`soulFetched: true` in the result). Only a spawn fills the cache or moves the
|
|
686
|
+
`agents/<soul>/soul` pointer. (0.25.x previews populated the cache: that stated
|
|
687
|
+
exception is gone.)
|
|
388
688
|
- **Exact root**: `spawn <soul> --agents-root <abs>` binds the soul to that root
|
|
389
689
|
(as inspect/readiness take it) — no team-soul / capability-agent / importable-
|
|
390
690
|
def fallback; mismatch → `E_SOUL_UNKNOWN`. The preview echoes
|
|
@@ -395,7 +695,19 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
395
695
|
per-module payload each provider will receive (`{ "<cap>": {…} }`, exactly
|
|
396
696
|
`settings.<cap>` of the preview) — so a confirmed apply binds every
|
|
397
697
|
provider fact (an identity choice, a delivery mode) **by value**; a Desktop
|
|
398
|
-
that changes a provider field re-previews.
|
|
698
|
+
that changes a provider field re-previews. From 0.26.0 the merged payload
|
|
699
|
+
includes the manifest's declared setting defaults (`settings.<key>.default`,
|
|
700
|
+
the lowest layer), and the preview's **`settingsOrigins.<cap>`** maps each
|
|
701
|
+
leaf of `settings.<cap>` (a JSON pointer, e.g. `/identity/mode`) to
|
|
702
|
+
`{ kind, at }`: `kind` is `manifest-default` | `workspace` | `soul` |
|
|
703
|
+
`host` | `spawn` — the last layer that set it. (`workspace-team` no longer
|
|
704
|
+
appears since teams amendment K: a label's `byTeam` entry is not merged into
|
|
705
|
+
the settings; it is in `teams[].payload`.) —
|
|
706
|
+
and `at` names where (`oats.json#/settings/identity/default`,
|
|
707
|
+
`soul.yaml#/messaging`, `oats-local.yaml#/settings/<cap>`,
|
|
708
|
+
`--provider <cap>`, …). A Desktop labels `manifest-default` values
|
|
709
|
+
"Default" from this, instead of hardcoding them (feature
|
|
710
|
+
**`settings-origins`**). `instances[].identity` (status)
|
|
399
711
|
and `selected.identity` (inspect) carry the served principal a messaging
|
|
400
712
|
provider reported: `{ mode: "local"|"global", alias, team, address|null,
|
|
401
713
|
resident|null, grant?: { id, expiresAt, scopes }, provider }`; absent when
|
|
@@ -429,7 +741,12 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
429
741
|
immediately after the decision check; a concurrent spawn that lost refuses
|
|
430
742
|
**`E_PLACEMENT_TAKEN`** having touched nothing. Two concurrent applies of
|
|
431
743
|
one decision yield exactly one home. There is no wider lock; this
|
|
432
|
-
reservation is the guarantee.
|
|
744
|
+
reservation is the guarantee. Instance names are deployment-wide
|
|
745
|
+
(0.26.0), so right after its reservation a spawn re-checks the whole agents
|
|
746
|
+
root: when another soul's concurrent spawn reserved the same name, it
|
|
747
|
+
removes its own empty reservation and refuses (`E_INSTANCE_NAME_TAKEN` for a
|
|
748
|
+
`--name`, `E_PLACEMENT_TAKEN` for a derived name). At most one wins, and
|
|
749
|
+
possibly neither.
|
|
433
750
|
- Gate confirmation AND the exec owner on `spawn-preview-2` +
|
|
434
751
|
`spawn-apply-2` + `spawn-idempotency`; a legacy local request on such a CLI
|
|
435
752
|
is refused by the Desktop (`E_PLAN_REQUIRED`), not routed around the fence.
|
|
@@ -468,75 +785,15 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
468
785
|
(provider contract), auto-PR (P1/ADE write approval), branch enumeration
|
|
469
786
|
(producer seam).
|
|
470
787
|
|
|
471
|
-
## Readiness quartet
|
|
472
|
-
|
|
473
|
-
> **0.25 status — the producers below are the 0.24 tier.** The readiness DTO
|
|
474
|
-
> (`readinessApi: 1`) still ships unchanged, but its four checks are *produced*
|
|
475
|
-
> by the classic observers: `installed` by `oats list` over the
|
|
476
|
-
> `.agents/capabilities/installed/` tier, `trusted` by the per-artifact approval
|
|
477
|
-
> that `oats trust` wrote, `configured` by `oats-config.yaml` activation, and
|
|
478
|
-
> `enrolled` by the `oats.yaml` backlink. On a **workspace deployment**
|
|
479
|
-
> (`oats-local.yaml` present) none of those sources exists: nothing is installed,
|
|
480
|
-
> approval is per package version in `oats-lock.json` v3 (`oats sync`), activation
|
|
481
|
-
> is derived (workspace defaults ⊕ soul `capabilities:`), and membership is
|
|
482
|
-
> `oats-membership.yaml` observed over the remotes. `oats list` and `oats trust`
|
|
483
|
-
> are removed verbs (`E_UNKNOWN_COMMAND`), so a `remedy` naming them cannot be
|
|
484
|
-
> run. Treat the field names and producer strings as the stable wire shape they
|
|
485
|
-
> are; for the workspace-model facts read `oats spawn <soul> --preview --json`
|
|
486
|
-
> (`modules[]` with from/commit/digest — the "installed" and "configured"
|
|
487
|
-
> truth), `oats sync --json` `approvalNeeded[]` (the "trusted" truth) and
|
|
488
|
-
> `oats workspace status --json` (the "enrolled" truth). Re-basing the quartet on
|
|
489
|
-
> those producers is an open thread of [the workspace model](#workspace-model-workspaceapi-2);
|
|
490
|
-
> when it lands it will be announced as a new feature name, not a silent change of
|
|
491
|
-
> `readinessApi: 1`.
|
|
492
|
-
|
|
493
|
-
`oats readiness [--soul <name>] [--home <abs>] [--verify-signatures] [--policy] [--dir <d>] --json`
|
|
494
|
-
is the first-run readiness view (frame 09) and the Capabilities readiness rows
|
|
495
|
-
(frame 04). Every fact is derived from the **same** data `oats inspect` reports
|
|
496
|
-
— never a second opinion — and rolled into four checks:
|
|
497
|
-
|
|
498
|
-
```json
|
|
499
|
-
{"readinessApi":1,"subject":{"kind":"soul","name":"dev"},"at":"<iso>",
|
|
500
|
-
"checks":{
|
|
501
|
-
"installed": {"status":"pass","items":[{"subject":"oats.core","status":"pass","required":true,"reason":null,"producer":"oats list","evidence":{"version":"1.1.3","integrity":"sha256-…","origin":"installed"},"remedy":null}]},
|
|
502
|
-
"trusted": {"status":"fail","items":[{"subject":"oats.core","status":"fail","required":true,"reason":"executable surface not approved","producer":"artifact approval","evidence":{"integrity":"sha256-…"},"remedy":"oats trust oats.core",
|
|
503
|
-
"signature":{"status":"unknown","signer":null,"reason":"signature verification needs a network fetch; pass --verify-signatures"}}]},
|
|
504
|
-
"configured":{"status":"pass","items":[{"subject":"oats.core activation","status":"pass","required":true,"producer":"oats-config.yaml","evidence":{"target":"declared","level":"/abs"},"remedy":null}]},
|
|
505
|
-
"enrolled": {"status":"not-applicable","items":[{"subject":"workspace membership","status":"not-applicable","required":false,"producer":"oats.yaml","reason":"standalone deployment: no workspace declared in oats.yaml"}]}},
|
|
506
|
-
"summary":{"ready":false,"required":3,"pass":2,"fail":1,"unknown":0},
|
|
507
|
-
"notes":["…"]}
|
|
508
|
-
```
|
|
788
|
+
## Readiness quartet (`readinessApi: 1`) — removed in 0.26.0
|
|
509
789
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
-
|
|
515
|
-
executable approval of the exact artifact (`oats trust`). Separately,
|
|
516
|
-
`signature {status: verified | unsigned | unknown | invalid | not-applicable,
|
|
517
|
-
signer: {id, label} | null, reason}` — the source commit's **verified Git
|
|
518
|
-
signature**, named signer or nothing. It is `unknown` unless
|
|
519
|
-
`--verify-signatures` (a network fetch of that one commit; `git log %G?`);
|
|
520
|
-
a catalog URL, repository owner or byte hash is never a signer. Render
|
|
521
|
-
"Trusted · signed by <label>" only for `verified`.
|
|
522
|
-
- **`configured`**: activation for the subject, runtime-package requirements
|
|
523
|
-
(`missingRequires`), runtime-settings problems. **`enrolled`**: workspace
|
|
524
|
-
**member admission** (decision §3) — `not-applicable` for a standalone
|
|
525
|
-
deployment (no `workspace:` in `oats.yaml`), `unknown` until admission is
|
|
526
|
-
verified against the workspace observation, `pass`/`fail` when it is. Never
|
|
527
|
-
login, never team registration; "Skip" leaves it not-applicable, never pass.
|
|
528
|
-
*(The readiness producer still reads the 0.24 `oats.yaml` backlink; under the
|
|
529
|
-
workspace model membership is `oats-membership.yaml` observed by
|
|
530
|
-
`oats workspace status` — re-basing this item is an open thread.)*
|
|
531
|
-
- Subject: `--soul <name>` scopes required items to the soul's declared
|
|
532
|
-
requirements; without it, to the scope's active capabilities.
|
|
533
|
-
|
|
534
|
-
`--policy` adds the **enforced** policy view with origins:
|
|
790
|
+
The quartet (`installed | trusted | configured | enrolled`), its signature
|
|
791
|
+
verification (`--verify-signatures`, feature `readiness-verify`) and the
|
|
792
|
+
scope subject were removed with the classic config chain. `oats readiness`
|
|
793
|
+
answers only [`readinessApi: 2`](#oats-readiness---home---soul---dir---policy---json-readinessapi-2);
|
|
794
|
+
`--verify-signatures` is `E_BAD_ARGS`.
|
|
535
795
|
|
|
536
|
-
|
|
537
|
-
"policy":{"childSpawns":{"allowed":false,"enforced":true,"origin":{"kind":"soul","detail":"children.spawn: false in soul.yaml"}},
|
|
538
|
-
"worktrees":{"allowed":true,"mode":"worktree","enforced":true,"origin":{"kind":"work-mode","detail":"work: worktree"}}}
|
|
539
|
-
```
|
|
796
|
+
### Enforced child-spawn policy (`--policy`)
|
|
540
797
|
|
|
541
798
|
`childSpawns` is **enforced by the spawn route**: `soul.yaml` may declare
|
|
542
799
|
`children: {spawn: false}`; `oats spawn --allow-child-spawns | --no-child-spawns`
|
|
@@ -550,58 +807,6 @@ instance's recorded (enforced) one; with only `--soul` it is the declaration
|
|
|
550
807
|
(`enforced: false`). It is a lifecycle-authority claim, not an OS sandbox —
|
|
551
808
|
the UI says so.
|
|
552
809
|
|
|
553
|
-
### Slice-5 producer pins (0.24.9+; all additive — gate items on field presence)
|
|
554
|
-
|
|
555
|
-
- **`configured` is EFFECTIVE activation.** `activation.enabled` is the resolved
|
|
556
|
-
verdict for the subject; a capability *declared* for the soul but disabled is
|
|
557
|
-
`fail` with reason `declared for soul <n> but disabled (…)`. Declaration is
|
|
558
|
-
never activation.
|
|
559
|
-
- **Trust is not-applicable for data-only capabilities.** The inspect row now
|
|
560
|
-
carries `health.executableSurface` (manifest commands/hooks/launch env — what
|
|
561
|
-
`oats trust` approves). No surface → `trusted` item `not-applicable`, reason
|
|
562
|
-
`no executable surface`, whatever the lock records. This is why a fresh
|
|
563
|
-
`oats trust <package> --all-capabilities` "skipped" `oats.core` and readiness
|
|
564
|
-
still said fail before 0.24.9.
|
|
565
|
-
- **Typed linkage on every item**: `capability {id, level, scope}` and
|
|
566
|
-
`origin {kind: requires|declares|default|inventory, target}`; plus
|
|
567
|
-
`summary.byCapability[] {capability, origin, required, checks{installed,
|
|
568
|
-
trusted, configured, enrolled}, ownReady, ready}` — the SAME items regrouped,
|
|
569
|
-
no second observation. `ownReady` is the capability's own four verdicts;
|
|
570
|
-
`ready` is `ownReady` AND no **subject-level blocker** — items that belong to
|
|
571
|
-
no capability (workspace membership, soul declarations) are listed in
|
|
572
|
-
`summary.subjectBlockers[] {check, subject, status}` and block every row.
|
|
573
|
-
A per-capability row never says ready while the subject is blocked, and a
|
|
574
|
-
row's verdict is never promoted to the subject's `summary.ready`. Render
|
|
575
|
-
per-capability rows from this; never parse subjects.
|
|
576
|
-
- **Selector echo**: `subject.selector` = the arguments **as given, byte-exact,
|
|
577
|
-
no realpath** — `{kind:"scope", dir|null}` · `{kind:"soul", soul,
|
|
578
|
-
agentsRoot|null, dir|null}` · `{kind:"home", home, soul, agentsRoot|null}`.
|
|
579
|
-
Compare with what you sent, byte for byte; never filesystem-normalize a
|
|
580
|
-
response path. The canonical scope is `subject.context` (may differ from
|
|
581
|
-
`dir`, e.g. `/var` vs `/private/var` on macOS).
|
|
582
|
-
- **Unreadable member document** (`oats.yaml` unreadable, or `workspace:`
|
|
583
|
-
present but not a mapping) → `enrolled` item `unknown` with
|
|
584
|
-
`evidence.file`, never `not-applicable`. A declared backlink stays `unknown`
|
|
585
|
-
with reason `reciprocal admission not observed …` until the CLI fetches the
|
|
586
|
-
workspace's members (K11).
|
|
587
|
-
- **Captured homes refuse**: `readiness --home <captured>` →
|
|
588
|
-
`E_UNSUPPORTED_MODE` (`details.captured: true`) before any current-config
|
|
589
|
-
interpretation.
|
|
590
|
-
- **`--agents-root <abs>`** is accepted with `--soul` (and with `--home`), as
|
|
591
|
-
inspect takes it — pin the exact root you admitted.
|
|
592
|
-
- **Signature verification (feature `readiness-verify`)**: `--verify-signatures`
|
|
593
|
-
is bounded custody — **one total budget per readiness read** (120 s default)
|
|
594
|
-
shared by every capability's fetch and verify (an exhausted budget refuses the
|
|
595
|
-
remaining capabilities with `budget-exhausted`, no fetch), each Git child in
|
|
596
|
-
its own process group and the **whole group** SIGKILLed on timeout or failure,
|
|
597
|
-
scratch repositories removed on normal exit and on SIGINT/SIGTERM/SIGHUP, `GIT_CONFIG_GLOBAL
|
|
598
|
-
=/dev/null` + no system config + no prompts/askpass, **only https/ssh**
|
|
599
|
-
transports. `signature.failure` is `null` or `{code}` from the closed set
|
|
600
|
-
`transport-not-allowed | fetch-failed | fetch-timeout | budget-exhausted |
|
|
601
|
-
verifier-failed | verifier-timeout | cannot-check`; `signature.reason` is a
|
|
602
|
-
fixed sentence, **never stderr**. Gate the *Verify signatures…* action on the
|
|
603
|
-
feature name; keep it an explicit user action.
|
|
604
|
-
|
|
605
810
|
## Lifecycle plans — Stop and Remove (`lifecycleApi: 1`, OATS 0.24.8+)
|
|
606
811
|
|
|
607
812
|
The Desktop's Stop and Remove confirmations render **plans**: a read-only
|
|
@@ -693,6 +898,12 @@ location. The receipt says so:
|
|
|
693
898
|
nothing is lost; retry or pass `--discard-worktree`.
|
|
694
899
|
- Non-worktree modes report `retention: null`. Quarantine/rollback paths keep
|
|
695
900
|
their removal semantics.
|
|
901
|
+
- A recovery (`workRecovery`, `workRecoveries[]`) is `{path, classes, bytes,
|
|
902
|
+
outputs?, repoCopy?}` (0.26.0: `bytes`, `outputs`): `bytes` is the recovery's
|
|
903
|
+
own size; `outputs: {paths: [{path, bytes}], bytes}` names what it copied
|
|
904
|
+
beyond tracked state — a worktree's untracked and ignored paths, or a
|
|
905
|
+
directory's work entries — grouped by top-level entry, largest first. Absent
|
|
906
|
+
when only home bytes were copied.
|
|
696
907
|
- The Remove dialog's "also delete worktree / branch" checkboxes map to these
|
|
697
908
|
two flags; the kernel never touches a PR.
|
|
698
909
|
- **Guarded apply** (what a GUI sends): `oats retire <i> --plan-revision <rev>
|
|
@@ -730,7 +941,8 @@ location. The receipt says so:
|
|
|
730
941
|
`retire-retention`, `readiness`, `spawn-preview`, `instance-events`,
|
|
731
942
|
`schedule-history`, and carries the API integers (`instanceGitApi`, `soulsApi`,
|
|
732
943
|
`lifecycleApi`, `readinessApi`, `spawnPreviewApi`, `eventsApi`,
|
|
733
|
-
`scheduleHistoryApi`).
|
|
944
|
+
`scheduleHistoryApi`, `operationsApi`). In 0.26.0, `soulsApi`, `readinessApi`
|
|
945
|
+
and `operationsApi` are **2** ([the workspace-model inspect](#inspect-readiness-and-operation-run-on-the-workspace-model-operationsapi-2-soulsapi-2-readinessapi-2-oats-0260)). **Gate on these, never on a version string and never by
|
|
734
946
|
optimistic invocation**: an older CLI ignores an unknown `--plan` on `retire`
|
|
735
947
|
and *retires*. Absent feature → the view is unavailable. (`catalog` was the
|
|
736
948
|
0.24 `oats catalog` verb's flag; the verb is removed under the workspace model
|
|
@@ -772,13 +984,12 @@ machine in the directory the operator chooses (any existing folder). It writes
|
|
|
772
984
|
`<dir>/oats-local.yaml` (`{ schemaVersion: 2, workspace: <ref> }`), creates
|
|
773
985
|
`<dir>/agents/` (the instance homes), then runs exactly the `oats sync` body
|
|
774
986
|
over the directory just written — discover over the remotes, confirm
|
|
775
|
-
membership, resolve `packages:`,
|
|
776
|
-
write `oats-lock.json`. It installs nothing, creates no soul, spawns nothing
|
|
987
|
+
membership, resolve `packages:`, write `oats-lock.json`. It installs nothing, creates no soul, spawns nothing
|
|
777
988
|
and writes no `oats-config.yaml`; the member clones and the setup-expert spawn
|
|
778
989
|
are printed as next steps. `<dir>` defaults to cwd; `--dir <d>` is the same
|
|
779
990
|
argument (give it once). `--workspace` is required and must be a ref
|
|
780
991
|
`lib/remote.mjs` parses (`E_REPO_REF`) — checked **before** anything is
|
|
781
|
-
written. Captured selectors are refused (`
|
|
992
|
+
written. Captured selectors are refused (`E_UNSUPPORTED_MODE`: the captured/portable path was removed in 0.26).
|
|
782
993
|
|
|
783
994
|
```json
|
|
784
995
|
{"onboardApi":2,
|
|
@@ -793,13 +1004,9 @@ written. Captured selectors are refused (`E_BAD_ARGS`).
|
|
|
793
1004
|
```
|
|
794
1005
|
|
|
795
1006
|
- `sync` is the `syncApi: 1` report of the first sync (members, packages,
|
|
796
|
-
changes, `
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
(0.25.2+; `<version>` is `approvalNeeded[].version` verbatim — a catalog
|
|
800
|
-
version, or the full commit OID for a git-pinned package); a Desktop renders
|
|
801
|
-
`approvalNeeded[].executables` (the digest of the executable set it is
|
|
802
|
-
approving) and `targets`, then runs that command. Exit `0` otherwise.
|
|
1007
|
+
changes, `problems`); `lock` is the lock it wrote. Exit `0` on success —
|
|
1008
|
+
there is no approval-pending outcome (0.26.0, feature
|
|
1009
|
+
`packages-no-approval`; earlier kernels exited `2` with `approvalNeeded`).
|
|
803
1010
|
- `hosting` states decision 26 (the kernel cannot see forge visibility, so it
|
|
804
1011
|
reports `hostIsMember` and the rule rather than judging).
|
|
805
1012
|
- `next.clone[]` is one row per **confirmed** member (`url` = what the
|
|
@@ -822,12 +1029,18 @@ written. Captured selectors are refused (`E_BAD_ARGS`).
|
|
|
822
1029
|
|
|
823
1030
|
### `oats sync [--dir <d>] --json` → `syncApi: 1`
|
|
824
1031
|
|
|
825
|
-
Discovers, confirms membership, resolves `packages:` to commits,
|
|
826
|
-
`oats-lock.json` (lockfileVersion 3), reports
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1032
|
+
Discovers, confirms membership, resolves `packages:` to commits + integrity,
|
|
1033
|
+
writes `oats-lock.json` (lockfileVersion 3), reports; exit `0` on success.
|
|
1034
|
+
**No package approval** (0.26.0, human decision 2026-09-24; feature
|
|
1035
|
+
`packages-no-approval`): declaring a package in `packages:` is the trust
|
|
1036
|
+
decision. The report has no `approvalNeeded`, package rows no `approved`,
|
|
1037
|
+
`changes[]` rows no `approvalNeeded`; there is no prompt and no exit `2`, and
|
|
1038
|
+
`--approve` is `E_BAD_ARGS`. A lock written by an earlier kernel keeps working
|
|
1039
|
+
(its `approved` records are ignored and dropped on the next write). The fields
|
|
1040
|
+
went away without an API-number bump — `syncApi`, `workspaceStatusApi` and
|
|
1041
|
+
`capabilitiesApi` stay `1`; the removal is signalled by the feature string
|
|
1042
|
+
alone — so a consumer reading `approvalNeeded`, `approval` or `approved` must
|
|
1043
|
+
gate that on the absence of `packages-no-approval`.
|
|
831
1044
|
|
|
832
1045
|
```json
|
|
833
1046
|
{"syncApi":1,
|
|
@@ -839,12 +1052,9 @@ the TTY fallback, not the contract. Exit `0` otherwise.
|
|
|
839
1052
|
"souls":["tools-expert"],"capabilities":["acme-tools-dev"],"publishes":{"package":"acme.tools","version":"0.4.0"}},
|
|
840
1053
|
{"key":"github.com/acme/billing","name":"billing","commit":"<oid>","confirmed":false,"status":"no-backlink","detail":"github.com/acme/billing@… has no oats-membership.yaml","team":null,
|
|
841
1054
|
"souls":[],"capabilities":[],"publishes":null}],
|
|
842
|
-
"packages":[{"id":"oats.okf","version":"2.1.3","source":"catalog:oats.okf","commit":"<oid>","integrity":"sha256-…","capabilities":["oats.okf"],
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
"changes":[{"id":"acme.tools","from":null,"to":"0.4.0","commit":"<oid>","approvalNeeded":true}],
|
|
846
|
-
"approvalNeeded":[{"id":"acme.tools","version":"0.4.0","commit":"<oid>","executables":"sha256-…",
|
|
847
|
-
"targets":["acme-deploy: command apply → bin/acme-deploy.mjs","acme-deploy: hook spawn → bin/acme-deploy.mjs"]}],
|
|
1055
|
+
"packages":[{"id":"oats.okf","version":"2.1.3","source":"catalog:oats.okf","commit":"<oid>","integrity":"sha256-…","capabilities":["oats.okf"]},
|
|
1056
|
+
{"id":"acme.tools","version":"0.4.0","source":"git:github.com/acme/tools@v0.4.0","commit":"<oid>","integrity":"sha256-…","capabilities":["acme-deploy","acme-lint"]}],
|
|
1057
|
+
"changes":[{"id":"acme.tools","from":null,"to":"0.4.0","commit":"<oid>"}],
|
|
848
1058
|
"problems":[]}
|
|
849
1059
|
```
|
|
850
1060
|
|
|
@@ -854,17 +1064,14 @@ the TTY fallback, not the contract. Exit `0` otherwise.
|
|
|
854
1064
|
capabilities are **not** in `capabilities[]`; the non-collapse rule).
|
|
855
1065
|
- `changes[]`: `from` = previously locked version or `null`; `to` = `null` when
|
|
856
1066
|
the package was dropped from `packages:` and from the lock.
|
|
857
|
-
- `approvalNeeded[].targets` are human-readable lines
|
|
858
|
-
(`<cap>: command|hook <name> → <relpath>`); `executables` is the digest an
|
|
859
|
-
approval would record.
|
|
860
1067
|
- `problems[]`: `{ code, path, message, repoKey? }` — per-item discovery
|
|
861
1068
|
problems (`E_WORKSPACE_SCHEMA`, `E_TEAM_UNKNOWN`, `E_REMOTE_*`, …). Never an
|
|
862
1069
|
abort: an unreadable member directory is a problem of that member.
|
|
863
1070
|
- Errors: `E_LOCAL_MISSING`, `E_WORKSPACE_SCHEMA { path, problems[] }`,
|
|
864
1071
|
`E_REMOTE_UNREADABLE`, `E_LOCK_SCHEMA`, `E_PACKAGE_MISSING` (catalog has no
|
|
865
1072
|
such id), `E_PACKAGE_INTEGRITY { why: "branch" | locked/observed }`,
|
|
866
|
-
`
|
|
867
|
-
|
|
1073
|
+
`E_PACKAGE_MANIFEST`, `E_REPO_REF`, `E_BAD_ARGS` (`--approve`: package
|
|
1074
|
+
approval was removed).
|
|
868
1075
|
|
|
869
1076
|
### `oats package add <id> <version|git:<repo>@<ref>> | remove <id> [--dir] --json`
|
|
870
1077
|
|
|
@@ -896,18 +1103,28 @@ found to check the declaration even though it does not edit it. `E_USAGE`,
|
|
|
896
1103
|
"declaredPackages":["acme.tools","oats.okf"],
|
|
897
1104
|
"unsynced":[],
|
|
898
1105
|
"stale":[],
|
|
899
|
-
"approval":{"approved":["oats.okf"],"needed":["acme.tools"]},
|
|
900
1106
|
"external":[{"source":"git:github.com/oss-collective/experts@<oid>","soul":"security-reviewer","team":"unassigned"}],
|
|
901
|
-
"problems":[]
|
|
1107
|
+
"problems":[],
|
|
1108
|
+
"warnings":[]}
|
|
902
1109
|
```
|
|
903
1110
|
|
|
1111
|
+
`warnings[]` (feature `teams`, also in the `sync` report): `{ code, label,
|
|
1112
|
+
souls, paths, message }` — one `unmapped-team-label` per label that is in
|
|
1113
|
+
`teams:` but not in `messaging.byTeam`, naming its souls (sorted) and each
|
|
1114
|
+
soul's `<repoKey>:<path>#/team`; sorted by label. Never a problem.
|
|
1115
|
+
|
|
904
1116
|
`unsynced` = declared in `packages:` but not in the lock (run `sync`);
|
|
905
1117
|
`stale` = locked but no longer declared. Read-only: does not write the lock.
|
|
1118
|
+
(0.26.0: the `approval` object is gone with package approval.)
|
|
906
1119
|
|
|
907
1120
|
### `oats capabilities [--dir] --json` → `capabilitiesApi: 1` · `oats souls [--dir] --json` → `soulsApi: 1`
|
|
908
1121
|
|
|
909
|
-
Every
|
|
910
|
-
|
|
1122
|
+
Every item of every confirmed member, external souls, and locked package
|
|
1123
|
+
capabilities, sorted by name then origin. Souls have no private mode (their
|
|
1124
|
+
`private` is always `false`); a repo-owned capability is listed with
|
|
1125
|
+
`private: true` — usable only by its own repo's souls. The Desktop shows its
|
|
1126
|
+
"Repo owned" section when `version --json` lists the `capabilities-private`
|
|
1127
|
+
feature. `origin` is the
|
|
911
1128
|
human string (`member <key> @ <8-char commit>` | `package <id> v<version>` |
|
|
912
1129
|
`external <key> @ <commit>`); `kind` is the machine field. `team` is the label
|
|
913
1130
|
or `"unassigned"`.
|
|
@@ -918,7 +1135,7 @@ or `"unassigned"`.
|
|
|
918
1135
|
{"name":"acme-house-style","origin":"member github.com/acme/agents @ 3f2a9c1e","kind":"member","repoKey":"github.com/acme/agents","commit":"<oid>",
|
|
919
1136
|
"team":"global","private":false,"path":"capabilities/acme-house-style","layer":null,"version":"0.0.0-workspace"},
|
|
920
1137
|
{"name":"oats.okf","origin":"package oats.okf v2.1.3","kind":"package","package":"oats.okf","version":"2.1.3","commit":"<oid>",
|
|
921
|
-
"team":"unassigned","private":false
|
|
1138
|
+
"team":"unassigned","private":false}],
|
|
922
1139
|
"problems":[]}
|
|
923
1140
|
```
|
|
924
1141
|
|
|
@@ -932,8 +1149,9 @@ or `"unassigned"`.
|
|
|
932
1149
|
```
|
|
933
1150
|
|
|
934
1151
|
Package capabilities of declared-but-unsynced packages are absent until `sync`.
|
|
935
|
-
(`soulsApi: 1`
|
|
936
|
-
|
|
1152
|
+
(`oats souls --json` keeps `soulsApi: 1` in 0.26.0: its shape is unchanged.
|
|
1153
|
+
The probe's `soulsApi` is **2** because it tracks the `oats inspect --json`
|
|
1154
|
+
soul rows. The two payloads are distinguished by their command.)
|
|
937
1155
|
|
|
938
1156
|
### `oats spawn <soul> … --preview --json` — additions (Preview API 2 unchanged)
|
|
939
1157
|
|
|
@@ -943,10 +1161,10 @@ between preview and apply is `E_DECISION_STALE`):
|
|
|
943
1161
|
|
|
944
1162
|
```json
|
|
945
1163
|
{"modules":[
|
|
946
|
-
{"name":"acme-release-tooling","from":{"kind":"member","repoKey":"github.com/acme/agents","commit":"<oid>"},"layer":null,"private":false,
|
|
1164
|
+
{"name":"acme-release-tooling","from":{"kind":"member","repoKey":"github.com/acme/agents","commit":"<oid>"},"layer":null,"private":false,"declares":[],
|
|
947
1165
|
"changedSince":{"instance":"release-manager-v2","was":"<old oid>"}},
|
|
948
1166
|
{"name":"oats.okf","from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"<oid>","integrity":"sha256-…","repoKey":"github.com/awebai/oats-okf"},
|
|
949
|
-
"layer":"knowledge","private":false,"changedSince":false}],
|
|
1167
|
+
"layer":"knowledge","private":false,"declares":["bindings-file","git-timeout","harvest-model","harvest-runtime","state-dir"],"changedSince":false}],
|
|
950
1168
|
"team":"engineering",
|
|
951
1169
|
"resolution":"6e3050c0d005879441ab017d",
|
|
952
1170
|
"workspace":"github.com/acme/agents",
|
|
@@ -956,6 +1174,8 @@ between preview and apply is `E_DECISION_STALE`):
|
|
|
956
1174
|
```
|
|
957
1175
|
|
|
958
1176
|
- `modules[].from` is exactly the `from` recorded in `instance.json` on apply.
|
|
1177
|
+
- `modules[].declares`: the manifest's declared setting keys, as in `inspect`
|
|
1178
|
+
(feature `settings-declared`).
|
|
959
1179
|
- `changedSince`: `null` (no previous instance of this soul), `false`
|
|
960
1180
|
(unchanged since the newest previous instance), or
|
|
961
1181
|
`{ instance, was }` (`was` = the previous commit, or `null` when the previous
|
|
@@ -982,7 +1202,7 @@ between preview and apply is `E_DECISION_STALE`):
|
|
|
982
1202
|
members or externals), `E_SOUL_AMBIGUOUS { name, repos[] }` (name it
|
|
983
1203
|
`<repo>/<soul>`). Resolution errors keep their codes (`E_NOT_A_MEMBER`,
|
|
984
1204
|
`E_MEMBERSHIP_UNCONFIRMED { repoKey, reason }`, `E_CAPABILITY_MISSING { hint? }`,
|
|
985
|
-
`E_CAPABILITY_PRIVATE`, `E_PACKAGE_MISSING`, `
|
|
1205
|
+
`E_CAPABILITY_PRIVATE`, `E_PACKAGE_MISSING`, `E_PACKAGE_INTEGRITY { why: "capabilities", listed, locked }`,
|
|
986
1206
|
`E_SLOT_CONFLICT { slot, modules[], reason? }`, `E_SKILL_DUPLICATE { name, modules[] }`,
|
|
987
1207
|
`E_COMPATIBILITY { capability, package, version, range, why? }`).
|
|
988
1208
|
|
|
@@ -1001,11 +1221,18 @@ Written by materialization inside the spawn transaction; read back by
|
|
|
1001
1221
|
"oats.okf":{"from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"<oid>","integrity":"sha256-…","repoKey":"github.com/awebai/oats-okf"},
|
|
1002
1222
|
"commit":"<oid>","digest":"sha256-…","materializedAt":"<iso>"}},
|
|
1003
1223
|
"providers":{"acme-release-tooling":{},"oats.okf":{"owns":"release-manager","reads":["platform-engineer"],"state-dir":"/Users/ana/.oats/okf"}},
|
|
1004
|
-
"workspace":{"key":"github.com/acme/agents","commit":"<oid>","resolution":"<24 hex>","standalone":false,
|
|
1224
|
+
"workspace":{"key":"github.com/acme/agents","name":"acme","deployment":"/Users/ana/acme-workspace","commit":"<oid>","resolution":"<24 hex>","standalone":false,
|
|
1005
1225
|
"soul":{"repoKey":"github.com/acme/agents","commit":"<oid>","team":"engineering"}},
|
|
1006
1226
|
"capabilities":[{"id":"oats.okf","capability":"oats.okf","origin":"package:oats.okf@2.1.3","…":"one row per module"}]}
|
|
1007
1227
|
```
|
|
1008
1228
|
|
|
1229
|
+
`workspace.name` (the workspace file's `name`) and `workspace.deployment` (the
|
|
1230
|
+
directory holding the deployment's `oats-local.yaml`) are recorded from 0.26.0,
|
|
1231
|
+
so a home answers them without discovery: `oats inspect --home` reports the
|
|
1232
|
+
recorded name, and `oats operation run --home` hands it to the provider as
|
|
1233
|
+
`OATS_WORKSPACE_NAME`. Homes spawned
|
|
1234
|
+
before 0.26.0 lack both; the name is then discovered, or `null`.
|
|
1235
|
+
|
|
1009
1236
|
`workspace.standalone` is `true` when the instance was spawned from the
|
|
1010
1237
|
**standalone view** (decisions 10/25: a *member* whose workspace could not be
|
|
1011
1238
|
read — `workspace.key` is then the member repo's key, and `modules` holds the
|
|
@@ -1014,9 +1241,14 @@ spawn. The same view is marked `standalone: true` in `oats sync --json` (with
|
|
|
1014
1241
|
`workspace.name` = `standalone:<repo>`) and in the roster. `capabilities[]` is
|
|
1015
1242
|
the per-module row set (`toCapabilityRows`) that `oats inspect`/`status` read.
|
|
1016
1243
|
|
|
1244
|
+
`soulDir` (0.26.0) is the absolute soul directory the instance incarnates — a
|
|
1245
|
+
workspace soul's per-commit copy `<deployment>/agents/<soul>/souls/<commit12>`, or
|
|
1246
|
+
the read-only soul inside a capability package — and is what every classic
|
|
1247
|
+
lifecycle hook and dispatched command receives as `OATS_SOUL`. Instance homes carry no `soul` link.
|
|
1248
|
+
|
|
1017
1249
|
`digest` is the sha256 of the copied module tree (`<home>/.oats/modules/<cap>/`);
|
|
1018
|
-
`providers.<cap>` is the merged payload (soul ⊕
|
|
1019
|
-
`settings.<cap>` ⊕ `--provider`), `{}` for a capability with none. Copies live at
|
|
1250
|
+
`providers.<cap>` is the merged payload (manifest defaults ⊕ soul ⊕
|
|
1251
|
+
`oats-local.yaml` `settings.<cap>` ⊕ `--provider`), `{}` for a capability with none. Copies live at
|
|
1020
1252
|
`<home>/.oats/modules/<cap>/` and `<home>/.agents/skills/<cap>/<skill>/`.
|
|
1021
1253
|
|
|
1022
1254
|
### `oats status [--dir] --json` — module drift
|
|
@@ -1047,54 +1279,59 @@ top-level `workspace` reachability field:
|
|
|
1047
1279
|
- Text mode prints `modules: <cap> from <member|package …> @ <7-char>` lines
|
|
1048
1280
|
for non-current modules (`--verbose` for all).
|
|
1049
1281
|
|
|
1282
|
+
### Eligible teams (feature `teams`, OATS 0.26.0)
|
|
1283
|
+
|
|
1284
|
+
A soul's `team` may be a list of labels; the first is the primary
|
|
1285
|
+
([teams contract](design/2026-09-25-teams-contract.md)). Every label is an
|
|
1286
|
+
**eligible** team — which the messaging provider may join on an explicit
|
|
1287
|
+
request (a spawn provider setting, or its own join/leave verbs); the kernel
|
|
1288
|
+
joins nothing. One entry per label, in soul order:
|
|
1289
|
+
|
|
1290
|
+
```json
|
|
1291
|
+
{"label":"engineering","team":"aweb:acme.eng","mapped":true,"payload":{"private":"per-human","team":"aweb:acme.eng"}}
|
|
1292
|
+
{"label":"reviewers","team":null,"mapped":false,"payload":{"private":"per-human"}}
|
|
1293
|
+
```
|
|
1294
|
+
|
|
1295
|
+
`payload` = `workspace.messaging` ⊕ `byTeam[label]` (base alone when unmapped);
|
|
1296
|
+
`team` = the mapped payload's team id, else `null`. No label → `[]`.
|
|
1297
|
+
|
|
1298
|
+
Where it appears:
|
|
1299
|
+
- `oats spawn … --preview --json`: top-level `teams` (next to `team`, which
|
|
1300
|
+
stays the primary label). `settings.<messaging>` stays the primary's merged
|
|
1301
|
+
payload and never carries `teams`.
|
|
1302
|
+
- `oats inspect --soul|--home --json`: top-level `teams` and `teamsSource`.
|
|
1303
|
+
For `--home` the teams are **live** (the soul's labels and the workspace's
|
|
1304
|
+
`messaging` as the deployment resolves them now, in two repository reads;
|
|
1305
|
+
the home's modules are unchanged): `teamsSource: "live"`, or `"recorded"`
|
|
1306
|
+
with the spawn-time list when the workspace cannot be read now. Providers
|
|
1307
|
+
get the same marker as `OATS_TEAMS_SOURCE`.
|
|
1308
|
+
- `instance.json`: `teams` (the spawn-time list, kept as evidence; never
|
|
1309
|
+
rewritten) and `workspace.soul.labels`.
|
|
1310
|
+
- `oats souls --json`: each row carries `labels` (`team` stays the primary).
|
|
1311
|
+
- A spawn, preview or `inspect --soul` whose labels give one capability
|
|
1312
|
+
different `defaults.byTeam` entries answers `E_TEAM_CONFLICT { capability,
|
|
1313
|
+
labels: [a, b], entries, paths }`.
|
|
1314
|
+
|
|
1050
1315
|
### Probe
|
|
1051
1316
|
|
|
1052
1317
|
```json
|
|
1053
|
-
{"…":"…","features":["…","workspace-v2","instance-modules","spawn-provider-payload"],"workspaceApi":2}
|
|
1318
|
+
{"…":"…","features":["…","workspace-v2","instance-modules","spawn-provider-payload","packages-no-approval","spawn-name","settings-origins","teams"],"workspaceApi":2}
|
|
1054
1319
|
```
|
|
1055
1320
|
|
|
1056
1321
|
A feature is listed only once the binary implements it. Gate `sync`/`package`/
|
|
1057
1322
|
`workspace status`/`capabilities`/`souls` on `workspace-v2`; gate reading
|
|
1058
1323
|
`instance.json.modules` and preview `modules[]` on `instance-modules`; gate
|
|
1059
|
-
`--provider` on `spawn-provider-payload
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
note}`. Same composer spawn used, the home's own `soul` link and recorded
|
|
1071
|
-
context/work mode. `changed:false` is a no-op (no receipt). On change the
|
|
1072
|
-
prior text is retained as `previous` (`<home>/.oats-agents-md.<stamp>.previous`),
|
|
1073
|
-
`instance.json` gains `instructions[]`/`recomposedAt`, and a `recomposed`
|
|
1074
|
-
event is appended.
|
|
1075
|
-
- **Nothing is signalled or restarted** — the harness re-reads on its own
|
|
1076
|
-
schedule; the receipt's `note` says so. Refuses a retiring home
|
|
1077
|
-
(`E_INSTANCE_RETIRING`), captured incarnations and capability-defined souls
|
|
1078
|
-
(`E_UNSUPPORTED_MODE`: those are refreshed by a new resolution / package).
|
|
1079
|
-
- **Module homes (0.25+) are `E_UNSUPPORTED_MODE` too.** A home whose
|
|
1080
|
-
`instance.json` carries `modules{}` (spawned on a workspace deployment,
|
|
1081
|
-
`instance-modules`) answers `E_UNSUPPORTED_MODE` ("recompose from
|
|
1082
|
-
materialized modules is not supported yet; re-spawn"): its `AGENTS.md` was
|
|
1083
|
-
composed from the soul at a recorded member commit plus the materialized
|
|
1084
|
-
modules' injects, and the instance never changes under itself (decision 7).
|
|
1085
|
-
**Desktop contract (Phase F, F4)**: for a module home, show the drift rows
|
|
1086
|
-
and offer *re-spawn* (preview → apply of the same soul/purpose, then retire
|
|
1087
|
-
the old instance); do not offer "recompose". A kernel recompose for module
|
|
1088
|
-
homes is not planned — an instance never changes under itself.
|
|
1089
|
-
The refresh path for such a home is a new spawn (the soul is re-fetched at
|
|
1090
|
-
the member's current commit). `session-recompose` **stays advertised** in
|
|
1091
|
-
`features[]` because the verb still works for classic homes; gate the UI
|
|
1092
|
-
action on the feature AND on the absence of `instance.json.modules`
|
|
1093
|
-
(`oats status --json` `instances[].modules` is non-empty for a module home),
|
|
1094
|
-
and render the typed refusal otherwise.
|
|
1095
|
-
- Gate on `features.includes("session-recompose")`. It is an **operator
|
|
1096
|
-
action** (the human or the instance's parent), never something a Desktop
|
|
1097
|
-
poll or an agent runs on itself.
|
|
1324
|
+
`--provider` on `spawn-provider-payload`; gate reading `teams`, `teamsSource`,
|
|
1325
|
+
`labels` and `warnings[]` on `teams`; gate reading `declares` on
|
|
1326
|
+
`settings-declared`.
|
|
1327
|
+
|
|
1328
|
+
## Instruction refresh (`oats session recompose`) — removed in 0.26.0
|
|
1329
|
+
|
|
1330
|
+
`oats session recompose` answers `E_UNKNOWN_COMMAND`, and the
|
|
1331
|
+
`session-recompose` feature is no longer advertised. An instance never changes
|
|
1332
|
+
under itself: the refresh path is a re-spawn (preview → apply of the same
|
|
1333
|
+
soul/purpose, then retire the old instance), which fetches the soul at the
|
|
1334
|
+
member's current commit.
|
|
1098
1335
|
|
|
1099
1336
|
## Mutations exposed to Desktop v1
|
|
1100
1337
|
|
|
@@ -1178,13 +1415,52 @@ Additional informative fields: `repo`, `runtime`, `model`, `parent`,
|
|
|
1178
1415
|
was declared, else null), `relation` (`child`/`sibling`/`parent` when a
|
|
1179
1416
|
relation was declared at spawn, else null), `spawnOrigin`, `attach`.
|
|
1180
1417
|
|
|
1181
|
-
Stable error codes: `E_USAGE`, `
|
|
1418
|
+
Stable error codes: `E_USAGE`, `E_LOCAL_MISSING` (no `oats-local.yaml` in reach:
|
|
1419
|
+
a spawn needs a workspace deployment), `E_NO_DEPLOYMENT` (the deployment's
|
|
1420
|
+
`agents/` root is missing), `E_SOUL_UNKNOWN`, `E_UNKNOWN_AGENT`,
|
|
1182
1421
|
`E_AMBIGUOUS_SOUL`, `E_PARENT_NOT_FOUND`, `E_RELATIVE_NOT_FOUND`,
|
|
1183
1422
|
`E_RELATIVE_AMBIGUOUS` (a `--relative-to`/`--parent` anchor name matches
|
|
1184
1423
|
multiple team instances — disambiguate with `--relative-root <agents-root>`
|
|
1185
1424
|
— or the chosen anchor is shadowed by a same-named instance so the lineage
|
|
1186
1425
|
edge would resolve wrongly), `E_BAD_ARGS`,
|
|
1187
|
-
`E_SPAWN_FAILED`.
|
|
1426
|
+
`E_INSTANCE_NAME_INVALID`, `E_INSTANCE_NAME_TAKEN`, `E_SPAWN_FAILED`.
|
|
1427
|
+
|
|
1428
|
+
**Instance names** (0.26.0, feature `spawn-name`). By default the name is
|
|
1429
|
+
derived: `<agent>-<purpose>` with `--purpose <slug>`, else `<agent>-<n>`.
|
|
1430
|
+
`--name <slug>` (human decision 2026-09-24) is the explicit opt-in: the
|
|
1431
|
+
instance name is **exactly** `<slug>`, with no `<agent>-` prefix.
|
|
1432
|
+
|
|
1433
|
+
- `--name` and `--purpose` are mutually exclusive (`E_BAD_ARGS`), and
|
|
1434
|
+
`--name` needs a value (`E_BAD_ARGS`).
|
|
1435
|
+
- The name is never rewritten. Input that is not already a slug (lowercase
|
|
1436
|
+
letters and digits, single dashes between them) is
|
|
1437
|
+
`E_INSTANCE_NAME_INVALID`, and so is a name equal to any soul name of the
|
|
1438
|
+
deployment (souls on the agents root, and every soul the workspace
|
|
1439
|
+
declares, fetched or not). Soul and instance references stay unambiguous.
|
|
1440
|
+
- **Instance names are at most 64 characters** (0.26.0; the tightest
|
|
1441
|
+
consumer is the messaging alias, which allows 1–64). This covers every
|
|
1442
|
+
name, explicit and derived. A longer name is `E_INSTANCE_NAME_INVALID`
|
|
1443
|
+
("instance names are at most 64 characters"), in preview and apply alike,
|
|
1444
|
+
and is never truncated. For a derived name the refusal names the purpose
|
|
1445
|
+
to shorten, and the de-duplication suffix counts: when `<agent>-<purpose>`
|
|
1446
|
+
is taken and `<agent>-<purpose>-2` would exceed 64, the spawn is refused.
|
|
1447
|
+
- **Names are unique across the deployment.** An explicit name that any
|
|
1448
|
+
`<agents-root>/<soul>/instances/` already holds (including homes whose soul
|
|
1449
|
+
was since removed), or that a live window in the target tmux session carries
|
|
1450
|
+
(tmux backend, launched or `--no-launch`), is `E_INSTANCE_NAME_TAKEN`
|
|
1451
|
+
(`details.instance`, `details.home` or `details.session`). There is never a
|
|
1452
|
+
silent `-2` for a name the operator typed. These checks run after
|
|
1453
|
+
idempotency-key recovery (a keyed retry replays its receipt), and a
|
|
1454
|
+
concurrent spawn of another soul under the same name is caught after
|
|
1455
|
+
placement (see *Exclusive placement*). The invariant covers spawns through
|
|
1456
|
+
the CLI. Homes from earlier kernels may already share a name.
|
|
1457
|
+
- Derived names de-duplicate deployment-wide too (`-2`, `-3`, …), against
|
|
1458
|
+
every soul's instances and every soul name. Two souls never derive the same
|
|
1459
|
+
name (soul `a` with `--purpose b-c` against soul `a-b` with `--purpose c`).
|
|
1460
|
+
- `--preview` reports the final name (`instance`, `decision.instance`) and
|
|
1461
|
+
refuses with the same codes. The name is part of the decision revision, so
|
|
1462
|
+
`--expect-decision` binds it: another name under a confirmed decision is
|
|
1463
|
+
`E_DECISION_STALE`.
|
|
1188
1464
|
|
|
1189
1465
|
Dispatch-level failures (any `--json` command): `E_UNKNOWN_COMMAND` (no
|
|
1190
1466
|
kernel subcommand or capability namespace matches, or unknown capability
|