dca35c78ec
---ci---
phase: 22
title: rename-and-production-static-assets-stack
status: complete
verification:
- scripts/run_ci.sh: PASS (CI PIPELINE OK)
- python3 -m pytest tests/ -v: 175 passed
- scripts/run_platform.sh --check-only: PASS (PLATFORM CHECK OK)
- grep -R "static-asset[^s]" . (excl .git/): 0 hits
- grep -R "static-asset$" . (excl .git/): 0 hits
- floating git tags v1.6 + v1 point at v1.6.0 (a90a756)
changed_files:
- Task 1 (rename): contracts/static-asset.yaml→static-assets.yaml (git mv); modules/l2/static-asset→static-assets (git mv); sed replaceAll static-asset→static-assets in 22 files (README, docs, scripts/run_platform.sh, pipelines/deploy.yaml, modules/registry.json, tests/*, .ciagent/* historical narrative)
- Task 2 (cloudfront primitive): modules/l1/cloudfront/interface.json + README.md
- Task 3 (waf primitive): modules/l1/waf/interface.json + README.md
- Task 4 (registry): modules/registry.json (+cloudfront, +waf, static-assets renamed)
- Task 5 (augment static-assets): modules/l2/static-assets/composition.json (s3+cloudfront+waf, depth 1); modules/l1/s3/interface.json +instance.json (+bucket_regional_domain_name output); modules/l2/static-assets/README.md (production stack docs)
- Task 6 (adapter): adapters/terraform/adapter.py (+TYPE_MAP/INPUT_MAP/OUTPUT_MAP for cloudfront distribution+OAC+wafv2 webacl; special handling in _emit_resource for OAC defaults, distribution origin/cache_behavior/restrictions/viewer_certificate/web_acl_id, waf scope/default_action/visibility_config/managed rules)
- Task 7 (contract schema): no change needed (generic inputs object; new module names match ^[a-z][a-z0-9-]*$)
- Task 8 (@v1.6 bump): contracts/static-assets.yaml, .gitea/.github/workflows/deploy.yml (ref: v1.6 + header comments), docs/consumer-guide.md, docs/contracts/index.md, docs/pipeline/versioning.md, docs/pipeline/index.md, docs/architecture.md, README.md, modules/l2/microservice/README.md, tests/test_environment_check.py, tests/test_pipeline_contract.py
- Task 9 (floating tags): git tag -f v1.6 v1.6.0; git tag -f v1 v1.6.0
- Task 10 (tests): tests/test_adapter.py (registry 11 entries/9 L1/2 L2; cloudfront+waf type map tests; TestS3Output bucket_regional_domain_name; TestStaticAssetsStack 4 tests); tests/test_contract_resolver.py (+s3/cloudfront/waf resource assertions)
generated:
- terraform/spike/main.tf + terraform.tf (regenerated by run_platform.sh --check-only; reflect static-assets production stack + backend key spike/static-assets/)
notes:
- D-048 full rewrite of .ciagent/ historical narrative (verbatim phase descriptions, REQ-25/27/50, D-036) — produces intentional tautologies (e.g. "Rename static-assets → static-assets") per the decision to override the v1.6 preservation precedent.
- cloudfront interface.json resources array ordered distribution-first so the resolver (first-match wire resolution) routes bucket_regional_domain_name/waf_web_acl_arn/region to the distribution; the OAC gets adapter-provided defaults (name=acdl-oac, origin_type=s3, signing_behavior=always).
- .ciagent/ @v1.4 references left as historical record (D-048 scope was static-asset rename only; @v1.4 is historical narrative of Phase 20).
- s3 OUTPUT_MAP bucket_regional_domain_name not added (identity fallback in adapt() already handles it; OUTPUT_MAP documents non-identity mappings only).
---ci---
439 lines
38 KiB
Markdown
439 lines
38 KiB
Markdown
# ACDL — Agentic Cloud Delivery Platform
|
||
|
||
## Vision / Core Value
|
||
|
||
Consumers declare intent; the platform delivers safe production
|
||
deployment through an agentic stack. The platform absorbs two frictions:
|
||
the cognitive load of getting the infrastructure right, and the
|
||
operational work of getting the change to production safely.
|
||
|
||
Source of truth for **why**: `docs/vision.md`.
|
||
Source of truth for **how**: `docs/architecture.md` + `.ciagent/ARCHITECTURE.md`.
|
||
Where the two conflict, the vision wins.
|
||
|
||
## North Star
|
||
|
||
A merged change progresses through lower environments end-to-end without a
|
||
platform engineer joining a thread, approving a ticket, or manually
|
||
triggering a stage gate. A non-technical consumer ships a production
|
||
deployment by declaring intent — without authoring a workflow, a
|
||
configuration file, or a Terraform module. Every production change is
|
||
traceable to a human attestation and an immutable evidence stream.
|
||
|
||
## Core Tenets (from `docs/vision.md`)
|
||
|
||
1. **Operations are Declared, Not Executed.** Consumers define what they
|
||
need; the platform reconciles, provisions, and progresses.
|
||
2. **The Delivery Lifecycle is a Sovereign Boundary.** The platform
|
||
governs infra and delivery; it does not penetrate upstream product/SDLC.
|
||
Integration is only through validated, published contracts.
|
||
3. **Lower Environments are Autonomous; Higher Environments are Attested.**
|
||
Dev = zero-touch agentic. QA/prod/dr = deliberate human attestation, not
|
||
rubber stamps.
|
||
4. **Safety is Computed, Not Assumed.** Every action produces a measurable,
|
||
explainable confidence signal. The signal is the platform's certified
|
||
answer to "is this safe to proceed?"
|
||
5. **Infrastructure is Consumed, Not Maintained.** Compute is abstract,
|
||
containerized, or serverless. No node/OS/bare-metal lifecycle.
|
||
6. **Two Consumer Surfaces, One Platform.** Technical developers (L3A) and
|
||
non-technical consumers (L3B) converge on the same contract schema, the
|
||
same policy envelope, and the same evidence stream.
|
||
|
||
## Domain Boundaries
|
||
|
||
- **In scope:** environment progression; cloud resource lifecycle; operational
|
||
security and observability NFRs; policy enforcement; immutable audit
|
||
lineage; confidence frameworks; two consumer surfaces (developer + agentic).
|
||
- **Out of scope:** application business logic; IDE workflows; product
|
||
backlog / sprint planning; compute requiring node-level or OS-level management.
|
||
- **Interface:** upstream systems integrate through a strict contract
|
||
boundary. The platform validates, enriches with operational standards,
|
||
and reconciles the target state.
|
||
|
||
## Objective for Milestone v1.1 (prior — complete, tag `v1.2.0`)
|
||
|
||
Finalize the architecture to v1.0 (resolve all 11 open design decisions in
|
||
`docs/architecture.md` §13) and prove the locked commitments with one
|
||
end-to-end v1 implementation spike:
|
||
|
||
- **One L1 module** (`l1-s3`) — substrate-agnostic, IR-typed interface.
|
||
- **One L2 thin-composition** (`l2-static-assets`) — references the L1.
|
||
- **Terraform adapter** — compiles the IR to a real `terraform plan`
|
||
against AWS via OIDC (no long-lived credentials, per §12.5).
|
||
- **One contract submission** → contract→IR resolution →
|
||
`terraform plan` → PolicyCheckResult (Checkov) → confidence signal →
|
||
evidence event to the DynamoDB outbox.
|
||
|
||
The spike validates the architecture's claim that the IR-shaped commitments
|
||
do not require a polyglot mess (`docs/architecture.md` §14, step 2).
|
||
|
||
**Status: COMPLETE — all 5 phases shipped (v1.1.1..v1.1.5) + verified; review
|
||
READY TO SHIP (0 P0); audit CLEAN; milestone tag `v1.2.0`; Gitea release
|
||
id 202 published. D-034 closed (root key deactivated by user).**
|
||
|
||
## Milestone v1.1 Phases (prior — complete)
|
||
|
||
| Phase | Name | Goal |
|
||
|-------|------|------|
|
||
| 06 | archive-demo-and-reorient | Move the v1.0 demo (`modules/`, `scripts/`, `evidence-ui/`, `contracts/`, demo workflows) to `demo/`; establish the new repo layout (`platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`); rewrite README. |
|
||
| 07 | architecture-v1-finalization | Resolve the 11 open decisions → architecture v1.0. Author IR JSON Schema, PolicyCheckResult schema, contract schema, confidence-signal spec, HITL matrix, outbox/ledger design under `schemas/` + `platform/`. |
|
||
| 08 | aws-oidc-bootstrap | One-shot use of a temporary long-lived key (waiver D-034) to create an IAM role + OIDC trust policy for the act_runner, an S3 state bucket, and a DynamoDB lock table. Rotate the key. Verify the runner assumes the role via OIDC with no long-lived secret. |
|
||
| 09 | v1-spike-ir-and-l1-and-adapter | Target Stack IR; one real L1 (`l1-s3`) with IR-typed interface; L1 registry; Terraform adapter (IR → Terraform var/output + `terraform plan`) running against AWS via OIDC. |
|
||
| 10 | v1-spike-l2-and-contract-e2e | One L2 thin-composition (`l2-static-assets`) referencing `l1-s3`; contract schema + contract→IR resolution; one end-to-end contract submission → `terraform plan` → Checkov → confidence signal → evidence event to outbox. Verify the IR commitments hold. |
|
||
|
||
Milestone COMPLETE gate: review → ship `v1.2.0` (feature milestone, next
|
||
minor per ship.md) → audit. **DONE.**
|
||
|
||
## Objective for Milestone v1.2 (prior — complete)
|
||
|
||
Platform hardening + first real consumer deployment. The v1.1 spike proved
|
||
the IR commitments hold on a single dev-only `terraform plan` for one S3
|
||
bucket. v1.2 takes the spike to a real, simpler, better-documented platform
|
||
that actually delivers a microservice to AWS ECS Fargate end-to-end.
|
||
|
||
Five scope axes (user-directed, 2026-07-21):
|
||
|
||
1. **Re-evaluate the current state.** Confirm go-gitea/gitea#36988 (OIDC for
|
||
Gitea Actions) is still unmerged (re-checked 2026-07-21: **open**, last
|
||
updated 2026-05-27). Extend the D-039 per-run-rotated-key waiver for
|
||
v1.2; real OIDC is deferred to v1.3+ (D-047).
|
||
2. **NFR improvements on the existing spike.** Least-privilege IAM audit,
|
||
idempotent bootstrap, proper exit codes / error handling, rotation
|
||
hygiene, P1-1 / P1-B redaction carried forward from the v1.1 audit.
|
||
3. **Streamline / simplify the current setup.** Consolidate the
|
||
`run_spike_*.sh` scripts into one `scripts/run_platform.sh`; remove
|
||
dead code and stale paths; one command runs the whole pipeline.
|
||
4. **README.md fully up to date on how the platform works.** The current
|
||
README still says "v1.1 (active)" — it must reflect v1.1 complete, the
|
||
actual spike flow, how to run it, the real repo layout, and the v1.2
|
||
objective.
|
||
5. **Bootstrap a consumer repo with a basic microservice deployed to ECS
|
||
end-to-end.** New Gitea repo `acdl-consumer-microservice` (org
|
||
`continuous-intelligence`) holding a tiny HTTP container + Dockerfile;
|
||
new IR-typed L1s (`l1-vpc`, `l1-ecs-cluster`, `l1-ecs-service`,
|
||
`l1-iam-role`, `l1-alb`, `l1-ecr`); new `l2-microservice`
|
||
thin-composition; one contract submission → `terraform apply` (dev,
|
||
autonomous) → a live ECS Fargate service serving HTTP 200 → evidence
|
||
event to the DynamoDB outbox → acdl-evidence timeline.
|
||
|
||
The milestone proves the platform delivers real value (a running
|
||
microservice), not just a plan.
|
||
|
||
## Milestone v1.2 Phases
|
||
|
||
| Phase | Name | Goal |
|
||
|-------|------|------|
|
||
| 11 | v1.2-research-and-readme | Re-eval #36988 (confirm open → extend D-039 as D-047). Audit the v1.1 spike for NFR gaps (least-privilege, idempotency, error handling, rotation hygiene) + simplification opportunities. **Rewrite README.md** to reflect v1.1 complete + how the platform actually works (spike flow, how to run, repo layout, v1.2 objective). Output: RESEARCH.md v1.2 addendum; updated README. |
|
||
| 12 | nfr-harden-and-simplify | Apply Phase 11 findings: tighten `spike_runner_policy.json` (least-privilege audit); make `terraform/bootstrap/create_*.py` idempotent; consolidate `run_spike_*.sh` → one `scripts/run_platform.sh`; proper exit codes / error handling; redact P1-1 AWS key IDs in `VERIFY.md`; fix any remaining stale `platform/` paths. Spike still runs e2e after the refactor. |
|
||
| 13 | l1-catalog-for-ecs | Author IR-typed L1s for an ECS Fargate microservice: `l1-vpc`, `l1-ecs-cluster`, `l1-ecs-service`, `l1-iam-role` (task + exec role), `l1-alb`, `l1-ecr`. Register all in `modules-ir/registry.json`. Expand the Terraform adapter `TYPE_MAP`. Each L1 produces a valid `terraform plan` fragment. |
|
||
| 14 | l2-microservice-and-contract-schema | Author `l2-microservice` thin-composition (references the ECS L1s, depth ≤ 5). Extend `schemas/contract.schema.json` for microservice inputs (image, port, env, healthcheck). Verify contract→IR resolution yields a complete target stack. |
|
||
| 15 | consumer-repo-and-terraform-apply | Create consumer repo `acdl-consumer-microservice` (Gitea org) with a basic microservice (tiny HTTP container + Dockerfile + ECR push). Lift the platform from `plan` → **`apply`** (dev, autonomous per §10). Submit `contracts/microservice.yaml` → pipeline → IR → plan → apply → a real ECS Fargate service running. |
|
||
| 16 | v1.2-capstone-e2e | End-to-end verification: consumer commit → pipeline → ECS service live serving HTTP 200 → evidence event to the DynamoDB outbox → acdl-evidence timeline renders it. Verify NFR improvements hold, the setup is simpler (one `run_platform.sh`), and the README is accurate. |
|
||
|
||
Milestone COMPLETE gate: review → ship `v1.3.0` (feature milestone, next
|
||
minor per ship.md — v1.1 shipped `v1.2.0`) → audit.
|
||
|
||
## Objective for Milestone v1.4 (active)
|
||
|
||
Central pipeline contract + shell reproducibility + output streaming. The
|
||
v1.3 milestone (Phases 17–18) created identical CI/CD pipelines for Gitea
|
||
and GitHub but they were duplicated copies with no single source of truth.
|
||
v1.4 makes the pipeline a declarative contract, enables full shell
|
||
reproducibility, and streams terraform/checkov output so users can see
|
||
what the platform is doing.
|
||
|
||
Three scope axes:
|
||
|
||
1. **Central pipeline contract.** A JSON Schema
|
||
(`schemas/pipeline.schema.json`) + YAML instance (`pipelines/ci.yaml`)
|
||
declares the pipeline stages, commands, triggers, and runner. Both
|
||
`.gitea/workflows/ci.yml` (Gitea Actions, dev) and
|
||
`.github/workflows/ci.yml` (GitHub Actions, production) implement the
|
||
contract. A test validates conformance.
|
||
2. **Shell reproducibility.** `scripts/run_ci.sh` mirrors the CI pipeline
|
||
locally — runs the same 3 stages (lint, test, check-only) in sequence.
|
||
The pipeline is fully reproducible from the shell, not just in CI.
|
||
3. **Output streaming.** `scripts/run_platform.sh` streams terraform
|
||
init/validate/plan output, Checkov compliance results, and
|
||
PolicyCheckResult records to stdout by default, so the user sees what
|
||
is happening. A `--quiet` flag suppresses streaming for log-only mode.
|
||
|
||
## Milestone v1.4 Phases
|
||
|
||
| Phase | Name | Goal |
|
||
|-------|------|------|
|
||
| 19 | central-pipeline-contract-and-shell-reproducibility | Create the central pipeline contract (JSON Schema + YAML instance). Create `scripts/run_ci.sh` for shell reproducibility. Update `run_platform.sh` to stream terraform/checkov output. Update both workflow YAMLs with contract references (staying byte-identical). Add tests for contract validation, workflow conformance, and streaming. |
|
||
|
||
Milestone COMPLETE gate: review → ship `v1.4.1` (feature milestone, next
|
||
minor per ship.md — v1.3 shipped `v1.3.2`) → audit.
|
||
|
||
## Objective for Milestone v1.7 (active)
|
||
|
||
Production platform + contract ingestion + pipeline maturation. The v1.6
|
||
milestone left the platform documented and environments-aware; v1.7 takes it
|
||
to a production-grade platform. 12 user-directed scope axes (2026-07-22):
|
||
|
||
1. **Rename `static-assets` → `static-assets`** (D-048 — including
|
||
`.ciagent/` historical narrative, overriding the v1.6 preservation
|
||
precedent). The reconstruction test is updated to expect `static-assets`.
|
||
2. **Augment `static-assets` to a production-ready stack** by authoring a
|
||
new `cloudfront` primitive + a `waf` primitive (D-049: S3 + CloudFront
|
||
OAC + WAF; Route53/ACM are domain-dependent and deferred to documented
|
||
extension points).
|
||
3. **DX-friendly deploy outputs** (D-050): SSM Parameter Store (KMS-encrypted
|
||
`SecureString`) for runtime-injectable values + GitHub PR comment / job
|
||
summary for human-readable connection strings. No raw secrets in logs.
|
||
4. **Central deploy pipeline error reporting** via the platform Lambda
|
||
`report_error` action (D-055): the Lambda creates a GitHub issue on the
|
||
platform repo. The consumer's onboarding-granted Lambda-invoke permission
|
||
is the only grant needed — uniform pathway, no separate GitHub
|
||
`issues: write` on the consumer side. Gitea is excluded (only the CIAgent
|
||
uses it).
|
||
5. **PR comments after every successful stage** so developers always know
|
||
where they stand.
|
||
6. **Three platform pipelines**: (1) platform-test (PR, unit + integration +
|
||
schema-validation); (2) primitives-plan (PR, plan-only for all L1
|
||
primitives); (3) patterns-plan (PR, plan-only for all L2 modules).
|
||
7. **Release job** on merge to `main`: computes MAJOR.MINOR.PATCH semver,
|
||
creates the tag, then updates (force-moves) or creates the MAJOR.MINOR +
|
||
MAJOR floating tags (D-057). Consumers on `@v1` or `@v1.6` receive updates
|
||
depending on their pinned version.
|
||
8. **Platform Lambda** for one-way consumer→platform communication
|
||
(contracts). Onboarding grants the consumer repo's environment the right
|
||
to trigger the Lambda (cross-account IAM). The Lambda ingests contracts
|
||
and stores them in a DynamoDB table `acdl-contracts` (D-051) for
|
||
historical reference, impact analysis, CMDB-style application-state
|
||
queries, and pattern detection. The IAM policy reflects cross-account
|
||
invocation.
|
||
9. **Tagging standards** in policy/compliance checks (D-054): a required-tag
|
||
set (`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`)
|
||
enforced by a Checkov custom YAML rule. Closes the D-043 deferral (the
|
||
SKIPPED `ACDL_TAG_NAMING` placeholder becomes a real check).
|
||
10. **Wiz adapter** for security checks (D-052): a stub + schema path that
|
||
translates Wiz API issues → `PolicyCheckResult` records, degrading
|
||
gracefully when unconfigured. Matches the Checkov adapter pattern.
|
||
11. **Kyverno adapter** for compliance/security checks (D-053): a
|
||
K8s-native policy adapter that translates Kyverno `PolicyReport` results
|
||
→ `PolicyCheckResult` records. Ready but inactive for Terraform-only
|
||
stacks (the platform emits Terraform, not K8s manifests); it activates
|
||
when the GitOps reconciler (roadmap) emits K8s manifests.
|
||
12. **Remove `consumer-repos/`** and add validated per-module examples
|
||
(D-058: `modules/<name>/examples/` with `simple.yaml` + `complex.yaml`
|
||
validated in CI) + a new RDS primitive demonstrating multi-engine
|
||
variation (D-059).
|
||
|
||
## Milestone v1.7 Phases
|
||
|
||
| Phase | Name | Goal |
|
||
|-------|------|------|
|
||
| 22 | rename-and-production-static-assets-stack | Rename `static-assets` → `static-assets` everywhere (D-048). Author `cloudfront` + `waf` primitives. Augment `static-assets` to S3 + CloudFront (OAC) + WAF (D-049). Expand adapter. Bump `uses:` to `@v1.6`; create floating `v1.6` + `v1` tags (D-057). |
|
||
| 23 | tagging-standards-and-security-adapters | Required-tag set + Checkov custom rule (D-054, D-043 closure). Wiz adapter stub (D-052). Kyverno K8s-native adapter (D-053). Schema engine enum updated. |
|
||
| 24 | platform-lambda-and-contract-ingestion | Platform Lambda + DynamoDB `acdl-contracts` table (D-051) + cross-account IAM + onboarding grant. |
|
||
| 25 | deploy-pipeline-dx-outputs-and-error-reporting | SSM SecureString + PR comment outputs (D-050). Lambda `report_error` → GitHub issue (D-055). Stage comments after each successful stage. |
|
||
| 26 | platform-pipelines-and-release-automation | 3 platform pipelines (platform-test, primitives-plan, patterns-plan). Release job with semver + MAJOR.MINOR/MAJOR tag updates (D-057). |
|
||
| 27 | remove-consumer-repos-and-module-documentation-examples | Delete `consumer-repos/`. RDS primitive (D-059). Validated per-module examples (D-058). Docs updates. |
|
||
|
||
Milestone COMPLETE gate: review → ship `v1.7.0` (feature milestone, next
|
||
minor per ship.md — v1.6 shipped `v1.6.0`) → audit.
|
||
|
||
## Requirements
|
||
|
||
### v1.0 (Prior milestone — the demo)
|
||
|
||
Status: complete. Tag `v1.1.0`. All REQ-01..15 satisfied by the stub-driven
|
||
executive demo. See `REQUIREMENTS.md` §v1 and the prior decisions table
|
||
appendix below. The demo is **archived** to `demo/` in Phase 06.
|
||
|
||
### v1.1 (Prior milestone — architecture finalization + v1 spike, complete)
|
||
|
||
New requirements REQ-16..REQ-28 — see `REQUIREMENTS.md` §v1.1. Summary:
|
||
|
||
- **REQ-16:** Architecture finalized to v1.0 (11 open decisions resolved).
|
||
- **REQ-17:** Target Stack IR defined as JSON Schema; substrate-agnostic.
|
||
- **REQ-18:** PolicyCheckResult normalized schema defined; Checkov adapter.
|
||
- **REQ-19:** Six-input confidence signal specified with per-env thresholds
|
||
(dev 0.50 / qa 0.75 / prod 0.90 / dr 0.95) and severity→penalty mapping.
|
||
- **REQ-20:** Tiered audit ledger design (S3 Object Lock 7-yr + DynamoDB
|
||
outbox, RPO=0, JWS detached signatures, `prev_event_hash` chain).
|
||
- **REQ-21:** Full 8-concern HITL matrix + separation-of-duties design
|
||
(CODEOWNERS + DynamoDB identity-distinctness).
|
||
- **REQ-22:** Contract schema (JSON Schema draft 2020-12) with per-env
|
||
mandatory/optional inputs and `profile: agentic` marker for L3B.
|
||
- **REQ-23:** AWS OIDC bootstrap (IAM role + trust policy for act_runner);
|
||
the long-lived key is used once then rotated (waiver D-034).
|
||
- **REQ-24:** One real L1 module (`l1-s3`) with an IR-typed interface.
|
||
- **REQ-25:** One real L2 thin-composition (`l2-static-assets`) referencing
|
||
`l1-s3`.
|
||
- **REQ-26:** Terraform adapter compiles the IR to a real `terraform plan`
|
||
against AWS via OIDC; state in S3 + DynamoDB.
|
||
- **REQ-27:** One end-to-end contract submission → contract→IR resolution →
|
||
`terraform plan` → Checkov → confidence signal → evidence event to outbox.
|
||
- **REQ-28:** Spike verification proves the IR-shaped commitments hold (no
|
||
polyglot mess; the adapter is the only substrate-specific code).
|
||
|
||
### v1.2 (Prior milestone — platform hardening + first real consumer deployment, complete)
|
||
|
||
New requirements REQ-29..REQ-35 — see `REQUIREMENTS.md` §v1.2. Summary:
|
||
|
||
- **REQ-29:** README.md fully documents the v1.1-complete platform: spike
|
||
flow, how to run, repo layout, v1.2 objective.
|
||
- **REQ-30:** NFR hardening — least-privilege IAM audit, idempotent
|
||
bootstrap, consolidated `run_platform.sh`, error handling, P1-1/P1-B
|
||
redaction.
|
||
- **REQ-31:** L1 catalog expanded for ECS — 6 new IR-typed L1s
|
||
(`l1-vpc`, `l1-ecs-cluster`, `l1-ecs-service`, `l1-iam-role`, `l1-alb`,
|
||
`l1-ecr`) registered and adapter-compiled.
|
||
- **REQ-32:** `l2-microservice` thin-composition + contract schema extended
|
||
for microservice inputs (image, port, env, healthcheck).
|
||
- **REQ-33:** `terraform apply` (dev, autonomous) — real provisioning, not
|
||
just `plan`.
|
||
- **REQ-34:** Consumer repo `acdl-consumer-microservice` with a basic
|
||
microservice (ECR image, Dockerfile, contract).
|
||
- **REQ-35:** End-to-end verification — consumer commit → live ECS service
|
||
(HTTP 200) → evidence event → timeline.
|
||
|
||
### v1.4 (Prior milestone — central pipeline contract + shell reproducibility + streaming)
|
||
|
||
New requirements REQ-43..REQ-45 — see `REQUIREMENTS.md` §v1.4. Summary:
|
||
|
||
- **REQ-43:** Central pipeline contract — `schemas/pipeline.schema.json` +
|
||
`pipelines/ci.yaml`. Both Gitea and GitHub workflows implement the
|
||
contract; a test validates conformance.
|
||
- **REQ-44:** `scripts/run_ci.sh` mirrors the CI pipeline locally (lint →
|
||
test → check-only), exiting 0 with "CI PIPELINE OK".
|
||
- **REQ-45:** `scripts/run_platform.sh` streams terraform/checkov output by
|
||
default (with `--quiet` for log-only mode). Both workflows byte-identical.
|
||
|
||
## Constraints
|
||
|
||
- **Forge:** Gitea at `https://git.cloudinit.dev`, org `continuous-intelligence`.
|
||
- **CI runtime:** act_runner / Gitea Actions (reuses GitHub Actions workflow YAML).
|
||
- **Cloud:** AWS via OIDC federation. **Long-lived credentials are forbidden**
|
||
(§12.5). The v1.1 spike uses a temporary long-lived key **once** to bootstrap
|
||
OIDC (waiver D-034), then rotates it.
|
||
- **Substrate:** Terraform adapter in v1 (the only adapter). L1/L2 are
|
||
substrate-agnostic in shape; the adapter is the only substrate-specific code.
|
||
- **State:** S3 (state files) + DynamoDB (locking), single-region in v1.
|
||
- **Environments:** dev (autonomous) → qa (QA HITL) → prod (SRE HITL) → dr
|
||
(SRE HITL). **Staging does not exist** (Path A locked).
|
||
- **Compute:** abstract / containerized / serverless. No VMs, bare metal, OS
|
||
lifecycle.
|
||
- **Autonomy:** Full. Escalation hooks: deploy, delete_data, merge_to_main.
|
||
|
||
## Anti-Goals (from `docs/vision.md` §7)
|
||
|
||
- Not an upstream development platform (no product backlogs, IDE, code authorship).
|
||
- Not a general-purpose AI (autonomy is narrow, bounded by policy envelopes).
|
||
- Not a legacy infrastructure bridge (no VMs/bare metal/OS).
|
||
- Not a permissive delivery highway (no escape hatches past confidence or HITL).
|
||
- Not a mutable audit log (VCS history ≠ regulatory evidence).
|
||
|
||
## Context
|
||
|
||
- The `acdl` repo exists at the org root. `acdl-contracts` and
|
||
`acdl-evidence` exist from the v1.0 demo and continue as the developer
|
||
surface and the audit-timeline host respectively.
|
||
- `docs/vision.md` and `docs/architecture.md` (v0.2) are the upstream
|
||
vision/architecture sources, pulled from `origin/main` at the start of v1.1.
|
||
- The v1.0 demo (tag `v1.1.0`) is the reference of intent — it proved the
|
||
shape (L1/L2/contract/confidence/evidence/HITL) on stubs. v1.1 replaces the
|
||
stubs with the real platform substrate.
|
||
|
||
## Key Decisions (v1.1)
|
||
|
||
Carries forward the still-valid v1.0 decisions (see appendix). New v1.1
|
||
decisions:
|
||
|
||
| ID | Decision | Rationale | Outcome |
|
||
|----|----------|-----------|---------|
|
||
| D-034 | Temporary long-lived AWS key (waiver) used once in Phase 08 to bootstrap the state backend + IAM user; rotated/deactivated immediately after | §12.5 forbids long-lived creds; the bootstrap needed one `aws iam` call before the spike user + rotated key could take over | Spike achieves real `terraform plan` against AWS without violating the locked target after bootstrap. **CLOSED 2026-07-21: root key `AKIA…ROOT-DEACTIVATED` deactivated by the user in the AWS IAM console (verified — `InvalidClientTokenId`); the spike uses the rotated `acdl-spike-runner` key per D-039. Key ID redacted in v1.2 Phase 12 (P1-1).** |
|
||
| D-035 | Milestone version = `v1.1` (feature), ship tag `v1.2.0` | Real platform is a breaking reframing of the demo, but treated as the next incremental milestone per user choice; ship.md: feature milestone → next minor | Tag `v1.2.0` on milestone COMPLETE |
|
||
| D-036 | Spike picks `l1-s3` + `l2-static-assets` | Simplest real AWS resource (no IAM/network deps); smallest real `terraform plan`; proves the IR + adapter end-to-end | Spike scope fixed |
|
||
| D-037 | Demo archived to `demo/` (not deleted) | Preserves the working v1.0 demo as intent reference; new platform layout under `platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/` | No churn on demo code; clean separation |
|
||
| D-038 | Open decisions resolved in "accept recommendations + decide rest" mode | User-locked mode: accept architecture's stated recommendations (W1.A, W1.B, W2.A, BA.A); lead-developer decides the remaining 8 (W3.D, W3.E, BA.B, BA.C, BA.D, BA.E, BA.F, OpenTofu timing) with rationale | Architecture reaches v1.0 in Phase 07 |
|
||
| D-039 | Spike-only waiver: per-run-rotated long-lived AWS key. OIDC federation deferred to v1.2, blocked on go-gitea/gitea#36988. | **RESEARCH TARGET 1 verdict (conf 0.95):** Gitea Actions does NOT support `id-token: write` / OIDC token issuance as of Gitea 1.27.x / gitea-runner v2.1.0. GitHub's OIDC pattern is not portable. The waiver satisfies §12.5's *intent* (no persistent long-lived key) for the spike: the key is rotated after each run by `scripts/rotate_spike_key.sh`. v1.2 implements real OIDC when the Gitea PR merges. | Spike achieves real `terraform plan` against AWS without a *persistently* long-lived key; real OIDC is a v1.2 deliverable |
|
||
| D-040 | The 6 confidence-signal inputs are: policy (0.30), validation (0.25), freshness (0.10), source (0.15), history (0.10), nfrs (0.10). Weights frozen for v1, tuned in v1.2 alongside thresholds (BA.B). | Architecture §8 locks "six canonical inputs" but does not enumerate them; RESEARCH TARGET 6 chose the platform-computable subset present in every environment (incl. dev). | Confidence signal (Phase 10) has a concrete input enumeration |
|
||
| D-041 | Spike audit ledger = v1.0 hash chain + DynamoDB outbox + `acdl-evidence` mirror. S3 Object Lock (compliance mode, 7-yr) + JWS (platform KMS key, quarterly rotation) + daily checkpoints are v1.2 build-out, authored as design in Phase 07. | REQ-20 is "design authored," not "implemented." The spike proves the outbox write path; the regulatory ledger is v1.2. | Spike scope stays bounded; REQ-20 satisfied by the Phase 07 design doc |
|
||
| D-042 | HITL approver identity in Gitea = `gitea.actor` of the `workflow_dispatch` run that sets `approve_qa=true`/`approve_prod=true`/`approve_dr=true`. Separation-of-duties reads `approver_qa` from the DynamoDB outbox and compares to the prod-dispatch `gitea.actor`. | Gitea has no Environments API (re-confirmed in RESEARCH); `gitea.actor` is the only approval-identity signal. | SoD design (Phase 07) is concrete for the Gitea forge |
|
||
| D-043 | Tag/naming compliance deferred for the spike: the Checkov adapter emits a single `SKIPPED` PolicyCheckResult (`ruleId: ACDL_TAG_NAMING`, `severity: info`) so the confidence policy input is non-empty. Custom Checkov YAML rule lands in v1.2. | Checkov has no built-in tag-presence check; a custom rule in the spike is scope creep. | Spike's policy input is non-empty without a custom-rule dependency |
|
||
| D-044 | DynamoDB outbox = `PAY_PER_REQUEST`; PK `contractId`, SK `eventType#eventTs`, TTL `expire_at` = now + 365d. No separate async worker/DLQ in the spike (RTO = workflow re-run); v1.2 outbox worker + DLQ is a Phase 07 design artifact. | On-demand is zero-cost-at-idle for the spike's single dev submission. | Spike outbox is minimal; v1.2 worker design authored in Phase 07 |
|
||
| D-045 | Runner tooling: `runs-on: ubuntu-latest`; install `terraform` via HashiCorp apt repo (pin `1.9.*`), `checkov` via pip (pin `>=3.2,<4`, `--break-system-packages`). Neither is pre-installed on the default runner image. | RESEARCH TARGET 2; pinning avoids mid-spike version drift. | Phase 09/10 workflows have a concrete setup step |
|
||
| D-046 | `act_runner` → `gitea-runner` rename: Phase 07 updates docs to use the current name `gitea-runner` (renamed 2026-04 in gitea/runner#850). | RESEARCH TARGET 1 + R-4: naming drift between v1.0 docs and the current runner. | Docs reflect the current binary name |
|
||
| D-047 | v1.2 carries forward the D-039 per-run-rotated-key waiver. Real OIDC federation remains deferred to v1.3+, blocked on go-gitea/gitea#36988 (re-checked 2026-07-21: still **open**, last updated 2026-05-27, not merged). | §12.5 forbids long-lived creds; the Gitea Actions OIDC provider is still not merged. The waiver continues to satisfy §12.5's *intent* (no *persistently* long-lived key) for v1.2: `scripts/rotate_spike_key.sh` rotates the key, and Phase 12 tightens the IAM scoping + rotation hygiene. | v1.2 achieves `terraform apply` against AWS without a persistently long-lived key; real OIDC is a v1.3+ deliverable. |
|
||
|
||
## Key Decisions (v1.7)
|
||
|
||
Resolved at the CLARIFY stage (full autonomy — all within locked constraints
|
||
or user-directed scope). New v1.7 decisions:
|
||
|
||
| ID | Decision | Rationale | Outcome |
|
||
|----|----------|-----------|---------|
|
||
| D-048 | Rename `static-assets` → `static-assets`: **rewrite all occurrences** including verbatim historical phase descriptions in `.ciagent/` (ROADMAP, REQUIREMENTS, RESEARCH, decision tables), overriding the v1.6 audit precedent that preserved some historical references. | User chose full rewrite. Maximally consistent; the reconstruction test is updated to expect `static-assets` throughout. | Phase 22 rewrites every `static-assets` string to `static-assets`; no preserved historical tokens remain. |
|
||
| D-049 | Production static-assets stack = S3 + CloudFront (OAC) + WAF. | Self-contained, domain-free production edge. Route53/ACM are domain-dependent (consumer-supplied) and deferred to documented extension points / a complex example. | Phase 22 authors `cloudfront` + `waf` primitives and augments the module. |
|
||
| D-050 | Deploy outputs: SSM Parameter Store (`SecureString`, KMS-encrypted, namespaced `/acdl/{env}/{contractId}/{output_name}`) for runtime-injectable values + GitHub PR comment / job summary for human-readable connection strings. | Two canonical mechanisms: SSM for resources that read at runtime; PR comment for developers. No raw secrets in logs. | Phase 25 implements `core/output_publisher.py` + two new pipeline stages. |
|
||
| D-051 | Contract ingestion storage = DynamoDB table `acdl-contracts` (PK `consumerRepo`, SK `contractId#submittedAt`, SSE via customer-managed CMK, point-in-time recovery). | Enables historical queries, impact analysis, CMDB-style application-state queries, and pattern detection via DynamoDB queries. S3 flat-file mirror deferred (DynamoDB is sufficient for v1.7). | Phase 24 defines the table + Lambda. |
|
||
| D-052 | Wiz adapter = stub + schema path (no live Wiz tenant in CI). | Matches the Checkov adapter pattern; typed interface, offline-testable, degrades gracefully when unconfigured (emits `WIZ_NOT_CONFIGURED` SKIPPED record). | Phase 23 authors `adapters/wiz/wiz_adapter.py`. |
|
||
| D-053 | Kyverno adapter = K8s-native policy adapter translating `PolicyReport` results → `PolicyCheckResult`. Ready but inactive for Terraform-only stacks. | The platform emits Terraform, not K8s manifests. The adapter activates when the GitOps reconciler (roadmap) emits K8s manifests. Sample policies included as documentation. | Phase 23 authors `adapters/kyverno/kyverno_adapter.py` + sample policies. |
|
||
| D-054 | Tagging standard = required-tag set (`acdl:owner`, `acdl:contract`, `acdl:environment`, `acdl:cost-center`) enforced by a Checkov custom YAML rule. | Closes the D-043 deferral (the SKIPPED `ACDL_TAG_NAMING` placeholder becomes a real check). Naming-convention regex deferred (brittle across AWS resource types). | Phase 23 authors `schemas/tagging-standard.json` + `adapters/terraform/policy/custom_rules/acdl_tagging.yaml`. |
|
||
| D-055 | Error reporting = the platform Lambda `report_error` action creates a GitHub issue on the platform repo (`acdl/acdl`). Uniform communication pathway via the Lambda; the consumer's onboarding-granted Lambda-invoke permission is the only grant needed. No separate GitHub `issues: write` on the consumer side. Gitea is excluded (only the CIAgent uses it; platform engineers and consumers use GitHub). | Unifies requirements 4 + 8 around one mechanism. The Lambda holds a GitHub token (Secrets Manager) scoped to the platform repo. Idempotent (comments on existing open issue rather than duplicating). | Phase 24 prepares the action; Phase 25 implements it + wires the `if: failure()` workflow step. |
|
||
| D-056 | Ship `v1.7.0`; bump `uses:`/`ref:` from `@v1.4` to `@v1.6`. | Consumer-facing version tracks the last released MAJOR.MINOR. Consumers on `@v1.4` stay on v1.4 behavior until they bump. | Phase 22 bumps the references. |
|
||
| D-057 | The `uses:`/`ref:` bump + floating `v1.6`/`v1` tag creation happen in Phase 22 (pointing at `v1.6.0`), so the reference never points at a non-existent tag. The release job (Phase 26) owns ongoing tag updates. | Sequencing: if Phase 22 bumps `uses:` to `@v1.6` but the tag doesn't exist, the reference is temporarily broken. Creating the tag early (pointing at the last release) fixes this. | Phase 22 creates the floating tags; Phase 26's release job maintains them. |
|
||
| D-058 | Module examples = separate validated files in `modules/<name>/examples/` (`simple.yaml` + `complex.yaml` + variation files), validated against `schemas/contract.schema.json` in the platform-test pipeline schema-validation stage. Each module's README `## Examples` section references + excerpts them. | Examples cannot drift from the schema silently. | Phase 27 authors the example files; Phase 26's platform-test pipeline validates them. |
|
||
| D-059 | Add an RDS primitive (`modules/l1/rds/`) with an `engine` input (enum: postgres, mysql, etc.) + a multi-engine example demonstrating the variation pattern. | Concrete demonstration of the multi-engine variation the requirement calls out. Adds one primitive + examples. | Phase 27 authors the primitive + adapter expansion + examples. |
|
||
| D-060 | (Consolidated into D-058.) | — | — |
|
||
|
||
### Open-decision resolutions (Phase 07 deliverable — recorded here for traceability)
|
||
|
||
| ID | Question | Resolution |
|
||
|---|---|---|
|
||
| W1.A | AI-refinement trigger | **Accept recommendation.** Joint condition: N ≥ 50 consecutive changes with zero rollbacks AND no L1/L2 incident in last 6 months AND Infra & Ops unilateral override. |
|
||
| W1.B | Multi-stack edge case rule | **Accept recommendation.** Permitted only for (a) DR-region mirror, (b) time-boxed experimental stack with TTL ≤ 30d, (c) explicit Infra & Ops approval with `multiStack.justification`. |
|
||
| W2.A | Tag mutability for prod | **Accept recommendation (Path B).** Tag for dev/qa, SHA for prod. Platform CLI resolves tag→SHA for prod-bound workflows. Justified by the "Audit truth lives outside the repository" bet. |
|
||
| BA.A | Initial L3B skill catalog | **Accept recommendation.** 5 skills: web API, worker, scheduled job, static asset, basic observability bootstrap. Addition criteria: (a) reviewable for sensitive data, (b) expressible as a single contract submission, (c) documented use case. |
|
||
| W3.D | L1/L2 standard versioning | **Decided.** Semver: interface → MAJOR, behavior → MINOR, lifecycle → PATCH (same as the v1.0 demo D-rule, lifted to the real platform). Pin model: L2 contracts pin L1 by `name@semver`; the resolver picks the highest compatible. Evolution: MAJOR bumps require a new registry entry (immutable publication); old entry enters a 12-month deprecation window. |
|
||
| W3.E | Schema mandatory vs optional inputs | **Decided.** Per-env mandatory table: dev requires `stack` + `environment`; qa adds `validation.e2eSuite` + `validation.loadTest`; prod adds `runbook` + `dashboard` + `oncall`; dr adds `drDrillRef`. `inputs` map is always optional. `profile: agentic` fields (`naturalLanguageIntent`, `confidenceAtSubmission`, `agentTrace`) optional everywhere. |
|
||
| BA.B | Confidence threshold tuning | **Decided.** Starting thresholds frozen for v1. Tuning begins in v1.2: track FP/FN per environment quarterly; override authority = Infra & Ops + SRE joint sign-off; any override is itself a confidence-event in the audit stream. |
|
||
| BA.C | On-call / operational ownership | **Decided.** Platform on-call = Infra & Ops rotation. Escalation: L3A/L3B halt → platform on-call pager (Sev2); consumer-visible outage → consumer on-call (Sev1) with platform on-call support. Consumer on-call relationship is contractual, defined at onboarding (BA.E). |
|
||
| BA.D | Cost / capacity governance | **Decided.** Cloud cost owner = Infra & Ops FinOps. Per-contract consumption reported monthly. Runaway spend: hard halt at 120% of contract-declared budget envelope via the confidence signal (cost is one of the 6 inputs); override = FinOps + SRE joint sign-off. |
|
||
| BA.E | Consumer onboarding | **Decided.** Two paths: developer (L3A) — `getting-started` walks through contract schema + central pipeline template; citizen developer (L3B) — onboarding grants a scoped agent + skill catalog, no workflow authoring. Both end in a sandbox dev submission that must pass the confidence gate before the consumer is promoted. |
|
||
| BA.F | Cross-platform evolution | **Decided.** The contract schema, IR, PolicyCheckResult, confidence signal, and audit stream are portable (substrate- and forge-agnostic). Forge-specific code: workflow YAML, OIDC trust, CODEOWNERS, Environments. A second forge (e.g., GitLab) requires a forge adapter + a workflow-template translator; no change to L1/L2/IR/confidence/audit. |
|
||
| Q1.3 | OpenTofu timing | **Decided (deferred).** Not in v1 or v1.1. The substrate abstraction (§12) makes OpenTofu a future adapter, not an architecture change. Revisit when an OpenTofu adapter is requested; no version committed. |
|
||
|
||
## Appendix — Prior milestone (v1.0 demo) decisions
|
||
|
||
The v1.0 demo (tag `v1.1.0`) carried decisions D-001..D-033. They governed
|
||
the stub-driven executive demo and remain valid **for the archived demo
|
||
under `demo/`**. They are **superseded** by the v1.1 decisions above for the
|
||
real platform. Full text preserved in git history at tag `v1.1.0`.
|
||
|
||
## Operational parameters (CLARIFY auto-resolution, full autonomy)
|
||
|
||
Resolved at the CLARIFY stage to unblock planning. None require user
|
||
sign-off (autonomy = full; all within locked constraints).
|
||
|
||
| Parameter | Value | Rationale |
|
||
|---|---|---|
|
||
| AWS region | `us-east-1` | Default; matches v1.0 demo references; single-region in v1 (§12.3) |
|
||
| Terraform state bucket | `acdl-tfstate-<account-id>-us-east-1` | Namespaced by account id to avoid collision; region-suffixed |
|
||
| Terraform lock table | `acdl-tflock` | DynamoDB; single-region v1 |
|
||
| OIDC IAM role | `acdl-act-runner-role` | Assumed by the act_runner via web-identity |
|
||
| OIDC trust subject | `repo:continuous-intelligence/acdl:ref:refs/heads/main` (+ phase branches) | Least-privilege; refined in Phase 08 |
|
||
| Spike L1 (`l1-s3`) inputs | `bucket_name: string`, `region: string` | Minimal S3 interface per §2 |
|
||
| Spike L2 (`l2-static-assets`) | thin-composition referencing `l1-s3` only; depth 1 | Smallest real plan per D-036 |
|
||
| Spike contract | `contracts/spike.yaml`: `stack: l2-static-assets`, `environment: dev`, `inputs: { bucket_name: acdl-spike-bucket, region: us-east-1 }` | One end-to-end submission (REQ-27) |
|
||
| Spike `terraform` command | `plan` only | `apply` is out of scope (Out of Scope table); HITL-gated in v1.2 |
|
||
| Checkov ruleset (spike) | the 4 L2 checks (secrets-in-plaintext, public ingress, IAM wildcard, KMS key reference) + tag/naming | §3 + §12.4; Kyverno/OPA deferred |
|
||
| v1.0 tags preserved | `v1.0.1`..`v1.0.5`, `v1.1.0` retained | Immutability; demo archive does not rewrite history |
|
||
| Next ship tag | `v1.3.0` | Feature milestone → next minor per ship.md (v1.1 shipped `v1.2.0`; v1.2 ships `v1.3.0`) |
|
||
|
||
### Items deferred to RESEARCH (not clarifications)
|
||
|
||
- **Gitea/act_runner OIDC support** — does act_runner emit an OIDC
|
||
`id-token`? Determines whether real-AWS plan is achievable in this
|
||
environment or whether a spike-only waiver is needed. Highest-priority
|
||
research target.
|
||
- **Terraform + Checkov availability on the runner image** — install in the
|
||
workflow if missing.
|
||
- **`actions/configure-aws-credentials` action on act_runner** — if
|
||
unavailable, fall back to `aws sts assume-role-with-web-identity` from a
|
||
step. |