v1.9.6
18 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7585c828f0 |
docs(P48): vision gaps + badge system + substrate→engine + CR format + agentic tags
9 requirements implemented across presentation decks and project docs: 1. DX closing slide: added 'Infrastructure as a utility, not a craft' bullet to convey the full vision (infrastructure consumed, not maintained; platform compounds value over time). 2. PW Problem slide: 'moving a merged change' → 'promoting a change'. 3. PW Problem slide: added 'Red tape' and 'Scalability without increasing headcount' bullets (4 frictions, not 2). 4. PW Roadmap slide: redesigned with side-by-side HTML table layout (Testing | Planned), 16px font, no overflow. 5. PW deck: added new slide 'What This Platform Is — and Isn't' after North Star (sovereign boundary, infrastructure as utility, 4 anti-goals). PW deck now 16 slides (was 15). 6. Maturity nomenclature: 'Available today'/'shipped' → 'Testing' across both decks + source markdown. New .testing badge (blue/teal #DBEAFE). Roadmap title: 'Testing vs. Planned'. The platform has 0 consumer adoption — 'shipped' was inaccurate. 7. Global: 'substrate' → 'engine' across entire project (88 matches, 30+ files including .ciagent/, docs/, modules/, adapters/, schemas/, code). 8. Presentation files only: 'forge' → 'VCS' / 'version control system' (6 occurrences in 4 files). 'forge' retained in all technical docs and code as the industry-standard term. 9. New .agentic badge (purple/violet #EDE9FE) appended to agentic features in both decks: confidence signal, autonomous dev, pattern recognition, dynamic module creation, citizen developer surface, auto-promotion. Also: Change Request ID format changed from 'CR-2026-001' to 'CHG0678912' across presentation files, consumer guide, and test fixtures. HTML re-rendered. PPTX rendered for release upload. ---ci--- phase: 48 milestone: v1.9 status: complete requirements: covered: [] partial: [] ---/ci--- |
||
|
|
e74a8c2f5d |
feat(P42): stub implementation — SoD, HITL gates, attestation matrix, Wiz, Kyverno
---ci--- project: acdl phase: 42 milestone: v1.9 status: execute ---/ci--- Phase 42 — stub-implementation (REQ-107..111, D-084): route_halt_artifact (REQ-107): - core/separation_of_duties.py: real SNS publish (ACDL_SOD_HALT_TOPIC_ARN) + outbox fallback (SEPARATION_OF_DUTIES_VIOLATION event via outbox_writer) + stderr emission. No silent print-only stub. - terraform/platform/main.tf: aws_sns_topic.acdl-sod-halt + output. HITL attestation gates (REQ-108): - core/hitl_gates.py: attest(contract_id, env, approver, evidence, outbox_client) records approver_qa/approver_prod/approver_dr to outbox, runs SoD check on prod, invokes attestation matrix, returns (ok, reason). Dev skips (autonomous). approver_from_env() reads GITHUB_ACTOR/GITEA_ACTOR. - scripts/run_platform.sh: Step 7b HITL gate before apply for qa/prod/dr. 8-concern attestation matrix (REQ-109, D-084): - core/attestation_matrix.py: check(env, evidence) runs the 8 concerns from hitl_matrix_design.md §10.4. Offline-testable (contract_nfrs, schema_validity, policy_pass) run for real. Operator-supplied accept signed artifacts validated for freshness (FRESHNESS_DAYS table) + schema. Signature skip when ACDL_ATTESTATION_SIGNING_KEY_ID unset (D-089). Fail loud if missing/expired for prod/dr. Wiz real client (REQ-110): - adapters/wiz/wiz_adapter.py: WizClient (GraphQL API, Bearer auth, pagination via pageInfo.hasNextPage + endCursor). fetch_and_adapt translates issues → PolicyCheckResult; graceful degrade when WIZ_API_TOKEN/WIZ_API_URL unset. Kyverno fleshed out (REQ-111): - adapters/kyverno/kyverno_adapter.py: full PolicyReport → PolicyCheckResult mapping (pass/fail/skip/warn + severity + skip-with- reason + resource ref construction from kind/name/namespace). adapt_inactive() emits KYVERNO_INACTIVE_TF_STACK guard. --kube-version stub parsed for future GitOps. Tests: +47 (test_route_halt_artifact.py, test_hitl_gates.py, test_attestation_matrix.py, test_wiz_adapter_real_client.py, expanded test_kyverno_adapter.py). Existing wiz_adapter tests updated for the real client's control.name ruleId. 493 passed; run_ci.sh green; run_platform.sh --check-only green. |
||
|
|
134f85d2df |
feat(P34): decommission alias + CMDB validation (REQ-92, REQ-93, REQ-94)
---ci--- project: acdl phase: 34 milestone: v1.8 status: execute ---/ci--- - DynamoDB acdl-change-requests table added to terraform/platform/main.tf (PK changeRequestId, SK submittedAt, SSE via CMK, PITR). - validate_change_request Lambda action added to contract_ingestor.py: queries CMDB, asserts status=approved + consumerRepo match. - decommission_transform() added to contract_resolver.py: zeroes all counts (desired_count, min/max_capacity) + sets deletion_protection=false. - Decommission mode added to deploy pipeline + both deploy workflows (mode: decommission + changeRequestId input). Byte-identical. - run_platform.sh --decommission flag: validates CR, resolves with deletion_protection=false (step 1), then decommission_transform (step 2). HITL SRE gates documented. - docs/consumer-guide.md: new "Decommissioning a stack" section with CR request, trigger, 2-step HITL SRE gates, CMK deletion window, uptime. Tests: +14 (318 -> 332). All pass. |
||
|
|
1e4133e11a |
fix(P30): temp dir isolation + forge-agnostic APIs + static-key override (P1-8, P1-9, S1)
---ci---
project: acdl
phase: 30
milestone: v1.8
status: execute
---/ci---
P1-8: run_platform.sh now emits adapter output to $WORK/tf (per-run temp
dir), not the committed terraform/spike/ directory. The committed
terraform/spike/*.tf files are removed — they were scratch artifacts.
Deploy workflow artifact upload path updated to /tmp/acdl_platform_run_v18/tf/.
P1-9: contract_ingestor.py now reads GITHUB_API_BASE env for forge-agnostic
API URLs. _forge_type() detects GitHub vs Gitea. Search URL is branched
(GitHub uses /search/issues, Gitea uses /repos/{owner}/{repo}/issues).
S1: Deploy workflow configure-aws-credentials step restructured as a single
conditional step. OIDC when no static key (role-to-assume), static-key
when ACDL_AWS_ACCESS_KEY_ID present (access-key-id/secret-access-key inputs).
Both deploy workflows remain byte-identical.
Tests: +8 (292 -> 300). All pass. run_platform.sh --check-only green.
|
||
|
|
843cd17b97 |
fix(P29): SSM fail-loud without CMK + Terraform-rendered invoke policy (P1-3, P1-6)
---ci---
project: acdl
phase: 29
milestone: v1.8
status: execute
---/ci---
P1-3: SSM publisher now raises RuntimeError when ACDL_KMS_KEY_ID is
unset. ACDL_ALLOW_DEFAULT_KMS=1 escape hatch for local testing.
P1-6: consumer_invoke_policy.json now uses ${account_id} and ${region}
placeholders. Terraform renders them via data.aws_caller_identity +
data.aws_region + replace() at apply time. No more hardcoded 000000000000.
Tests: +7 (285 -> 292). All pass.
|
||
|
|
0eb578c606 |
fix(P28): WAF nested rules + default_action + resolver outputs (P1-4, P1-5, P1-7)
---ci---
project: acdl
phase: 28
milestone: v1.8
status: execute
---/ci---
P1-4: WAF custom rules now emit nested `rules { ... }` blocks per rule
instead of `rules = [...]` attribute syntax (invalid HCL).
P1-5: WAF default_action input is honored (allow/block) instead of
hardcoding `allow {}`. Default is `allow` when absent (backward compat).
P1-7: L2 composition outputs[] array is now processed by resolve_l2().
The resolver builds stack.outputs from the composition outputs wires.
The adapter emits `output` blocks from stack.outputs.
Tests: +10 (275 -> 285). All pass. run_platform.sh --check-only green.
|
||
|
|
07c0349131 |
feat(P24): platform Lambda + DynamoDB contract ingestion + cross-account IAM
Phase 24 — platform-lambda-and-contract-ingestion. - core/lambda/contract_ingestor.py: AWS Lambda handler invoked via Function URL (IAM auth). Parses JSON body, validates required fields, writes the contract to DynamoDB table acdl-contracts (PK consumerRepo, SK contractId#submittedAt, status submitted, ISO-8601 submittedAt). report_error action is a stub returning "error_report_prepared"; GitHub issue creation is wired in Phase 25. Returns 400 on missing fields / unknown action, 500 on error. Table name + GitHub-token secret ID come from env (set by Terraform). - core/lambda/__init__.py: empty package marker. - terraform/platform/main.tf: DynamoDB acdl-contracts (PITR, SSE via CMK), KMS customer-managed key with alias/acdl-platform, Secrets Manager secret acdl/github-token, IAM execution role (DynamoDB write + Secrets Manager read + KMS decrypt + CloudWatch logs), Lambda acdl-contract-ingestor (Python 3.12, handler contract_ingestor.lambda_handler), Function URL with AWS_IAM auth. State key platform/terraform.tfstate (distinct from spike/microservice). - terraform/platform/README.md: documents what it deploys, the state key, how to apply, and the cross-account invocation model. - terraform/platform/consumer_invoke_policy.json: ABAC-scoped policy template applied to consumer deploy roles during onboarding; grants lambda:InvokeFunctionUrl conditioned on aws:PrincipalTag/acdl:owner == consumerRepo. - tests/test_contract_ingestor.py: 11 tests (moto-backed DynamoDB mock) covering submit_contract put_item shape, report_error stub, missing-field 400, unknown action 400, the lambda_handler wrapper with a Function-URL-style event, dict body, default action, and internal-error 500. - docs/environments/index.md: new section documenting the cross-account contract-ingestion grant (one-way consumer→platform, D-051) and that onboarding now also grants the consumer deploy role InvokeFunctionUrl. - scripts/run_ci.sh, pipelines/ci.yaml, .gitea/workflows/ci.yml, .github/workflows/ci.yml: add core/lambda/contract_ingestor.py to the lint py_compile list. The two workflow YAMLs remain byte-identical. Verification: scripts/run_ci.sh passes all 3 stages (lint/test/check-only); python3 -m pytest tests/ -v passes all 213 tests (11 new + 202 existing). ---ci--- project: acdl phase: 24 milestone: v1.7 status: execute ---/ci--- |
||
|
|
dca35c78ec |
feat(P22): rename static-asset→static-assets + cloudfront/waf primitives + production stack + @v1.6 bump
---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 (
|
||
|
|
f68f85c9fd |
review(v1.5): READY TO SHIP — multi-persona code review
---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
|
||
|
|
1598c54a8b |
feat(P18): testing + CI/CD pipelines - pytest suite, check-only mode, Gitea + GitHub workflows (v1.3.2)
90 offline tests covering adapter, confidence_signal, checkov_adapter, outbox_writer, and pipeline integration. Identical CI/CD workflows for Gitea Actions (dev) and GitHub Actions (production). New --check-only mode for run_platform.sh (offline, no AWS). ---ci--- project: acdl phase: 18 milestone: v1.3 status: verify ---/ci--- |
||
|
|
f874879973 |
fix: compress spike_runner_policy.json to fit AWS 2048-char inline limit
---ci--- project: acdl phase: 0 milestone: v1.2 status: fix ---/ci--- The expanded policy (4727 chars pretty / 3464 compact) exceeded the AWS 2048-char inline policy limit (total across all inline policies on a user). Compressed to 1667 chars by: (1) removing DenyEverythingElse (redundant — IAM is default-deny; the user has no other inline policies), (2) using action-prefix wildcards (ecs:Create*, ecr:Get*, etc.) instead of listing every action, (3) removing SIDs. The compressed policy grants the same effective permissions. The repo file now matches what should be applied in the AWS Console. |
||
|
|
699aa542df |
docs(P15): plan-as-execute + verify (v1.2.5, PARTIAL — terraform apply blocked by IAM)
---ci---
project: acdl
phase: 15
milestone: v1.2
status: verify
verdict: PARTIAL
requirements:
covered: [REQ-34]
partial: [REQ-33]
blocker:
- id: P0-IAM
description: terraform apply fails with AccessDenied on ECS/ECR/IAM/EC2 — live spike_runner_policy.json not pushed (root key deactivated per D-034)
unblock: operator runs create_iam_user.py with root/admin creds to push the expanded policy, then terraform apply succeeds (plan valid, 13 to add)
---/ci---
Phase 15 plan-as-execute + verify. PARTIAL: terraform apply blocked by IAM.
- Consumer microservice content authored (app.py + Dockerfile + README.md).
- Docker image acdl-microservice:latest built.
- Adapter fixed: ref emission (bare), JSON-string jsonencode, ECS service
network_configuration/load_balancer/desired_count/launch_type/task_definition,
listener default_action/load_balancer_arn, target group target_type/vpc_id/protocol,
VPC tags (not name), IGW + route table association, managed_policy_arns list.
- L1 fixes: l1-ecs-service (removed port from service sub-resource),
l1-vpc (added intra_refs, removed igw_id output).
- Resolver: intra_refs resolution (refs between sub-resources of same L1).
- terraform validate + plan succeed (13 to add).
- terraform apply BLOCKED (AccessDenied — live IAM policy not updated).
- Evidence event TERRAFORM_APPLY_BLOCKED written to DynamoDB outbox.
- v1.1 S3 regression: byte-identical.
Ready to ship v1.2.5 (partial).
|
||
|
|
0fea29cdbb |
docs(P12): plan-as-execute + verify (v1.2.2)
---ci--- project: acdl phase: 12 milestone: v1.2 status: verify verdict: VERIFIED requirements: covered: [REQ-30] ---/ci--- Phase 12 plan-as-execute + verify. scripts/verify_phase12.sh green (22 assertions). All Wave 1 + Wave 2 tasks complete: - T-12.1: run_spike_*.sh -> run_platform.sh (D-048, --plan-only flag) - T-12.2: spike_runner_policy.json expanded (ECS + ECR + ELB + IAM + EC2) - T-12.3: idempotency documented in bootstrap scripts - T-12.4: P1-1 redacted (no live AWS key IDs in .ciagent/) - T-12.5: P1-B fixed (PERSONAS.md platform/registry -> modules-ir/registry.json) Subagent confirmed run_platform.sh --plan-only runs against real AWS, exit 0. Ready to ship v1.2.2. |
||
|
|
622abe015b |
phase: 10, status: plan-as-execute, persona: backend-engineer, task: T-10.4+T-10.5+T-10.7
---ci---
project: acdl
phase: 10
milestone: v1.1
status: plan-as-execute
persona: backend-engineer
task: [T-10.4, T-10.5, T-10.7]
requirements.covered: [REQ-27]
---/ci---
Wave 2: contract + resolver + outbox writer.
- T-10.4: contracts/spike.yaml - the spike contract (stack:
l2-static-asset, environment: dev, inputs bucket_name + region). D-P10-2:
YAML consumer surface; the resolver parses YAML -> validates against the
JSON contract schema.
- T-10.5: acdl_platform/contract_resolver.py - resolve(contract_path) ->
IR instance. 7 steps: load YAML, validate contract schema, look up L2 in
registry, load composition.json, map inputs through wires, emit IR
instance, validate IR against ir.schema.json. Verified end-to-end:
spike.yaml -> IR instance with kind=l2, one l1-s3 resource, validates
against ir.schema.json.
- T-10.7: acdl_platform/outbox_writer.py - write_event(event) ->
DynamoDB put_item. SHA-256 over canonical JSON, prev_event_hash=GENESIS
for the first event (D-P10-3), PK contractId, SK eventType#eventTs, TTL
expire_at = now + 365d (D-044). stdlib + boto3.
Also regenerated terraform/spike/{main.tf,terraform.tf} by running the
adapter against the resolved L2 IR (the backend key is now
spike/l2-static-asset/terraform.tfstate, derived from the stack name per
D-P10-1).
|
||
|
|
3070a68e1d |
phase: 9, status: plan-as-execute, persona: platform-engineer, task: T-9.5..T-9.7+T-9.9
---ci---
project: acdl
phase: 9
milestone: v1.1
status: plan-as-execute
persona: platform-engineer
task: [T-9.5, T-9.6, T-9.7, T-9.9]
requirements.covered: [REQ-26]
---/ci---
Waves 2+3: Terraform adapter + generated spike TF + run script.
- T-9.5: adapters/terraform/adapter.py - compiles an IR instance to a
Terraform root module. TYPE_MAP {aws:s3:bucket -> aws_s3_bucket}. Thin
layer; does not own L1 content. Emits main.tf (resource + outputs) +
terraform.tf (required_version/providers + S3 backend, NO
dynamodb_table per D-P09-1) + providers.tf (aws provider region from
the IR). CLI: adapter.py <ir_instance.json> <out_dir>.
- T-9.6: terraform/spike/{main.tf,terraform.tf,providers.tf} - generated
by running the adapter against modules-ir/l1/l1-s3/spike_instance.json.
Committed so verify_phase09.sh can validate/plan without regenerating
(D-P09-4); the verify script will regenerate + diff to prove
reproducibility.
- T-9.7: scripts/run_spike_plan.sh - loads rotated spike key from
gitignored .env.secrets, exports AWS env vars, cd terraform/spike,
terraform init -lock=false, terraform validate, terraform plan
-lock=false -out=tfplan. Plan-only; no apply.
- T-9.9: .gitignore - add terraform/spike/.terraform/ + tfplan +
*.tfstate*.
EXECUTE: ran scripts/run_spike_plan.sh against real AWS via the rotated
spike key (D-039). terraform plan succeeded: 1 to add (the S3 bucket),
outputs computed. One non-blocking deprecation warning (aws_s3_bucket
versioning block -> use aws_s3_bucket_versioning in v1.2). No long-lived
credential in the workflow (key loaded from .env.secrets at runtime).
|
||
|
|
1d5c4d2ae7 |
phase: 8, status: plan-as-execute, persona: platform-engineer+lead-developer, task: T-8.5..T-8.7
---ci--- project: acdl phase: 8 milestone: v1.1 status: plan-as-execute persona: platform-engineer+lead-developer task: [T-8.5, T-8.6, T-8.7] requirements.covered: [REQ-23] ---/ci--- Waves 3+4: rotation script + verify script + README + .gitignore. - T-8.5 (platform): scripts/rotate_spike_key.sh - boto3 with bootstrap root key from env (ACDL_BOOTSTRAP_AWS_*); creates new key for acdl-spike-runner, deactivates+deletes old, writes new to gitignored .env.secrets (chmod 600); idempotent (re-run ends with exactly 1 active key); optional Gitea secret upload if ACDL_GITEA_TOKEN set; does NOT rotate the root key (D-034 closure = manual user step). - T-8.6 (lead): scripts/verify_phase08.sh - loads rotated key from .env.secrets, asserts caller identity is acdl-spike-runner (not root), S3 bucket + DynamoDB table + IAM user + scoped policy with DenyEverythingElse all present, .env.secrets + .bootstrap_state.json gitignored. Uses heredoc python to avoid bash quoting issues. - T-8.7 (lead): terraform/bootstrap/README.md runbook (6 steps incl. manual D-034 root-key rotation) + .gitignore (.env.secrets + .bootstrap_state.json). Spike vs v1.2 boundary table. bash -n + gitignore checks pass. |
||
|
|
f8ddd8b182 |
phase: 8, status: plan-as-execute, persona: security-engineer+platform-engineer, task: T-8.1..T-8.4
---ci--- project: acdl phase: 8 milestone: v1.1 status: plan-as-execute persona: security-engineer+platform-engineer task: [T-8.1, T-8.2, T-8.3, T-8.4] requirements.covered: [REQ-23] ---/ci--- Waves 1+2: IAM policy + state backend + IAM user creation scripts. - T-8.1 (security): terraform/bootstrap/spike_runner_policy.json — least-privilege IAM policy: Allow S3 r/w on the state bucket, DynamoDB r/w on the outbox table, sts:GetCallerIdentity; final Deny statement (Action *, NotResource = the above ARNs) enforcing least privilege. No terraform apply permission (plan-only spike). - T-8.2/T-8.3 (platform): terraform/bootstrap/create_state_backend.py — boto3, idempotent: creates S3 bucket acdl-tfstate-581513795199-us-east-1 (versioning enabled) + DynamoDB table acdl-outbox (PAY_PER_REQUEST, PK contractId, SK eventType#eventTs per D-P08-1 one table for both lock + outbox). Writes .bootstrap_state.json marker. - T-8.4 (platform + security review): terraform/bootstrap/create_iam_user.py — boto3, idempotent: creates IAM user acdl-spike-runner, attaches the inline policy from spike_runner_policy.json, creates an initial access key if none active exists (prints to stdout for the orchestrator to capture; NEVER committed). py_compile + policy JSON valid. |
||
|
|
e044a2de0d |
phase: 6, status: plan-as-execute, persona: lead-developer, task: T-6.1..T-6.4
---ci--- project: acdl phase: 6 milestone: v1.1 status: plan-as-execute persona: lead-developer tasks: [T-6.1, T-6.2, T-6.3, T-6.4] ---/ci--- Archive the v1.0 demo under demo/ (D-037) and reorient the repo to the real platform. Wave 1 of the Phase 06 plan. - T-6.1: git mv modules/, scripts/, evidence-ui/, contracts/, contracts-repo/, .gitea/ -> demo/; mv ACDL_DEMO.md + runner-data/ -> demo/ - T-6.2: scaffold new v1.1 top-level dirs (platform/, schemas/, adapters/, terraform/, modules-ir/) with .gitkeep - T-6.3: create top-level scripts/verify_phase06.sh (v1.1 verify scripts live at top-level, NOT demo/scripts/ which holds the v1.0 demo verify scripts) - T-6.4: rewrite README.md to reflect the real platform (vision + architecture links, new layout, status v1.1 active); add runner-data/ to .gitignore All moves via git mv (history preserved). Repo root now contains only README.md, demo/, docs/, .ciagent/, and the new empty v1.1 dirs. |