refactor(P57): contract surface redesign + rename + .yml repo-wide

Contract surface redesign:
- New top-level fields: id (3-6 char acronym → stack.name), name (full → stack.title),
  infrastructure (map keyed by module name, replaces module:)
- Drop uses: field (dead reference; version pin lives in CI workflow uses: line)
- Drop top-level module/inputs (now nested under infrastructure map)
- Per-module optional version (defaults to latest published from registry)
- Multi-module contracts: one file deploys N modules in one pipeline run,
  resource IDs namespaced with module name to avoid collisions
- stack.schema.json: add optional title field for display name

Rename:
- pipelines/deploy.yaml → pipelines/contract.yml (declarative spec, not a pipeline)
- pipelines/ci.yaml → pipelines/ci.yml
- All 44 .yaml files → .yml repo-wide (contracts, module examples, kyverno policies)
- .acdl/contract.yaml → .acdl/contract.yml

Resolver (core/contract_resolver.py):
- Rewrite resolve() to loop infrastructure map, default version to latest,
  merge module fragments into one stack with namespaced resource IDs
- _latest_version() picks highest non-deprecated from registry
- _namespace_resources() prefixes IDs + rewrites ref: expressions for multi-module
- Single-module path: unprefixed IDs (backward compatible)

Verification:
- 494 tests pass (0 contract-shape failures)
- Local E2E passes (contract → resolver → adapter → local ECS HTTP 200 → outbox)

---ci---
project: acdl
phase: 57
milestone: v1.10.2
status: execute
---/ci---
This commit is contained in:
Jon Chery
2026-07-27 21:37:40 +00:00
parent 7f36df5610
commit 031887ec56
127 changed files with 1597 additions and 1100 deletions
+60 -42
View File
@@ -7,10 +7,10 @@ step applies to `microservice` and any future module.
## The model
Consumers have their own repos and consume ACDL by referencing `uses:` the
central pipeline definitions. The consumer declares a **contract** (which
module, which environment, which inputs); the ACDL platform owns the
pipelines, modules, engine adapter, and evidence stream.
Consumers have their own repos and consume ACDL by writing a contract
that declares infrastructure (one or more modules), an environment, and inputs. The consumer declares a **contract** (which infrastructure, which
environment, which inputs); the ACDL platform owns the pipelines, modules,
engine adapter, and evidence stream.
You do not write infrastructure modules, workflow YAML, or adapter code.
You write a contract YAML file and the platform does the rest. Your
@@ -27,13 +27,13 @@ flowchart LR
## Versioning the `uses:` reference
The central deployment pipeline is **always versioned with floating MAJOR
and MINOR tags** (e.g. `acdl/pipelines/deploy.yaml@v1.9`). Version
and MINOR tags** (e.g. `acdl/pipelines/contract.yml@v1.9`). Version
constraints cannot be expressed inside the contract, so the tag in
`uses:` is the only immutability lever a consumer has. See
[Versioning](pipeline/versioning) for the full rationale.
**Unversioned references are discouraged.** Do not use `@main` or a bare
`acdl/pipelines/deploy.yaml`.
`acdl/pipelines/contract.yml`.
## Prerequisites
@@ -53,7 +53,7 @@ platform-managed. See [Environments](environments/).
## Step 1 — Create a consumer repo
Create a repository for your application. The top level holds your app
code; your contract lives at `.acdl/contract.yaml`. Example for a static
code; your contract lives at `.acdl/contract.yml`. Example for a static
site:
```
@@ -83,53 +83,64 @@ my-microservice/
```
Your app code lives at the top level. Your contract lives at
`.acdl/contract.yaml` regardless of the module you deploy. Your CI
`.acdl/contract.yml` regardless of the module you deploy. Your CI
definition lives at `.github/workflows/deploy.yml`.
## Step 2 — Reference the central pipeline
In your contract YAML, declare `uses:` pointing at the central ACDL
deployment pipeline with a **versioned tag** (floating MAJOR + MINOR):
In your CI workflow (`.github/workflows/deploy.yml`), reference the central
ACDL deployment workflow with a **versioned tag** (floating MAJOR + MINOR):
```yaml
uses: acdl/pipelines/deploy.yaml@v1.9
jobs:
deploy:
uses: acdl/.github/workflows/deploy.yml@v1.9
with:
contract: .acdl/contract.yml
environment: dev
```
This tells the platform to run the standard deployment pipeline:
validate-contract → resolve-stack → security checks → infrastructure plan →
policy checks → confidence → evidence event → apply.
The versioned tag is the only immutability lever — the consumer's CI workflow
pins the platform version. The contract itself no longer carries a `uses:`
field; the version pin lives in the CI workflow reference.
## Step 3 — Define the contract
Write `.acdl/contract.yaml`. The `static-assets` example:
Write `.acdl/contract.yml`. The `static-assets` example:
```yaml
uses: acdl/pipelines/deploy.yaml@v1.9
module: static-assets
environment: dev
inputs:
bucket_name: my-static-site-assets
region: us-east-1
id: assets
infrastructure:
static-assets:
inputs:
bucket_name: my-static-site-assets
region: us-east-1
version: 1.0.0
name: static-assets
```
A `microservice` example:
```yaml
uses: acdl/pipelines/deploy.yaml@v1.9
module: microservice
environment: dev
inputs:
image: my-registry/my-microservice:latest
port: 8080
env:
LOG_LEVEL: info
id: msvc
infrastructure:
microservice:
inputs:
env:
LOG_LEVEL: info
image: my-registry/my-microservice:latest
port: 8080
version: 1.0.0
name: microservice
```
### Contract fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/deploy.yaml@v1.9`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/contract.yml@v1.9`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). |
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](environments/). |
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
@@ -168,7 +179,7 @@ jobs:
deploy:
uses: acdl/.github/workflows/deploy.yml@v1.9
with:
contract: .acdl/contract.yaml
contract: .acdl/contract.yml
```
That is the entire consumer-side workflow. When you push to `main`:
@@ -181,7 +192,7 @@ That is the entire consumer-side workflow. When you push to `main`:
never clone the platform repo yourself.
4. The runner installs the runtime dependencies the platform requires.
5. The runner invokes `scripts/run_platform.sh` against your
`.acdl/contract.yaml`.
`.acdl/contract.yml`.
You see the streamed output (infrastructure plan, policy-check results,
confidence signal) in your run logs. The `--check-only` and `--plan-only`
@@ -202,7 +213,7 @@ static key in `.env.secrets` (gitignored) is rotated **out of band by you**
locally-held copies.
```bash
bash scripts/run_platform.sh --check-only path/to/your/.acdl/contract.yaml
bash scripts/run_platform.sh --check-only path/to/your/.acdl/contract.yml
```
## Step 5 — What the pipeline does
@@ -278,11 +289,16 @@ push your container image to the ECR repo the platform created.
## Step 8 — Promote to qa / prod
Change `environment` in your contract (keeping the same versioned `uses:`):
Change `environment` in your contract (the infrastructure stays the same):
```yaml
uses: acdl/pipelines/deploy.yaml@v1.9
id: assets
name: static-assets
environment: qa # QA attestation + confidence >= 0.75
infrastructure:
static-assets:
version: "1.0.0"
inputs: { ... }
```
Higher environments require human attestation (a platform-runner deployment
@@ -305,7 +321,7 @@ per-module extension points. Common examples:
| Resource | Path | Description |
|----------|------|-------------|
| Central deployment pipeline contract | `pipelines/deploy.yaml` | The pipeline stages your contract references. |
| Central deployment pipeline contract | `pipelines/contract.yml` | The pipeline stages your contract references. |
| Reusable deploy workflow | `.github/workflows/deploy.yml` | The workflow your repo invokes via `uses:`. |
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
@@ -339,7 +355,7 @@ destruction:
```yaml
uses: acdl/.github/workflows/deploy.yml@v1.8
with:
contract: .acdl/contract.yaml
contract: .acdl/contract.yml
mode: decommission
changeRequestId: "CHG0678912"
```
@@ -393,13 +409,15 @@ contract per environment (e.g. `.acdl/static-assets.dev.yaml`,
name and uses interpolation so env-specific values differ automatically:
```yaml
# .acdl/static-assets.qa.yaml
uses: acdl/pipelines/deploy.yaml@v1.9
module: static-assets
environment: qa
inputs:
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
region: ${env.region}
id: assets
infrastructure:
static-assets:
inputs:
bucket_name: acdl-${env.environment}-${contract.module}-${env.account_id}-${env.region}
region: ${env.region}
version: 1.0.0
name: static-assets
```
**Shape 2 — single contract + `environment` workflow input:** the
@@ -421,7 +439,7 @@ jobs:
uses: acdl/.github/workflows/deploy.yml@v1.9
with:
environment: qa
contract: .acdl/contract.yaml
contract: .acdl/contract.yml
```
### One job per environment
+61 -28
View File
@@ -1,44 +1,57 @@
# Contracts
A consumer declares intent in a **contract** — a small YAML file that
references the central deploy pipeline, names a module, selects an
environment, and supplies module-specific inputs. The platform validates,
resolves, and deploys it.
names infrastructure (one or more modules), selects an environment, and
supplies module-specific inputs. The platform validates, resolves, and
deploys it.
## The contract file
A consumer repo keeps its contract at `.acdl/contract.yaml`. A minimal
A consumer repo keeps its contract at `.acdl/contract.yml`. A minimal
example (the `static-assets` module):
```yaml
uses: acdl/pipelines/deploy.yaml@v1.6
module: static-assets
id: assets
name: static-assets
environment: dev
inputs:
bucket_name: my-static-site-assets
region: us-east-1
infrastructure:
static-assets:
version: "1.0.0"
inputs:
bucket_name: my-static-site-assets
region: us-east-1
```
A `microservice` example:
```yaml
uses: acdl/pipelines/deploy.yaml@v1.6
module: microservice
id: msvc
name: microservice
environment: dev
inputs:
image: my-registry/my-microservice:latest
port: 8080
env:
LOG_LEVEL: info
infrastructure:
microservice:
version: "1.0.0"
inputs:
image: my-registry/my-microservice:latest
port: 8080
env:
LOG_LEVEL: info
```
## Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `uses` | string | yes | Reference to the central deploy pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/deploy.yaml@v1.6`). Bare or `@main` references are discouraged. See [Versioning](../pipeline/versioning). |
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](../modules/). |
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](../environments/). |
| `id` | string | yes | Short operational acronym (3-6 chars, `^[a-z][a-z0-9-]{2,5}$`). Becomes the stack name used for the Terraform state key, ECS service name, outbox event identity, and resource naming prefix. |
| `name` | string | yes | Full human-readable stack name (min 3 chars). Becomes the stack title used for display in PR comments, evidence records, and leadership dashboards. |
| `environment` | string | yes | The platform-managed environment to deploy to (`dev`/`qa`/`prod`/`dr`). See [Environments](../environments/). |
| `infrastructure` | object | yes | Map of modules to deploy, keyed by module registry name. Each entry has an optional `version` (defaults to latest published) and required `inputs`. One entry = single-module deploy; N entries = multi-module manifest deployed in one pipeline run. |
### Infrastructure entry fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `version` | string | no | Module version pin (semver `X.Y.Z`). Omitted = latest non-deprecated version from the registry. |
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
## Validation
@@ -52,19 +65,39 @@ validate-contract stage with a clear error.
Two reference examples exist in `contracts/`:
- [`contracts/static-assets.yaml`](https://github.com/acdl/acdl/blob/main/contracts/static-assets.yaml)
— the `static-assets` module (uses `@v1.6`).
- [`contracts/microservice.yaml`](https://github.com/acdl/acdl/blob/main/contracts/microservice.yaml)
— the `microservice` module (uses `@v1.6`).
- [`contracts/static-assets.yml`](https://github.com/acdl/acdl/blob/main/contracts/static-assets.yml)
— the `static-assets` module.
- [`contracts/microservice.yml`](https://github.com/acdl/acdl/blob/main/contracts/microservice.yml)
— the `microservice` module.
Additionally, every module has a `modules/<name>/examples/` directory with
validated example contracts (`simple.yaml` + `complex.yaml` + variation
validated example contracts (`simple.yml` + `complex.yml` + variation
files). See the [module catalog](../modules/) for the full list.
## Multiple modules per contract
A contract may declare multiple modules under the `infrastructure` map.
All modules deploy to the same `environment` in one pipeline run. Resource
IDs are namespaced with the module name to avoid collisions (e.g.
`microservice-vpc`, `static-assets-s3`).
```yaml
id: app
name: pricing-service-api
environment: dev
infrastructure:
microservice:
version: "1.0.0"
inputs: { ... }
static-assets:
version: "1.0.0"
inputs: { ... }
```
## Multiple contracts
A consumer repo may contain more than one contract (e.g. one per service or
one per environment). Each contract is a separate deployment; each is
referenced by a CI definition in `.github/workflows/` that invokes the
central reusable workflow with the contract path. See the
A consumer repo may also contain more than one contract file (e.g. one per
environment). Each contract is a separate deployment; each is referenced by a
CI definition in `.github/workflows/` that invokes the central reusable
workflow with the contract path. See the
[Consumer Guide](../consumer-guide/) for the multi-contract pattern.
+1 -1
View File
@@ -16,7 +16,7 @@ There are two kinds of repository in the ACDL model:
and the reusable workflow files. Platform engineers work here. A consumer
never clones it.
- **Consumer repo (yours).** A consumer repo contains only its application
code, one or more contracts (`.acdl/contract.yaml`), and one or more CI
code, one or more contracts (`.acdl/contract.yml`), and one or more CI
definitions (a thin `.github/workflows/deploy.yml` that `uses:` the central
reusable workflow, pointing at the appropriate environment + contract).
The consumer does not write infrastructure modules, workflow YAML, or
+2 -2
View File
@@ -6,7 +6,7 @@ are the single source of truth for the workflow files.
## CI pipeline
The CI pipeline runs on every push and pull request to `main`. It is defined
by [`pipelines/ci.yaml`](https://github.com/acdl/acdl/blob/main/pipelines/ci.yaml),
by [`pipelines/ci.yml`](https://github.com/acdl/acdl/blob/main/pipelines/ci.yml),
validated against
[`schemas/pipeline.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/pipeline.schema.json).
Both platform-runner workflow files implement the same contract and are
@@ -31,7 +31,7 @@ bash scripts/run_ci.sh --quiet # suppress per-stage banners
## Deployment pipeline
The deployment pipeline runs when a consumer submits a contract. It is
defined by [`pipelines/deploy.yaml`](https://github.com/acdl/acdl/blob/main/pipelines/deploy.yaml),
defined by [`pipelines/contract.yml`](https://github.com/acdl/acdl/blob/main/pipelines/contract.yml),
validated against
[`schemas/deploy-pipeline.schema.json`](https://github.com/acdl/acdl/blob/main/schemas/deploy-pipeline.schema.json).
It is exposed to consumer repos as a **reusable workflow**:
+12 -7
View File
@@ -18,21 +18,26 @@ primitives by `name@semver`; the resolver picks the highest compatible.
Module versions are tracked in
[`registry.json`](https://github.com/acdl/acdl/blob/main/modules/registry.json).
## Deploy-pipeline versioning (the `uses:` tag)
## Deploy-pipeline versioning (the CI workflow `uses:` tag)
The central deploy pipeline is referenced by a **floating MAJOR + MINOR
tag** in a consumer's contract and CI definition:
tag** in a consumer's CI workflow definition:
```yaml
uses: acdl/pipelines/deploy.yaml@v1.6
jobs:
deploy:
uses: acdl/.github/workflows/deploy.yml@v1.6
with:
contract: .acdl/contract.yml
```
Version constraints cannot be expressed inside the contract, so the tag in
`uses:` is the only immutability lever a consumer has.
The version pin lives in the CI workflow reference (not in the contract
itself — the contract no longer carries a `uses:` field). The CI workflow
`uses:` tag is the only immutability lever a consumer has.
**Unversioned references are discouraged.** Do not use `@main` or a bare
`acdl/pipelines/deploy.yaml``main` is constantly updated and can cause
unexpected failures. Pinning to a MAJOR+MINOR tag means:
`acdl/.github/workflows/deploy.yml``main` is constantly updated and can
cause unexpected failures. Pinning to a MAJOR+MINOR tag means:
- **Immutability** — the pipeline behavior you tested is the behavior you
get. Patch fixes flow within the tag; breaking changes land under the