No way to compose a role without its boundaries, and a deployment that owns its own live surface needs one #304

Closed
opened 2026-08-18 15:56:38 +00:00 by coilyco-ops · 1 comment
Member

Found while staging the sirens-dowel lane for the 2026-08-19 Temporal session. Read-only investigation of this repository, nothing changed here.

What is true today

A role's boundaries come from two places and neither is overridable at compose time:

  • role.kdl's own boundary node, read into role.Boundaries.
  • Owned boundaries derived from affinity, appended by RoleActiveBoundaries (internal/person/person.go:1217).

ParseRequest (internal/schema/schema.go:225) accepts exactly nine keys: person-policy, person-source, role, delivery, model-tier, personality-library, source, identity, and the legacy density which is rejected outright. None of them touches boundaries.

So the only ways to get a boundary-free bundle are to edit the role's role.kdl in the roster, which changes that role everywhere, or to add a near-duplicate role that exists only to omit a boundary.

Why that is a real gap

boundary-modify-live-system encodes "DevOps changes running systems, other roles observe and hand the action over." That is correct whenever a DevOps seat exists.

It is wrong for a deployment that is the operator of its own surface. The concrete case: a lane whose entire granted tool surface is a small guarded MCP over its own content, with no operator standing behind it. There, the boundary instructs the agent to hand off the only work it was deployed to do, to nobody. The deferral protects nothing and costs the whole function.

This is not "remove safety." The boundary is a statement about which seat owns an action, and a deployment where that seat does not exist needs to say so at compose time rather than by forking the role.

Shape worth considering

A request-level node, so the roster stays the single description of the role and the deployment states its own situation:

compose {
    role "engineer"
    boundary-omit "modify-live-system"
}

Constraints worth building in rather than bolting on later:

  • Omitting a boundary a role owns is probably an error rather than a silent drop, since an owner losing its own boundary is a different and larger claim than a deferrer losing one.
  • The omission belongs in the rendered identity card and in the compose review artifact. A bundle that quietly lacks a boundary is worse than one that never had it, because the review surface stops telling the truth.
  • An unknown boundary name should fail rather than no-op, matching how the rest of the request parser behaves.

What was done instead, for the immediate need

The lane names the precedence in its own local skill root, which lands after <composed-identity> in the sirens-echo prompt and is the pattern composedVoicePolicy already sets for voice. That works and it shipped, but it leaves the boundary prose in the bundle and relies on a later instruction to override it, which is strictly weaker than not composing it.

Recorded in coilyco-gaming/sirens-echo#956.

Not built here because the change reaches this repository, sirens-echo's bake, an image publish, and a rollout, roughly 19 hours before a live recording. The knob is the right fix on any other day.

Found while staging the `sirens-dowel` lane for the 2026-08-19 Temporal session. Read-only investigation of this repository, nothing changed here. ## What is true today A role's boundaries come from two places and neither is overridable at compose time: * `role.kdl`'s own `boundary` node, read into `role.Boundaries`. * Owned boundaries derived from affinity, appended by `RoleActiveBoundaries` (`internal/person/person.go:1217`). `ParseRequest` (`internal/schema/schema.go:225`) accepts exactly nine keys: `person-policy`, `person-source`, `role`, `delivery`, `model-tier`, `personality-library`, `source`, `identity`, and the legacy `density` which is rejected outright. **None of them touches boundaries.** So the only ways to get a boundary-free bundle are to edit the role's `role.kdl` in the roster, which changes that role everywhere, or to add a near-duplicate role that exists only to omit a boundary. ## Why that is a real gap `boundary-modify-live-system` encodes "DevOps changes running systems, other roles observe and hand the action over." That is correct whenever a DevOps seat exists. It is wrong for a deployment that **is** the operator of its own surface. The concrete case: a lane whose entire granted tool surface is a small guarded MCP over its own content, with no operator standing behind it. There, the boundary instructs the agent to hand off the only work it was deployed to do, to nobody. The deferral protects nothing and costs the whole function. This is not "remove safety." The boundary is a statement about which seat owns an action, and a deployment where that seat does not exist needs to say so at compose time rather than by forking the role. ## Shape worth considering A request-level node, so the roster stays the single description of the role and the deployment states its own situation: ```kdl compose { role "engineer" boundary-omit "modify-live-system" } ``` Constraints worth building in rather than bolting on later: * Omitting a boundary a role **owns** is probably an error rather than a silent drop, since an owner losing its own boundary is a different and larger claim than a deferrer losing one. * The omission belongs in the rendered identity card and in the compose review artifact. A bundle that quietly lacks a boundary is worse than one that never had it, because the review surface stops telling the truth. * An unknown boundary name should fail rather than no-op, matching how the rest of the request parser behaves. ## What was done instead, for the immediate need The lane names the precedence in its own local skill root, which lands after `<composed-identity>` in the sirens-echo prompt and is the pattern `composedVoicePolicy` already sets for voice. That works and it shipped, but it leaves the boundary prose in the bundle and relies on a later instruction to override it, which is strictly weaker than not composing it. Recorded in coilyco-gaming/sirens-echo#956. Not built here because the change reaches this repository, sirens-echo's bake, an image publish, and a rollout, roughly 19 hours before a live recording. The knob is the right fix on any other day.
Author
Member

Adding to this rather than opening a second issue. I filed #309 before finding this one and am closing it as a duplicate; the part of it worth keeping is below.

The ask got broader tonight

Kai, on the Dowel lane: "dowel really shouldn't be loading any boundaries. at all." Not modify-live-system specifically. The whole set.

That is consistent with what this issue found and slightly larger than the knob it proposes. Dowel runs engineer, which defers two:

role-engineer      boundary "modify-live-system" "seek-external-validation"

Both name an absent seat. seek-external-validation hands to exec, and there is no exec on that lane either, so the second one is dead for the same reason as the first. A boundary-omit list answers it, but it answers it by making the deployment restate per boundary something it already knows once.

An alternative shape: derive it from who is present

docs/ownership.md is explicit that this is a relationship:

Every boundary names the role holding the other side with a required owner. ... The owner is a relationship, not authority.

validateBoundaryOwners enforces both halves of that at load. So the defer side is routing - "hand this to ops". A deployment with no ops seat is not a deployment that wants an exception; it is one where the relationship has a single participant and the rule has nothing to say.

So instead of listing omissions, the request could state the roster and let composition evaluate the owner link it currently assumes:

compose {
    role "engineer"
    roster-seats "engineer"          // this deployment, entire
}

A defer-side boundary whose owner is not in roster-seats does not compose. Owned boundaries are untouched, which preserves the constraint this issue already flags - an owner losing its own boundary stays impossible rather than merely discouraged, because nothing in the mechanism can produce it.

It also degrades correctly at sizes neither of us has a case for yet: a two-seat deployment with engineer and ops keeps modify-live-system and drops seek-external-validation, with no per-deployment list to maintain.

The honest trade is that boundary-omit is more auditable. It says what was dropped, in one place, in the request. roster-seats says what exists and leaves the drop to be computed, which is exactly the kind of thing that is harder to review. This issue's requirement that the omission appear in the rendered identity card and the compose review artifact matters more under my shape than under yours, not less.

Two supporting facts

  • A boundary-free role is already legal. role-creator ships with no declared boundary node. So neither shape needs a new capability in the role model; both are compose-time questions about an existing one.
  • The cost just went up. coilyco-gaming/sirens-echo#1008 holds roster:core eager, so boundary bodies are now at full weight in every prompt rather than named on the card and fetched on demand. That sharpens this rather than causing it - the prose was always composed - but it is worth knowing when weighing the work.

Still not before the recording

Agreed with this issue's closing note, and the window is shorter now. Filed for after.

Adding to this rather than opening a second issue. I filed #309 before finding this one and am closing it as a duplicate; the part of it worth keeping is below. ## The ask got broader tonight Kai, on the Dowel lane: *"dowel really shouldn't be loading any boundaries. at all."* Not `modify-live-system` specifically. The whole set. That is consistent with what this issue found and slightly larger than the knob it proposes. Dowel runs `engineer`, which defers two: ``` role-engineer boundary "modify-live-system" "seek-external-validation" ``` Both name an absent seat. `seek-external-validation` hands to `exec`, and there is no `exec` on that lane either, so the second one is dead for the same reason as the first. A `boundary-omit` list answers it, but it answers it by making the deployment restate per boundary something it already knows once. ## An alternative shape: derive it from who is present `docs/ownership.md` is explicit that this is a relationship: > Every boundary names the role holding the other side with a required `owner`. ... **The owner is a relationship, not authority.** `validateBoundaryOwners` enforces both halves of that at load. So the defer side is routing - "hand this to `ops`". A deployment with no `ops` seat is not a deployment that wants an exception; it is one where the relationship has a single participant and the rule has nothing to say. So instead of listing omissions, the request could state the roster and let composition evaluate the `owner` link it currently assumes: ```kdl compose { role "engineer" roster-seats "engineer" // this deployment, entire } ``` A defer-side boundary whose owner is not in `roster-seats` does not compose. Owned boundaries are untouched, which preserves the constraint this issue already flags - an owner losing its own boundary stays impossible rather than merely discouraged, because nothing in the mechanism can produce it. It also degrades correctly at sizes neither of us has a case for yet: a two-seat deployment with `engineer` and `ops` keeps `modify-live-system` and drops `seek-external-validation`, with no per-deployment list to maintain. **The honest trade** is that `boundary-omit` is more auditable. It says what was dropped, in one place, in the request. `roster-seats` says what exists and leaves the drop to be computed, which is exactly the kind of thing that is harder to review. This issue's requirement that the omission appear in the rendered identity card and the compose review artifact matters more under my shape than under yours, not less. ## Two supporting facts * **A boundary-free role is already legal.** `role-creator` ships with no declared `boundary` node. So neither shape needs a new capability in the role model; both are compose-time questions about an existing one. * **The cost just went up.** coilyco-gaming/sirens-echo#1008 holds `roster:core` eager, so boundary bodies are now at full weight in every prompt rather than named on the card and fetched on demand. That sharpens this rather than causing it - the prose was always composed - but it is worth knowing when weighing the work. ## Still not before the recording Agreed with this issue's closing note, and the window is shorter now. Filed for after.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agent-compose#304
No description provided.