0.1.2 — 2026-09-11
0.1.1's fix was incomplete and still crashed. It wrapped use Boundary in a plain
if Code.ensure_loaded?(Boundary) do use Boundary, ... end, which does not work: use inside a
plain if still expands at compile time regardless of the branch (confirmed against Elixir 1.20.2
with an isolated elixirc repro naming a nonexistent module). Any :dev/:test consumer that does
not itself depend on :boundary still hit module Boundary is not loaded and could not be found.
use Boundary is now wrapped in Code.eval_quoted/3, which genuinely defers macro expansion until
the if branch runs.
0.1.1 — 2026-09-10
Fixed: every consumer's :dev/:test build failed to compile this package.
compilers/1 (mix.exs) and AshSandbox's use Boundary declaration both ran unconditionally
whenever the GLOBAL Mix.env() was :dev or :test — which is every umbrella app's env, not just
this package's own. :boundary is only: [:dev, :test] on this package's own deps, scoped there so
a consumer never has to resolve it; a first real Hex consumer hit module Boundary is not loaded and could not be found at lib/ash_sandbox.ex:130 the moment it built in :dev, because nothing made
either use of Boundary conditional on the module actually being present.
Both are now gated on Code.ensure_loaded?(Boundary). use Boundary additionally moved into a
Code.eval_quoted/3 block — use inside a plain if still expands unconditionally at compile time
(confirmed against Elixir 1.20.2), so wrapping it in if alone does not defer it.
0.1.0 — 2026-09-10
Extracted from the Axonn umbrella (apps/ash_sandbox) into a standalone package, carrying its
full commit history via git subtree split. No behavior changed by the extraction itself.
Public interface at extraction: AshSandbox.RegistryTemplate, AshSandbox.ProjectTemplate,
AshSandbox.EnvironmentTemplate, AshSandbox.TemplateTemplate,
AshSandbox.OperationRecordTemplate, AshSandbox.SandboxCredentialTemplate, and
AshSandbox.EncryptedSecret (public by consequence). See priv/boundary.md for the full contract.
AshSandbox.RunPolicy, the AshSandbox.Resource DSL extension, and AshSandbox.Plug were
withdrawn from the umbrella before extraction (R-12) and do not exist in this package.
Fixed during extraction
Update actions raised MustBeAtomic on every non-PostgreSQL data layer. MEASURED 2026-09-10:
each of the registry's seven mark_* transitions and the project's :rename failed on
Ash.DataLayer.Ets, while passing on PostgreSQL. An identity carrying pre_check_with makes Ash
add an eager_validate_identities before_action hook, and any before_action hook makes an
update non-atomic. This falsified 012-FR-009, the data-layer independence these templates exist
to provide — and no test had caught it, because the ETS host app in test/support/host_app.ex had
only ever been read from and created through, never updated through.
AshSandbox.Internal.DataLayerSection.require_atomic/1 now emits require_atomic?(false) for
every data layer except AshPostgres.DataLayer, which keeps the atomicity expr(now()) buys where
it is available. :record_request is unchanged: it already carried an unconditional
require_atomic? false for its own reason.
Documentation
The package now ships a Diátaxis documentation set under docs/, published as ExDoc extras: a
getting-started tutorial executed as a test (test/getting_started_doc_test.exs), two how-to
guides, an explanation of why the host owns the resource module, and reference pages decoding the
requirement IDs and the citations to the originating umbrella.
test/public_interface_test.exs asserts that the four statements of the public interface —
use Boundary's exports:, priv/boundary.md, the AshSandbox moduledoc, and the README — name
the same seven modules, and that the shipped boundary document resolves through
Application.app_dir/2. The moduledoc had drifted to naming one module of the seven.
mix precommit now runs mix docs --warnings-as-errors.