polyc-capability 2026.7.1

Capability taxonomy, derivation functions, and the pure gate decision engine for polychrome.
Documentation

Capability taxonomy, derivation functions, and the pure gate decision engine (#587, #591).

One tool call requires a set of [Capability]s (derived from its spec's MCP-style annotations plus its registry provenance) and is granted a set (derived from the agent's policy plus the conversation's provenance/taint state at that moment). [decide] compares them and returns the single [GateOutcome] for the call — the one decision path that replaces the previous OR of an argument-aware policy check, a sandbox-denial escalation, and a runtime "untrusted content + egress" override.

The containment invariants live here as pure logic, testable exhaustively:

  • Untrusted content in context removes [Capability::ArbitraryEgress] and [Capability::MutateExternal] from the granted set — a message body or an issue title carries attacker-steered bytes out as surely as a fetch does.
  • An operator-declared taint-resilient set survives that subtraction, and only when the policy declares it ([GrantPolicy::taint_resilient]).
  • A tool whose spec cannot be classified requires the full privileged set ([CapabilitySet::all]) — fail closed.
  • The model is monotonic: under a fixed policy, adding taint never adds a capability.

Everything here is a pure function over its inputs. No gate wiring, no IO, no clock: the executor surface (#592) derives the inputs and the agent's per-call gate (#593) is a thin adapter over [decide].