@awebai/oats 0.37.0 → 0.38.1
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/bin/oats.mjs +71 -65
- package/docs/capabilities.md +11 -7
- package/docs/configuration.md +10 -20
- package/docs/design/2026-09-27-team-model-v2.md +1 -1
- package/docs/design/2026-10-02-team-model-3.md +146 -0
- package/docs/design/README.md +5 -1
- package/docs/desktop-cli-api.md +159 -121
- package/docs/desktop.md +25 -5
- package/docs/first-team.md +14 -7
- package/docs/integrations.md +5 -2
- package/docs/oats-local.schema.json +2 -14
- package/docs/oats-workspace.schema.json +4 -4
- package/docs/official-catalog.md +2 -2
- package/docs/packages.md +10 -9
- package/docs/release-notes/v0.38.0.md +128 -0
- package/docs/release-notes/v0.38.1.md +50 -0
- package/docs/souls-and-instances.md +4 -3
- package/docs/workspaces.md +122 -77
- package/lib/instance-inspect.mjs +18 -15
- package/lib/instance-resolution.mjs +9 -6
- package/lib/resolve.mjs +5 -4
- package/lib/session-viewer.mjs +1 -0
- package/lib/teams-verbs.mjs +44 -70
- package/lib/teams.mjs +151 -112
- package/lib/workspace.mjs +15 -5
- package/package-catalog.json +2 -2
- package/package.json +1 -1
- package/skills/oats-getting-started/SKILL.md +20 -9
|
@@ -25,21 +25,21 @@
|
|
|
25
25
|
"type": "object",
|
|
26
26
|
"propertyNames": { "$ref": "#/$defs/label" },
|
|
27
27
|
"additionalProperties": { "$ref": "#/$defs/team" },
|
|
28
|
-
"description": "SHARED teams: <label>: { description?, team? }. `team` is the messaging provider's team id, the same for everyone; without it the team is declared but not yet created (readiness team-unmapped).
|
|
28
|
+
"description": "SHARED teams: <label>: { description?, team? }. `team` is the messaging provider's team id, the same for everyone; without it the team is declared but not yet created (readiness team-unmapped). The default team and which teams each soul may join are defaultTeam and souls: below; a deployment's local teams live in its oats-local.yaml where localTeams allows them. A label never gates, restricts or partitions anything."
|
|
29
29
|
},
|
|
30
30
|
"defaultTeam": {
|
|
31
31
|
"$ref": "#/$defs/label",
|
|
32
|
-
"description": "
|
|
32
|
+
"description": "The workspace's fallback default team, a label of teams: in this file: a soul's souls: default wins over it, and so does a deployment's local defaultTeam where localTeams: true."
|
|
33
33
|
},
|
|
34
34
|
"localTeams": {
|
|
35
35
|
"type": "boolean",
|
|
36
|
-
"description": "
|
|
36
|
+
"description": "Whether deployments may declare their own teams and defaultTeam in oats-local.yaml (every soul may join a local team). Absent: false; then local teams and a local defaultTeam are refused (E_WORKSPACE_SCHEMA reason local-teams-closed)."
|
|
37
37
|
},
|
|
38
38
|
"souls": {
|
|
39
39
|
"type": "object",
|
|
40
40
|
"propertyNames": { "type": "string", "pattern": "^(?:\\*|[a-z0-9][a-z0-9._-]*/(?:\\*|[a-z0-9]+(?:-[a-z0-9]+)*))$" },
|
|
41
41
|
"additionalProperties": { "$ref": "#/$defs/soulTeams" },
|
|
42
|
-
"description": "
|
|
42
|
+
"description": "Per soul pattern (\"*\", <member|package>/* or <member|package>/<soul>), the soul's default team and the other teams it may join. The most specific key gives the soul's teams outright (lists never merge); the most specific key that sets a default gives its default. A soul no key matches has its default only. Every label is a label of teams: in this file."
|
|
43
43
|
},
|
|
44
44
|
"defaults": { "$ref": "#/$defs/defaults" },
|
|
45
45
|
"stores": {
|
package/docs/official-catalog.md
CHANGED
|
@@ -9,9 +9,9 @@ or workspace membership alone does not make a package official.
|
|
|
9
9
|
|
|
10
10
|
| package | release | capabilities | package souls |
|
|
11
11
|
|---|---|---|---|
|
|
12
|
-
| `oats.framework` | `oats-framework/v1.
|
|
12
|
+
| `oats.framework` | `oats-framework/v1.5.0` (this repository) | `oats.core`, `oats.setup`, `oats.knowledge-theory` | `knowledge-theory-expert` |
|
|
13
13
|
| `oats.okf` | `v4.1.1` | `oats.okf` (knowledge), `oats.okf-harvest`, `oats.okf-maintenance` | `knowledge-harvester`, `knowledge-maintainer` |
|
|
14
|
-
| `oats.aweb` | `v1.
|
|
14
|
+
| `oats.aweb` | `v1.20.0` | `oats.aweb` (messaging) | |
|
|
15
15
|
| `oats.engineering` | `v1.5.0` | `oats.engineering-expert`, `oats.developer`, `oats.code-review` | `code-reviewer` |
|
|
16
16
|
| `oats.authoring` | `v1.0.3` | `oats.authoring` | |
|
|
17
17
|
| `oats.jira` | `v1.0.1` | `oats.jira` (tasks) | |
|
package/docs/packages.md
CHANGED
|
@@ -51,7 +51,7 @@ packages:
|
|
|
51
51
|
- **Bare version** (`v4.1.1`, `4.1.1`, `1.0.0-rc.1`): the id is looked up in
|
|
52
52
|
the official catalog — `package-catalog.json` in the `oats` repo, or the file
|
|
53
53
|
named by `OATS_PACKAGE_CATALOG` — which supplies the repo url, the tag
|
|
54
|
-
convention (`v4.1.1` or `oats-framework/v1.
|
|
54
|
+
convention (`v4.1.1` or `oats-framework/v1.5.0`) and the payload path. An id
|
|
55
55
|
the catalog does not know is `E_PACKAGE_MISSING` ("use `git:<repo>@<ref>` for
|
|
56
56
|
a package outside the catalog"). The catalog is the reviewed official list
|
|
57
57
|
([official-catalog.md](official-catalog.md)) and the only way a
|
|
@@ -74,9 +74,9 @@ members:
|
|
|
74
74
|
- git:github.com/acme/agents
|
|
75
75
|
- git:github.com/acme/platform
|
|
76
76
|
packages:
|
|
77
|
-
oats.framework: v1.
|
|
77
|
+
oats.framework: v1.5.0
|
|
78
78
|
oats.okf: v4.1.1
|
|
79
|
-
oats.aweb: v1.
|
|
79
|
+
oats.aweb: v1.20.0
|
|
80
80
|
teams:
|
|
81
81
|
platform: { team: "platform:acme.aweb.ai", description: Platform engineering }
|
|
82
82
|
defaults:
|
|
@@ -134,7 +134,7 @@ Declaring a package in the workspace's `packages:` is the trust decision
|
|
|
134
134
|
## `oats package add | remove`
|
|
135
135
|
|
|
136
136
|
```bash
|
|
137
|
-
oats package add oats.aweb v1.
|
|
137
|
+
oats package add oats.aweb v1.20.0 # a catalog version
|
|
138
138
|
oats package add acme.tools git:github.com/acme/tools@v0.4.0
|
|
139
139
|
oats package remove acme.tools
|
|
140
140
|
```
|
|
@@ -247,8 +247,9 @@ oats-package/
|
|
|
247
247
|
souls; otherwise `E_SOUL_AMBIGUOUS` names each qualified form
|
|
248
248
|
(`details.qualified`). A member soul's qualified form is `<member name>/<soul>`.
|
|
249
249
|
- **Resolved** like any soul: the workspace defaults apply, `off` and
|
|
250
|
-
`<slot>: none` work, its teams
|
|
251
|
-
|
|
250
|
+
`<slot>: none` work, its teams are keyed `<package>/<soul>` (or `<package>/*`)
|
|
251
|
+
in the workspace's `souls:` (unlisted, it joins its default only), and
|
|
252
|
+
`from: here` means **this package** at the locked commit
|
|
252
253
|
(a capability it does not provide is `E_CAPABILITY_MISSING`).
|
|
253
254
|
- **Spawned** at the locked commit: the soul is fetched into the per-commit
|
|
254
255
|
soul cache and its digest must equal the lock's (`E_PACKAGE_INTEGRITY
|
|
@@ -332,11 +333,11 @@ A soul that names one of the package's capabilities with
|
|
|
332
333
|
"policy": "docs/official-catalog.md",
|
|
333
334
|
"packages": {
|
|
334
335
|
"oats.okf": { "url": "https://github.com/awebai/oats-okf.git", "ref": "v4.1.1", "path": "oats-package" },
|
|
335
|
-
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.
|
|
336
|
+
"oats.framework": { "url": "https://github.com/awebai/oats.git", "ref": "oats-framework/v1.5.0", "path": "oats-package" }
|
|
336
337
|
}
|
|
337
338
|
}
|
|
338
339
|
```
|
|
339
340
|
|
|
340
|
-
`ref` carries the tag convention: a workspace's `oats.framework: v1.
|
|
341
|
-
resolves to tag `oats-framework/v1.
|
|
341
|
+
`ref` carries the tag convention: a workspace's `oats.framework: v1.5.0`
|
|
342
|
+
resolves to tag `oats-framework/v1.5.0`. Resolving through the catalog never
|
|
342
343
|
advances a lock by itself: `oats sync` does, and says so.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# OATS 0.38.0
|
|
2
|
+
|
|
3
|
+
## Changed (breaking)
|
|
4
|
+
|
|
5
|
+
- **The Desktop accepts OATS CLIs `>=0.25.8 <0.39.0`**, so it runs against
|
|
6
|
+
this release's kernel. Install the CLI and the Desktop 0.38.0 together: the
|
|
7
|
+
Desktop 0.37.x refuses a 0.38 CLI.
|
|
8
|
+
|
|
9
|
+
- **Team model 3: the teams a soul may join, and its default, are committed
|
|
10
|
+
in the workspace** (awebai/oats#484; feature `team-model-3` replaces
|
|
11
|
+
`team-model-2`). An instance in two teams is a bridge between them, so which
|
|
12
|
+
teams an organisation's instances may be in is now its decision, committed
|
|
13
|
+
in `oats-workspace.yaml` and closed by default. This prevents accidental
|
|
14
|
+
joins and makes the intended team set reviewable in git; it does not stop a
|
|
15
|
+
deliberate relay by someone holding credentials for two teams (see
|
|
16
|
+
docs/design/2026-10-02-team-model-3.md).
|
|
17
|
+
- `oats-workspace.yaml` `defaultTeam`, `localTeams` and `souls:` (accepted
|
|
18
|
+
since 0.36.1) now apply. `souls:` keys are `"*"`, `<member|package>/*` and
|
|
19
|
+
`<member|package>/<soul>`; the most specific key gives a soul's teams
|
|
20
|
+
outright (lists never merge), and the most specific key that sets one
|
|
21
|
+
gives its default: `a/*: {default: security}` and `a/x: {teams: [docs]}`
|
|
22
|
+
give `a/x` the default `security` and the teams `[docs]` only. `teams:
|
|
23
|
+
any` is every shared team. A soul no key matches has its default only,
|
|
24
|
+
package souls included.
|
|
25
|
+
- A soul's default team is its `souls:` default; else the deployment's
|
|
26
|
+
`oats-local.yaml` `defaultTeam`, only when the workspace says
|
|
27
|
+
`localTeams: true`; else the workspace's `defaultTeam`; else none.
|
|
28
|
+
`defaultTeam.from` and `OATS_DEFAULT_TEAM_FROM` are `soul`, `deployment`
|
|
29
|
+
or `workspace`; `soul` now means the workspace's `souls:` default (it was
|
|
30
|
+
the local `souls.default`).
|
|
31
|
+
- A soul may join its default, its `souls:` teams and, with
|
|
32
|
+
`localTeams: true`, every local team. Every team row carries `via`
|
|
33
|
+
(`default`, `workspace`, `local`), in reports, `OATS_TEAMS` and
|
|
34
|
+
`instance.json`.
|
|
35
|
+
- `oats-local.yaml` `souls.teams` and `souls.default` are refused
|
|
36
|
+
(`E_WORKSPACE_SCHEMA`, reason `removed-key`). The refusal names each key
|
|
37
|
+
and prints the `souls:` to commit instead (`details.replacement`); `oats
|
|
38
|
+
doctor` answers with it.
|
|
39
|
+
- `oats-local.yaml` `teams` and `defaultTeam` are refused unless the
|
|
40
|
+
workspace says `localTeams: true` (`E_WORKSPACE_SCHEMA`, reason
|
|
41
|
+
`local-teams-closed`, naming both fixes). Spawn, preview and inspect
|
|
42
|
+
refuse; `oats teams`, readiness and `oats doctor` (offline, from the cached
|
|
43
|
+
workspace file) report it as a failure. The standalone view allows them.
|
|
44
|
+
- `oats teams add` and `oats teams default` are refused where local teams
|
|
45
|
+
are closed; `oats teams remove` still works there. `oats soul teams` is
|
|
46
|
+
read only: `--add`, `--remove`, `--default` and `--clear-default` are
|
|
47
|
+
`E_BAD_ARGS`, naming `souls:` in `oats-workspace.yaml`.
|
|
48
|
+
- `oats teams --json` is `teamsApi: 2` (`localTeams`, a `DefaultTeam`, the
|
|
49
|
+
workspace's `souls:`) and now discovers the workspace's souls; `oats soul
|
|
50
|
+
teams --json` is `soulTeamsApi: 2` (`key` is the qualified soul key,
|
|
51
|
+
`match` and `defaultMatch` name the `souls:` keys that applied).
|
|
52
|
+
- New readiness items: local-teams-closed (failure) and `team-soul-unknown`
|
|
53
|
+
(a warning for a `souls:` key that names no soul of the workspace).
|
|
54
|
+
`team-model-3-migration` is retired, and the kernel no longer reports
|
|
55
|
+
`E_TEAM_NOT_ELIGIBLE` (a soul's default is always eligible).
|
|
56
|
+
- Shapes: docs/desktop-cli-api.md, under Teams.
|
|
57
|
+
- **The Desktop follows team model 3.** With a 0.38 CLI (feature `team-model-3`) the Workspace ›
|
|
58
|
+
Teams page shows the workspace's `souls:` read only, and offers Add and Make default for local teams
|
|
59
|
+
only where the workspace says `localTeams: true`; where it doesn't, a local team still in
|
|
60
|
+
`oats-local.yaml` keeps Remove under the kernel's local-teams-closed message. A soul's *Teams here* is
|
|
61
|
+
read only: it says where the soul's default and teams come from and that they change as `souls:` in
|
|
62
|
+
`oats-workspace.yaml` (a PR to the workspace file). The Desktop never sends the removed `oats soul
|
|
63
|
+
teams` edit flags, and a 0.37 or older CLI keeps the team model v2 views.
|
|
64
|
+
- **oats.aweb 1.19.0** (catalog and workspace pin, and the bundled mirror),
|
|
65
|
+
for team model 3. A default team that comes from the workspace's
|
|
66
|
+
`defaultTeam` is reported as `from: "workspace"` in `oats aweb teams --json`
|
|
67
|
+
and the recorded meta (1.18 reported it as `deployment`). `oats aweb setup
|
|
68
|
+
--create` asks the kernel whether the workspace allows local teams before it
|
|
69
|
+
creates anything; where it does not, it creates the team and its per-team
|
|
70
|
+
root, records no local team, and prints the `teams:` entry (and
|
|
71
|
+
`defaultTeam:`) to commit in `oats-workspace.yaml`, or the `localTeams: true`
|
|
72
|
+
alternative. `--username` and setup's other advice say the same where local
|
|
73
|
+
teams are closed, and an unmapped default's remedy names `defaultTeam:` in
|
|
74
|
+
the workspace file. Joining stays limited to the eligible `OATS_TEAMS` rows.
|
|
75
|
+
|
|
76
|
+
- **oats.framework 1.5.0** (oats.core 2.3.0, oats.setup 2.3.0; catalog and
|
|
77
|
+
workspace pin `oats-framework/v1.5.0`): the skills
|
|
78
|
+
describe team model 3. `oats-teams`, `oats-workspace-config`,
|
|
79
|
+
`oats-setup-model`, `oats-onboarding` and the setup inject say a soul's teams
|
|
80
|
+
and default are committed in `souls:` of `oats-workspace.yaml`, local teams
|
|
81
|
+
only with `localTeams: true`; `oats-operate` and `oats-souls` say where an
|
|
82
|
+
instance's teams come from.
|
|
83
|
+
|
|
84
|
+
## Upgrade
|
|
85
|
+
|
|
86
|
+
Before upgrading a deployment, its workspace must say what its
|
|
87
|
+
`oats-local.yaml` said. 0.36.1 and 0.37.x already accept the workspace keys,
|
|
88
|
+
and their `team-model-3-migration` warnings list what each deployment has to
|
|
89
|
+
move.
|
|
90
|
+
|
|
91
|
+
1. **In the workspace file** (a PR, once everyone who reads it runs 0.36.1 or
|
|
92
|
+
later):
|
|
93
|
+
- commit `souls:` entries for what the deployments' `souls.teams` and
|
|
94
|
+
`souls.default` said. A v2 soul's teams were its default, `"*"`'s teams
|
|
95
|
+
and its own, and patterns never merge, so a soul's own entry lists
|
|
96
|
+
`"*"`'s teams too. Qualify bare soul names with their member's name
|
|
97
|
+
(`<member>/<soul>`, as `souls.disabled` names it). Running 0.38.0 against
|
|
98
|
+
an unmigrated `oats-local.yaml` prints this snippet for you;
|
|
99
|
+
- commit `defaultTeam: <label>` when the deployments' default is a shared
|
|
100
|
+
team;
|
|
101
|
+
- for local (personal) teams and a local default, choose: (a) keep them
|
|
102
|
+
personal, with `localTeams: true`; or (b) commit them in `teams:` (and the
|
|
103
|
+
default as `defaultTeam:`).
|
|
104
|
+
2. **In each `oats-local.yaml`**, when the deployment moves to 0.38.0: remove
|
|
105
|
+
`souls.teams` and `souls.default`, and, under (b), the local `teams` and
|
|
106
|
+
`defaultTeam` (`oats teams remove` works while local teams are closed).
|
|
107
|
+
3. **Check:** `oats teams` lists no problems, and `oats spawn <soul>
|
|
108
|
+
--preview` shows each soul's `defaultTeam` and `teams` as intended. Running
|
|
109
|
+
instances keep their spawn-time default; readiness reports
|
|
110
|
+
`default-team-changed` where it moved, and a respawn follows it.
|
|
111
|
+
|
|
112
|
+
What each kind of deployment changes:
|
|
113
|
+
|
|
114
|
+
- **A workspace whose people keep personal teams or a personal default** (for
|
|
115
|
+
example awebai/oats): `localTeams: true`, plus `souls:` for the souls'
|
|
116
|
+
shared teams (e.g. `"*": { teams: [oats] }`); drop `souls.teams` locally.
|
|
117
|
+
- **A workspace whose default team is a committed shared team**: commit
|
|
118
|
+
`defaultTeam: <label>` and the `souls:` entries; drop the local
|
|
119
|
+
`defaultTeam` (and any `souls.*` team keys).
|
|
120
|
+
- **A standalone deployment**: nothing changes for local teams and the local
|
|
121
|
+
default; drop `souls.teams` and `souls.default` (a standalone view has no
|
|
122
|
+
`souls:` patterns: every soul may join every local team).
|
|
123
|
+
|
|
124
|
+
Consumers: the Desktop's team views read `teamsApi: 1` / `soulTeamsApi: 1` and
|
|
125
|
+
edit through `oats soul teams --add`; they follow in their own change. Another
|
|
126
|
+
messaging provider receives `OATS_DEFAULT_TEAM_FROM=workspace` and `via` on
|
|
127
|
+
the `OATS_TEAMS` rows; one that records local teams with `oats teams add` needs
|
|
128
|
+
the workspace to allow them (oats.aweb 1.19.0 does both, above).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# OATS 0.38.1
|
|
2
|
+
|
|
3
|
+
## Changed
|
|
4
|
+
|
|
5
|
+
- **oats.aweb 1.20.0: `oats aweb roster` lists who is on the team and says
|
|
6
|
+
what each entry is** (awebai/oats-aweb#46). The roster printed only the
|
|
7
|
+
team's membership certificates (`aw id team members`). That listing omits
|
|
8
|
+
identities the registry does not list, such as a team's retained global
|
|
9
|
+
coordinator and dashboard humans. It also listed deployment roots and
|
|
10
|
+
certificates with no workspace exactly like agents. The roster is now the
|
|
11
|
+
union of those certificates and the team's workspace presence
|
|
12
|
+
(`aw workspace status`), read from the minting root. Each entry has its
|
|
13
|
+
sources (`certificate`, `presence` or both), its status (active, when last
|
|
14
|
+
seen, or `certificate only: no workspace record`) and a kind:
|
|
15
|
+
- `global identity`, from the identity scope, listed first;
|
|
16
|
+
- `human` and `hosted agent`, from the session context;
|
|
17
|
+
- `instance` and `deployment root`, from the workspace path;
|
|
18
|
+
- `unknown`.
|
|
19
|
+
An inferred kind says so, and nothing is called retired. aw does not mark a
|
|
20
|
+
team's coordinator, and the roster says so. When a source cannot be read,
|
|
21
|
+
or presence is past its 200-workspace cap, the roster still lists what it
|
|
22
|
+
has and says it is incomplete. `oats aweb roster --json` is now an oats.aweb
|
|
23
|
+
document (`{team, members, certificatesComplete, presenceComplete,
|
|
24
|
+
problems}`) instead of aw's raw listing. The oats-aweb skill, the inject
|
|
25
|
+
and the spawn brief say what the roster shows and what it can't tell you.
|
|
26
|
+
|
|
27
|
+
## Fixed
|
|
28
|
+
|
|
29
|
+
- **The Desktop opened from Finder no longer shows `/` as its workspace**
|
|
30
|
+
(awebai/oats#518). A Finder launch starts in `/`, and when no saved
|
|
31
|
+
workspace was a deployment, the Desktop served `/`. It titled the window
|
|
32
|
+
`/`, showed `E_NO_DEPLOYMENT` in Instances, and remembered `/` in
|
|
33
|
+
`windows.json` for every later launch. A folder that is not a deployment is
|
|
34
|
+
now never served, bound to a window or saved. With no deployment to open,
|
|
35
|
+
the window opens on the workspace switcher, which lists the deployments
|
|
36
|
+
directly inside `~/Agents` under "On this computer": one click opens one in
|
|
37
|
+
that window. A window remembered on `/` is forgotten at launch.
|
|
38
|
+
|
|
39
|
+
- **A window with no workspace lists the workspaces served after it opened**
|
|
40
|
+
(awebai/oats#521). Its choices were fixed when it opened, so remote
|
|
41
|
+
workspaces arriving a few seconds after launch never appeared and the
|
|
42
|
+
switcher said "No workspace choices reported."
|
|
43
|
+
|
|
44
|
+
- **Copying an agent's output in the Desktop** (awebai/oats#520). A plain
|
|
45
|
+
drag over a terminal now selects the text (tmux's copy mode, so it can
|
|
46
|
+
reach into the scrollback) and copies it to the clipboard on release;
|
|
47
|
+
⌘C, Edit › Copy and the right-click menu find it there. The Desktop takes
|
|
48
|
+
tmux's copies (OSC 52) write-only: nothing in a terminal can read the
|
|
49
|
+
clipboard. Remote terminals (`oats session attach --server`) bind the same
|
|
50
|
+
drag. ⌘V is unchanged; Option-drag still makes a terminal-only selection.
|
|
@@ -164,8 +164,8 @@ composed skills and instructions, a spawn records:
|
|
|
164
164
|
"key": "github.com/acme/agents", "commit": "3f2a9c1e…", "resolution": "20ec8ec527311d71d0973086",
|
|
165
165
|
"soul": { "repoKey": "github.com/acme/agents", "commit": "3f2a9c1e…" }
|
|
166
166
|
},
|
|
167
|
-
"teams": [{ "label": "ana-acme", "team": "ana-acme:ana.aweb.ai", "default": true, "from": "local" },
|
|
168
|
-
{ "label": "engineering", "team": "engineering:acme.aweb.ai", "default": false, "from": "shared" }],
|
|
167
|
+
"teams": [{ "label": "ana-acme", "team": "ana-acme:ana.aweb.ai", "default": true, "from": "local", "via": ["default", "local"] },
|
|
168
|
+
{ "label": "engineering", "team": "engineering:acme.aweb.ai", "default": false, "from": "shared", "via": ["workspace"] }],
|
|
169
169
|
"defaultTeam": { "label": "ana-acme", "team": "ana-acme:ana.aweb.ai", "from": "deployment" }
|
|
170
170
|
}
|
|
171
171
|
```
|
|
@@ -183,7 +183,8 @@ composed skills and instructions, a spawn records:
|
|
|
183
183
|
compares `workspace.soul` with the member's current commit too: `soul: <name>
|
|
184
184
|
from <member> @ <c7> [member moved since …]` (`--json`: `instances[].soul`).
|
|
185
185
|
- `teams` / `defaultTeam`: the soul's teams at spawn, exactly as the providers
|
|
186
|
-
received them (mapped teams only
|
|
186
|
+
received them (mapped teams only, each with `via`: why the soul may join it)
|
|
187
|
+
and its default: evidence, never rewritten.
|
|
187
188
|
A running home's hooks and messaging commands read the teams live
|
|
188
189
|
([capabilities.md](capabilities.md#teams-in-the-provider-environment)).
|
|
189
190
|
|
package/docs/workspaces.md
CHANGED
|
@@ -35,7 +35,7 @@ workspace commit. Schemas: [`oats-workspace.schema.json`](oats-workspace.schema.
|
|
|
35
35
|
[`oats-membership.schema.json`](oats-membership.schema.json),
|
|
36
36
|
[`soul.schema.json`](soul.schema.json), [`oats-local.schema.json`](oats-local.schema.json);
|
|
37
37
|
the lock's format is in [packages](packages.md#lock-v3). The JSON schemas encode
|
|
38
|
-
shapes; domain rules (
|
|
38
|
+
shapes; domain rules (labels that are shared teams, duplicate members, canonical `from:` keys,
|
|
39
39
|
the two `packages:` value forms) live in the kernel's `validateWorkspace` /
|
|
40
40
|
`validateSoul`, which are the authority.
|
|
41
41
|
|
|
@@ -54,13 +54,16 @@ members: # repo refs, NO @revision (E_WORKSPAC
|
|
|
54
54
|
- git:github.com/acme/tools # a member that ALSO publishes a package (see below)
|
|
55
55
|
|
|
56
56
|
packages: # the ONLY versioned things
|
|
57
|
-
oats.framework: v1.
|
|
57
|
+
oats.framework: v1.5.0 # bare version → resolves through the official catalog
|
|
58
58
|
oats.okf: v4.1.1
|
|
59
59
|
acme.tools: git:github.com/acme/tools@v0.4.0 # outside the catalog → git:<repo>@<tag|OID>; still a package
|
|
60
60
|
|
|
61
61
|
teams: # SHARED teams: the same provider team for everyone
|
|
62
62
|
engineering: { description: Platform and release automation, team: "engineering:acme.aweb.ai" }
|
|
63
63
|
reviewers: { description: Code review } # declared, not created yet (no `team` id): readiness team-unmapped
|
|
64
|
+
defaultTeam: engineering # the workspace's default team; see Teams below
|
|
65
|
+
souls: # which teams each soul may join besides its default
|
|
66
|
+
platform/*: { teams: [reviewers] }
|
|
64
67
|
|
|
65
68
|
defaults:
|
|
66
69
|
capabilities:
|
|
@@ -146,7 +149,8 @@ its content digest.
|
|
|
146
149
|
### `oats-local.yaml`: the only per-machine file
|
|
147
150
|
|
|
148
151
|
Which workspace this machine realizes, where member clones live, host-owned
|
|
149
|
-
provider settings, this deployment's local teams and
|
|
152
|
+
provider settings, this deployment's local teams and default (where the
|
|
153
|
+
workspace allows them), host
|
|
150
154
|
facts for automations, and launch configurations. The full reference is
|
|
151
155
|
[configuration.md](configuration.md).
|
|
152
156
|
|
|
@@ -308,89 +312,130 @@ Harnesses start normally, with their own skill discovery intact
|
|
|
308
312
|
|
|
309
313
|
## Teams
|
|
310
314
|
|
|
311
|
-
A team is a messaging-provider team (for oats.aweb, an
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
315
|
+
A team is a messaging-provider team (for oats.aweb, an aweb team id
|
|
316
|
+
`<team>:<namespace>`) under a **label**. An instance that sits in two teams is
|
|
317
|
+
a bridge between them: one process reads both inboxes and could relay anything
|
|
318
|
+
from one to the other. So which teams an organisation's instances may be in is
|
|
319
|
+
the organisation's decision, committed in its workspace file and closed by
|
|
320
|
+
default; a deployment's `oats-local.yaml` adds a team only where the workspace
|
|
321
|
+
allows it. Souls stay team-free in their own repositories: an organisation
|
|
322
|
+
says how souls behave in its teams in its `oats-workspace.yaml`, and someone
|
|
323
|
+
running the same souls standalone sees none of it.
|
|
324
|
+
|
|
325
|
+
**What this protects against, and what it does not.** The workspace's team
|
|
326
|
+
rules prevent **accidental** joins in a cooperative installation, and they make
|
|
327
|
+
the organisation's intended team set visible and reviewable in its git. That is
|
|
328
|
+
all they claim. They do not stop an operator from bridging teams on purpose:
|
|
329
|
+
whoever holds credentials for two teams can read under one identity and relay
|
|
330
|
+
under the other, or copy content outside the messaging layer, and distinct keys
|
|
331
|
+
do not prove distinct processes. The messaging provider's admission (for aweb,
|
|
332
|
+
the team controller key signs memberships; hosted invites are mediated by the
|
|
333
|
+
service) controls who holds credentials for a team, not what a process does
|
|
334
|
+
with what it reads. A deployment's authority comes from the credentials it
|
|
335
|
+
holds, not from its path or its name, and a team certificate carries no soul,
|
|
336
|
+
workspace or home-team claim (such metadata would be self-asserted, not a
|
|
337
|
+
control).
|
|
338
|
+
|
|
339
|
+
### Where teams are declared
|
|
340
|
+
|
|
341
|
+
```yaml
|
|
342
|
+
# oats-workspace.yaml (committed, edited by PR)
|
|
343
|
+
teams: # SHARED teams: the same provider team for everyone
|
|
344
|
+
engineering: { team: "engineering:acme.aweb.ai" }
|
|
345
|
+
security: { team: "security:acme.aweb.ai" }
|
|
346
|
+
docs: { description: Docs rota } # declared, not created yet: team-unmapped
|
|
347
|
+
defaultTeam: engineering # the workspace's fallback default team (a shared label)
|
|
348
|
+
localTeams: false # may deployments declare their own teams? (absent: false)
|
|
349
|
+
souls: # per soul pattern: its default team and the other teams it may join
|
|
350
|
+
"*": { teams: [] } # unlisted souls: default only (also what no entry means)
|
|
351
|
+
security-souls/*: { default: security, teams: [engineering] }
|
|
352
|
+
security-souls/incident-responder: { default: security, teams: [engineering, docs] }
|
|
353
|
+
oats.engineering/*: { teams: any } # any: every shared team of this file
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
- **Shared teams** (`teams.<label> = { description?, team? }`): a shared team
|
|
357
|
+
without `team` is declared but not created yet (readiness `team-unmapped`):
|
|
358
|
+
its owner creates it with the messaging provider, then commits the id.
|
|
359
|
+
- **`souls:` keys are patterns**: `<member>/<soul>` or `<package>/<soul>`,
|
|
360
|
+
`<member>/*` or `<package>/*`, and `"*"`, with the member and package names
|
|
361
|
+
`souls.disabled` uses (a member's name is its repository's). A bare soul name
|
|
362
|
+
is refused. A key that is neither a pattern nor a soul the workspace offers
|
|
363
|
+
is the warning `team-soul-unknown` (a typo guard).
|
|
364
|
+
- **The most specific key wins outright** for a soul's teams, and lists never
|
|
365
|
+
merge: the soul's own key, then `<member|package>/*`, then `"*"`. The
|
|
366
|
+
default comes from the most specific key that sets one. So
|
|
367
|
+
`a/*: {default: security}` and `a/x: {teams: [docs]}` give `a/x` the default
|
|
368
|
+
`security` and the teams `[docs]` only.
|
|
369
|
+
- **A soul no key matches** (and no `"*"`) has its default only. That applies
|
|
370
|
+
to member and package souls alike: a workspace opens a package explicitly.
|
|
371
|
+
- **Every label** (`defaultTeam`, each `default`, each of `teams`) must be a
|
|
372
|
+
shared team of the same file; anything else is `E_WORKSPACE_SCHEMA` when the
|
|
373
|
+
file is read, never at a spawn on someone else's machine.
|
|
374
|
+
- **Local teams** (`oats-local.yaml` `teams.<label> = { team, description? }`)
|
|
375
|
+
and a local `defaultTeam` are allowed only when the workspace says
|
|
376
|
+
`localTeams: true`. Otherwise every spawn, preview and inspect is refused with
|
|
377
|
+
`E_WORKSPACE_SCHEMA` (reason `local-teams-closed`), naming both fixes: add
|
|
378
|
+
`localTeams: true` to the workspace file, or commit the teams and the
|
|
379
|
+
default there and remove them locally. A label in both files is
|
|
380
|
+
`team-label-collision` (a warning): the **shared** definition wins, and the
|
|
381
|
+
fix is renaming the local label.
|
|
382
|
+
- **The standalone view** (an explicit `standalone:`, or a fallback when the
|
|
383
|
+
workspace cannot be read) has no workspace rules: local teams and the local
|
|
384
|
+
`defaultTeam` apply there.
|
|
385
|
+
- `soul.yaml` and `oats-membership.yaml` say nothing about teams.
|
|
386
|
+
`oats-local.yaml` `souls.teams` and `souls.default` were removed in 0.38.0:
|
|
387
|
+
they are refused, and the refusal prints the `souls:` to commit instead.
|
|
388
|
+
|
|
389
|
+
### A soul's default team and its teams
|
|
390
|
+
|
|
391
|
+
The default, in order:
|
|
392
|
+
|
|
393
|
+
1. the soul's `default` from `souls:` (the most specific matching key that
|
|
394
|
+
sets one);
|
|
395
|
+
2. else the deployment's `oats-local.yaml` `defaultTeam`, only when
|
|
396
|
+
`localTeams: true`;
|
|
397
|
+
3. else the workspace's `defaultTeam`;
|
|
398
|
+
4. else none (`E_TEAM_UNCONFIGURED` when a messaging layer is active).
|
|
399
|
+
|
|
400
|
+
`defaultTeam.from` says which: `soul`, `deployment` or `workspace`.
|
|
401
|
+
|
|
402
|
+
The teams a soul may join: its default, plus the `teams` of its most specific
|
|
403
|
+
matching `souls:` key, plus, with `localTeams: true`, every local team the
|
|
404
|
+
deployment declares. Each row says why (`via`: `default`, `workspace`,
|
|
405
|
+
`local`). At spawn an instance joins its **default** only; the others are
|
|
337
406
|
eligible: offered, and joined on request through the provider (`join=` at
|
|
338
|
-
spawn, or its own verbs later)
|
|
339
|
-
|
|
340
|
-
and the soul only, the same for everyone. **A
|
|
341
|
-
changes trust or partitions the knowledge
|
|
407
|
+
spawn, or its own verbs later), which refuses a team that is not eligible.
|
|
408
|
+
A local default no file declares is `E_TEAM_UNKNOWN`. Capabilities compose
|
|
409
|
+
from the workspace defaults and the soul only, the same for everyone. **A
|
|
410
|
+
label never gates, restricts, changes trust or partitions the knowledge
|
|
411
|
+
store.**
|
|
412
|
+
|
|
413
|
+
### The verbs
|
|
342
414
|
|
|
343
|
-
|
|
415
|
+
They never call a provider. `oats teams add` and `oats teams default` edit
|
|
416
|
+
`oats-local.yaml` in place, where the workspace allows local teams; `oats teams
|
|
417
|
+
remove` runs anywhere (removing local teams is how a deployment moves them into
|
|
418
|
+
the workspace). A soul's teams are edited by a PR to `oats-workspace.yaml`:
|
|
419
|
+
`oats soul teams` only reads them.
|
|
344
420
|
|
|
345
421
|
```
|
|
346
|
-
oats teams [--json] #
|
|
347
|
-
oats teams add <label> --team <id> [--description d] # declare a local team (the first one becomes the default)
|
|
348
|
-
oats teams remove <label> # refused while
|
|
349
|
-
oats teams default <label>
|
|
350
|
-
oats soul teams <soul>|'*' [--
|
|
422
|
+
oats teams [--json] # the teams, ids, the default, localTeams, the workspace's souls:, problems
|
|
423
|
+
oats teams add <label> --team <id> [--description d] # declare a local team (the first one becomes the local default)
|
|
424
|
+
oats teams remove <label> # refused while it is the local default (E_TEAM_IN_USE) or shared (E_TEAM_SHARED)
|
|
425
|
+
oats teams default <label> # the local default
|
|
426
|
+
oats soul teams <soul>|'*' [--json] # a soul's default and teams here, and which souls: key gave them
|
|
351
427
|
```
|
|
352
428
|
|
|
353
|
-
The messaging provider's own setup creates provider teams
|
|
354
|
-
|
|
429
|
+
The messaging provider's own setup creates provider teams (see the provider's
|
|
430
|
+
documentation). The spawn preview, `inspect` and `oats souls` report a soul's
|
|
355
431
|
`teams` and `defaultTeam`; readiness reports the team problems in
|
|
356
432
|
`checks.configured` (`E_TEAM_UNCONFIGURED` when a messaging layer is active and
|
|
357
433
|
there is no default; `team-unmapped`, blocking when it is the default;
|
|
358
|
-
`default-team-changed` for a running
|
|
359
|
-
its environment — see
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
OATS 0.38.0 commits a soul's teams in the workspace (team model 3,
|
|
365
|
-
awebai/oats#484): the teams an organisation's instances may join become its
|
|
366
|
-
own decision, visible and reviewable in its git, so a deployment's
|
|
367
|
-
`oats-local.yaml` no longer adds one by accident. 0.36.x and 0.37.x prepare for it, so
|
|
368
|
-
every workspace and deployment can migrate first:
|
|
369
|
-
|
|
370
|
-
- **`oats-workspace.yaml` accepts the new keys** and validates them, but
|
|
371
|
-
**does not apply them**: a soul's teams and default are still resolved as
|
|
372
|
-
above, from `oats-local.yaml`.
|
|
373
|
-
|
|
374
|
-
```yaml
|
|
375
|
-
defaultTeam: engineering # the workspace's fallback default team
|
|
376
|
-
localTeams: true # deployments may declare their own teams (absent: false)
|
|
377
|
-
souls: # per pattern: "*", <member|package>/*, <member|package>/<soul>
|
|
378
|
-
"*": { teams: [] } # default only ({} says the same)
|
|
379
|
-
security-souls/*: { default: security, teams: [engineering] }
|
|
380
|
-
oats.engineering/*: { teams: any } # every shared team
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
`<member|package>` is the name `souls.disabled` uses. Every label (`defaultTeam`,
|
|
384
|
-
a `souls:` `default`, each of its `teams`) must be a shared team in `teams:` of
|
|
385
|
-
the same file; anything else is `E_WORKSPACE_SCHEMA` when the file is read. A
|
|
386
|
-
key naming a member or package the workspace does not have is not an error.
|
|
387
|
-
- **The readiness warning `team-model-3-migration`** (never blocking) names
|
|
388
|
-
what 0.38.0 will refuse: `souls.teams` / `souls.default` in `oats-local.yaml`
|
|
389
|
-
(they move to `souls:`), and local `teams` / `defaultTeam` while the workspace
|
|
390
|
-
does not say `localTeams: true` (fix: add `localTeams: true`, or commit the
|
|
391
|
-
teams and `defaultTeam` in the workspace file). `oats teams`, readiness (and
|
|
392
|
-
so the Desktop) and `oats doctor` show it. The migration steps are in the
|
|
393
|
-
[0.36.1 release notes](release-notes/v0.36.1.md).
|
|
434
|
+
local-teams-closed; `team-soul-unknown`; `default-team-changed` for a running
|
|
435
|
+
instance). The provider receives them in its environment — see
|
|
436
|
+
[capabilities.md](capabilities.md#teams-in-the-provider-environment). Exact
|
|
437
|
+
shapes: [desktop-cli-api.md](desktop-cli-api.md#team-model-3-feature-team-model-3-oats-0370-replaces-feature-team-model-2).
|
|
438
|
+
Why it is shaped this way: [team model 3](design/2026-10-02-team-model-3.md).
|
|
394
439
|
|
|
395
440
|
## Provider payloads have three homes
|
|
396
441
|
|