@awebai/oats 0.25.8 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -6
- package/bin/oats.mjs +576 -1714
- package/capabilities/oats-authoring/oats-package.json +2 -2
- package/capabilities/oats-authoring/oats.json +2 -2
- package/capabilities/oats-authoring/skills/integration-authoring/SKILL.md +46 -25
- package/capabilities/oats-authoring/skills/soul-craft/SKILL.md +13 -6
- package/capabilities/oats-aweb/bin/oats-aweb.mjs +334 -72
- package/capabilities/oats-aweb/injects/aweb.md +7 -2
- package/capabilities/oats-aweb/lib/binding-wire.mjs +107 -5
- 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 +14 -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-okf/bin/oats-okf.mjs +1 -1
- package/capabilities/oats-okf/lib/binding-wire.mjs +1 -1
- package/capabilities/oats-okf/lib/migration.mjs +2 -2
- package/capabilities/oats-okf/lib/sources.mjs +5 -4
- package/capabilities/oats-okf/lib/stores.mjs +40 -9
- package/capabilities/oats-okf/lib/worker.mjs +3 -3
- package/capabilities/oats-okf/oats.json +1 -1
- package/capabilities/oats-review/oats.json +3 -2
- package/docs/capabilities.md +218 -47
- package/docs/capability-manifest.schema.json +13 -4
- package/docs/configuration.md +17 -5
- package/docs/conventions.md +16 -26
- package/docs/design/2026-09-08-expert-assisted-deployment-proposal.md +1 -1
- package/docs/design/2026-09-13-knowledge-and-memory-direction.md +3 -3
- package/docs/design/2026-09-14-portable-souls-and-git-workspaces.md +2 -2
- package/docs/design/2026-09-15-portable-souls-handoff.md +2 -2
- package/docs/design/2026-09-15-portable-souls-implementation.md +1 -1
- package/docs/design/2026-09-20-redesign-program-board.md +2 -2
- package/docs/design/2026-09-23-workspace-module-contracts.md +1 -1
- package/docs/design/2026-09-23-workspace-v2-implementation-plan.md +1 -1
- package/docs/design/2026-09-24-desktop-phase-f-boundary.md +54 -10
- package/docs/design/2026-09-24-phase-d-plan.md +77 -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 +546 -264
- package/docs/desktop-instance-start.md +1 -1
- package/docs/desktop.md +7 -13
- package/docs/execution-targets.md +16 -18
- package/docs/first-team.md +14 -17
- package/docs/implementation.md +28 -59
- package/docs/integrations.md +88 -32
- package/docs/knowledge-capability-authoring.md +1 -1
- package/docs/knowledge-reference/package-craft.md +10 -8
- package/docs/knowledge-theory.md +1 -1
- package/docs/knowledge.md +10 -11
- package/docs/layers.md +16 -17
- package/docs/oats-local.schema.json +29 -1
- package/docs/oats-membership.schema.json +5 -3
- package/docs/oats-package.schema.json +2 -2
- package/docs/oats-workspace.schema.json +1 -1
- package/docs/{official-marketplace.md → official-catalog.md} +15 -16
- package/docs/packages.md +75 -52
- package/docs/release-notes/v0.22.0.md +1 -1
- package/docs/release-notes/v0.23.1.md +1 -1
- package/docs/release-notes/v0.25.9.md +23 -0
- package/docs/release-notes/v0.26.0.md +670 -0
- package/docs/schedules.md +48 -126
- package/docs/soul.schema.json +11 -4
- package/docs/souls-and-instances.md +56 -43
- package/docs/workspaces.md +80 -58
- package/injects/instance-boundary.md +1 -1
- package/injects/work-attached.md +1 -1
- package/injects/work-workspace.md +2 -2
- package/lib/{portable-files.mjs → bounded-read.mjs} +6 -6
- package/lib/{portable-values.mjs → canonical-json.mjs} +3 -12
- package/lib/capability-contract.mjs +110 -0
- package/lib/config-data.mjs +2 -2
- package/lib/core.mjs +700 -4824
- package/lib/digest.mjs +12 -0
- package/lib/instance-inspect.mjs +396 -0
- package/lib/instance-lifecycle.mjs +3 -4
- package/lib/instance-resolution.mjs +212 -26
- package/lib/instruction-composition.mjs +0 -20
- package/lib/materialize.mjs +6 -4
- package/lib/operator-dispatch.mjs +33 -13
- package/lib/packages.mjs +25 -190
- package/lib/provider-binding.mjs +4 -2
- package/lib/provider-reasons.mjs +3 -68
- package/lib/resolve.mjs +204 -68
- package/lib/schedule.mjs +97 -272
- package/lib/servers.mjs +13 -13
- package/lib/{portable-shape.mjs → shape.mjs} +4 -3
- package/lib/tree-copy.mjs +44 -0
- package/lib/workspace.mjs +125 -20
- package/package-catalog.json +7 -7
- package/package.json +1 -1
- package/skills/integration-authoring/SKILL.md +48 -40
- package/skills/oats-getting-started/SKILL.md +105 -110
- package/skills/oats-support/SKILL.md +2 -2
- package/skills/soul-craft/SKILL.md +13 -6
- package/bin/oats-pi-sdk-host.mjs +0 -17
- package/docs/2026-09-03-architecture-proposal.md +0 -642
- package/docs/artifact-approvals.schema.json +0 -7
- package/docs/captured-invocation-context.schema.json +0 -7
- package/docs/captured-resolution.schema.json +0 -7
- package/docs/design/package-engine-contract.md +0 -813
- package/docs/design/package-runtime-api.md +0 -588
- package/docs/desktop-succession.md +0 -57
- package/docs/execution-capsule.schema.json +0 -108
- package/docs/first-team-demo.md +0 -92
- package/docs/knowledge-migration.md +0 -147
- package/docs/migration-from-oas.md +0 -103
- package/docs/oats-config.schema.json +0 -172
- package/docs/oats-lock-v3.schema.json +0 -7
- package/docs/oats-lock.schema.json +0 -175
- package/docs/operating-team-migration.md +0 -470
- package/docs/portable.schema.json +0 -2512
- package/docs/provider-check-input.schema.json +0 -7
- package/docs/rebuild-to-v2.md +0 -511
- package/docs/workspace-adoption.md +0 -74
- package/injects/framework-workspace.md +0 -7
- package/injects/local-soul.md +0 -19
- package/injects/oats-portable.md +0 -20
- package/injects/oats.md +0 -11
- package/injects/portable-instance-boundary.md +0 -39
- package/injects/portable-work-directory.md +0 -29
- package/lib/artifact-approvals.mjs +0 -120
- package/lib/artifact-tree.mjs +0 -141
- package/lib/capability-artifacts.mjs +0 -179
- package/lib/capability-execution.mjs +0 -15
- package/lib/capability-inputs.mjs +0 -39
- package/lib/capability-provenance.mjs +0 -231
- package/lib/captured-action-shape.mjs +0 -21
- package/lib/captured-admission-shape.mjs +0 -20
- package/lib/captured-binding-file.mjs +0 -36
- package/lib/captured-dispatch.mjs +0 -66
- package/lib/captured-instance-index.mjs +0 -277
- package/lib/captured-invocation-context.mjs +0 -130
- package/lib/captured-launch-request.mjs +0 -66
- package/lib/captured-operation-process.mjs +0 -15
- package/lib/captured-pi-custody.mjs +0 -29
- package/lib/captured-pi-host.mjs +0 -167
- package/lib/captured-pi-outcome.mjs +0 -172
- package/lib/captured-resolutions.mjs +0 -275
- package/lib/captured-scaffold.mjs +0 -87
- package/lib/captured-selector.mjs +0 -28
- package/lib/captured-session-backend.mjs +0 -52
- package/lib/captured-source-receipt-file.mjs +0 -72
- package/lib/helper-injection-policy.mjs +0 -104
- package/lib/legacy-lock-codec.mjs +0 -106
- package/lib/manifest-settings.mjs +0 -84
- package/lib/package-closure.mjs +0 -48
- package/lib/package-materialization.mjs +0 -83
- package/lib/pi-sdk-host.mjs +0 -229
- package/lib/portable-artifacts.mjs +0 -115
- package/lib/portable-choices.mjs +0 -82
- package/lib/portable-composition.mjs +0 -136
- package/lib/portable-digest.mjs +0 -105
- package/lib/portable-identity.mjs +0 -40
- package/lib/portable-lock.mjs +0 -117
- package/lib/portable-onboarding-request.mjs +0 -49
- package/lib/portable-onboarding.mjs +0 -256
- package/lib/portable-package-preparation.mjs +0 -188
- package/lib/portable-policy.mjs +0 -44
- package/lib/portable-soul.mjs +0 -42
- package/lib/portable-state.mjs +0 -80
- package/lib/prepare-composition.mjs +0 -170
- package/lib/prepared-bindings.mjs +0 -92
- package/lib/prepared-resources.mjs +0 -127
- package/lib/provider-binding-broker.mjs +0 -65
- package/lib/provider-binding-wire.mjs +0 -116
- package/lib/readiness.mjs +0 -225
- package/lib/repository-observation.mjs +0 -226
- package/lib/resolution-shape.mjs +0 -393
- package/lib/schedule-capsule.mjs +0 -206
- package/lib/soul-constraints.mjs +0 -40
- package/lib/source-projection.mjs +0 -84
- package/lib/source-spec.mjs +0 -189
- package/lib/workspace-definition.mjs +0 -126
- package/lib/workspace-discovery.mjs +0 -146
- package/skills/oats/SKILL.md +0 -162
- package/skills/oats-config/SKILL.md +0 -164
- package/skills/oats-packages/SKILL.md +0 -184
- package/skills/oats-portable/SKILL.md +0 -115
- package/skills/oats-portable-artifacts/SKILL.md +0 -63
|
@@ -1,588 +0,0 @@
|
|
|
1
|
-
# Package-runtime API contract (addendum to the package-engine contract)
|
|
2
|
-
|
|
3
|
-
Status: **SUPERSEDED** with its parent (workspace model v2 — see [2026-09-23-workspace-module-contracts.md](2026-09-23-workspace-module-contracts.md)); kept as history. Original status: **FROZEN** for the capability-materialization delivery, as an addendum to
|
|
4
|
-
[`package-engine-contract.md`](./package-engine-contract.md). It answers the
|
|
5
|
-
maintainer's four clarifications on the M1 freeze (maintainer review of
|
|
6
|
-
1db919b): the public package-runtime boundary, the npm runtime closure,
|
|
7
|
-
incremental transaction semantics, and runtime-validated schema invariants.
|
|
8
|
-
Changes go through the coordinator to the maintainer.
|
|
9
|
-
|
|
10
|
-
**What capability materialization changed here.** §1 (the public CLI boundary) is
|
|
11
|
-
unchanged. §2 keeps every npm rule and moves the materialization root from the
|
|
12
|
-
package root to each declared *capability* root, because the closure now lives
|
|
13
|
-
inside the materialized artifact. §3 becomes incremental with respect to the
|
|
14
|
-
*capability store*. §4 states the current lock invariants and the prototype-safety
|
|
15
|
-
requirement. §5 records that a `"."` capability root is read compatibility only,
|
|
16
|
-
discriminated by `configTemplates` rather than `configs`. §6 records that v1 is
|
|
17
|
-
the only legacy format supported, and that the earlier transitional package-root
|
|
18
|
-
`lockfileVersion: 2` is unsupported input rather than something to migrate.
|
|
19
|
-
|
|
20
|
-
## 1. Public package-runtime boundary (structured CLI API)
|
|
21
|
-
|
|
22
|
-
**Transport choice: the structured CLI API.** Rationale (tradeoff surfaced to
|
|
23
|
-
the coordinator/maintainer before freezing, mail 09447984): a process contract
|
|
24
|
-
is a true version boundary — it survives kernel-internal refactors and node/ESM
|
|
25
|
-
changes, nothing private is importable by construction, and it extends the
|
|
26
|
-
already-proven Desktop CLI API v1 envelope discipline instead of creating a
|
|
27
|
-
second public JS surface that must be kept in semver lockstep with the CLI
|
|
28
|
-
forever. The rejected alternative (a blessed `lib/runtime.mjs` import resolved
|
|
29
|
-
via `oats root`) preserves exactly the dynamic-import coupling the maintainer
|
|
30
|
-
ruled out.
|
|
31
|
-
|
|
32
|
-
**Rule: independently released packages MUST NOT import kernel-private
|
|
33
|
-
`lib/core.mjs` (including via `oats root` + dynamic import).** Package
|
|
34
|
-
commands/hooks execute the CLI at the exact absolute path the dispatcher
|
|
35
|
-
provides in `OATS_CLI_BIN` (§1 item 4) — never by resolving `oats` from PATH,
|
|
36
|
-
which is untrusted inside worktrees.
|
|
37
|
-
|
|
38
|
-
### Envelope and versioning
|
|
39
|
-
|
|
40
|
-
Every boundary command supports `--json` with the Desktop CLI API v1 envelope:
|
|
41
|
-
exactly one JSON object on stdout — `{ schemaVersion: 1, ok: true, result }`
|
|
42
|
-
or `{ schemaVersion: 1, ok: false, error: { code, message } }` — nonzero exit
|
|
43
|
-
on failure; progress prose only on stderr.
|
|
44
|
-
|
|
45
|
-
- **Versioning** (maintainer ruling): the boundary is versioned by the
|
|
46
|
-
**compatibility floor plus a pinned consumer fixture** — the boundary shipped
|
|
47
|
-
in kernel **0.19.0** and is unchanged by capability materialization; the
|
|
48
|
-
materialized store and the capability-materialization lock (which REPLACES the
|
|
49
|
-
earlier package-root spelling in place and remains `lockfileVersion: 2`) raise
|
|
50
|
-
the floor for packages that rely on the new manifest surface
|
|
51
|
-
(`configTemplates`, dedicated capability roots), which
|
|
52
|
-
declare the materialization release's floor instead. Official packages declare
|
|
53
|
-
their floor as `compatibility.oats: ">=<floor>"` in `oats-package.json` (and
|
|
54
|
-
capability `compatibility.oats` likewise), and each consumer repo pins the kernel
|
|
55
|
-
consumer-fixture version its CI probes against. The exact Desktop
|
|
56
|
-
`oats version --json` probe payload is NOT extended (no `packageRuntimeApi`
|
|
57
|
-
field) — Desktop API compatibility is a separate contract.
|
|
58
|
-
- Kernels below the floor are rejected by the consumer's normal
|
|
59
|
-
compatibility check (`incompatible-oats` at acquire; the consumer fixture
|
|
60
|
-
asserts the rejection).
|
|
61
|
-
|
|
62
|
-
### Commands (exact surface, boundary v1 — maintainer-ruled minimal)
|
|
63
|
-
|
|
64
|
-
The public boundary is HIGHER-LEVEL than the private core calls it replaces:
|
|
65
|
-
private `findAgent`/`upsertLocalAgent`/`spawnInstance`/`resolveOatsConfig`
|
|
66
|
-
usage maps onto capability-defined agents, `oats spawn`, and dispatch-provided
|
|
67
|
-
settings — not onto one-for-one public equivalents. File-of-record for the
|
|
68
|
-
consumer inventory: `packaging/oats-okf/KERNEL-API-NEEDS.md` on kernel branch
|
|
69
|
-
`integrations-expert/official-packages-staging` @ `60d5eb6` (design input;
|
|
70
|
-
this contract remains authoritative).
|
|
71
|
-
|
|
72
|
-
1. **Capability-defined agents own lookup/registration/ephemerality.** A
|
|
73
|
-
package capability declares its service agents in its manifest `agents:`
|
|
74
|
-
(package-relative soul dirs, e.g. oats.okf ships
|
|
75
|
-
`agents/memory-harvest/{soul.yaml,AGENTS.md}`). `oats spawn <agent>`
|
|
76
|
-
resolves capability-defined agents for the active context, scaffolds a
|
|
77
|
-
fresh soul homed locally, and applies ephemeral (`kind: "capability"`)
|
|
78
|
-
semantics automatically. There is NO public `oats agent show`,
|
|
79
|
-
`oats agent upsert`, or generic `--ephemeral` flag — add such a surface
|
|
80
|
-
only when a reusable use case proves it.
|
|
81
|
-
2. **Spawn** — `oats spawn <agent> ... --json` with the EXISTING flags:
|
|
82
|
-
`--purpose <slug>` (deterministic derived naming
|
|
83
|
-
`<agent>-<purpose>`; no raw instance-name authority), `--parent`,
|
|
84
|
-
`--repo`, `--work attached|worktree|checkout|workspace|directory`, `--work-dir`,
|
|
85
|
-
`--branch`, `--model`, `--task`/`--task-file` (owner-only tempfiles:
|
|
86
|
-
mode 0600, removed on every outcome). Existing validation and error codes
|
|
87
|
-
(`E_BAD_ARGS`, `E_PARENT_NOT_FOUND`, `E_SPAWN_FAILED`, ...) are part of
|
|
88
|
-
the contract; result is the fixed Desktop CLI API v1 spawn shape
|
|
89
|
-
(`{ instance, agent, home, work, tmux, ... }`). If an accepted consumer
|
|
90
|
-
mode cannot be expressed by an existing flag, ONE narrow flag is added
|
|
91
|
-
with JSON tests — never a general override.
|
|
92
|
-
3. **Settings via dispatch** — `oats <namespace> <command>` passes the active
|
|
93
|
-
capability's EFFECTIVE settings to the dispatched process as
|
|
94
|
-
`OATS_SETTINGS` (JSON; from the instance metadata snapshot or the resolved
|
|
95
|
-
context), the same contract lifecycle hooks already have. Capabilities
|
|
96
|
-
read their settings (e.g. oats.okf's `harvest-model`) from `OATS_SETTINGS`;
|
|
97
|
-
there is NO public resolved-config read command.
|
|
98
|
-
4. **Consumer rules**: a package command executes the CLI at the exact
|
|
99
|
-
absolute path the dispatcher or lifecycle runner provides in the
|
|
100
|
-
**`OATS_CLI_BIN`** environment variable (beside `OATS_SETTINGS`), via
|
|
101
|
-
`execFile` on that path — **never** by resolving `oats` from `PATH` and
|
|
102
|
-
never through a shell: PATH is not a trusted runtime boundary, and package
|
|
103
|
-
commands run in worktrees where it can be shadowed. The consumer parses
|
|
104
|
-
the one schema-v1 envelope, emits its own envelope, and never imports
|
|
105
|
-
`lib/core.mjs` or calls `oats root` for kernel-file resolution.
|
|
106
|
-
|
|
107
|
-
Error codes are part of the contract: `E_USAGE`, `E_BAD_ARGS`,
|
|
108
|
-
`E_UNKNOWN_COMMAND`, `E_SPAWN_FAILED`, `E_PARENT_NOT_FOUND`,
|
|
109
|
-
`E_RELATIVE_NOT_FOUND`, `E_RELATIVE_AMBIGUOUS`, `E_CAPABILITY_BLOCKED`,
|
|
110
|
-
`E_CAPABILITY_INACTIVE`.
|
|
111
|
-
|
|
112
|
-
### Directory execution for capability workers
|
|
113
|
-
|
|
114
|
-
A worker may explicitly select `work: directory` in its packaged `soul.yaml`,
|
|
115
|
-
pass `--work directory` to spawn, or use `spawnInstance(..., {work: "directory"})`.
|
|
116
|
-
This is a generic execution mode, independent of any knowledge provider.
|
|
117
|
-
Consumers using it must declare the directory-mode release as their
|
|
118
|
-
`compatibility.oats` floor, not the older boundary-v1 floor alone.
|
|
119
|
-
|
|
120
|
-
- `repo` / `--repo` is an **existing config context directory** in this mode,
|
|
121
|
-
not a Git requirement or an edit target. Relative paths resolve from the
|
|
122
|
-
agents root's parent; absent a selector it defaults to that deployment scope.
|
|
123
|
-
The CLI does not substitute its ambient Git checkout. A configured workspace
|
|
124
|
-
can discover and spawn declared package agents before it has an `agents/` or
|
|
125
|
-
`local-agents/` directory. Laptop config alone does not declare a deployment.
|
|
126
|
-
- `<home>/work` is a new, owned directory, not a symlink and not a fake Git
|
|
127
|
-
repository. The kernel creates no branch, copies no source tree, and does not
|
|
128
|
-
require Git in a non-Git deployment. Git-owned deployment placement still
|
|
129
|
-
requires readable Git metadata to establish the canonical home location.
|
|
130
|
-
- `--work-dir` / `workDir` and `--branch` / `branch` are contradictory and
|
|
131
|
-
rejected with `E_BAD_ARGS`, even if empty or inherited from a caller bug.
|
|
132
|
-
Directory execution never takes ownership of a caller-selected filesystem
|
|
133
|
-
path. Existing modes retain their Git/context requirements and semantics;
|
|
134
|
-
failed Git operations never implicitly fall back to directory execution.
|
|
135
|
-
- Canonical `AGENTS.md` / `CLAUDE.md`, skill composition, provider trust,
|
|
136
|
-
lifecycle hooks, frozen launch recipes, runtime preflight and no-launch
|
|
137
|
-
metadata are unchanged. Hooks receive `OATS_WORK=directory`, an empty
|
|
138
|
-
`OATS_BRANCH`, and the context in `OATS_REPO` / `OATS_CONTEXT` at spawn.
|
|
139
|
-
Worktree-only setup scripts are not run in this mode.
|
|
140
|
-
- Retirement authenticates directory ownership against the independent spawn
|
|
141
|
-
baseline. Nonempty execution work is preserved in verified recovery custody
|
|
142
|
-
(`workRecovery.path/work`, with home bytes under `home/`) before removal;
|
|
143
|
-
post-hook changes produce another verified snapshot. No work is designated
|
|
144
|
-
disposable in this initial mode, including hook-created work. Symlinks inside
|
|
145
|
-
work are copied as links, never followed; an exchanged work-root symlink,
|
|
146
|
-
unsupported filesystem entry, or unverifiable copy fails closed. Recovery is
|
|
147
|
-
not provider delivery or publication, and retains the existing single-host,
|
|
148
|
-
quiesced-runtime safety model rather than a hostile-filesystem atomicity claim.
|
|
149
|
-
|
|
150
|
-
### Lifecycle and scheduled-command context
|
|
151
|
-
|
|
152
|
-
Lifecycle hooks receive `OATS_CLI_BIN` as the real, absolute `bin/oats.mjs`
|
|
153
|
-
path belonging to the **running kernel**. This is authored by
|
|
154
|
-
`runLifecycleHooks` itself, including direct core callers; neither ambient
|
|
155
|
-
`OATS_CLI_BIN` nor a caller's `extraEnv.OATS_CLI_BIN` can override it. Spawn
|
|
156
|
-
hooks also receive the known agents root as `OATS_ROOT`, rather than an empty
|
|
157
|
-
value or the ambient caller's root.
|
|
158
|
-
|
|
159
|
-
Scheduled command execution starts without the invoking instance's identity:
|
|
160
|
-
`OATS_INSTANCE`, `OATS_INSTANCE_HOME`, legacy `OATS_HOME`, the `PI_AGENT_*`
|
|
161
|
-
aliases and `PI_AGENTS_ROOT`, plus kernel-authored soul, root, context, work,
|
|
162
|
-
team, capability, operation, settings and lifecycle metadata are removed.
|
|
163
|
-
The command's explicit cwd and selectors (for example `--soul`) determine
|
|
164
|
-
its dispatch; a scheduler invoked from another home must not select that
|
|
165
|
-
home's frozen capabilities/settings. Host configuration (`HOME`,
|
|
166
|
-
`OATS_HOME_DIR`, package catalog configuration) and ordinary credentials are
|
|
167
|
-
preserved. No job schema or knowledge-provider policy is implied by this
|
|
168
|
-
isolation.
|
|
169
|
-
|
|
170
|
-
### Native record capture result
|
|
171
|
-
|
|
172
|
-
`oats capture --home <dir>` (also `turn-record capture --home <dir>` and the
|
|
173
|
-
standalone `capture.mjs`) answers **native JSON**, not a Desktop schema-v1
|
|
174
|
-
`{ok,result}` envelope. Do not add `--json`: `--home` already selects JSON.
|
|
175
|
-
Diagnostics go to stderr, including lock contention without `--quiet`.
|
|
176
|
-
Existing home/owner/appended/session boundary fields remain; the outcome adds:
|
|
177
|
-
|
|
178
|
-
- `status`: `complete`, `skipped`, `held`, `incomplete`, or `failed` (failure
|
|
179
|
-
takes priority, then skip/held/incomplete).
|
|
180
|
-
- `complete`: true only for a performed pass with no holds, incomplete source
|
|
181
|
-
records, unattributed candidates or reported errors.
|
|
182
|
-
An unchanged performed pass may be complete with `appended: 0`.
|
|
183
|
-
- `skipped`: true when another pass owns the capture lock. `lock` then carries
|
|
184
|
-
holder/liveness/recovery details; no capture or indexing was performed.
|
|
185
|
-
- `held`: count of sessions the underlying pass held pending a timestamp.
|
|
186
|
-
- `incomplete`: count of source files with pending torn, oversized, invalid
|
|
187
|
-
UTF-8 or otherwise incomplete records; later complete input can recover.
|
|
188
|
-
- `issues`: optional metadata-only diagnostics identifying source paths and
|
|
189
|
-
reasons; no record bodies are embedded. `unattributed` candidates also make
|
|
190
|
-
the pass incomplete rather than silently certifying missing source evidence.
|
|
191
|
-
- `failed`: zero on success, nonzero for a capture/read/index/lock-release
|
|
192
|
-
failure; `error` describes the failure. `appended: null` on a thrown failure
|
|
193
|
-
means the number appended before the failure is unknown, not zero.
|
|
194
|
-
- `ignored`: count excluded by configured privacy rules, distinct from a
|
|
195
|
-
whole-pass skip. Home capture pins exactly attributed source files; sharing
|
|
196
|
-
a Codex day directory does not authorize capturing unrelated records.
|
|
197
|
-
|
|
198
|
-
Lock skips, held sessions and incomplete input retain exit status 0 (background reconciliation
|
|
199
|
-
must stay nonfatal on contention); errors and failed lock release exit 1.
|
|
200
|
-
Previously captured visible boundaries may still be returned on a skipped or
|
|
201
|
-
held pass. They are **not** evidence that final capture ran. Consumers requiring
|
|
202
|
-
a final pass must check `complete === true`, not exit status or the presence of
|
|
203
|
-
boundaries alone; older results lacking that field cannot certify a pass.
|
|
204
|
-
|
|
205
|
-
**Snapshot boundary:** completion describes a performed pass over the exact
|
|
206
|
-
attributed source files, not a promise about future appends. Discovery carries
|
|
207
|
-
an open-descriptor-derived identity and content witness through capture-lock
|
|
208
|
-
acquisition. Capture stages bytes from one descriptor and validates that witness
|
|
209
|
-
and source stability **before appending**: replacement, truncation and prefix
|
|
210
|
-
rewrites fail without appending the replacement's bytes. Same-inode append
|
|
211
|
-
growth is allowed only when the witnessed prefix is unchanged. Discovery/read
|
|
212
|
-
failures and files disappearing during the pass fail closed; pending trailing
|
|
213
|
-
records and unattributed candidates cannot certify completion. A retirement
|
|
214
|
-
consumer must first quiesce its writers and then preserve the captured evidence
|
|
215
|
-
under its own durable-input protocol. `complete:true` alone does not mean a
|
|
216
|
-
harvest was delivered or that a consumer stored those inputs. Configured privacy
|
|
217
|
-
exclusions remain exclusions, not an invitation to copy excluded source bytes.
|
|
218
|
-
|
|
219
|
-
**Native roots:** managed `--home` capture uses independent execution history,
|
|
220
|
-
not the observer's environment or the latest relaunch recipe. New scaffolds
|
|
221
|
-
initialize `<instances>/.oats-native-record/<sha256(canonical-home)>/history.json`.
|
|
222
|
-
Each managed spawn/start/restart writes a separate pending receipt before
|
|
223
|
-
backend dispatch. Inside the backend shell, under the exact environment prefix
|
|
224
|
-
and cwd that will exec the harness, the native recorder atomically replaces
|
|
225
|
-
that receipt with the effective absolute **record locations** and runtime. Only
|
|
226
|
-
these allowlisted locations, home, start id/time and custody state are saved;
|
|
227
|
-
no environment map, credential reference value, task or argv is persisted.
|
|
228
|
-
The saved `instance.json` command/recipe remains a relaunch **template**, not
|
|
229
|
-
execution evidence: use `oats session start`, not a manual shell replay of it.
|
|
230
|
-
|
|
231
|
-
Location rules at execution are `CLAUDE_CONFIG_DIR/projects` (default exactly
|
|
232
|
-
`$HOME/.claude/projects`), `PI_CODING_AGENT_DIR/sessions` (default
|
|
233
|
-
`$HOME/.pi/agent/sessions`), and `CODEX_HOME/sessions` (default
|
|
234
|
-
`$HOME/.codex/sessions`). Pi's `--session-dir` wins over
|
|
235
|
-
`PI_CODING_AGENT_SESSION_DIR`, which wins over its agent-dir location; Pi tilde
|
|
236
|
-
paths expand against the effective HOME. Relative paths resolve from the source
|
|
237
|
-
home. Existing symlinks resolve at recording time, including existing ancestors
|
|
238
|
-
of not-yet-created roots. Inherited overrides and resolved `fromEnv` location
|
|
239
|
-
inputs are thereby retained **after backend shell startup**, independently of
|
|
240
|
-
later observer/config/reference changes. The recorder runs before the native
|
|
241
|
-
exec; unsupported explicit Pi `--session` or a receipt write failure refuses
|
|
242
|
-
that exec and leaves pending custody, rather than claiming a default root.
|
|
243
|
-
Wrappers must preserve this native storage contract: arbitrary scripts which
|
|
244
|
-
change storage internally cannot be inferred from their executable name.
|
|
245
|
-
|
|
246
|
-
History is never replaced by a newer runtime selection, truncated with the
|
|
247
|
-
bounded restart log, or deleted with the source home. Capture unions all
|
|
248
|
-
historically recorded locations for each runtime and still attributes every
|
|
249
|
-
file by its own cwd. Missing/unreadable historical roots, unreadable/invalid
|
|
250
|
-
receipts, and pending/unfinished launches fail closed. A newly scaffolded home
|
|
251
|
-
with no managed launches has an authoritative empty managed-launch inventory.
|
|
252
|
-
A legacy home without that scaffold authority cannot acquire proof of its
|
|
253
|
-
**earlier** roots merely by restarting: later starts retain new locations but
|
|
254
|
-
its history remains incomplete. Do not remove pending/history receipts just
|
|
255
|
-
to get a green capture; recovery requires establishing the source inventory.
|
|
256
|
-
|
|
257
|
-
Standalone fixtures and explicit legacy inventories can opt into
|
|
258
|
-
`capture --home <dir> --current-roots` (`sourceRoots: "current-env"` in JSON),
|
|
259
|
-
or use `sessionsForHome(home, {roots: {cc: [...], pi: [...], codex: [...]}})`.
|
|
260
|
-
Explicit API roots exclude unspecified formats, and every supplied root must
|
|
261
|
-
exist. The CLI fallback uses current environment plus recorded hook/config
|
|
262
|
-
location overrides; `fromEnv` resolves from that **current** base environment.
|
|
263
|
-
Its `complete:true` certifies only that chosen observer-time inventory, **not**
|
|
264
|
-
all historical roots; do not enable it implicitly for final-capture consumers.
|
|
265
|
-
Normal managed reports say `sourceRoots: "launch-history"`. Background capture
|
|
266
|
-
without `--home` retains observer-time discovery (including `.claude*` profiles)
|
|
267
|
-
and optional absent native defaults. Neither fallback introduces knowledge
|
|
268
|
-
policy into the kernel.
|
|
269
|
-
|
|
270
|
-
**Claude children:** discovery also enumerates the native
|
|
271
|
-
`<project>/<sessionId>/subagents/*.jsonl` layout, including children whose parent
|
|
272
|
-
transcript is absent. Each child requires its own cwd attribution; neither its
|
|
273
|
-
parent's cwd nor its directory supplies missing attribution. Child streams use
|
|
274
|
-
`cc.<sessionId>.<child-file-stem>` (threads
|
|
275
|
-
`cc:session:<sessionId>.<child-file-stem>`) so identical child filenames under
|
|
276
|
-
different sessions cannot collide. Complete native lines are preserved verbatim;
|
|
277
|
-
torn, unstamped or unattributed child evidence blocks certification, and child
|
|
278
|
-
read/discovery failures fail the pass. Ignore rules run before child opens and
|
|
279
|
-
can match its path, filename, qualified id, native child id or parent session id.
|
|
280
|
-
|
|
281
|
-
**Piped recall:** native `oats recall` JSON responses drain stdout before process
|
|
282
|
-
termination, including large thread windows and individual `--show` records.
|
|
283
|
-
Consumers must still bound their own reads/buffers (use `--ids-only` for sizing);
|
|
284
|
-
a successful producer does not imply an unbounded consumer buffer.
|
|
285
|
-
|
|
286
|
-
### Consumer fixture
|
|
287
|
-
|
|
288
|
-
The engine ships a consumer fixture driving the full oats.okf pattern
|
|
289
|
-
exclusively through this surface: a capability-defined `memory-harvest`
|
|
290
|
-
agent resolved and spawned via `oats spawn --json` in all three source modes
|
|
291
|
-
(local-soul / workspace-mode / repo-resident), parent relation,
|
|
292
|
-
purpose-derived naming + debounce, model via `OATS_SETTINGS` dispatch,
|
|
293
|
-
task-file privacy/cleanup, clean JSON success/failure, no private
|
|
294
|
-
import/`oats root` lookup, Pi + Claude scaffold parity, and sub-floor kernel
|
|
295
|
-
rejection. WS3 reuses the fixture shape as each official repo's per-repo CI
|
|
296
|
-
probe, combined with the acquire → lock → trust → activate → spawn probe
|
|
297
|
-
from `test/packages.test.mjs`. (The oats.okf tree changes themselves —
|
|
298
|
-
`agents/memory-harvest`, dropping the core import — are WS3 deliverables.)
|
|
299
|
-
|
|
300
|
-
## 2. Capability-local npm runtime closure
|
|
301
|
-
|
|
302
|
-
- **Detection and placement**: materialization roots are the manifest-declared
|
|
303
|
-
CAPABILITY roots — each one carrying BOTH `package.json` AND
|
|
304
|
-
`package-lock.json` is materialized independently, and the resulting
|
|
305
|
-
`node_modules` becomes part of that capability's materialized artifact. This
|
|
306
|
-
is what lets an inner `oats.json` resolve resources via `node_modules/...`
|
|
307
|
-
relative to its own manifest (e.g. oats-aweb's
|
|
308
|
-
`node_modules/@awebai/pi/skills/...`) inside a self-contained artifact.
|
|
309
|
-
A **package-root-only** closure has no durable home and is NOT
|
|
310
|
-
materialized: it is package tooling. If a capability actually depends on it,
|
|
311
|
-
self-containment fails and the package is rejected
|
|
312
|
-
(`capability-not-self-contained`) rather than silently installing a broken
|
|
313
|
-
artifact. For a legacy `"."` capability root the capability root *is* the
|
|
314
|
-
package root, so that package-root lock is the capability's own closure (it is
|
|
315
|
-
detected once, not twice). Directories not enumerated by the manifest are
|
|
316
|
-
never scanned.
|
|
317
|
-
- **When and how**: materialization runs IN STAGING during acquire, update and
|
|
318
|
-
restore, after payload integrity verification and BEFORE the capability
|
|
319
|
-
artifact's integrity is computed or swapped in. The command is exactly
|
|
320
|
-
`npm ci --omit=dev --omit=peer --ignore-scripts` (plus `--no-audit
|
|
321
|
-
--no-fund` noise suppression) per materialization root — **dev AND host
|
|
322
|
-
peer dependencies are omitted**; **no npm lifecycle scripts ever run**, at
|
|
323
|
-
any phase. A package may consume host-provided peer APIs only through an
|
|
324
|
-
explicit supported host boundary (§1) — never by auto-materializing an
|
|
325
|
-
unrelated harness peer into its closure.
|
|
326
|
-
- **Closure/integrity/audit scope**: the runtime-closure contract covers the
|
|
327
|
-
ACTUALLY MATERIALIZED production dependency tree, not the full lock
|
|
328
|
-
metadata (a lockfile may describe dev/peer subtrees that are never
|
|
329
|
-
materialized and are out of contract). Vulnerability audit uses the
|
|
330
|
-
identical scope: `npm audit --omit=dev --omit=peer --ignore-scripts`.
|
|
331
|
-
Consumer/package CI must include a fixture asserting omitted peer
|
|
332
|
-
dependencies are ABSENT from the materialized tree.
|
|
333
|
-
- **Integrity coverage**: the lock has TWO digests at two levels, and the
|
|
334
|
-
closure sits inside one of them.
|
|
335
|
-
- The package row's `integrity` covers the staged package PAYLOAD only —
|
|
336
|
-
every `node_modules` (at any depth) and a root `oats-lock.json` are excluded,
|
|
337
|
-
so it is stable whether or not materialization has run. Root source-control
|
|
338
|
-
metadata (`.git`) is stripped before staging; if it later appears in a
|
|
339
|
-
managed artifact it is ordinary drift, not an integrity exclusion.
|
|
340
|
-
- The capability row's `integrity` covers the MATERIALIZED ARTIFACT with **no
|
|
341
|
-
exclusions at all**: capability source bytes, the materialized
|
|
342
|
-
`node_modules`, and the generated `.oats-installation.json` provenance file.
|
|
343
|
-
- There is consequently NO separate dependency digest anywhere in the model —
|
|
344
|
-
tampering with a materialized dependency changes the capability artifact
|
|
345
|
-
integrity directly, which invalidates `trusted` exactly like source drift and
|
|
346
|
-
makes bare restore reproject. A lock row carrying `depsIntegrity` is
|
|
347
|
-
evidence of the unsupported transitional shape (contract §4.1), not a field
|
|
348
|
-
to honour.
|
|
349
|
-
- `npm ci` fails closed on any lockfile mismatch. Doctor reports the package
|
|
350
|
-
payload integrity and each capability artifact's integrity/trust state.
|
|
351
|
-
- **Reproducibility (v1 MUST: platform-invariant closures)**: `node_modules`
|
|
352
|
-
is a derived artifact — never part of the package payload hash, never
|
|
353
|
-
committed, always reproduced from the locked `package-lock.json` and verified
|
|
354
|
-
through the capability artifact integrity that contains it. Because that
|
|
355
|
-
single artifact digest lives in a shared lock, **v1 packages MUST have
|
|
356
|
-
platform-invariant materialized closures**: no native builds, no
|
|
357
|
-
platform-specific optional dependencies, no install-time variance of any kind.
|
|
358
|
-
A closure that cannot materialize byte-identically across supported platforms
|
|
359
|
-
is UNSUPPORTED in v1 — the package must vendor a pure-JS closure or drop the
|
|
360
|
-
dependency; official dependency-bearing packages gate this across their
|
|
361
|
-
published platforms in CI. The engine ENFORCES this at materialization as a
|
|
362
|
-
transaction-wide preflight PLUS a post-materialization scan: every
|
|
363
|
-
materialization root's lockfile (every declared capability root, kept and
|
|
364
|
-
fresh) is scanned BEFORE any `npm ci` — only entries in the materialized
|
|
365
|
-
non-dev/non-peer closure are evaluated (omitted metadata cannot fail an
|
|
366
|
-
otherwise valid closure); an INCLUDED entry with os/cpu/libc constraints,
|
|
367
|
-
optional-dependency variance, or an install script is rejected (an included
|
|
368
|
-
install script is disallowed even though `--ignore-scripts` inerts it — the
|
|
369
|
-
runtime almost certainly expects the artifacts it would have built). After
|
|
370
|
-
`npm ci`, before digest/swap, the materialized tree is scanned for `.node`
|
|
371
|
-
native binaries alongside symlink containment. npm lockfileVersion 1 (no
|
|
372
|
-
`packages` map) fails closed — regenerate with modern npm. (A future keyed
|
|
373
|
-
per-platform closure map may relax this.)
|
|
374
|
-
- **Containment**: capability code/hook/skill/agent paths must resolve inside
|
|
375
|
-
the CAPABILITY root after symlink resolution — that is what makes the
|
|
376
|
-
materialized artifact self-contained and independently hashable. Materialized
|
|
377
|
-
`node_modules` trees under that root are inside the boundary by construction —
|
|
378
|
-
and ENFORCED: after `npm ci`, before any digest or swap, every symlink under
|
|
379
|
-
every materialized `node_modules` is realpath-checked to resolve inside the
|
|
380
|
-
capability root; a broken or escaping link fails the transaction
|
|
381
|
-
(`path-escape`) with full rollback. Node import resolution follows symlinks,
|
|
382
|
-
so this check is global, not best-effort.
|
|
383
|
-
- **Rollback**: materialization happens IN STAGING before any destination
|
|
384
|
-
mutation; a materialization failure fails the whole acquire/update
|
|
385
|
-
transaction with the capability store and lock unchanged, and a restore whose
|
|
386
|
-
reprojected artifact does not reproduce the locked capability `integrity`
|
|
387
|
-
fails as `integrity-drift` with the prior artifact left in place. Staging
|
|
388
|
-
directories are removed wholesale on any failure.
|
|
389
|
-
|
|
390
|
-
## 3. Incremental transaction semantics
|
|
391
|
-
|
|
392
|
-
Acquire/update of one package closure is **incremental with respect to the
|
|
393
|
-
scope's capability store**, never a wholesale store replacement:
|
|
394
|
-
|
|
395
|
-
- Capability artifacts, package rows and capability rows belonging to packages
|
|
396
|
-
NOT in the resolved closure are untouched — bytes on disk and lock JSON both.
|
|
397
|
-
- Within the closure, a capability whose newly projected artifact integrity
|
|
398
|
-
EQUALS its currently locked integrity is kept in place ("kept" in reports) and
|
|
399
|
-
its `trusted` flag is preserved verbatim.
|
|
400
|
-
- Only capabilities whose artifact integrity CHANGES have their artifact
|
|
401
|
-
replaced and their `trusted` reset to `false`. Trust is per capability, so an
|
|
402
|
-
unchanged capability inside a changed package keeps its approval.
|
|
403
|
-
- All validation (manifests, self-containment, cycles, identity and
|
|
404
|
-
capability-ID collisions, compatibility, platform invariance) completes against
|
|
405
|
-
a staging area BEFORE any destination mutation; the artifact swaps and the lock
|
|
406
|
-
write happen only after full-closure validation. On any failure before that
|
|
407
|
-
point the staging area is removed and the destination store + lock are
|
|
408
|
-
byte-identical to the pre-operation state.
|
|
409
|
-
- An update replaces ALL of one package's exports together or none of them; a
|
|
410
|
-
removed export is retired only when no config references it (otherwise
|
|
411
|
-
`remove-blocked`, with nothing changed).
|
|
412
|
-
- Restore is per-capability transactional: a failure (`integrity-drift`,
|
|
413
|
-
`capability-list-mismatch`) leaves that capability absent or untouched — never
|
|
414
|
-
partially installed — and does not affect other capabilities' restores.
|
|
415
|
-
|
|
416
|
-
## 4. Runtime-validated schema invariants
|
|
417
|
-
|
|
418
|
-
JSON Schema cannot express these in the current shapes, so they are normative
|
|
419
|
-
SEMANTIC validation rules with tests; validators of the schemas alone are not
|
|
420
|
-
complete:
|
|
421
|
-
|
|
422
|
-
- `oats-package.json`:
|
|
423
|
-
- `capabilities` is REQUIRED and non-empty — config-only and empty packages
|
|
424
|
-
are `invalid-package-manifest`;
|
|
425
|
-
- `configTemplates` is OPTIONAL and is the canonical spelling; `configs` is a
|
|
426
|
-
deprecated read-only alias; both spellings normalize to one descriptor shape
|
|
427
|
-
carrying a diagnostic `legacySpelling`, and carrying BOTH is
|
|
428
|
-
`invalid-package-manifest`;
|
|
429
|
-
- a `"."` capability root is accepted only when the manifest does NOT carry
|
|
430
|
-
`configTemplates` (§5), and remains exclusive with any other capability path;
|
|
431
|
-
authoring never emits it;
|
|
432
|
-
- at most one `configTemplates.*.default === true` (equivalently
|
|
433
|
-
`configs.*.default`) per manifest → `invalid-package-manifest`;
|
|
434
|
-
- `compatibility.oats` is REQUIRED with exactly the grammar `>=x.y.z`,
|
|
435
|
-
`^x.y.z`, or `x.y.z` — schema and runtime agree; malformed/missing →
|
|
436
|
-
`invalid-package-manifest`, valid-but-unsatisfied → `incompatible-oats`;
|
|
437
|
-
- every declared capability must be projectable self-contained — each declared
|
|
438
|
-
resource exists and realpath-resolves inside its own capability root →
|
|
439
|
-
`capability-not-self-contained` / `path-escape`. JSON Schema cannot see this
|
|
440
|
-
at all: it is a filesystem property of the staged payload.
|
|
441
|
-
- `oats-lock.json`, validated BEFORE restore, trust/approval, update/remove
|
|
442
|
-
planning, migration planning, the locked-template reader, and doctor/list
|
|
443
|
-
consumption → `invalid-lock` (fail closed before executable approval or
|
|
444
|
-
artifact replacement; no normalization, no auto-repair, NO side effects;
|
|
445
|
-
message/provenance carry lock file, package or capability identity, and the
|
|
446
|
-
violated field/edge):
|
|
447
|
-
- both top-level `packages` and `capabilities` maps are required;
|
|
448
|
-
- `dependencies` is required on every package row (empty array when none), so
|
|
449
|
-
a reader never distinguishes absent from empty;
|
|
450
|
-
- normalized source prefix (`git:`/`path:`/`catalog:`) and source/commit
|
|
451
|
-
pairing: `path:` requires `commit: "local"` AND `path: "."`; `git:`/`catalog:`
|
|
452
|
-
require an exact 40-hex `commit`;
|
|
453
|
-
- canonical `path` spelling on both row kinds — a non-canonical spelling is
|
|
454
|
-
invalid, never repaired;
|
|
455
|
-
- every `capabilities.*.package` is a key of the same lock's `packages` map
|
|
456
|
-
(the provider back-reference is the single truth for which capabilities a
|
|
457
|
-
package supplies — package rows never list them);
|
|
458
|
-
- every `packages.*.dependencies[]` id is a key of the same lock's `packages`
|
|
459
|
-
map; no self-dependency and no cycle in the locked dependency graph;
|
|
460
|
-
- `trusted` is a boolean, and it is the ONLY trust field: there is no
|
|
461
|
-
package-level approval anywhere in the model;
|
|
462
|
-
- `integrity` digests are well-formed sha256 on both row kinds;
|
|
463
|
-
- arrays retain schema uniqueness (no duplicates);
|
|
464
|
-
- `.oats-installation.json` inside a materialized artifact must AGREE with the
|
|
465
|
-
capability and package rows it was projected from (§3.1 of the contract);
|
|
466
|
-
disagreement is `invalid-lock`, modification is `integrity-drift`;
|
|
467
|
-
- the unsupported transitional v2 shape is rejected centrally by the exact
|
|
468
|
-
predicate of contract §4.1, using direct raw lock-scope reads rather than
|
|
469
|
-
`configChain` so lock-only scopes are visible, with own-property presence —
|
|
470
|
-
never truthiness or array length — as the row test;
|
|
471
|
-
- v1 documents are validated against their own historical shape when read, so
|
|
472
|
-
migration planning and doctor operate on verified data.
|
|
473
|
-
|
|
474
|
-
**Prototype safety is required at EVERY lookup or membership check keyed by a
|
|
475
|
-
package or capability ID** — central read, dependency graph, provider
|
|
476
|
-
resolution, trust, approval, update and remove alike, not merely at the
|
|
477
|
-
transitional tell fields. Raw parsed JSON objects return inherited
|
|
478
|
-
`constructor`, `toString` or `valueOf` for `map[id]` even when no own entry
|
|
479
|
-
exists, so identity keys are charset-validated and every map is null-prototype
|
|
480
|
-
or accessed through `Object.hasOwn`. A prototype-named or hostile raw-JSON ID
|
|
481
|
-
must never impersonate a provider, a dependency or a trust entry, nor bypass a
|
|
482
|
-
membership check. Fixtures cover empty transitional arrays and falsey values
|
|
483
|
-
plus prototype-named package AND capability IDs across central read, graph,
|
|
484
|
-
provider and trust lookups.
|
|
485
|
-
|
|
486
|
-
Fail-closed enforcement points: `parseLockFileStrict`, `readPackageLocks` and
|
|
487
|
-
`listInstalledPackages` RAISE `invalid-lock` — consumers never see invalid
|
|
488
|
-
locks as absent or usable data; `writePackageLock` and
|
|
489
|
-
`writeCapabilityLockEntry` validate the complete prospective document (both
|
|
490
|
-
maps, together) before writing; restore, trust queries, approval, update/remove
|
|
491
|
-
planning, migration planning and the locked-template reader validate before
|
|
492
|
-
acting. Doctor (human and `--json`) catches the typed error and renders the
|
|
493
|
-
actionable diagnosis — it is the only consumer that continues past an invalid
|
|
494
|
-
lock, and it never uses the invalid data.
|
|
495
|
-
|
|
496
|
-
`invalid-lock` joins the error taxonomy of the main contract (§8).
|
|
497
|
-
|
|
498
|
-
## 5. Flat single-capability packages (`capabilities: ["."]`)
|
|
499
|
-
|
|
500
|
-
**Read compatibility only.** A capability directory may BE the package root —
|
|
501
|
-
`oats-package.json` and `oats.json` side by side with `capabilities: ["."]` — in
|
|
502
|
-
an already-published manifest. The discriminator is `configTemplates`, NOT
|
|
503
|
-
`configs`: `oats.authoring@1.0.0` is `capabilities: ["."]` and ships no template
|
|
504
|
-
map at all, so keying acceptance on the deprecated spelling would strand a
|
|
505
|
-
package the kernel is required to keep reading. A manifest carrying
|
|
506
|
-
`configTemplates` is unambiguously new and its `"."` is
|
|
507
|
-
`invalid-package-manifest`; authoring tooling never emits `"."` either way.
|
|
508
|
-
Semantics for the layouts that still exist:
|
|
509
|
-
|
|
510
|
-
- **The projection is still a capability artifact.** The capability root equals
|
|
511
|
-
the package root, so the materialized artifact under
|
|
512
|
-
`.agents/capabilities/installed/<id>/` contains the whole package root
|
|
513
|
-
including `oats-package.json` and any config templates. That is a superset, not
|
|
514
|
-
a leak: everything in it is validated payload from one exact locked source,
|
|
515
|
-
and the artifact remains self-contained, independently hashable and
|
|
516
|
-
independently trustable. Its `integrity` is the artifact hash like any other.
|
|
517
|
-
- **Two digests, no double counting.** The package row's payload `integrity` and
|
|
518
|
-
the capability row's artifact `integrity` cover overlapping bytes on purpose:
|
|
519
|
-
one proves the distribution, the other proves the installation. Trust binds to
|
|
520
|
-
the capability digest only.
|
|
521
|
-
- **Resource indexing**: only the manifest-declared `.` is indexed; `oats.json`
|
|
522
|
-
loads from the root with normal capability validation. `oats-package.json`
|
|
523
|
-
living inside the capability's file set is harmless — each file has exactly
|
|
524
|
-
one loader (`oats-package.json` → package manifest, `oats.json` → capability
|
|
525
|
-
manifest), so no manifest-kind ambiguity can arise.
|
|
526
|
-
- **Constraint**: `.` implies a SINGLE-capability package. Listing `.` together
|
|
527
|
-
with any other capability path would nest one capability inside another and is
|
|
528
|
-
rejected as `invalid-package-manifest`.
|
|
529
|
-
- Per-capability npm closures (§2) degenerate to the package root: a root
|
|
530
|
-
`package.json` + `package-lock.json` pair is the capability's closure (it is
|
|
531
|
-
detected once, not twice).
|
|
532
|
-
- **Fail rather than degrade**: if such a package's capability cannot be
|
|
533
|
-
projected self-contained, acquisition and migration fail
|
|
534
|
-
(`capability-not-self-contained`) instead of silently retaining package-only
|
|
535
|
-
paths.
|
|
536
|
-
|
|
537
|
-
## 6. Legacy locks: v1 compatibility, and no transitional-v2 compatibility
|
|
538
|
-
|
|
539
|
-
There is exactly one legacy format to support, and it is v1.
|
|
540
|
-
|
|
541
|
-
1. The kernel **writes only** the capability-materialization lock.
|
|
542
|
-
`writePackageLock` and `writeCapabilityLockEntry` refuse an existing v1
|
|
543
|
-
file — **including an empty one** — with `legacy-lock`. Only an ABSENT lock
|
|
544
|
-
is a fresh document; an empty v1 file still carries a format decision the
|
|
545
|
-
user has not made, and converting it implicitly would contradict explicit
|
|
546
|
-
migration.
|
|
547
|
-
2. **v1 stays usable.** Runtime discovery, exact restore, trust checks,
|
|
548
|
-
approval updates and doctor/list diagnosis keep working against v1 locks and
|
|
549
|
-
the existing v1 artifacts in `.agents/capabilities/installed/`. Ordinary use
|
|
550
|
-
of an unconverted deployment never requires migration; only lifecycle
|
|
551
|
-
mutation through the package surface does. `readPackageLocks` surfaces v1
|
|
552
|
-
files in `legacy` and in `migration` (with kind `v1` or `v1-empty`), and
|
|
553
|
-
nothing is normalized, repaired or rewritten on read.
|
|
554
|
-
3. **Conversion is explicit, transactional and all-or-nothing per scope.** The
|
|
555
|
-
lock has no residue container, so a v1 scope with even one unmappable entry
|
|
556
|
-
stays v1 in full — reported as `hold`/`manual` — and keeps working.
|
|
557
|
-
Re-running `oats migrate` retries it once the catalog can map it. Guided
|
|
558
|
-
official migration converts directly into flat capability materialization.
|
|
559
|
-
4. **Trust is never carried over from v1.** A v1 capability artifact and a
|
|
560
|
-
materialized artifact are different bytes, so every executable surface is
|
|
561
|
-
re-earned and listed in the returned `trust[]`.
|
|
562
|
-
5. **Rollback is byte-exact.** Any conversion failure restores the original v1
|
|
563
|
-
lock byte-identically, removes every artifact the conversion created, leaves
|
|
564
|
-
superseded v1 artifacts in place, and rolls back the ignore bytes. Owned/path
|
|
565
|
-
capabilities are never touched.
|
|
566
|
-
6. **The earlier transitional package-root v2 is not supported at all.** It is
|
|
567
|
-
detected centrally by contract §4.1 and rejected as `invalid-lock` with an
|
|
568
|
-
actionable message naming the unsupported shape and scope recreation. It is
|
|
569
|
-
never converted, never partially interpreted, and there is no
|
|
570
|
-
`.agents/packages/installed/` handling, offline projection, or trust
|
|
571
|
-
carry-over anywhere in the engine. Existing local pre-adoption state is
|
|
572
|
-
recreated by reinstalling. The one exception is the state-free empty
|
|
573
|
-
document `{ "lockfileVersion": 2, "packages": {} }`, which carries no state
|
|
574
|
-
and normalizes to the empty current lock.
|
|
575
|
-
7. **Cutover gate**: zero lockfileVersion 1 files (including empty
|
|
576
|
-
`{capabilities:{}}` ones) and zero `.agents/packages/` directories across
|
|
577
|
-
every reconciled scope. Doctor reports each remaining one with its exact
|
|
578
|
-
command.
|
|
579
|
-
|
|
580
|
-
Required engine tests (`test/package-engine.test.mjs`): v1 empty file stays
|
|
581
|
-
pending (never implicitly converted), v1 partial-mappability hold with the scope
|
|
582
|
-
untouched, v1 full conversion with trust not carried, byte-exact rollback on
|
|
583
|
-
failure, unsupported transitional v2 rejected with no side effects (both
|
|
584
|
-
predicate arms, including empty transitional arrays and a dependency-free old
|
|
585
|
-
row), state-free empty transitional v2 normalization, prototype-named package
|
|
586
|
-
and capability IDs across central read / graph / provider / trust lookups, and
|
|
587
|
-
`.oats-installation.json` determinism, field agreement, tamper failure and
|
|
588
|
-
future-kernel restore.
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# BREAKING: desktop succession — `oats.web`, `oats pane`, and the control-pane library are retired
|
|
2
|
-
|
|
3
|
-
**The next release of `@awebai/oats` containing this change is a
|
|
4
|
-
BREAKING release.** Three previously shipped surfaces were removed in favor of
|
|
5
|
-
the OATS Desktop app (`packages/desktop/` in the framework repo):
|
|
6
|
-
|
|
7
|
-
| Removed surface | Replacement |
|
|
8
|
-
|---|---|
|
|
9
|
-
| `oats.web` marketplace capability (`oats web start`, browser panel) | OATS Desktop app — the same zero-dependency loopback server is bundled at `packages/desktop/server/` and spawned by the app |
|
|
10
|
-
| `oats pane` CLI command and the Control Pane TUI | OATS Desktop app (Active overview / instance roster) |
|
|
11
|
-
| `@awebai/oats/control-pane` package export (`lib/control-pane/model.mjs`) | The roster model moved into the Desktop app; under workspace model v2 the app reads the kernel's `oats status --json` instead ([deployment model](../packages/desktop/docs/desktop-deployment-model.md)). It is not a public kernel export |
|
|
12
|
-
|
|
13
|
-
## Migrating a deployment that used `oats.web`
|
|
14
|
-
|
|
15
|
-
Under the **0.25 workspace model** there is nothing to uninstall: remove
|
|
16
|
-
`oats.web` from `oats-workspace.yaml` `packages:` / `defaults.capabilities`
|
|
17
|
-
and from any `soul.yaml` `capabilities:`, run `oats sync` (the lock v3 entry
|
|
18
|
-
disappears with the declaration), and use the Desktop app (step 3 below).
|
|
19
|
-
|
|
20
|
-
For a **0.24 classic** deployment:
|
|
21
|
-
|
|
22
|
-
1. Remove the `oats.web` entry from `capabilities.additive` in every
|
|
23
|
-
`oats-config.yaml` in your config chain.
|
|
24
|
-
2. Remove the `oats.web` entry from `oats-lock.json` (v2) at the same scope(s),
|
|
25
|
-
and delete any stale installed copy under `.agents/capabilities/installed/`.
|
|
26
|
-
3. Use the OATS Desktop app instead: `cd packages/desktop && npm install &&
|
|
27
|
-
npm run rebuild && npm start` (see `packages/desktop/README.md`).
|
|
28
|
-
|
|
29
|
-
The CLI diagnoses stale references instead of failing opaquely:
|
|
30
|
-
|
|
31
|
-
- `oats doctor` warns when an `oats-lock.json` still pins `oats.web`, with the
|
|
32
|
-
fix spelled out.
|
|
33
|
-
- A soul or workspace default naming `oats.web` fails at resolution with a
|
|
34
|
-
message naming the successor and the exact cleanup steps (remove it from
|
|
35
|
-
`packages:` / the soul's `capabilities:`).
|
|
36
|
-
|
|
37
|
-
## Migrating `oats pane` usage
|
|
38
|
-
|
|
39
|
-
`oats pane` now exits with a pointer to the desktop app. Scripts or docs
|
|
40
|
-
invoking it should launch OATS Desktop instead. The `--theme` themes (dark,
|
|
41
|
-
solarized) exist in the app's theme system.
|
|
42
|
-
|
|
43
|
-
## Consumers of the `./control-pane` export
|
|
44
|
-
|
|
45
|
-
`import ... from "@awebai/oats/control-pane"` no longer resolves. The
|
|
46
|
-
model's pure helpers (`readMarkdownSection`, `parseTmuxWindows`,
|
|
47
|
-
`parseGitStatus`, `parseGitDiffStat`, `buildConstellation`, `relativeAge`)
|
|
48
|
-
moved into the private Desktop app and were retired with its workspace-model v2
|
|
49
|
-
rebuild. If you depended on this export, vendor the helpers from a released tag
|
|
50
|
-
or open an issue — no known external consumer existed at removal time.
|
|
51
|
-
|
|
52
|
-
## Release gating (maintainers)
|
|
53
|
-
|
|
54
|
-
Downstream installers/packaging for the desktop app must exist **before** the
|
|
55
|
-
next release ships; this migration note travels with the release notes and
|
|
56
|
-
the release must be flagged **BREAKING** (major or clearly-marked minor per
|
|
57
|
-
the project's pre-1.0 conventions).
|