Refuses a change to an Environment's network_allowlist while that
environment has a live sandbox (029 T018 ruling, FR-011).
Why a refusal and not a silent success
A sandbox is policed by rules its mechanism installs at launch, from the allowlist as it read then; nothing re-reads it afterwards. So an accepted narrowing would be persisted while every already-running sandbox kept the wider rules — a control that reports success and changes nothing. Widening is the mirror: the destination is recorded and unreachable until the sandbox is replaced.
⚠️ Re-policing the running sandbox is the better product answer and was
deliberately not built here. Rewriting a live ruleset is its own correctness
problem — a narrowing must never be observable as a half-applied ruleset — and
029 has not yet demonstrated that the rules it installs at launch hold at
all: the observation halves of its enforcement tasks need a Linux network
namespace and have never run. Building live re-policing on top of enforcement
whose boundary is unverified is the same inversion the spec exists to catch.
So the update either takes effect at the next launch, or it is refused in a sentence naming why.
The registry resource arrives as data
The host names its own registry binding at its own use AshSandbox.EnvironmentTemplate call site (012-FR-009); this library
compiles knowing no host module. That is the same rule
AshSandbox.Internal.CopyOwnerRefFromProject follows for the Project
binding, reached differently only because there is no relationship to read it
off: environment_ref is an opaque string on the registry row while
Environment.id is a uuid, so a has_one would emit a uuid = text
comparison the data layer refuses.
What "live" means
AshSandbox.RegistryTemplate.live_states/0, enumerated there rather than
written here as "not stopped" — see that function for why.