@awebai/oats 0.37.0 → 0.38.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/lib/workspace.mjs CHANGED
@@ -18,7 +18,8 @@ import { oatsError } from "./errors.mjs";
18
18
  import * as defaultRemote from "./remote.mjs";
19
19
  import { bindRemote, classifyPackageValue, lockedPackageRef } from "./packages.mjs";
20
20
  import { manifestContractProblems } from "./capability-contract.mjs";
21
- import { BY_TEAM_REMOVED, TEAM_MEMBERSHIP_MOVED } from "./teams.mjs";
21
+ import YAML from "yaml";
22
+ import { BY_TEAM_REMOVED, TEAM_MEMBERSHIP_MOVED, soulsReplacement } from "./teams.mjs";
22
23
 
23
24
  /* ───────────────────────────── errors ─────────────────────────────────── */
24
25
 
@@ -261,7 +262,7 @@ function sharedLabelProblems(value, problems) {
261
262
  }
262
263
  export function validateMembership(value) { return withRemovedKeys(validateAgainst(schemaFor("membership"), value), value, REMOVED_KEYS.membership); }
263
264
 
264
- /** Keys 0.30 removed (team model v2): each is a schema problem naming its replacement, in place of
265
+ /** Keys 0.30 (team model v2) and 0.38.0 (team model 3) removed: each is a schema problem naming its replacement, in place of
265
266
  * the schema's generic "unexpected property". `at(value)` → the JSON pointers where it appears. */
266
267
  const REMOVED_KEYS = {
267
268
  workspace: [
@@ -270,6 +271,11 @@ const REMOVED_KEYS = {
270
271
  ],
271
272
  membership: [{ at: (v) => (isObject(v) && Object.hasOwn(v, "team") ? ["/team"] : []), message: TEAM_MEMBERSHIP_MOVED }],
272
273
  soul: [{ at: (v) => (isObject(v) && Object.hasOwn(v, "team") ? ["/team"] : []), message: TEAM_MEMBERSHIP_MOVED }],
274
+ // 0.38.0 (team model 3): a soul's teams and default are committed in the workspace's souls:.
275
+ local: [
276
+ { at: (v) => (isObject(v?.souls) && Object.hasOwn(v.souls, "teams") ? ["/souls/teams"] : []), message: "souls.teams was removed in 0.38.0 (team model 3): which teams a soul may join is souls: in oats-workspace.yaml" },
277
+ { at: (v) => (isObject(v?.souls) && Object.hasOwn(v.souls, "default") ? ["/souls/default"] : []), message: "souls.default was removed in 0.38.0 (team model 3): a soul's default team is souls: in oats-workspace.yaml (default:)" },
278
+ ],
273
279
  };
274
280
  function withRemovedKeys(problems, value, removed) {
275
281
  const found = removed.flatMap((r) => r.at(value).map((path) => ({ path, reason: "removed-key", message: r.message })));
@@ -316,7 +322,7 @@ function fromProblems(remote, capabilities, path, problems, { here }) {
316
322
  }
317
323
  /** Schema + domain problems for an oats-local.yaml value: settings[<cap>] may not carry the removed `byTeam` key. */
318
324
  export function validateLocal(value) {
319
- const problems = validateAgainst(schemaFor("local"), value);
325
+ const problems = withRemovedKeys(validateAgainst(schemaFor("local"), value), value, REMOVED_KEYS.local);
320
326
  if (isObject(value) && isObject(value.settings)) for (const [cap, payload] of Object.entries(value.settings)) reservedKeyProblems(payload, `/settings/${pointerKey(cap)}`, problems);
321
327
  // runtime → harness (0.27.0, lead call 6): `runtime`, the pre-0.27 name, is still read; both,
322
328
  // disagreeing, are refused.
@@ -361,8 +367,12 @@ function schemaError(kind, origin, problems, value) {
361
367
  const where = origin.repoKey ? `${origin.repoKey}@${(origin.commit || "").slice(0, 12)}:${origin.path}` : origin.path;
362
368
  // A single-cause refusal (reserved-key, …) surfaces its reason on details for callers that branch on it.
363
369
  const reasons = new Set(problems.map((p) => p.reason).filter(Boolean));
364
- return fail("E_WORKSPACE_SCHEMA", `${FILE_KINDS[kind].file} at ${where} is invalid${schemaHint(kind, value)}: ${problems.map((p) => `${p.path || "/"}: ${p.message}`).join("; ")}`, {
365
- path: origin.path, repoKey: origin.repoKey, commit: origin.commit, problems, ...(reasons.size === 1 ? { reason: [...reasons][0] } : {}),
370
+ // Team model 3: the refused souls.teams / souls.default come with the souls: that replaces them.
371
+ const moved = kind === "local" && problems.some((p) => p.reason === "removed-key" && /^\/souls\/(teams|default)$/.test(p.path)) ? soulsReplacement(value) : null;
372
+ const replacement = moved ? YAML.stringify(moved, { lineWidth: 0 }) : null;
373
+ return fail("E_WORKSPACE_SCHEMA", `${FILE_KINDS[kind].file} at ${where} is invalid${schemaHint(kind, value)}: ${problems.map((p) => `${p.path || "/"}: ${p.message}`).join("; ")}`
374
+ + (replacement ? `; commit this in oats-workspace.yaml (<member> is the soul's member repository name, as souls.disabled names it), then remove souls.teams and souls.default from oats-local.yaml:\n${replacement}` : ""), {
375
+ path: origin.path, repoKey: origin.repoKey, commit: origin.commit, problems, ...(reasons.size === 1 ? { reason: [...reasons][0] } : {}), ...(replacement ? { replacement } : {}),
366
376
  });
367
377
  }
368
378
 
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "oats.aweb": {
10
10
  "url": "https://github.com/awebai/oats-aweb.git",
11
- "ref": "v1.18.1",
11
+ "ref": "v1.19.0",
12
12
  "path": "oats-package"
13
13
  },
14
14
  "oats.jira": {
@@ -33,7 +33,7 @@
33
33
  },
34
34
  "oats.framework": {
35
35
  "url": "https://github.com/awebai/oats.git",
36
- "ref": "oats-framework/v1.4.3",
36
+ "ref": "oats-framework/v1.5.0",
37
37
  "path": "oats-package"
38
38
  }
39
39
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awebai/oats",
3
- "version": "0.37.0",
3
+ "version": "0.38.0",
4
4
  "description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
5
5
  "keywords": [
6
6
  "agents",
@@ -60,7 +60,7 @@ members:
60
60
  - git:github.com/acme/agents # the host is a member too
61
61
  - git:github.com/acme/platform
62
62
  packages:
63
- oats.framework: v1.4.3 # bare versions resolve through the official catalog
63
+ oats.framework: v1.5.0 # bare versions resolve through the official catalog
64
64
  oats.okf: v4.1.1
65
65
  defaults:
66
66
  capabilities: { oats.core: { from: package } }
@@ -135,18 +135,29 @@ membership.
135
135
  ## 6. Give the deployment a team (with messaging)
136
136
 
137
137
  If a messaging capability fills the messaging slot, every instance lives in a
138
- team, and readiness fails with `E_TEAM_UNCONFIGURED` until this deployment has
139
- a default team. Create the team with the messaging provider (its own skills say
140
- how), then record it:
138
+ team, and readiness fails with `E_TEAM_UNCONFIGURED` until there is a default
139
+ team. Create the team with the messaging provider (its own skills say how),
140
+ then commit it in `oats-workspace.yaml` with its provider id, and make it the
141
+ default:
142
+
143
+ ```yaml
144
+ teams:
145
+ research: { team: <provider team id> }
146
+ defaultTeam: research
147
+ ```
148
+
149
+ Push, then sync and check:
141
150
 
142
151
  ```bash
143
- oats teams add research --team <provider team id> # a local team; the first one becomes the default
144
- oats teams
152
+ oats sync --dir <dir>
153
+ oats teams --dir <dir>
145
154
  ```
146
155
 
147
- A team the whole organisation uses is committed in `oats-workspace.yaml` as
148
- `teams.<label>` with its provider id; which souls join which team on this
149
- machine is `oats soul teams` (`/oats-teams`, in `oats.setup`).
156
+ The teams an instance may join, and its default, are the organisation's
157
+ decision, committed in this file: by default a soul joins its default team
158
+ only, and `souls:` entries open other teams to a soul. A deployment declares
159
+ teams of its own (`oats teams add`) only when the workspace says
160
+ `localTeams: true` (`/oats-teams`, in `oats.setup`).
150
161
 
151
162
  ## 7. Spawn the first soul
152
163