feat(fleet): add shared role semantics (#768)
All checks were successful
ci/woodpecker/push/publish Pipeline was successful
ci/woodpecker/push/ci Pipeline was successful

This commit was merged in pull request #768.
This commit is contained in:
2026-07-15 00:53:47 +00:00
parent eb4e14ae5c
commit a5e8e55401
19 changed files with 1650 additions and 177 deletions

View File

@@ -0,0 +1,45 @@
# Fleet Role Classes and Authority
A fleet role class is a machine identity resolved from the persona library. Resolution uses the
canonical class before consulting the baseline `fleet/roles/` and operator `fleet/roles.local/`
layers. A readable role contract is required; an index entry alone is not semantic success.
## Canonicalization
Only these legacy class aliases are recognized:
| Requested class | Canonical class |
| ---------------------- | --------------- |
| `implementer` | `code` |
| `reviewer` | `review` |
| `operator-interaction` | `interaction` |
No other alias is inferred. In particular, `worker`, `analyst`, and `canary` are custom classes only
when an operator supplies a readable contract for that exact class. Tess and Ultron are instance
names, not classes. `agents[].alias` is display-only and cannot grant authority.
Canonicalization happens before role lookup. For example, requesting `implementer` resolves
`code.md`; a separate `roles.local/implementer.md` cannot redefine the legacy alias. A canonical
`roles.local/code.md` still overrides the baseline `roles/code.md` contract.
## Protected authority
Protected authority is immutable metadata derived only from canonical class. Role prose, instance
name, display alias, tool policy, runtime, and custom role files cannot grant it.
| Canonical class | Granted authority | Explicit limits |
| ----------------- | -------------------------------------------------- | --------------------------------------------------------------------------------- |
| `merge-gate` | Sole approve-to-land and merge authority | No authority is inferred by similarly named custom roles or policies. |
| `validator` | May issue a validation certificate | Cannot approve-to-land or merge. |
| `orchestrator` | May orchestrate, manage topology, and issue leases | Cannot approve-to-land or merge. |
| `team-leader` | May use orchestrator-leased capacity | Cannot issue leases or mutate roster, configuration, credentials, or merge state. |
| `interaction` | Request and status surface | Cannot orchestrate, issue leases, mutate roster/configuration, or merge. |
| all other classes | No protected authority implicitly | Custom contracts do not acquire protected powers from prose. |
Roster-v2 semantic validation requires a protected class and its canonical tool policy to match. It
also rejects an unprotected class paired with a protected tool policy. The legacy tool-policy name
`operator-interaction` canonicalizes to `interaction`.
This mapping describes authority metadata only. Lease issuance, validation-certificate storage or
workflow, lifecycle reconciliation, credentials, roster mutation, and merge execution are outside
this resolver contract.

View File

@@ -81,6 +81,35 @@ agents:
| `agents[].lifecycle.desired_state` | yes | `running` or `stopped` |
| `agents[].launch.yolo` | yes | boolean; structured data only, not an arbitrary command escape hatch |
## Semantic handoff
`parseRosterV2` and `normalizeRosterV2` remain synchronous and structural. After structural success,
call the asynchronous `validateRosterV2Semantics` handoff before using persona identity or authority.
That validator batches the baseline `fleet/roles/` and operator `fleet/roles.local/` scans, then
delegates every agent to the shared persona resolver.
Semantic validation:
- requires the winning role contract to be readable and non-empty; `LIBRARY.md` membership alone does
not resolve a class;
- retains `requestedClass` separately from `canonicalClass` in typed output;
- canonicalizes only `implementer` to `code`, `reviewer` to `review`, and
`operator-interaction` to `interaction`;
- canonicalizes `tool_policy` with the same exact alias table;
- rejects protected class/tool-policy mismatches in either direction, while accepting
`class: operator-interaction` with `tool_policy: operator-interaction` as canonical
`interaction`;
- derives immutable protected authority only from canonical class; and
- accepts custom baseline or `roles.local` classes without granting protected authority.
`agents[].alias` remains display-only. Tess and Ultron are instance names, never semantic classes.
Canonicalization happens before role-layer lookup, so a legacy-named override cannot redefine an
alias as separate authority. See [Role Classes and Authority](./role-classes.md) and
[Customize Fleet Roles](../how-to/customize-roles.md).
This handoff performs no filesystem, systemd, tmux, roster, credential, lease, certificate, or
lifecycle mutation.
## Fail-closed boundary
Every object is `additionalProperties: false`. The compiler rejects unknown, missing, malformed,