ADR 0011 — Require pinned ACES SDL validation for scenario packs

  • Status: Accepted

  • Date: 2026-07-11

  • Extends: ADR 0009

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.