@awebai/oats 0.25.9 → 0.27.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 +648 -1755
- 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 +229 -58
- package/docs/capability-manifest.schema.json +29 -9
- package/docs/configuration.md +17 -5
- package/docs/conventions.md +18 -28
- 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 +604 -271
- package/docs/desktop-instance-start.md +3 -3
- package/docs/desktop.md +7 -13
- package/docs/execution-targets.md +16 -18
- package/docs/first-team.md +15 -18
- package/docs/implementation.md +31 -62
- 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 +30 -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 +76 -53
- 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/release-notes/v0.27.0.md +100 -0
- package/docs/schedules.md +54 -132
- package/docs/servers.md +4 -4
- package/docs/soul.schema.json +11 -4
- package/docs/souls-and-instances.md +60 -47
- package/docs/workspaces.md +80 -58
- package/injects/instance-boundary.md +2 -2
- 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 +947 -5023
- package/lib/deprecation.mjs +24 -0
- package/lib/digest.mjs +12 -0
- package/lib/instance-inspect.mjs +397 -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/process-group.mjs +1 -1
- package/lib/provider-binding.mjs +4 -2
- package/lib/provider-reasons.mjs +3 -68
- package/lib/remote.mjs +1 -1
- package/lib/resolve.mjs +204 -68
- package/lib/schedule.mjs +136 -292
- package/lib/servers.mjs +70 -38
- package/lib/{portable-shape.mjs → shape.mjs} +4 -3
- package/lib/tree-copy.mjs +44 -0
- package/lib/workspace.mjs +132 -20
- package/package-catalog.json +6 -6
- package/package.json +1 -1
- package/packages/record/lib/session-roots.mjs +8 -6
- 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,15 +18,17 @@ 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
|
|
|
27
29
|
Optional features are negotiated from the probe's `features` array. Starting
|
|
28
30
|
an existing home requires `session-start`; named launch configurations and
|
|
29
|
-
|
|
31
|
+
harness/permission overrides require `launch-config`; restarting a running
|
|
30
32
|
home also requires `session-restart`. Desktop checks the corresponding
|
|
31
33
|
`remote` entries before offering these operations for a server. The router
|
|
32
34
|
then probes the execution host before sending a mutation. An absent feature
|
|
@@ -46,49 +48,405 @@ 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
|
+
Either may also carry `"warnings":[…]` (0.27.0+), present only when there is
|
|
52
|
+
something to say. The one warning so far is `deprecated-runtime-name` (see
|
|
53
|
+
[the harness rename](#the-harness-rename-feature-harness-oats-0270)). `oats status
|
|
54
|
+
--json`, whose document is not an envelope, carries the same `warnings` beside
|
|
55
|
+
its `problems`. In text mode the warning is one `oats: warning: …` line on stderr.
|
|
56
|
+
|
|
57
|
+
## The harness rename (feature `harness`, OATS 0.27.0)
|
|
58
|
+
|
|
59
|
+
What starts an instance (pi, claude or codex) is its **harness**. 0.27.0 renames
|
|
60
|
+
the kernel's `runtime` to `harness` everywhere it means that:
|
|
61
|
+
|
|
62
|
+
- Outputs speak only the new names.
|
|
63
|
+
- Every input written before 0.27.0 still works. The rule is read either,
|
|
64
|
+
write new: the old spelling is read as the new one, and the next write
|
|
65
|
+
records the new one.
|
|
66
|
+
- A command that read an old spelling answers **one** warning:
|
|
67
|
+
`{"code":"deprecated-runtime-name","key":"runtime","replacement":"harness","sources":[…],"message":"…"}`.
|
|
68
|
+
`sources` names each place it read the old spelling (a flag, a file and its
|
|
69
|
+
key, a home).
|
|
70
|
+
- A pair that disagrees is refused rather than guessed, for example
|
|
71
|
+
`--harness pi --runtime claude`, or both keys with different values.
|
|
72
|
+
- A later release drops the old spellings.
|
|
73
|
+
|
|
74
|
+
Gate on the feature `harness`. A kernel without it speaks the old names: the
|
|
75
|
+
routed commands (`--server`) already translate for such a host, sending
|
|
76
|
+
`--runtime` and `runtime` keys to it and reading its `runtimes` list.
|
|
77
|
+
|
|
78
|
+
| Surface | Before 0.27.0 | 0.27.0 | Old spelling still accepted? |
|
|
79
|
+
|---|---|---|---|
|
|
80
|
+
| `oats version --json` | `runtimes: [pi, claude, codex]` | `harnesses: [...]`, feature `harness` | **Dropped**: no `runtimes` alias; gate on the feature |
|
|
81
|
+
| Flag on `spawn` (and `--preview`), `session start`/`restart`, `launch-config preview`, and their `--server` forms | `--runtime <h>` | `--harness <h>` | Yes, with the warning (the okf 2.1.5 harvest worker passes `--runtime` to spawn). Both flags disagreeing → `E_BAD_ARGS` |
|
|
82
|
+
| `oats status --json`: `agents[]` rows (soul default) and `agents[].instances[]` rows | `runtime` | `harness` | Output only |
|
|
83
|
+
| `oats status --json`: instance rows' `composition.materialized` | `runtimePackages`, `runtimePosture` | `harnessPackages`, `harnessPosture` | Output only |
|
|
84
|
+
| `oats inspect --json` `souls[]` rows, remote roster rows | `runtime` | `harness` | Output only; a pre-0.27 host's `runtime` rows are read as `harness` |
|
|
85
|
+
| `oats inspect --home --json` `instance` | `runtime` | `harness` | Output only |
|
|
86
|
+
| The launch plan's package check (`launch-config preview` `problems[]`) | `runtime-packages` | `harness-packages` | Output only |
|
|
87
|
+
| Soul `soul.yaml` (member souls and capability-defined agents) | `runtime:` | `harness:` | Yes, **without** a warning: released capabilities (oats.aweb 1.13.1) ship `runtime:`, and the operator cannot fix a provider's file. Both, disagreeing → `E_BAD_MANIFEST` |
|
|
88
|
+
| `oats-local.yaml` `launch-configs.<name>` | `runtime:` | `harness:` | Yes, with the warning. Both, disagreeing → `E_WORKSPACE_SCHEMA`. `launch-config set` writes `harness` (a `runtime` in its `--file` definition too) |
|
|
89
|
+
| `launch-config list/preview --json` rows and `selection` | `runtime` | `harness` | Output only |
|
|
90
|
+
| Home `instance.json` | `runtime`; launch recipe `launch` version 1 `{runtime}` | `harness`; recipe version **2** `{harness}` | Yes, with the warning naming the home: a 0.26.0 home inspects, starts, restarts and retires; its next start or restart records the new names |
|
|
91
|
+
| `oats spawn … --preview` decision | `effective.runtime` | `effective.harness` | Output only. The revision digests the key names, so a decision previewed by a 0.26 kernel is `E_DECISION_STALE` at apply (with the fresh decision) |
|
|
92
|
+
| `spawn`/`session` results, `spawned` event `data` | `runtime` | `harness` | Output only |
|
|
93
|
+
| Schedule definitions (`schedule add/update --spec-json`, stored jobs, `schedule list`) | `runtime` | `harness` | Yes, with the warning. A stored job is read in the new name and saved in it next time. Both, disagreeing → `E_SCHEDULE_INVALID` (a stored job: invalid on its own) |
|
|
94
|
+
| Schedule run records (`lastRun`, `recentRuns`) | `startedRuntime` | `startedHarness` | Output; a 0.26.0 record's `startedRuntime` is still read |
|
|
95
|
+
| Error codes | `E_UNSUPPORTED_RUNTIME`, `E_RUNTIME_PACKAGE`, `E_RUNTIME_RESOURCE_MISSING` | `E_UNSUPPORTED_HARNESS`, `E_HARNESS_PACKAGE`, `E_HARNESS_RESOURCE_MISSING` | Output only |
|
|
96
|
+
| Hook environment (spawn and launch hooks) | `OATS_RUNTIME`, `OATS_PREVIOUS_RUNTIME` | `OATS_HARNESS`, `OATS_PREVIOUS_HARNESS` | Both are set, with the same values, for released hooks |
|
|
97
|
+
| Capability manifest `requires[]` harness package | `runtime` | `harness` | Yes, **without** a warning (a provider's file); a row naming both is refused |
|
|
98
|
+
| Package verification `loadedBy` | `runtime-discovery` | `harness-discovery` | Output only |
|
|
99
|
+
|
|
100
|
+
Unchanged, because they do not name the harness: the session endpoint
|
|
101
|
+
vocabulary (`runtimeAuthority`, `runtimeState`/`runtimeError` in liveness,
|
|
102
|
+
`E_RUNTIME_ENDPOINT_UNKNOWN`, `E_RUNTIME_AUTHORITY_MISMATCH`,
|
|
103
|
+
`E_RUNTIME_QUIESCE_FAILED`); `capabilityRuntime`; the retirement baseline's
|
|
104
|
+
`runtime`; oats.okf's `harvest-runtime` setting; and the kernel's
|
|
105
|
+
"runtime-neutral" design. Hook stdin carries no `launch.runtime` (no 0.26 hook
|
|
106
|
+
emitter wrote it).
|
|
107
|
+
|
|
108
|
+
## Inspect, readiness and operation run on the workspace model (`operationsApi: 2`, `soulsApi: 2`, `readinessApi: 2`, OATS 0.26.0)
|
|
109
|
+
|
|
110
|
+
On a workspace deployment (an `oats-local.yaml` in reach of `--dir`), and for
|
|
111
|
+
any home whose `instance.json` records `modules`, these three commands read the
|
|
112
|
+
workspace model's own records and **never the classic config chain**. The probe
|
|
113
|
+
integers are the gate; there is no feature string. The probe's integer says
|
|
114
|
+
this kernel CAN answer the v2 shape. **Dispatch on the payload's own integer**.
|
|
115
|
+
There is no v1 shape any more (0.26.0 removed the classic chain's answers):
|
|
116
|
+
with no `oats-local.yaml` in reach and no `--home`, the three commands answer
|
|
117
|
+
`E_LOCAL_MISSING`; a `--home` whose `instance.json` records no `modules`
|
|
118
|
+
(spawned by an earlier kernel) answers `E_UNSUPPORTED_MODE` (re-spawn it from
|
|
119
|
+
the deployment); an unreadable `--home` answers `E_SESSION_UNKNOWN`.
|
|
120
|
+
|
|
121
|
+
**The captured/portable path was removed in 0.26.** A *captured home* (its `instance.json`
|
|
122
|
+
records `executionBinding`, `incarnationId` or `captured`: spawned through
|
|
123
|
+
0.24–0.25's `prepare` / `--deployment --resolution`) answers `E_UNSUPPORTED_MODE`
|
|
124
|
+
(`details: {home, captured: true}`) to these three commands, to `session
|
|
125
|
+
start|restart` and to its in-home commands; `oats retire` still works on it, and
|
|
126
|
+
its result's `warnings[]` names each capability whose retire hook did NOT run
|
|
127
|
+
(what it created is not revoked). `oats status --json` / `oats doctor --json`
|
|
128
|
+
name captured homes once, in `problems[]`, as `legacy-captured-home` `{instances,
|
|
129
|
+
homes, message}`. The captured selectors `--deployment`, `--resolution` and
|
|
130
|
+
`--artifact-set`, and an inherited `OATS_DEPLOYMENT`/`OATS_RESOLUTION`, are refused
|
|
131
|
+
by every command except `version` (`E_UNSUPPORTED_MODE`, `details.selector` or
|
|
132
|
+
`details.inherited`); `oats prepare` and `oats inspect --request` are removed
|
|
133
|
+
verbs (`E_UNKNOWN_COMMAND`, `details: {removed, replacement}`). The version
|
|
134
|
+
document no longer carries `capturedDispatchApi` / `capturedDispatchActions`.
|
|
135
|
+
|
|
136
|
+
| Command | Integer (probe and payload) | 0.25.x value |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `oats inspect --json` | `operationsApi: 2` (top level); each `souls[]` row `soulsApi: 2` | 1 / 1 |
|
|
139
|
+
| `oats readiness --json` | `readinessApi: 2` | 1 |
|
|
140
|
+
| `oats operation run --json` | `operationsApi: 2` on the result | absent |
|
|
141
|
+
|
|
142
|
+
The probe's `soulsApi` follows the inspect soul rows. The `oats souls --json`
|
|
143
|
+
document keeps its own `soulsApi: 1`, because its shape did not change (see
|
|
144
|
+
[`oats souls`](#oats-capabilities---dir---json-capabilitiesapi-1-oats-souls---dir---json-soulsapi-1)).
|
|
145
|
+
|
|
146
|
+
**The subject is an instance or a soul, never a scope.** Pass `--home <abs>`
|
|
147
|
+
or `--soul <name>`. A workspace deployment with neither is `E_BAD_ARGS`. An
|
|
148
|
+
`oats-local.yaml` that exists but cannot be read is reported with its own
|
|
149
|
+
error code, never answered from the classic chain. For
|
|
150
|
+
inspect, the message points to `oats souls` and `oats capabilities`, the
|
|
151
|
+
scope-wide lists.
|
|
152
|
+
- `--home` selects the instance, from its `instance.json` and the module copies
|
|
153
|
+
under `<home>/.oats/modules/`. Everything is as spawned.
|
|
154
|
+
- `--soul` selects the soul, resolved exactly as a spawn of it would be:
|
|
155
|
+
discovery, then soul `capabilities:` plus workspace defaults, then the lock.
|
|
156
|
+
- A v2 home lives at `<deployment>/agents/<soul>/instances/<name>`, and its
|
|
157
|
+
deployment is derived from that path. `<deployment>/oats-local.yaml` must
|
|
158
|
+
exist exactly there (never found by walking up), otherwise
|
|
159
|
+
`E_HOME_MISMATCH`. A v2 spawn ignores an ambient `PI_AGENTS_ROOT` /
|
|
160
|
+
`OATS_ROOT`, so its homes always have this layout.
|
|
161
|
+
- `--dir`, if given with `--home`, must be that home's deployment
|
|
162
|
+
(`E_HOME_MISMATCH`). `--agents-root`, if given, must be
|
|
163
|
+
`<deployment>/agents` (`E_HOME_MISMATCH` with `--home`, `E_SOUL_UNKNOWN`
|
|
164
|
+
with `--soul`).
|
|
165
|
+
|
|
166
|
+
**Gone from every payload:** `scope` (`context`, `chain`, `team`,
|
|
167
|
+
`agentsRoots`), config `levels`, `activation {declaredAt, target, level,
|
|
168
|
+
source}`, `currentConfig`, `snapshot.drift`, `health {trusted, approved,
|
|
169
|
+
locked, installedIntegrity}`, soul `provenance`/`readiness`, and the scope's
|
|
170
|
+
portable `sources`. A capability's origin is its module's `from` (member
|
|
171
|
+
commit, or package version + commit + integrity). Its settings are the merged
|
|
172
|
+
payload the spawn recorded for a home, or the resolution computes for a soul.
|
|
173
|
+
|
|
174
|
+
### `oats inspect (--home <abs> | --soul <name> [--dir <d>]) --json` → `operationsApi: 2`
|
|
79
175
|
|
|
80
176
|
```json
|
|
81
|
-
{"
|
|
82
|
-
"
|
|
177
|
+
{"operationsApi":2,"kernel":"0.26.0",
|
|
178
|
+
"subject":{"kind":"instance","instance":"release-manager-x","home":"/w/agents/release-manager/instances/release-manager-x","soul":"release-manager"},
|
|
179
|
+
"workspace":{"key":"github.com/northwind/agents","name":null,"deployment":"/w","commit":"461b9c24…","standalone":false},
|
|
180
|
+
"souls":[{"soulsApi":2,"name":"release-manager","repoKey":"github.com/northwind/agents","commit":"461b9c24…","team":"engineering",
|
|
181
|
+
"kind":null,"path":null,"description":"Cuts, verifies and announces platform releases.","work":"worktree","harness":null,"model":null,
|
|
182
|
+
"declarations":{"requires":null,"defaults":null,"knowledge":{"owns":"release-manager","reads":["platform-engineer"]},"teams":null,"resources":null,"children":null,
|
|
183
|
+
"capabilities":{"nw-release-tooling":{"from":"here"},"nw-deploy":{"from":"package"}}},
|
|
184
|
+
"declarationProblems":[],
|
|
185
|
+
"instructions":{"file":"/w/agents/release-manager/souls/461b9c24929c/AGENTS.md","text":"# release-manager\n…","truncated":false}}],
|
|
186
|
+
"layers":{"knowledge":{"id":"oats.okf","from":"workspace"},"messaging":{"id":null,"from":null},"tasks":{"id":null,"from":null}},
|
|
187
|
+
"capabilities":[
|
|
188
|
+
{"id":"nw-house-style","version":"0.0.0-workspace","layer":null,"command":null,
|
|
189
|
+
"from":{"kind":"member","repoKey":"github.com/northwind/agents","commit":"461b9c24…"},
|
|
190
|
+
"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":[]},
|
|
191
|
+
{"id":"oats.okf","version":"2.1.3","layer":"knowledge","command":"okf",
|
|
192
|
+
"from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"71f53649…","integrity":"sha256-5019…","repoKey":"github.com/awebai/oats-okf"},
|
|
193
|
+
"dir":"/w/agents/release-manager/instances/release-manager-x/.oats/modules/oats.okf",
|
|
194
|
+
"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":[],
|
|
195
|
+
"operations":[{"name":"inspect","kind":"view","command":"inspect","context":"home","description":"…","args":[],"argv":["okf","inspect"],"available":true,"reason":null}]}],
|
|
196
|
+
"knowledge":{"provider":"oats.okf","version":"2.1.3","operations":[{"name":"inspect","kind":"view","available":true,"reason":null}]},
|
|
197
|
+
"instance":{"home":"/w/agents/release-manager/instances/release-manager-x","instance":"release-manager-x","agent":"release-manager",
|
|
198
|
+
"harness":"pi","model":null,"yolo":null,"launched":false,"createdAt":"<iso>","resolution":"7217670b…",
|
|
199
|
+
"soulDir":"/w/agents/release-manager/souls/461b9c24929c",
|
|
200
|
+
"instructions":{"file":"/w/agents/release-manager/instances/release-manager-x/AGENTS.md","text":"…","truncated":false,
|
|
201
|
+
"sources":[{"source":"kernel:instance-boundary","file":"…"},{"source":"capability:oats.okf","file":"…/.oats/modules/oats.okf/injects/okf.md"}]}},
|
|
202
|
+
"identity":null,"problems":[]}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
- `subject` is `{kind:"instance", instance, home, soul}` for `--home` (the
|
|
206
|
+
`home` as you passed it) or `{kind:"soul", soul, repoKey, commit, team}` for
|
|
207
|
+
`--soul`.
|
|
208
|
+
- `workspace.deployment` is canonical (realpath). `workspace.name` is
|
|
209
|
+
observed, so it is `null` on `inspect --home`: that command never contacts
|
|
210
|
+
the remotes, and `instance.json` records the workspace `key`, not its name.
|
|
211
|
+
Identify the workspace by `key`.
|
|
212
|
+
- `souls` holds exactly the subject's soul. For a home, it is read from the
|
|
213
|
+
recorded `soulDir` (the per-commit copy the instance incarnates), with
|
|
214
|
+
`path: null`. For a soul, it is the member's current definition, with
|
|
215
|
+
`path` inside the member repository. `kind` is `member` or `external`.
|
|
216
|
+
It is observed from discovery, so it is `null` on `inspect --home`
|
|
217
|
+
(readiness `--home` observes it).
|
|
218
|
+
`declarations` gains `capabilities` (the soul's own `capabilities:`).
|
|
219
|
+
- `layers.<layer>` is `{ id, from }`: the capability filling the slot
|
|
220
|
+
(`null` when empty) and where it came from (feature `layers-from`):
|
|
221
|
+
`"soul"` (the soul's own `capabilities:`), `"workspace"`
|
|
222
|
+
(`defaults.<slot>` or `defaults.capabilities`) or `"team:<label>"`
|
|
223
|
+
(`defaults.byTeam.<label>.capabilities`). `from` is `null` for an empty
|
|
224
|
+
slot. A soul answers from its resolution now. A home answers what its spawn
|
|
225
|
+
recorded, even after the workspace changes. A home spawned before
|
|
226
|
+
`layers-from` recorded nothing, so its `from` is `null`.
|
|
227
|
+
- `capabilities[]` lists the subject's resolved modules, sorted by id:
|
|
228
|
+
- `dir` is the home's module copy, or `null` for a soul (nothing is
|
|
229
|
+
materialized to answer inspect).
|
|
230
|
+
- `settings` is the merged provider payload.
|
|
231
|
+
- `compatibility` is `{ ok, range, kernel }`: the manifest's
|
|
232
|
+
`compatibility.oats` (`null` when none) against the running kernel. A
|
|
233
|
+
soul's resolution refuses an incompatible module (`E_CAPABILITY_INCOMPATIBLE`),
|
|
234
|
+
so `ok: false` appears only for a home, together with a
|
|
235
|
+
`capability-incompatible` entry in `problems`.
|
|
236
|
+
- `declares` lists the setting keys the manifest declares (`settings.<key>`),
|
|
237
|
+
sorted; names only, never descriptions or defaults; `[]` when it declares
|
|
238
|
+
none. Gate on feature `settings-declared` (e.g. offer a Teams choice only
|
|
239
|
+
when the messaging module declares `join`).
|
|
240
|
+
- `missingRequires` lists the manifest `requires` commands absent from PATH.
|
|
241
|
+
- `operations[].available` is `false` with a `reason` when it cannot run
|
|
242
|
+
here: a `context: "home"` operation for a soul subject says `needs a
|
|
243
|
+
running home (--home)`.
|
|
244
|
+
- `instance` is `null` for a soul. For a home, `instructions.sources` names
|
|
245
|
+
each composed inject in order.
|
|
246
|
+
- A soul whose resolution is refused (for example, a package the lock does
|
|
247
|
+
not provide) is an error for inspect (`E_PACKAGE_MISSING`,
|
|
248
|
+
`E_PACKAGE_INTEGRITY`, `E_CAPABILITY_MISSING`, `E_LOCK_SCHEMA`). Readiness
|
|
249
|
+
reports the same condition as a failing item.
|
|
250
|
+
|
|
251
|
+
### `oats readiness (--home <abs> | --soul <name> [--dir <d>]) [--policy] --json` → `readinessApi: 2`
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{"readinessApi":2,
|
|
255
|
+
"subject":{"kind":"soul","soul":"release-manager","repoKey":"github.com/northwind/agents","commit":"461b9c24…","team":"engineering"},
|
|
256
|
+
"selector":{"kind":"soul","soul":"release-manager","agentsRoot":null,"dir":"/w"},"at":"<iso>",
|
|
257
|
+
"checks":{
|
|
258
|
+
"installed":{"status":"pass","items":[
|
|
259
|
+
{"subject":"oats.okf","status":"pass","required":true,"reason":null,"producer":"workspace resolution",
|
|
260
|
+
"evidence":{"from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"71f53649…","integrity":"sha256-5019…"}},"remedy":null,"capability":{"id":"oats.okf"}}]},
|
|
261
|
+
"configured":{"status":"not-applicable","items":[]},
|
|
262
|
+
"member":{"status":"pass","items":[
|
|
263
|
+
{"subject":"member github.com/northwind/agents","status":"pass","required":true,"reason":null,"producer":"workspace discovery",
|
|
264
|
+
"evidence":{"repoKey":"github.com/northwind/agents","workspace":"github.com/northwind/agents","commit":"461b9c24…"},"remedy":null}]},
|
|
265
|
+
"providers":{"status":"fail","items":[
|
|
266
|
+
{"subject":"oats.okf","status":"fail","required":true,"reason":"setting state-dir is required (absolute host path)","producer":"provider binding check",
|
|
267
|
+
"evidence":null,"remedy":null,"capability":{"id":"oats.okf"},
|
|
268
|
+
"result":{"status":"needs-configuration","problems":[{"code":"needs-configuration","message":"setting state-dir is required (absolute host path)"}],"warnings":[]}}]}},
|
|
269
|
+
"summary":{"ready":false,"required":3,"pass":2,"fail":1,"unknown":0,
|
|
270
|
+
"byCapability":[{"capability":{"id":"oats.okf"},"checks":{"installed":"pass","configured":"not-applicable","member":"not-applicable","providers":"fail"},"ownReady":false,"ready":false}],
|
|
271
|
+
"subjectBlockers":[]},
|
|
272
|
+
"notes":["…"]}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
For `--home`, `subject` is `{kind:"instance", instance, home, soul}`, and
|
|
276
|
+
`selector` is `{kind:"home", home, soul, agentsRoot}`. The selector echoes
|
|
277
|
+
your arguments byte-exact, as before. It is now a top-level field, not
|
|
278
|
+
`subject.selector`.
|
|
279
|
+
|
|
280
|
+
The four checks are `installed | configured | member | providers`, each
|
|
281
|
+
`{status, items}` with the item fields as before (`subject, status, required,
|
|
282
|
+
reason, producer, evidence, remedy`, plus `capability {id}` on
|
|
283
|
+
per-capability items). Item and check statuses are `pass | fail | unknown |
|
|
284
|
+
not-applicable`. **`summary.ready`** means every required item passes or is
|
|
285
|
+
not-applicable, and at least one required item exists. `byCapability` and
|
|
286
|
+
`subjectBlockers` keep their 0.24.9 meaning over the four new checks.
|
|
287
|
+
|
|
288
|
+
- **`installed`**:
|
|
289
|
+
- For `--home` (producer `instance modules`): each recorded module, `pass`
|
|
290
|
+
when its copy under `<home>/.oats/modules/<id>/` holds its `oats.json`.
|
|
291
|
+
A missing copy fails, with remedy "spawn a new instance".
|
|
292
|
+
- For `--soul` (producer `workspace resolution`): each resolved module, with
|
|
293
|
+
its `from` as evidence.
|
|
294
|
+
- A resolution refusal is one failing item carrying `code` (`E_PACKAGE_MISSING`,
|
|
295
|
+
`E_PACKAGE_INTEGRITY`, `E_CAPABILITY_MISSING`, `E_LOCK_SCHEMA` or
|
|
296
|
+
`E_REQUIREMENT_INACTIVE`), the kernel's message as `reason`, its details as
|
|
297
|
+
`evidence`, and `remedy: "oats sync (…)"`. That covers a soul whose
|
|
298
|
+
packages are not locked, or whose lock no longer matches. The item's
|
|
299
|
+
`subject` is the soul; it lands in `summary.subjectBlockers`.
|
|
300
|
+
- **`configured`** (producer `capability manifest`): each module's manifest
|
|
301
|
+
`requires` command (`evidence.command`), `pass` on PATH and `fail`
|
|
302
|
+
otherwise, with the manifest's `install` hint as the remedy. It is `not-applicable` when nothing declares a
|
|
303
|
+
requirement. Settings problems are the provider's to say, in `providers`.
|
|
304
|
+
- **`member`** (producer `workspace discovery`): the soul's member
|
|
305
|
+
repository is confirmed in the workspace. It is the host's `members:` with
|
|
306
|
+
the member's `oats-membership.yaml` backlink, as `oats workspace status`
|
|
307
|
+
reports it. The kernel never reads `oats.yaml` here.
|
|
308
|
+
- `fail` when the backlink is not confirmed; the remedy names
|
|
309
|
+
`oats-membership.yaml`.
|
|
310
|
+
- `unknown` when discovery could not be read.
|
|
311
|
+
- `not-applicable` (`required: false`) for an external soul (it has no
|
|
312
|
+
member backlink) and on a standalone view (decision 10, an allowed mode:
|
|
313
|
+
membership is declared there, never confirmed). The reason starts with
|
|
314
|
+
`standalone view (explicit | unreadable-host)`, and
|
|
315
|
+
`evidence.standaloneReason` carries the reason code. A standalone soul can
|
|
316
|
+
therefore read Ready.
|
|
317
|
+
- Never login, never team registration.
|
|
318
|
+
- **`providers`** (producer `provider binding check`): for each module whose
|
|
319
|
+
manifest declares `binding`, the kernel runs the provider's own check
|
|
320
|
+
(`binding.check`). It relays **the provider's answer verbatim** as
|
|
321
|
+
`item.result: {status, problems: [{code, message}], warnings: [{code,
|
|
322
|
+
message}]}`. The status maps to the item:
|
|
323
|
+
|
|
324
|
+
| `result.status` | item `status` |
|
|
325
|
+
|---|---|
|
|
326
|
+
| `ready` | `pass` |
|
|
327
|
+
| `needs-configuration` | `fail` |
|
|
328
|
+
| `authorization-required` | `fail` |
|
|
329
|
+
| `unavailable` | `unknown` |
|
|
330
|
+
|
|
331
|
+
The reason is the first problem's message (`null` on pass).
|
|
332
|
+
`authorization-required` and `unavailable` were added in 0.26.0 (additive):
|
|
333
|
+
treat an unrecognized status as `unknown` and show `result` as sent.
|
|
334
|
+
- `warnings` is always present (`[]` when the provider sends none). It
|
|
335
|
+
**never changes the status** and is not counted in `summary`. A ready
|
|
336
|
+
binding can still say, for example, that end-to-end encryption is off:
|
|
337
|
+
show it next to the pass. A `warnings` that is not an array of `{code,
|
|
338
|
+
message}` strings makes the whole answer `unknown`, the same as a
|
|
339
|
+
malformed `problems`.
|
|
340
|
+
- A provider that cannot answer is `unknown`, with `item.problems` carrying
|
|
341
|
+
its error `{code, message}` and `result: null`. That covers:
|
|
342
|
+
- a refusal (`ok:false`, whose code is relayed);
|
|
343
|
+
- an invalid answer (`provider-unavailable`, see the wire below);
|
|
344
|
+
- a timeout;
|
|
345
|
+
- a module tree that cannot be made available.
|
|
346
|
+
- **One time budget per readiness read**: 60 s for all provider checks
|
|
347
|
+
together, and at most 30 s for each. Checks the budget does not reach are
|
|
348
|
+
not run; they are `unknown` with code `time-budget-exhausted`.
|
|
349
|
+
- For `--home` the check runs from the home's module copy, as the home's
|
|
350
|
+
hooks do. For `--soul` it runs from the module in the deployment's module
|
|
351
|
+
store (the tree `oats <ns> …` dispatch uses). A store tree is used only
|
|
352
|
+
while its content digest matches the digest verified when it was fetched
|
|
353
|
+
at the locked commit. A drifted tree is fetched again, and a fetch that
|
|
354
|
+
does not verify is `E_PACKAGE_INTEGRITY` (the item is `unknown` with that
|
|
355
|
+
code).
|
|
356
|
+
- A module without `binding` has no item; the check is `not-applicable`
|
|
357
|
+
when there are none.
|
|
358
|
+
- This check reads the provider; it does not bind. A spawn's fail-closed
|
|
359
|
+
hooks are unchanged.
|
|
360
|
+
- **Removed:** `trusted` and its `signature` block (declaring a package in
|
|
361
|
+
`packages:` is the trust decision). `--verify-signatures` answers
|
|
362
|
+
`E_BAD_ARGS`. `enrolled` is now `member`.
|
|
363
|
+
|
|
364
|
+
`--policy` is **kept**: it means the same without the chain. With `--home` it
|
|
365
|
+
is the instance's recorded, enforced policy (`instance.json` `policy`, plus
|
|
366
|
+
the recorded work mode). With `--soul` it is the soul's declaration
|
|
367
|
+
(`children.spawn`, `work`), `enforced: false`. The shape is unchanged:
|
|
368
|
+
|
|
369
|
+
```json
|
|
370
|
+
"policy":{"childSpawns":{"allowed":true,"enforced":true,"origin":{"kind":"default","detail":"no declaration: children allowed"}},
|
|
371
|
+
"worktrees":{"allowed":false,"mode":"directory","enforced":true,"origin":{"kind":"work-mode","detail":"work: directory"}}}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
**The provider check wire (the request `binding.check` receives).** The
|
|
375
|
+
request is one JSON line on stdin, and the provider answers one envelope line
|
|
376
|
+
on stdout:
|
|
377
|
+
|
|
378
|
+
```json
|
|
379
|
+
{"schemaVersion":1,"phase":"check","slot":"knowledge","capability":"oats.okf","settings":{"…":"the merged payload"},
|
|
380
|
+
"input":{"context":{"kind":"workspace","workspace":"<workspace key>","deployment":"/w","soul":"release-manager","team":"engineering",
|
|
381
|
+
"instance":"release-manager-x","home":"/w/agents/…/release-manager-x"},
|
|
382
|
+
"action":{"kind":"readiness"}}}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{"schemaVersion":1,"phase":"check","slot":"knowledge","capability":"oats.okf","ok":true,
|
|
387
|
+
"result":{"status":"ready","problems":[],"warnings":[]}}
|
|
83
388
|
```
|
|
84
389
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
390
|
+
The environment is the provider's module environment:
|
|
391
|
+
- `OATS_CAPABILITY`, `OATS_SETTINGS`, `OATS_CLI_BIN` and `OATS_WORKSPACE`;
|
|
392
|
+
- the team variables (`OATS_TEAM_*`, `OATS_WORKSPACE_NAME`/`_KEY`);
|
|
393
|
+
- `OATS_AGENT` (the soul), and `OATS_SOUL` when the soul directory is known;
|
|
394
|
+
- for a home, `OATS_INSTANCE` and `OATS_INSTANCE_HOME`.
|
|
395
|
+
|
|
396
|
+
For a home, `OATS_WORKSPACE_NAME` is `""` until spawn records the workspace
|
|
397
|
+
name (planned).
|
|
398
|
+
|
|
399
|
+
Ambient `OATS_*`/`PI_*` is removed. For a soul, `instance` and `home` are
|
|
400
|
+
`null`. The answer is decoded by the binding wire's response rules:
|
|
401
|
+
- the process exits 0;
|
|
402
|
+
- stdout is exactly one JSON document within the wire limits;
|
|
403
|
+
- the envelope has exactly `schemaVersion`, `phase`, `slot`, `capability`,
|
|
404
|
+
`ok` and `result` (or `error`), echoing the request's first four;
|
|
405
|
+
- `result` has `status`, `problems` and optionally `warnings`, and nothing else;
|
|
406
|
+
- `ready` carries no problems;
|
|
407
|
+
- problems and warnings are `{code, message}` strings. Their codes are the
|
|
408
|
+
provider's own and are not checked against `binding.reasons`.
|
|
409
|
+
|
|
410
|
+
Anything else is `unknown` (`provider-unavailable`). The check executable must
|
|
411
|
+
resolve (realpath) inside its module directory and be a regular file; otherwise
|
|
412
|
+
the item is `unknown` (`resource-not-found`). The request carries no
|
|
413
|
+
`binding`. A provider whose check
|
|
414
|
+
still requires one answers `invalid-binding`, and readiness reports it as
|
|
415
|
+
`unknown`.
|
|
416
|
+
|
|
417
|
+
### `oats operation run <layer>:<name> (--home <abs> | --soul <name> [--dir <d>]) [--arg k=v …] --json` → `operationsApi: 2`
|
|
418
|
+
|
|
419
|
+
```json
|
|
420
|
+
{"operationsApi":2,"operation":"knowledge:inspect","capability":"oats.okf","version":"2.1.3","argv":["okf","inspect"],
|
|
421
|
+
"cwd":"/w/agents/release-manager/instances/release-manager-x",
|
|
422
|
+
"target":{"home":"/w/agents/release-manager/instances/release-manager-x","instance":"release-manager-x"},
|
|
423
|
+
"result":{"documents":[{"label":"Working state (STATE.md)","kind":"markdown","text":"…"}]}}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
The provider is the module that fills `<layer>`:
|
|
427
|
+
- for `--home`, the home's module copy, with its recorded settings;
|
|
428
|
+
- for `--soul`, the resolved module, materialized into the deployment's module
|
|
429
|
+
store if needed.
|
|
430
|
+
|
|
431
|
+
The rest of the contract is unchanged ([operations contract](design/operations-contract.md)):
|
|
432
|
+
- errors: `E_OPERATION_UNKNOWN`, `E_OPERATION_UNAVAILABLE` (also a
|
|
433
|
+
`context: "home"` operation without `--home`), `E_CAPABILITY_REQUIRES`;
|
|
434
|
+
- the receipt rules and the `E_OPERATION_TIMEOUT` / `E_OPERATION_RESULT`
|
|
435
|
+
unconfirmed outcomes.
|
|
436
|
+
|
|
437
|
+
There is no `E_CAPABILITY_BLOCKED` (no trust gate). `cwd` is the home for a
|
|
438
|
+
`context: "home"` operation, and the deployment otherwise.
|
|
439
|
+
|
|
440
|
+
**Remote.** `--server` routes as before. The destination must advertise
|
|
441
|
+
`operations` with `operationsApi` 1 or 2; a 0.26 CLI routes to either.
|
|
442
|
+
Payload shapes are the destination kernel's.
|
|
443
|
+
|
|
444
|
+
## Souls and sources (`oats inspect --json`, `soulsApi: 1`) — removed in 0.26.0
|
|
445
|
+
|
|
446
|
+
The classic scope document (`souls[].provenance`, `souls[].readiness`, the
|
|
447
|
+
scope's portable `sources`) was removed with the classic config chain.
|
|
448
|
+
`oats inspect` answers only [`soulsApi: 2`](#oats-inspect---home---soul---dir---json-operationsapi-2)
|
|
449
|
+
rows; the soul's declarations are in `oats souls --json`.
|
|
92
450
|
|
|
93
451
|
## Instance Git state (`oats instance git|diff`, `instanceGitApi: 1`, OATS 0.24.7+)
|
|
94
452
|
|
|
@@ -170,7 +528,7 @@ home's removal, so a retired instance's `retired` event is still readable).
|
|
|
170
528
|
|
|
171
529
|
```json
|
|
172
530
|
{"eventsApi":1,"instance":"dev-1","home":"/abs/home","count":7,"returned":7,"truncated":false,
|
|
173
|
-
"events":[{"eventsApi":1,"at":"<iso>","instance":"dev-1","home":"/abs/home","producer":"kernel","kind":"spawned","data":{"agent":"dev","work":"worktree","branch":"agents/dev-1","
|
|
531
|
+
"events":[{"eventsApi":1,"at":"<iso>","instance":"dev-1","home":"/abs/home","producer":"kernel","kind":"spawned","data":{"agent":"dev","work":"worktree","branch":"agents/dev-1","harness":"claude","model":null,"parentInstance":null,"relation":null,"launched":true}},
|
|
174
532
|
{"…":"launched | restarted | stopped | stop-refused | retire-planned | worktree-retained | worktree-removed | branch-deleted | retired | child-spawn-refused"}],
|
|
175
533
|
"lastEvent":{"kind":"stopped","at":"<iso>","producer":"kernel"},
|
|
176
534
|
"waitingOnYou":null,
|
|
@@ -258,8 +616,8 @@ deduplicated per run. Where the run launched or targeted an instance, a
|
|
|
258
616
|
session to open (the existing `oats session` surface); the kernel does not
|
|
259
617
|
copy transcripts. `nextRun`/`lastRun`/`executionStatus` are unchanged. The
|
|
260
618
|
Schedules view (frame 08) renders `recentRuns` as the recent-runs list and the
|
|
261
|
-
transcript pointer as the handoff
|
|
262
|
-
|
|
619
|
+
transcript pointer as the handoff (definition fields are untouched by this
|
|
620
|
+
addition). A stored captured definition (removed in 0.26) lists as `invalid`.
|
|
263
621
|
|
|
264
622
|
### History API 3 (`scheduleHistoryApi: 3`, feature `schedule-read-2`, OATS 0.24.13+) — K8b
|
|
265
623
|
|
|
@@ -327,20 +685,21 @@ unchecked, echoed a stored `definition.id` without checking it, and named a
|
|
|
327
685
|
The Spawn modal's fields are backed by the kernel's own decision, taken **before
|
|
328
686
|
any side effect**: `oats spawn <agent> [same flags as a real spawn] --preview --json`
|
|
329
687
|
runs every preflight a spawn runs (placement, composition, resources,
|
|
330
|
-
executable,
|
|
688
|
+
executable, harness packages, child-spawn policy) and returns what the spawn
|
|
331
689
|
*would* do — then returns without creating a home, branch or worktree.
|
|
332
690
|
|
|
333
691
|
```json
|
|
334
692
|
{"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","
|
|
693
|
+
"repo":"/abs/repo","work":"worktree","harness":"claude","model":"opus","modelSource":"explicit","launchConfig":null,"yolo":false,"backend":"tmux",
|
|
336
694
|
"branch":"agents/dev-fix-login","base":{"ref":"HEAD","oid":"<oid>"},"worktree":"/abs/agents/dev/instances/dev-fix-login/work",
|
|
337
695
|
"relation":null,"parentInstance":null,"policy":{"childSpawns":{"allowed":true,"origin":{"kind":"default","detail":"…"}}},
|
|
338
696
|
"executable":"/abs/bin/claude","capabilities":["oats.core"],"skills":["oats-operate","oats-souls"],"task":"…"}
|
|
339
697
|
```
|
|
340
698
|
|
|
341
|
-
- **Name / work area**: `instance` is the
|
|
342
|
-
de-duplicated with `-2`, `-3`…)
|
|
343
|
-
|
|
699
|
+
- **Name / work area**: `instance` is the name: by default the derived shape
|
|
700
|
+
`<agent>-<purpose>` (de-duplicated with `-2`, `-3`…), or exactly the
|
|
701
|
+
`--name <slug>` the caller gave (see *Instance names* below); `home` and
|
|
702
|
+
`worktree` are the canonical paths. The renderer never derives paths.
|
|
344
703
|
- **Branch / base** (worktree mode): `branch` defaults to `agents/<instance>`
|
|
345
704
|
(`--branch <name>` overrides; validated); `base` is `--base <ref>` resolved
|
|
346
705
|
to its commit oid (default `HEAD`). `E_BRANCH_EXISTS` and `E_BASE_UNKNOWN`
|
|
@@ -348,7 +707,7 @@ executable, runtime packages, child-spawn policy) and returns what the spawn
|
|
|
348
707
|
the worktree **from that exact oid**.
|
|
349
708
|
- **Model**: `model`/`modelSource` are the resolved selection. Omitting
|
|
350
709
|
`--model` **inherits** the launch configuration's or soul's preference;
|
|
351
|
-
`--model @native-default` is the explicit "use the
|
|
710
|
+
`--model @native-default` is the explicit "use the harness's own default"
|
|
352
711
|
(`modelSource: "native default (explicit)"`). These are different requests
|
|
353
712
|
and the UI must not relabel one as the other.
|
|
354
713
|
- **Policy**: `policy.childSpawns` is what this instance will record (soul
|
|
@@ -376,15 +735,13 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
376
735
|
import; `--instructions-file`/`--def-file` refused with `E_BAD_ARGS`). Test:
|
|
377
736
|
the deployment tree is byte-identical after a success, a refusal and an
|
|
378
737
|
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.
|
|
738
|
+
**Workspace deployments (0.26.0+)**: this holds for the FIRST preview of a
|
|
739
|
+
soul or commit too. A preview reads the soul from the deployment's per-commit
|
|
740
|
+
cache (`agents/<soul>/souls/<commit>/`) when a spawn already filled it, else
|
|
741
|
+
fetches it to a temporary copy outside the deployment and removes it
|
|
742
|
+
(`soulFetched: true` in the result). Only a spawn fills the cache or moves the
|
|
743
|
+
`agents/<soul>/soul` pointer. (0.25.x previews populated the cache: that stated
|
|
744
|
+
exception is gone.)
|
|
388
745
|
- **Exact root**: `spawn <soul> --agents-root <abs>` binds the soul to that root
|
|
389
746
|
(as inspect/readiness take it) — no team-soul / capability-agent / importable-
|
|
390
747
|
def fallback; mismatch → `E_SOUL_UNKNOWN`. The preview echoes
|
|
@@ -395,7 +752,19 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
395
752
|
per-module payload each provider will receive (`{ "<cap>": {…} }`, exactly
|
|
396
753
|
`settings.<cap>` of the preview) — so a confirmed apply binds every
|
|
397
754
|
provider fact (an identity choice, a delivery mode) **by value**; a Desktop
|
|
398
|
-
that changes a provider field re-previews.
|
|
755
|
+
that changes a provider field re-previews. From 0.26.0 the merged payload
|
|
756
|
+
includes the manifest's declared setting defaults (`settings.<key>.default`,
|
|
757
|
+
the lowest layer), and the preview's **`settingsOrigins.<cap>`** maps each
|
|
758
|
+
leaf of `settings.<cap>` (a JSON pointer, e.g. `/identity/mode`) to
|
|
759
|
+
`{ kind, at }`: `kind` is `manifest-default` | `workspace` | `soul` |
|
|
760
|
+
`host` | `spawn` — the last layer that set it. (`workspace-team` no longer
|
|
761
|
+
appears since teams amendment K: a label's `byTeam` entry is not merged into
|
|
762
|
+
the settings; it is in `teams[].payload`.) —
|
|
763
|
+
and `at` names where (`oats.json#/settings/identity/default`,
|
|
764
|
+
`soul.yaml#/messaging`, `oats-local.yaml#/settings/<cap>`,
|
|
765
|
+
`--provider <cap>`, …). A Desktop labels `manifest-default` values
|
|
766
|
+
"Default" from this, instead of hardcoding them (feature
|
|
767
|
+
**`settings-origins`**). `instances[].identity` (status)
|
|
399
768
|
and `selected.identity` (inspect) carry the served principal a messaging
|
|
400
769
|
provider reported: `{ mode: "local"|"global", alias, team, address|null,
|
|
401
770
|
resident|null, grant?: { id, expiresAt, scopes }, provider }`; absent when
|
|
@@ -410,11 +779,11 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
410
779
|
- **Bounded preflight**: every native probe a preview runs (`pi --list-models`,
|
|
411
780
|
`pi list`, `claude plugin list`) shares ONE budget (20 s default), runs in its
|
|
412
781
|
own process group and is group-killed on timeout; `preflight {status:
|
|
413
|
-
complete|timeout, budgetMs, elapsedMs}` says which. A hanging
|
|
782
|
+
complete|timeout, budgetMs, elapsedMs}` says which. A hanging harness CLI cannot
|
|
414
783
|
hang a preview.
|
|
415
784
|
- **Confirmed apply contract** (0.24.10+, feature `spawn-apply-2`,
|
|
416
785
|
`spawnApplyApi: 1`) — what a GUI may promise at "Confirm spawn":
|
|
417
|
-
- `decision` gains **`effective {repo, work,
|
|
786
|
+
- `decision` gains **`effective {repo, work, harness, model, launchConfig,
|
|
418
787
|
yolo, backend, childSpawns, relation{kind, anchor{instance, agentsRoot}}}`**
|
|
419
788
|
and `revision` hashes placement + effective. An inherited default that would
|
|
420
789
|
change what launches (the soul's model edited between preview and apply,
|
|
@@ -429,7 +798,12 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
429
798
|
immediately after the decision check; a concurrent spawn that lost refuses
|
|
430
799
|
**`E_PLACEMENT_TAKEN`** having touched nothing. Two concurrent applies of
|
|
431
800
|
one decision yield exactly one home. There is no wider lock; this
|
|
432
|
-
reservation is the guarantee.
|
|
801
|
+
reservation is the guarantee. Instance names are deployment-wide
|
|
802
|
+
(0.26.0), so right after its reservation a spawn re-checks the whole agents
|
|
803
|
+
root: when another soul's concurrent spawn reserved the same name, it
|
|
804
|
+
removes its own empty reservation and refuses (`E_INSTANCE_NAME_TAKEN` for a
|
|
805
|
+
`--name`, `E_PLACEMENT_TAKEN` for a derived name). At most one wins, and
|
|
806
|
+
possibly neither.
|
|
433
807
|
- Gate confirmation AND the exec owner on `spawn-preview-2` +
|
|
434
808
|
`spawn-apply-2` + `spawn-idempotency`; a legacy local request on such a CLI
|
|
435
809
|
is refused by the Desktop (`E_PLAN_REQUIRED`), not routed around the fence.
|
|
@@ -468,75 +842,15 @@ the pre-fix marker and is never accepted for dispatch.
|
|
|
468
842
|
(provider contract), auto-PR (P1/ADE write approval), branch enumeration
|
|
469
843
|
(producer seam).
|
|
470
844
|
|
|
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
|
-
```
|
|
845
|
+
## Readiness quartet (`readinessApi: 1`) — removed in 0.26.0
|
|
509
846
|
|
|
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:
|
|
847
|
+
The quartet (`installed | trusted | configured | enrolled`), its signature
|
|
848
|
+
verification (`--verify-signatures`, feature `readiness-verify`) and the
|
|
849
|
+
scope subject were removed with the classic config chain. `oats readiness`
|
|
850
|
+
answers only [`readinessApi: 2`](#oats-readiness---home---soul---dir---policy---json-readinessapi-2);
|
|
851
|
+
`--verify-signatures` is `E_BAD_ARGS`.
|
|
535
852
|
|
|
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
|
-
```
|
|
853
|
+
### Enforced child-spawn policy (`--policy`)
|
|
540
854
|
|
|
541
855
|
`childSpawns` is **enforced by the spawn route**: `soul.yaml` may declare
|
|
542
856
|
`children: {spawn: false}`; `oats spawn --allow-child-spawns | --no-child-spawns`
|
|
@@ -550,58 +864,6 @@ instance's recorded (enforced) one; with only `--soul` it is the declaration
|
|
|
550
864
|
(`enforced: false`). It is a lifecycle-authority claim, not an OS sandbox —
|
|
551
865
|
the UI says so.
|
|
552
866
|
|
|
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
867
|
## Lifecycle plans — Stop and Remove (`lifecycleApi: 1`, OATS 0.24.8+)
|
|
606
868
|
|
|
607
869
|
The Desktop's Stop and Remove confirmations render **plans**: a read-only
|
|
@@ -693,6 +955,12 @@ location. The receipt says so:
|
|
|
693
955
|
nothing is lost; retry or pass `--discard-worktree`.
|
|
694
956
|
- Non-worktree modes report `retention: null`. Quarantine/rollback paths keep
|
|
695
957
|
their removal semantics.
|
|
958
|
+
- A recovery (`workRecovery`, `workRecoveries[]`) is `{path, classes, bytes,
|
|
959
|
+
outputs?, repoCopy?}` (0.26.0: `bytes`, `outputs`): `bytes` is the recovery's
|
|
960
|
+
own size; `outputs: {paths: [{path, bytes}], bytes}` names what it copied
|
|
961
|
+
beyond tracked state — a worktree's untracked and ignored paths, or a
|
|
962
|
+
directory's work entries — grouped by top-level entry, largest first. Absent
|
|
963
|
+
when only home bytes were copied.
|
|
696
964
|
- The Remove dialog's "also delete worktree / branch" checkboxes map to these
|
|
697
965
|
two flags; the kernel never touches a PR.
|
|
698
966
|
- **Guarded apply** (what a GUI sends): `oats retire <i> --plan-revision <rev>
|
|
@@ -730,7 +998,8 @@ location. The receipt says so:
|
|
|
730
998
|
`retire-retention`, `readiness`, `spawn-preview`, `instance-events`,
|
|
731
999
|
`schedule-history`, and carries the API integers (`instanceGitApi`, `soulsApi`,
|
|
732
1000
|
`lifecycleApi`, `readinessApi`, `spawnPreviewApi`, `eventsApi`,
|
|
733
|
-
`scheduleHistoryApi`).
|
|
1001
|
+
`scheduleHistoryApi`, `operationsApi`). In 0.26.0, `soulsApi`, `readinessApi`
|
|
1002
|
+
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
1003
|
optimistic invocation**: an older CLI ignores an unknown `--plan` on `retire`
|
|
735
1004
|
and *retires*. Absent feature → the view is unavailable. (`catalog` was the
|
|
736
1005
|
0.24 `oats catalog` verb's flag; the verb is removed under the workspace model
|
|
@@ -772,13 +1041,12 @@ machine in the directory the operator chooses (any existing folder). It writes
|
|
|
772
1041
|
`<dir>/oats-local.yaml` (`{ schemaVersion: 2, workspace: <ref> }`), creates
|
|
773
1042
|
`<dir>/agents/` (the instance homes), then runs exactly the `oats sync` body
|
|
774
1043
|
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
|
|
1044
|
+
membership, resolve `packages:`, write `oats-lock.json`. It installs nothing, creates no soul, spawns nothing
|
|
777
1045
|
and writes no `oats-config.yaml`; the member clones and the setup-expert spawn
|
|
778
1046
|
are printed as next steps. `<dir>` defaults to cwd; `--dir <d>` is the same
|
|
779
1047
|
argument (give it once). `--workspace` is required and must be a ref
|
|
780
1048
|
`lib/remote.mjs` parses (`E_REPO_REF`) — checked **before** anything is
|
|
781
|
-
written. Captured selectors are refused (`
|
|
1049
|
+
written. Captured selectors are refused (`E_UNSUPPORTED_MODE`: the captured/portable path was removed in 0.26).
|
|
782
1050
|
|
|
783
1051
|
```json
|
|
784
1052
|
{"onboardApi":2,
|
|
@@ -793,13 +1061,9 @@ written. Captured selectors are refused (`E_BAD_ARGS`).
|
|
|
793
1061
|
```
|
|
794
1062
|
|
|
795
1063
|
- `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.
|
|
1064
|
+
changes, `problems`); `lock` is the lock it wrote. Exit `0` on success —
|
|
1065
|
+
there is no approval-pending outcome (0.26.0, feature
|
|
1066
|
+
`packages-no-approval`; earlier kernels exited `2` with `approvalNeeded`).
|
|
803
1067
|
- `hosting` states decision 26 (the kernel cannot see forge visibility, so it
|
|
804
1068
|
reports `hostIsMember` and the rule rather than judging).
|
|
805
1069
|
- `next.clone[]` is one row per **confirmed** member (`url` = what the
|
|
@@ -822,12 +1086,18 @@ written. Captured selectors are refused (`E_BAD_ARGS`).
|
|
|
822
1086
|
|
|
823
1087
|
### `oats sync [--dir <d>] --json` → `syncApi: 1`
|
|
824
1088
|
|
|
825
|
-
Discovers, confirms membership, resolves `packages:` to commits,
|
|
826
|
-
`oats-lock.json` (lockfileVersion 3), reports
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1089
|
+
Discovers, confirms membership, resolves `packages:` to commits + integrity,
|
|
1090
|
+
writes `oats-lock.json` (lockfileVersion 3), reports; exit `0` on success.
|
|
1091
|
+
**No package approval** (0.26.0, human decision 2026-09-24; feature
|
|
1092
|
+
`packages-no-approval`): declaring a package in `packages:` is the trust
|
|
1093
|
+
decision. The report has no `approvalNeeded`, package rows no `approved`,
|
|
1094
|
+
`changes[]` rows no `approvalNeeded`; there is no prompt and no exit `2`, and
|
|
1095
|
+
`--approve` is `E_BAD_ARGS`. A lock written by an earlier kernel keeps working
|
|
1096
|
+
(its `approved` records are ignored and dropped on the next write). The fields
|
|
1097
|
+
went away without an API-number bump — `syncApi`, `workspaceStatusApi` and
|
|
1098
|
+
`capabilitiesApi` stay `1`; the removal is signalled by the feature string
|
|
1099
|
+
alone — so a consumer reading `approvalNeeded`, `approval` or `approved` must
|
|
1100
|
+
gate that on the absence of `packages-no-approval`.
|
|
831
1101
|
|
|
832
1102
|
```json
|
|
833
1103
|
{"syncApi":1,
|
|
@@ -839,12 +1109,9 @@ the TTY fallback, not the contract. Exit `0` otherwise.
|
|
|
839
1109
|
"souls":["tools-expert"],"capabilities":["acme-tools-dev"],"publishes":{"package":"acme.tools","version":"0.4.0"}},
|
|
840
1110
|
{"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
1111
|
"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"]}],
|
|
1112
|
+
"packages":[{"id":"oats.okf","version":"2.1.3","source":"catalog:oats.okf","commit":"<oid>","integrity":"sha256-…","capabilities":["oats.okf"]},
|
|
1113
|
+
{"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"]}],
|
|
1114
|
+
"changes":[{"id":"acme.tools","from":null,"to":"0.4.0","commit":"<oid>"}],
|
|
848
1115
|
"problems":[]}
|
|
849
1116
|
```
|
|
850
1117
|
|
|
@@ -854,17 +1121,14 @@ the TTY fallback, not the contract. Exit `0` otherwise.
|
|
|
854
1121
|
capabilities are **not** in `capabilities[]`; the non-collapse rule).
|
|
855
1122
|
- `changes[]`: `from` = previously locked version or `null`; `to` = `null` when
|
|
856
1123
|
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
1124
|
- `problems[]`: `{ code, path, message, repoKey? }` — per-item discovery
|
|
861
1125
|
problems (`E_WORKSPACE_SCHEMA`, `E_TEAM_UNKNOWN`, `E_REMOTE_*`, …). Never an
|
|
862
1126
|
abort: an unreadable member directory is a problem of that member.
|
|
863
1127
|
- Errors: `E_LOCAL_MISSING`, `E_WORKSPACE_SCHEMA { path, problems[] }`,
|
|
864
1128
|
`E_REMOTE_UNREADABLE`, `E_LOCK_SCHEMA`, `E_PACKAGE_MISSING` (catalog has no
|
|
865
1129
|
such id), `E_PACKAGE_INTEGRITY { why: "branch" | locked/observed }`,
|
|
866
|
-
`
|
|
867
|
-
|
|
1130
|
+
`E_PACKAGE_MANIFEST`, `E_REPO_REF`, `E_BAD_ARGS` (`--approve`: package
|
|
1131
|
+
approval was removed).
|
|
868
1132
|
|
|
869
1133
|
### `oats package add <id> <version|git:<repo>@<ref>> | remove <id> [--dir] --json`
|
|
870
1134
|
|
|
@@ -896,18 +1160,28 @@ found to check the declaration even though it does not edit it. `E_USAGE`,
|
|
|
896
1160
|
"declaredPackages":["acme.tools","oats.okf"],
|
|
897
1161
|
"unsynced":[],
|
|
898
1162
|
"stale":[],
|
|
899
|
-
"approval":{"approved":["oats.okf"],"needed":["acme.tools"]},
|
|
900
1163
|
"external":[{"source":"git:github.com/oss-collective/experts@<oid>","soul":"security-reviewer","team":"unassigned"}],
|
|
901
|
-
"problems":[]
|
|
1164
|
+
"problems":[],
|
|
1165
|
+
"warnings":[]}
|
|
902
1166
|
```
|
|
903
1167
|
|
|
1168
|
+
`warnings[]` (feature `teams`, also in the `sync` report): `{ code, label,
|
|
1169
|
+
souls, paths, message }` — one `unmapped-team-label` per label that is in
|
|
1170
|
+
`teams:` but not in `messaging.byTeam`, naming its souls (sorted) and each
|
|
1171
|
+
soul's `<repoKey>:<path>#/team`; sorted by label. Never a problem.
|
|
1172
|
+
|
|
904
1173
|
`unsynced` = declared in `packages:` but not in the lock (run `sync`);
|
|
905
1174
|
`stale` = locked but no longer declared. Read-only: does not write the lock.
|
|
1175
|
+
(0.26.0: the `approval` object is gone with package approval.)
|
|
906
1176
|
|
|
907
1177
|
### `oats capabilities [--dir] --json` → `capabilitiesApi: 1` · `oats souls [--dir] --json` → `soulsApi: 1`
|
|
908
1178
|
|
|
909
|
-
Every
|
|
910
|
-
|
|
1179
|
+
Every item of every confirmed member, external souls, and locked package
|
|
1180
|
+
capabilities, sorted by name then origin. Souls have no private mode (their
|
|
1181
|
+
`private` is always `false`); a repo-owned capability is listed with
|
|
1182
|
+
`private: true` — usable only by its own repo's souls. The Desktop shows its
|
|
1183
|
+
"Repo owned" section when `version --json` lists the `capabilities-private`
|
|
1184
|
+
feature. `origin` is the
|
|
911
1185
|
human string (`member <key> @ <8-char commit>` | `package <id> v<version>` |
|
|
912
1186
|
`external <key> @ <commit>`); `kind` is the machine field. `team` is the label
|
|
913
1187
|
or `"unassigned"`.
|
|
@@ -918,7 +1192,7 @@ or `"unassigned"`.
|
|
|
918
1192
|
{"name":"acme-house-style","origin":"member github.com/acme/agents @ 3f2a9c1e","kind":"member","repoKey":"github.com/acme/agents","commit":"<oid>",
|
|
919
1193
|
"team":"global","private":false,"path":"capabilities/acme-house-style","layer":null,"version":"0.0.0-workspace"},
|
|
920
1194
|
{"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
|
|
1195
|
+
"team":"unassigned","private":false}],
|
|
922
1196
|
"problems":[]}
|
|
923
1197
|
```
|
|
924
1198
|
|
|
@@ -932,8 +1206,9 @@ or `"unassigned"`.
|
|
|
932
1206
|
```
|
|
933
1207
|
|
|
934
1208
|
Package capabilities of declared-but-unsynced packages are absent until `sync`.
|
|
935
|
-
(`soulsApi: 1`
|
|
936
|
-
|
|
1209
|
+
(`oats souls --json` keeps `soulsApi: 1` in 0.26.0: its shape is unchanged.
|
|
1210
|
+
The probe's `soulsApi` is **2** because it tracks the `oats inspect --json`
|
|
1211
|
+
soul rows. The two payloads are distinguished by their command.)
|
|
937
1212
|
|
|
938
1213
|
### `oats spawn <soul> … --preview --json` — additions (Preview API 2 unchanged)
|
|
939
1214
|
|
|
@@ -943,10 +1218,10 @@ between preview and apply is `E_DECISION_STALE`):
|
|
|
943
1218
|
|
|
944
1219
|
```json
|
|
945
1220
|
{"modules":[
|
|
946
|
-
{"name":"acme-release-tooling","from":{"kind":"member","repoKey":"github.com/acme/agents","commit":"<oid>"},"layer":null,"private":false,
|
|
1221
|
+
{"name":"acme-release-tooling","from":{"kind":"member","repoKey":"github.com/acme/agents","commit":"<oid>"},"layer":null,"private":false,"declares":[],
|
|
947
1222
|
"changedSince":{"instance":"release-manager-v2","was":"<old oid>"}},
|
|
948
1223
|
{"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}],
|
|
1224
|
+
"layer":"knowledge","private":false,"declares":["bindings-file","git-timeout","harvest-model","harvest-runtime","state-dir"],"changedSince":false}],
|
|
950
1225
|
"team":"engineering",
|
|
951
1226
|
"resolution":"6e3050c0d005879441ab017d",
|
|
952
1227
|
"workspace":"github.com/acme/agents",
|
|
@@ -956,6 +1231,8 @@ between preview and apply is `E_DECISION_STALE`):
|
|
|
956
1231
|
```
|
|
957
1232
|
|
|
958
1233
|
- `modules[].from` is exactly the `from` recorded in `instance.json` on apply.
|
|
1234
|
+
- `modules[].declares`: the manifest's declared setting keys, as in `inspect`
|
|
1235
|
+
(feature `settings-declared`).
|
|
959
1236
|
- `changedSince`: `null` (no previous instance of this soul), `false`
|
|
960
1237
|
(unchanged since the newest previous instance), or
|
|
961
1238
|
`{ instance, was }` (`was` = the previous commit, or `null` when the previous
|
|
@@ -982,7 +1259,7 @@ between preview and apply is `E_DECISION_STALE`):
|
|
|
982
1259
|
members or externals), `E_SOUL_AMBIGUOUS { name, repos[] }` (name it
|
|
983
1260
|
`<repo>/<soul>`). Resolution errors keep their codes (`E_NOT_A_MEMBER`,
|
|
984
1261
|
`E_MEMBERSHIP_UNCONFIRMED { repoKey, reason }`, `E_CAPABILITY_MISSING { hint? }`,
|
|
985
|
-
`E_CAPABILITY_PRIVATE`, `E_PACKAGE_MISSING`, `
|
|
1262
|
+
`E_CAPABILITY_PRIVATE`, `E_PACKAGE_MISSING`, `E_PACKAGE_INTEGRITY { why: "capabilities", listed, locked }`,
|
|
986
1263
|
`E_SLOT_CONFLICT { slot, modules[], reason? }`, `E_SKILL_DUPLICATE { name, modules[] }`,
|
|
987
1264
|
`E_COMPATIBILITY { capability, package, version, range, why? }`).
|
|
988
1265
|
|
|
@@ -1001,11 +1278,18 @@ Written by materialization inside the spawn transaction; read back by
|
|
|
1001
1278
|
"oats.okf":{"from":{"kind":"package","package":"oats.okf","version":"2.1.3","commit":"<oid>","integrity":"sha256-…","repoKey":"github.com/awebai/oats-okf"},
|
|
1002
1279
|
"commit":"<oid>","digest":"sha256-…","materializedAt":"<iso>"}},
|
|
1003
1280
|
"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,
|
|
1281
|
+
"workspace":{"key":"github.com/acme/agents","name":"acme","deployment":"/Users/ana/acme-workspace","commit":"<oid>","resolution":"<24 hex>","standalone":false,
|
|
1005
1282
|
"soul":{"repoKey":"github.com/acme/agents","commit":"<oid>","team":"engineering"}},
|
|
1006
1283
|
"capabilities":[{"id":"oats.okf","capability":"oats.okf","origin":"package:oats.okf@2.1.3","…":"one row per module"}]}
|
|
1007
1284
|
```
|
|
1008
1285
|
|
|
1286
|
+
`workspace.name` (the workspace file's `name`) and `workspace.deployment` (the
|
|
1287
|
+
directory holding the deployment's `oats-local.yaml`) are recorded from 0.26.0,
|
|
1288
|
+
so a home answers them without discovery: `oats inspect --home` reports the
|
|
1289
|
+
recorded name, and `oats operation run --home` hands it to the provider as
|
|
1290
|
+
`OATS_WORKSPACE_NAME`. Homes spawned
|
|
1291
|
+
before 0.26.0 lack both; the name is then discovered, or `null`.
|
|
1292
|
+
|
|
1009
1293
|
`workspace.standalone` is `true` when the instance was spawned from the
|
|
1010
1294
|
**standalone view** (decisions 10/25: a *member* whose workspace could not be
|
|
1011
1295
|
read — `workspace.key` is then the member repo's key, and `modules` holds the
|
|
@@ -1014,9 +1298,14 @@ spawn. The same view is marked `standalone: true` in `oats sync --json` (with
|
|
|
1014
1298
|
`workspace.name` = `standalone:<repo>`) and in the roster. `capabilities[]` is
|
|
1015
1299
|
the per-module row set (`toCapabilityRows`) that `oats inspect`/`status` read.
|
|
1016
1300
|
|
|
1301
|
+
`soulDir` (0.26.0) is the absolute soul directory the instance incarnates — a
|
|
1302
|
+
workspace soul's per-commit copy `<deployment>/agents/<soul>/souls/<commit12>`, or
|
|
1303
|
+
the read-only soul inside a capability package — and is what every classic
|
|
1304
|
+
lifecycle hook and dispatched command receives as `OATS_SOUL`. Instance homes carry no `soul` link.
|
|
1305
|
+
|
|
1017
1306
|
`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
|
|
1307
|
+
`providers.<cap>` is the merged payload (manifest defaults ⊕ soul ⊕
|
|
1308
|
+
`oats-local.yaml` `settings.<cap>` ⊕ `--provider`), `{}` for a capability with none. Copies live at
|
|
1020
1309
|
`<home>/.oats/modules/<cap>/` and `<home>/.agents/skills/<cap>/<skill>/`.
|
|
1021
1310
|
|
|
1022
1311
|
### `oats status [--dir] --json` — module drift
|
|
@@ -1047,54 +1336,59 @@ top-level `workspace` reachability field:
|
|
|
1047
1336
|
- Text mode prints `modules: <cap> from <member|package …> @ <7-char>` lines
|
|
1048
1337
|
for non-current modules (`--verbose` for all).
|
|
1049
1338
|
|
|
1339
|
+
### Eligible teams (feature `teams`, OATS 0.26.0)
|
|
1340
|
+
|
|
1341
|
+
A soul's `team` may be a list of labels; the first is the primary
|
|
1342
|
+
([teams contract](design/2026-09-25-teams-contract.md)). Every label is an
|
|
1343
|
+
**eligible** team — which the messaging provider may join on an explicit
|
|
1344
|
+
request (a spawn provider setting, or its own join/leave verbs); the kernel
|
|
1345
|
+
joins nothing. One entry per label, in soul order:
|
|
1346
|
+
|
|
1347
|
+
```json
|
|
1348
|
+
{"label":"engineering","team":"aweb:acme.eng","mapped":true,"payload":{"private":"per-human","team":"aweb:acme.eng"}}
|
|
1349
|
+
{"label":"reviewers","team":null,"mapped":false,"payload":{"private":"per-human"}}
|
|
1350
|
+
```
|
|
1351
|
+
|
|
1352
|
+
`payload` = `workspace.messaging` ⊕ `byTeam[label]` (base alone when unmapped);
|
|
1353
|
+
`team` = the mapped payload's team id, else `null`. No label → `[]`.
|
|
1354
|
+
|
|
1355
|
+
Where it appears:
|
|
1356
|
+
- `oats spawn … --preview --json`: top-level `teams` (next to `team`, which
|
|
1357
|
+
stays the primary label). `settings.<messaging>` stays the primary's merged
|
|
1358
|
+
payload and never carries `teams`.
|
|
1359
|
+
- `oats inspect --soul|--home --json`: top-level `teams` and `teamsSource`.
|
|
1360
|
+
For `--home` the teams are **live** (the soul's labels and the workspace's
|
|
1361
|
+
`messaging` as the deployment resolves them now, in two repository reads;
|
|
1362
|
+
the home's modules are unchanged): `teamsSource: "live"`, or `"recorded"`
|
|
1363
|
+
with the spawn-time list when the workspace cannot be read now. Providers
|
|
1364
|
+
get the same marker as `OATS_TEAMS_SOURCE`.
|
|
1365
|
+
- `instance.json`: `teams` (the spawn-time list, kept as evidence; never
|
|
1366
|
+
rewritten) and `workspace.soul.labels`.
|
|
1367
|
+
- `oats souls --json`: each row carries `labels` (`team` stays the primary).
|
|
1368
|
+
- A spawn, preview or `inspect --soul` whose labels give one capability
|
|
1369
|
+
different `defaults.byTeam` entries answers `E_TEAM_CONFLICT { capability,
|
|
1370
|
+
labels: [a, b], entries, paths }`.
|
|
1371
|
+
|
|
1050
1372
|
### Probe
|
|
1051
1373
|
|
|
1052
1374
|
```json
|
|
1053
|
-
{"…":"…","features":["…","workspace-v2","instance-modules","spawn-provider-payload"],"workspaceApi":2}
|
|
1375
|
+
{"…":"…","features":["…","workspace-v2","instance-modules","spawn-provider-payload","packages-no-approval","spawn-name","settings-origins","teams"],"workspaceApi":2}
|
|
1054
1376
|
```
|
|
1055
1377
|
|
|
1056
1378
|
A feature is listed only once the binary implements it. Gate `sync`/`package`/
|
|
1057
1379
|
`workspace status`/`capabilities`/`souls` on `workspace-v2`; gate reading
|
|
1058
1380
|
`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.
|
|
1381
|
+
`--provider` on `spawn-provider-payload`; gate reading `teams`, `teamsSource`,
|
|
1382
|
+
`labels` and `warnings[]` on `teams`; gate reading `declares` on
|
|
1383
|
+
`settings-declared`.
|
|
1384
|
+
|
|
1385
|
+
## Instruction refresh (`oats session recompose`) — removed in 0.26.0
|
|
1386
|
+
|
|
1387
|
+
`oats session recompose` answers `E_UNKNOWN_COMMAND`, and the
|
|
1388
|
+
`session-recompose` feature is no longer advertised. An instance never changes
|
|
1389
|
+
under itself: the refresh path is a re-spawn (preview → apply of the same
|
|
1390
|
+
soul/purpose, then retire the old instance), which fetches the soul at the
|
|
1391
|
+
member's current commit.
|
|
1098
1392
|
|
|
1099
1393
|
## Mutations exposed to Desktop v1
|
|
1100
1394
|
|
|
@@ -1105,7 +1399,7 @@ are described in [the operations contract](design/operations-contract.md).
|
|
|
1105
1399
|
|
|
1106
1400
|
```text
|
|
1107
1401
|
oats session start --home /absolute/home [--server id] \
|
|
1108
|
-
[--launch-config name] [--
|
|
1402
|
+
[--launch-config name] [--harness pi|claude|codex] \
|
|
1109
1403
|
[--model id] [--yolo|--no-yolo] --json
|
|
1110
1404
|
oats session restart --home /absolute/home [the same options] --json
|
|
1111
1405
|
```
|
|
@@ -1129,7 +1423,7 @@ oats launch-config list [--dir /scope | --home /home | --soul name --agents-root
|
|
|
1129
1423
|
oats launch-config set name --file /private/definition.json [--keep-env] --dir /scope --json
|
|
1130
1424
|
oats launch-config remove name --dir /scope --json
|
|
1131
1425
|
oats launch-config preview (--home /home | --soul name --agents-root /scope/agents --dir /scope) \
|
|
1132
|
-
[--launch-config name] [--
|
|
1426
|
+
[--launch-config name] [--harness harness] [--model id] [--yolo|--no-yolo] --json
|
|
1133
1427
|
```
|
|
1134
1428
|
|
|
1135
1429
|
All accept `--server id`. Scope edits follow the registration; inspection and
|
|
@@ -1138,7 +1432,7 @@ is serialized to SSH stdin and read on the host with `--file -`; the local
|
|
|
1138
1432
|
filename is never passed to the server as though it existed there.
|
|
1139
1433
|
|
|
1140
1434
|
The list result supplies `context`, `selected` and `configurations`. Each
|
|
1141
|
-
configuration has a name,
|
|
1435
|
+
configuration has a name, harness, executable, literal argument array,
|
|
1142
1436
|
environment, model, permission choice and declaring `source`. Environment
|
|
1143
1437
|
literals appear as `{ "redacted": true }`; references appear as
|
|
1144
1438
|
`{ "fromEnv": "VARIABLE_NAME" }`. Optional executable/model/yolo fields can be
|
|
@@ -1173,18 +1467,57 @@ See [launch configuration syntax](configuration.md) and
|
|
|
1173
1467
|
| `warnings` | string[] | non-fatal warnings (always an array) |
|
|
1174
1468
|
| `tmux` | {session,window} \| null | tmux target |
|
|
1175
1469
|
|
|
1176
|
-
Additional informative fields: `repo`, `
|
|
1470
|
+
Additional informative fields: `repo`, `harness`, `model`, `parent`,
|
|
1177
1471
|
`sibling` (explicit sibling cluster link when a root-level sibling relation
|
|
1178
1472
|
was declared, else null), `relation` (`child`/`sibling`/`parent` when a
|
|
1179
1473
|
relation was declared at spawn, else null), `spawnOrigin`, `attach`.
|
|
1180
1474
|
|
|
1181
|
-
Stable error codes: `E_USAGE`, `
|
|
1475
|
+
Stable error codes: `E_USAGE`, `E_LOCAL_MISSING` (no `oats-local.yaml` in reach:
|
|
1476
|
+
a spawn needs a workspace deployment), `E_NO_DEPLOYMENT` (the deployment's
|
|
1477
|
+
`agents/` root is missing), `E_SOUL_UNKNOWN`, `E_UNKNOWN_AGENT`,
|
|
1182
1478
|
`E_AMBIGUOUS_SOUL`, `E_PARENT_NOT_FOUND`, `E_RELATIVE_NOT_FOUND`,
|
|
1183
1479
|
`E_RELATIVE_AMBIGUOUS` (a `--relative-to`/`--parent` anchor name matches
|
|
1184
1480
|
multiple team instances — disambiguate with `--relative-root <agents-root>`
|
|
1185
1481
|
— or the chosen anchor is shadowed by a same-named instance so the lineage
|
|
1186
1482
|
edge would resolve wrongly), `E_BAD_ARGS`,
|
|
1187
|
-
`E_SPAWN_FAILED`.
|
|
1483
|
+
`E_INSTANCE_NAME_INVALID`, `E_INSTANCE_NAME_TAKEN`, `E_SPAWN_FAILED`.
|
|
1484
|
+
|
|
1485
|
+
**Instance names** (0.26.0, feature `spawn-name`). By default the name is
|
|
1486
|
+
derived: `<agent>-<purpose>` with `--purpose <slug>`, else `<agent>-<n>`.
|
|
1487
|
+
`--name <slug>` (human decision 2026-09-24) is the explicit opt-in: the
|
|
1488
|
+
instance name is **exactly** `<slug>`, with no `<agent>-` prefix.
|
|
1489
|
+
|
|
1490
|
+
- `--name` and `--purpose` are mutually exclusive (`E_BAD_ARGS`), and
|
|
1491
|
+
`--name` needs a value (`E_BAD_ARGS`).
|
|
1492
|
+
- The name is never rewritten. Input that is not already a slug (lowercase
|
|
1493
|
+
letters and digits, single dashes between them) is
|
|
1494
|
+
`E_INSTANCE_NAME_INVALID`, and so is a name equal to any soul name of the
|
|
1495
|
+
deployment (souls on the agents root, and every soul the workspace
|
|
1496
|
+
declares, fetched or not). Soul and instance references stay unambiguous.
|
|
1497
|
+
- **Instance names are at most 64 characters** (0.26.0; the tightest
|
|
1498
|
+
consumer is the messaging alias, which allows 1–64). This covers every
|
|
1499
|
+
name, explicit and derived. A longer name is `E_INSTANCE_NAME_INVALID`
|
|
1500
|
+
("instance names are at most 64 characters"), in preview and apply alike,
|
|
1501
|
+
and is never truncated. For a derived name the refusal names the purpose
|
|
1502
|
+
to shorten, and the de-duplication suffix counts: when `<agent>-<purpose>`
|
|
1503
|
+
is taken and `<agent>-<purpose>-2` would exceed 64, the spawn is refused.
|
|
1504
|
+
- **Names are unique across the deployment.** An explicit name that any
|
|
1505
|
+
`<agents-root>/<soul>/instances/` already holds (including homes whose soul
|
|
1506
|
+
was since removed), or that a live window in the target tmux session carries
|
|
1507
|
+
(tmux backend, launched or `--no-launch`), is `E_INSTANCE_NAME_TAKEN`
|
|
1508
|
+
(`details.instance`, `details.home` or `details.session`). There is never a
|
|
1509
|
+
silent `-2` for a name the operator typed. These checks run after
|
|
1510
|
+
idempotency-key recovery (a keyed retry replays its receipt), and a
|
|
1511
|
+
concurrent spawn of another soul under the same name is caught after
|
|
1512
|
+
placement (see *Exclusive placement*). The invariant covers spawns through
|
|
1513
|
+
the CLI. Homes from earlier kernels may already share a name.
|
|
1514
|
+
- Derived names de-duplicate deployment-wide too (`-2`, `-3`, …), against
|
|
1515
|
+
every soul's instances and every soul name. Two souls never derive the same
|
|
1516
|
+
name (soul `a` with `--purpose b-c` against soul `a-b` with `--purpose c`).
|
|
1517
|
+
- `--preview` reports the final name (`instance`, `decision.instance`) and
|
|
1518
|
+
refuses with the same codes. The name is part of the decision revision, so
|
|
1519
|
+
`--expect-decision` binds it: another name under a confirmed decision is
|
|
1520
|
+
`E_DECISION_STALE`.
|
|
1188
1521
|
|
|
1189
1522
|
Dispatch-level failures (any `--json` command): `E_UNKNOWN_COMMAND` (no
|
|
1190
1523
|
kernel subcommand or capability namespace matches, or unknown capability
|