You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[coordination][security] Supervision must not escalate authority — a delegation lends a goal, never privileges #185
Design: docs/supervision.md. Companion to #183. Security-critical.
The risk
Under configurable multi-layer supervision (#183), if a subordinate executed with its supervisor's tools — or with the union of both — then supervision becomes a consent bypass: wire a low-trust agent beneath a high-trust one and it inherits reach it was never granted.
The platform has a real consent boundary today (write-consent gate, migration 0051; capabilities.tools allowlist gating via toolNamesFor). Multi-layer delegation is a new path that could route straight around it, and it would do so by configuration — no code change, no review.
The rule
A delegation carries the owner's authority plus a goal. It lends nothing else.
A subordinate executes with its own consent-gated tools and its own guardrails — never the supervisor's, never a union.
Both instances already belong to the same owner (as agent_connections enforces); that ownership is the authority, not the supervisor's tool grants.
A supervisor cannot widen a subordinate's reach by delegating to it, and cannot borrow a subordinate's reach by delegating through it.
This is the same boundary #142 draws for creators declaring runtime/workflow capabilities.
Scope
Explicit non-inheritance at the delegation boundary: the subordinate's tool resolution ignores the supervisor entirely.
Consent checks evaluate against the executing instance, not the originating one.
Tests as the contract: a supervisor with a write-consented connector delegating to a subordinate without it must not enable the write.
Audit: the trace records which instance's authority each tool call ran under.
Acceptance
A subordinate cannot perform an action it lacks consent for, regardless of its supervisor's grants.
A supervisor cannot reach a connector it lacks consent for by delegating to a subordinate that has it.
Trace attributes every tool call to the instance whose authority ran it.
Tests cover both escalation directions (down-lending and up-borrowing).
Shipped — lib/execution-authority.ts states the rule once, as a named function, so it has a single reviewable/testable home instead of a decision re-made at each future call site:
consentInstanceOf(authority)// === authority.instanceId, never onBehalfOf
RegistryToolCtx gains onBehalfOf, documented as audit-only. Both the write-consent gate and connector token-minting now resolve through consentInstanceOf() — the second one matters: instance-resource grants must not follow the asker either, or up-borrowing would work where the consent bypass does not.
Tested at the gate, not only in the pure module, per the acceptance criteria — the tests assert which instance id the consent lookup was actually performed against:
down-lending: a consented supervisor cannot enable a write for an unconsented subordinate
up-borrowing: a supervisor gains nothing from a consented subordinate
containment is not a blanket denial: the subordinate's OWN consent still works
Verified by mutation. Changing consentInstanceOf to onBehalfOf ?? instanceId — the exact one-line bypass this exists to prevent — fails 6 tests across both files. The invariant is pinned, not documented.
Encoded before the path that could violate it exists, which is why #156 still refuses instance targets at dispatch. Remaining: trace attribution of each tool call to its authority (describeAuthority is written and unit-tested but not yet emitted into the trace).
Trace attribution landed in ddd5e0c — the last open item here.
describeAuthority() was written and unit-tested but never reached the trace, so "whose authority ran this?" was answerable in principle and not in practice. Now emitted from runRegistryTool.
Scoped to delegated calls only. For ordinary work the authority is trivially the instance already on the event, so logging every tool call would be noise at real cost; when a supervisor asked, "who asked" and "whose permissions applied" are genuinely different questions and both belong in the record.
Everything on this ticket is now done: the invariant, both enforcement points (consent gate and token minting), the mutation-verified tests, and the audit trail.
Done in 957ed33 (invariant + consent gate + token minting, mutation-verified) and ddd5e0c (trace attribution for delegated calls). Confirmed in production: delegate_goal was refused until the owner granted supervision write consent.
Activity
feat(coordination): delegate to a TARGET, not a repoId (#156)
feat(coordination)!: supervision lends a goal, never privileges (#185)
serge-ivo commentedon Aug 3, 2026
Landed in
957ed33.Shipped —
lib/execution-authority.tsstates the rule once, as a named function, so it has a single reviewable/testable home instead of a decision re-made at each future call site:RegistryToolCtxgainsonBehalfOf, documented as audit-only. Both the write-consent gate and connector token-minting now resolve throughconsentInstanceOf()— the second one matters: instance-resource grants must not follow the asker either, or up-borrowing would work where the consent bypass does not.Tested at the gate, not only in the pure module, per the acceptance criteria — the tests assert which instance id the consent lookup was actually performed against:
Verified by mutation. Changing
consentInstanceOftoonBehalfOf ?? instanceId— the exact one-line bypass this exists to prevent — fails 6 tests across both files. The invariant is pinned, not documented.Encoded before the path that could violate it exists, which is why #156 still refuses
instancetargets at dispatch. Remaining: trace attribution of each tool call to its authority (describeAuthorityis written and unit-tested but not yet emitted into the trace).feat(coordination): platform-provided durable agent loop (#158)
feat(coordination): turn on agent-to-agent delegation; console rides …
feat(coder2): declare the Coder as data — two templates, no bespoke c…
feat(coordination): account spend backstop + delegated-tool attributi…
serge-ivo commentedon Aug 4, 2026
Trace attribution landed in
ddd5e0c— the last open item here.describeAuthority()was written and unit-tested but never reached the trace, so "whose authority ran this?" was answerable in principle and not in practice. Now emitted fromrunRegistryTool.Scoped to delegated calls only. For ordinary work the authority is trivially the instance already on the event, so logging every tool call would be noise at real cost; when a supervisor asked, "who asked" and "whose permissions applied" are genuinely different questions and both belong in the record.
Everything on this ticket is now done: the invariant, both enforcement points (consent gate and token minting), the mutation-verified tests, and the audit trail.
serge-ivo commentedon Aug 4, 2026
Done in
957ed33(invariant + consent gate + token minting, mutation-verified) andddd5e0c(trace attribution for delegated calls). Confirmed in production:delegate_goalwas refused until the owner granted supervision write consent.feat(capabilities): an agent declares what it may PASS, and the dispa…