f68f85c9fd
---ci---
project: acdl
phase: 20
milestone: v1.5
status: review
verdict: READY TO SHIP
p0: 1 (fixed — contract path resolution in deploy workflow)
p1: 6 (flagged post-hoc)
---/ci---
Multi-persona review of v1.5 phase 20 (docs + reusable deploy workflow).
P0 (blocking) — AUTO-FIXED:
- C1: scripts/run_platform.sh contract path resolution broken in deploy
workflow. The reusable workflow invokes run_platform.sh from the consumer
workspace root with a relative contract path (.acdl/contract.yaml), but
run_platform.sh does `cd "$ROOT"` (platform repo) early, so the relative
path resolved against the platform repo and the pipeline could never run.
Fix (commit 75c2274): capture CALLER_CWD before cd "$ROOT"; resolve
caller-supplied relative paths against CALLER_CWD; default no-arg contract
stays relative to ROOT (preserves platform-local CI). Reproduced pre-fix;
verified post-fix.
P1 (important) — FLAGGED FOR POST-HOC REVIEW (do not block ship):
- C2: ref: v1.4 in the deploy workflow platform checkout — no v1.4 tag exists
(only v1.4.0 / v1.4.1). Operator must create a floating v1.4 tag or change
the ref to v1.4.1.
- C3: modules/l2/{static-asset,microservice}/README.md still use @v1 in their
Usage examples; missed by the v1.4 bump.
- S1: static-key override is not wired. ACDL_AWS_* env vars on the OIDC step
are not read by aws-actions/configure-aws-credentials@v4 (it reads AWS_*
or its own access-key/secret-key inputs). The README/CONSUMER_GUIDE claim
a working override that doesn't function as written. Needs a conditional
step or renamed env vars + input wiring.
- S2: README overstates ABAC repo:org/repo:ref:... scoping. The workflow
constructs a numeric role name (github.repository_id); the actual claim
enforcement lives in the IAM trust policy, not in this workflow.
- T1: no deploy-workflow triggers conformance test (CI workflow has one;
deploy doesn't). Minor — reusable workflows use workflow_call, not push
triggers, but the contract's triggers field is then unenforced.
- A1: terraform/spike/terraform.tf uploaded as artifact leaks the AWS account
ID via the state-backend bucket name. Recommend excluding terraform.tf or
gating artifact upload to non-public repos.
P2 (nits) — listed for awareness: floating-tag terminology imprecision (M1),
header comment "Gitea Actions" in the GitHub copy (M2, intentional byte-
identical), pip install split (P1-perf), comment drift in pipelines/deploy.yaml
header (C4), module README internal inconsistency (C5).
Verdict: READY TO SHIP. The one P0 is fixed. The 6 P1s are post-hoc items —
the deploy workflow is a scaffold whose first real consumer run requires
operator setup (tag, IAM role, secrets) that gates go-live. The P1s should
be addressed before any consumer invokes uses: acdl/.gitea/workflows/
deploy.yml@v1.4 in earnest.
Tests: 154 pass (19 new). run_ci.sh green.
118 lines
5.4 KiB
JSON
118 lines
5.4 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://acdl.cloudinit.dev/schemas/stack.schema.json",
|
|
"title": "ACDL Target Stack",
|
|
"description": "Substrate-neutral description of a target stack: resources with typed inputs/outputs/NFRs, relationships (single parent per child), composition tree (max depth 5), and policy hooks. The L1 registry, L2 composition tree, contract YML, and PolicyCheckResult schema are all defined against this stack schema. Substrate adapters (the Terraform adapter in v1) are the only substrate-specific code.",
|
|
"$comment": "v1 ships one adapter (Terraform). The stack is nearly isomorphic to Terraform in v1 (ARCHITECTURE.md §12.1); the adapter compiles resource.module -> module block, resource.inputs -> variable + arg, resource.outputs -> output, relationship.kind=uses_output -> interpolation, relationship.kind=parent -> composition ordering hint. As more adapters appear (v2+), the stack gains expressiveness; the L1 content + contract YML + composition tree do not change. The schema body is substrate-agnostic: no Terraform block keywords (variable/output/resource as blocks) and no aws_ provider prefixes in the schema keywords; type values are stack types (aws:s3:bucket), not Terraform resource types (aws_s3_bucket).",
|
|
"type": "object",
|
|
"required": ["version", "stack", "resources"],
|
|
"properties": {
|
|
"version": {
|
|
"type": "string",
|
|
"description": "Stack schema version (semver).",
|
|
"pattern": "^\\d+\\.\\d+\\.\\d+$"
|
|
},
|
|
"stack": {
|
|
"type": "object",
|
|
"description": "The L1/L2 stack identity this instance represents.",
|
|
"required": ["name", "kind", "depth"],
|
|
"properties": {
|
|
"name": {
|
|
"type": "string",
|
|
"pattern": "^[a-z][a-z0-9-]*$",
|
|
"description": "Stack name matching the module folder name."
|
|
},
|
|
"kind": {
|
|
"type": "string",
|
|
"enum": ["l1", "l2"],
|
|
"description": "l1 = primitive; l2 = composition."
|
|
},
|
|
"depth": {
|
|
"type": "integer",
|
|
"minimum": 1,
|
|
"maximum": 5,
|
|
"description": "Composition depth (ARCHITECTURE.md §3: max depth 5). L2->L1 is depth 1."
|
|
}
|
|
}
|
|
},
|
|
"resources": {
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": {"$ref": "#/$defs/resource"}
|
|
},
|
|
"relationships": {
|
|
"type": "array",
|
|
"description": "Optional in v1; present when the adapter needs explicit ordering/output wiring hints beyond parent composition.",
|
|
"items": {"$ref": "#/$defs/relationship"}
|
|
}
|
|
},
|
|
"$defs": {
|
|
"resource": {
|
|
"type": "object",
|
|
"required": ["id", "type", "module", "inputs"],
|
|
"properties": {
|
|
"id": {
|
|
"type": "string",
|
|
"pattern": "^[a-z][a-z0-9-]*$",
|
|
"description": "Local stack resource id (unique within the stack)."
|
|
},
|
|
"type": {
|
|
"type": "string",
|
|
"description": "Stack-typed resource identifier (substrate-agnostic), e.g. 'aws:s3:bucket'. NOT a Terraform resource type ('aws_s3_bucket'); the adapter translates stack type -> substrate type."
|
|
},
|
|
"module": {
|
|
"type": "string",
|
|
"pattern": "^[a-z][a-z0-9-]*@\\d+\\.\\d+\\.\\d+$",
|
|
"description": "Module registry reference: name@semver (W3.D). MAJOR bumps require a new registry entry (immutable publication); old entry enters a 12-month deprecation window."
|
|
},
|
|
"parent": {
|
|
"type": "string",
|
|
"description": "Parent resource id. Absent for the root. Single parent per child (ARCHITECTURE.md §12.1)."
|
|
},
|
|
"inputs": {
|
|
"type": "object",
|
|
"description": "Input values keyed by the module's declared inputs. Free-form in v1 (validated at contract->stack resolution against the module registry); typed per-module in v1.2.",
|
|
"additionalProperties": {"type": ["string", "number", "boolean"]}
|
|
},
|
|
"outputs": {
|
|
"type": "object",
|
|
"description": "Typed output contract. The adapter translates this to a substrate output block (e.g. Terraform output).",
|
|
"additionalProperties": {"$ref": "#/$defs/outputSpec"}
|
|
},
|
|
"nfrs": {
|
|
"type": "object",
|
|
"description": "Declared non-functional requirements (latency, throughput, error rate). Opaque to the adapter; consumed by the confidence signal's NFR input.",
|
|
"additionalProperties": true
|
|
}
|
|
}
|
|
},
|
|
"outputSpec": {
|
|
"type": "object",
|
|
"required": ["type"],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"description": "Stack-typed output type: a primitive ('string', 'arn') or a reference ('ref:<resourceId>.<outputName>')."
|
|
},
|
|
"description": {"type": "string"}
|
|
}
|
|
},
|
|
"relationship": {
|
|
"type": "object",
|
|
"required": ["from", "to", "kind"],
|
|
"properties": {
|
|
"from": {"type": "string", "description": "Source resource id."},
|
|
"to": {"type": "string", "description": "Target resource id."},
|
|
"kind": {
|
|
"type": "string",
|
|
"enum": ["parent", "depends_on", "uses_output"],
|
|
"description": "v1 uses 'parent' (composition ordering) + 'uses_output' (interpolation). 'depends_on' is reserved for v2 explicit-dependency cases."
|
|
},
|
|
"shared_keyword": {
|
|
"type": "string",
|
|
"description": "Reserved for v2 multi-relationship dependencies. Unused in v1."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |