# ADR 0011 — Require pinned ACES SDL validation for scenario packs - Status: Accepted - Date: 2026-07-11 - Extends: [ADR 0009](0009-scenario-packs-subordinate-to-aces.md) ## Context Every scenario pack requires an `sdl/` start state, but `aces-pack-validate` does not currently parse those files. Pack-local `sdl/validate_*.py` scripts are optional authoring checks and cannot establish ACES conformance. The pack-owned `flags/placement.yaml` contract also says that each `host` resolves to an SDL start-state node, but the validator does not enforce that join. ACES publishes the `aces-sdl` package as the authority for SDL structural and semantic validation. Its public `parse_sdl_file()` API returns the validated `Scenario`, including the resolved `nodes` mapping and non-fatal advisories. The current published line is `0.20.0`, declares Python `>=3.11`, and the ACES SDL contracts remain draft. Making ACES optional would leave the package's required content unchecked in ordinary installations and would not satisfy the fail-closed pack contract. ## Decision `aces-sdl` is a mandatory, exactly pinned runtime dependency while the upstream SDL contracts are draft. The initial pin was `aces-sdl==0.19.1`; issue 95 advances it to `aces-sdl==0.20.0` after compatibility tests against this package's validator and template. The package's Python requirement follows the pinned ACES requirement, currently Python `>=3.11`. A compatible version range may replace the exact pin only after ACES publishes a stability and compatibility guarantee that justifies it. `aces-pack-validate` remains the single catalog-validation entry point. It parses every direct `sdl/*.sdl.yaml` document through ACES `parse_sdl_file()` with full semantic validation. A pack with no such document, or with any document ACES rejects, fails. The local JSON-Schema subset validator continues to serve only pack-owned schemas; it must not validate SDL, consume a copied SDL schema, or become a second SDL parser. Pack-local validator scripts remain additive checks, not ACES-conformance authorities. The returned ACES `Scenario` objects are retained for pack-to-SDL checks rather than reloading SDL as generic YAML. A `flags/placement.yaml.flags[].host` reference resolves when its exact identifier is present in `Scenario.nodes` in at least one validated SDL document in the pack. This union rule supports a pack's full and reduced start-state variants without inventing a local notion of which variant is canonical. A future profile-specific placement contract must make its target SDL variant explicit; it must not change this rule by filename guessing. Only structured references declared by the canonical pack contract are checked. The current such reference is `flags[].host`. The validator must not infer SDL references from prose, from arbitrary keys ending in `host` or `entity`, or from downstream catalog vocabulary. A future pack-owned structured reference is added explicitly and resolves against the already parsed upstream `Scenario` section that owns its target concept. ACES owns SDL error classification. The integration consumes the public `SDLError` hierarchy and preserves ACES errors as fatal and advisories as non-fatal; it does not create a parallel exception hierarchy or promote an ACES advisory to failure. Diagnostics join the existing `failures` aggregation and CLI exit-status convention. They identify the pack and file and remain bounded; they must not dump a parsed scenario, raw SDL, flag values, credentials, or other source payloads into CI output. ## Consequences - This deliberately expands the runtime dependency and SBOM surface. Existing Dependabot updates, CI `pip-audit`, and release CycloneDX generation remain the supply-chain controls for ACES and its transitive dependencies. - SDL root files must pass the same real-path containment discipline as other pack reads. ACES itself remains responsible for local-import containment, lock/trust validation, digest/signature checks, network bounds, and OCI bundle extraction safety; this repository does not implement a second module resolver. - ACES OCI imports can perform allowlisted network reads and populate `sdl/.aces/module-cache`. No document content or credentials are passed in process arguments, and this package adds no persistence of its own. Catalog CI must treat ACES trust-policy changes as security-sensitive input and run untrusted contributions without repository secrets. - The SDL path adds one stdout section to the existing CLI, with bounded per-file diagnostics, a final aggregate failure count, and exit status `1` on any failure. There is no database, service, controller, or pack-side state in this validation path beyond the ACES-owned import cache described above. - ACES API drift, Python-floor changes, or materially different import side effects are reviewed when advancing the exact dependency pin rather than being accepted implicitly by a resolver.