Commit Graph

13 Commits

Author SHA1 Message Date
Jon Chery e1be05287b feat(P39): refresh design docs + parameterize adapter (P1-1)
---ci---
project: acdl
phase: 39
milestone: v1.9
status: execute
---/ci---

Phase 39 — design-doc-refresh-and-p1-1-parameterization:

Design docs (REQ-100, REQ-101):
- hitl_matrix_design.md: 'dev-only spike'/'v1.2 wires the gates' framing
  replaced with v1.9 wired-gates reality; 8-concern matrix marked
  implemented (offline-testable subset + signed evidence artifacts,
  D-084); v1.9 wiring section cross-references hitl_gates.py +
  attestation_matrix.py; approver_dr noted.
- audit_ledger_design.md: outbox marked shipped+production since v1.8;
  S3 Object Lock + JWS + async worker + DLQ + daily checkpoints clearly
  labeled 'Deferred to a future milestone (D-083)'; RPO/RTO table updated;
  approver fields note v1.9 hitl_gates.attest.

P1-1 adapter parameterization (REQ-102, D-085):
- ecs-service interface.json: desired_count (default 1), launch_type
  (FARGATE), family (app) inputs added.
- alb interface.json: load_balancer_type (application), target_type (ip).
- adapter.py: hardcoded defaults replaced with inputs.get(<name>, <default>);
  hardcoded 'acdl-microservice-rt'/'acdl-microservice-igw' Name tags
  derive from the VPC name input.
- contract_resolver.py: child_input_map routes wires to the sub-resource
  that declares the input (desired_count → aws:ecs:service, family →
  aws:ecs:task_definition, target_type → targetgroup, etc.).
- microservice composition.json: wires added for the new inputs.

Tests: +21 (test_p1_1_adapter_parameterization.py, test_design_docs_current.py).
371 passed; run_ci.sh green; run_platform.sh --check-only green; v1.1 S3
regression preserved.
2026-07-23 04:24:25 +00:00
Jon Chery 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.
2026-07-22 22:18:28 +00:00
Jon Chery 8145eee8fc feat(P32): deletion-protection-by-default + L2 feature flag (REQ-86, REQ-87)
---ci---
project: acdl
phase: 32
milestone: v1.8
status: execute
---/ci---

- All 11 L1 primitives now have deletion_protection NFR (boolean, default true).
- Adapter emits `lifecycle { prevent_destroy = true }` when NFR is true;
  omits it when false. Default is true when NFR is absent.
- L2 composition resolver propagates inputs.deletion_protection to all
  children NFRs. When false, all resources get deletion_protection=false.
- Stack schema updated with optional features object (deletion_protection,
  uptime_enabled).
- Contract schema description updated to document deletion_protection
  and uptime_enabled inputs.

Tests: +5 (307 -> 312). All pass.
2026-07-22 22:12:42 +00:00
Jon Chery 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.
2026-07-22 22:08:23 +00:00
Jon Chery 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.
2026-07-22 22:05:40 +00:00
Jon Chery 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.
2026-07-22 22:03:54 +00:00
Jon Chery 2e2064559a verify(P22-27): code review — 1 P0 auto-fixed, 2 P1 security fixes, 2 P2 nits
acdl-ci / Lint (push) Successful in 8s
acdl-ci / Test (push) Successful in 25s
acdl-ci / Platform check-only (offline) (push) Successful in 10s
---ci---
project: acdl
phase: 22-27
milestone: v1.7
status: verify
lessons:
  - P0 fix: run_platform.sh check-only assertions were hardcoded to static-assets; generalized for all contracts
  - P1 fix: URL-encode contractId in GitHub issue search to prevent query injection
  - P1 fix: validate consumerRepo format against invoking principal identity (P1-2)
  - P2 fix: tagging-standard.json description referenced .yaml instead of .py
  - P2 fix: removed unused graph_resource_name_utils import in acdl_tagging.py
---/ci---

Multi-persona code review of the v1.7 milestone (130 files, +5568/-353).

P0 (1, auto-fixed):
  - run_platform.sh --check-only hardcoded static-assets assertions broke
    for other contracts (microservice). Generalized to structural checks.

P1 security fixes applied (2 of 9):
  - P1-1: URL-encode contractId in GitHub search query (injection prevention)
  - P1-2: Validate consumerRepo format (org/repo) when caller identity present

P1 flagged for post-hoc (7):
  - P1-3: SSM uses AWS-managed key, not platform CMK (ACDL_KMS_KEY_ID not set)
  - P1-4: WAF custom rules emit invalid HCL (attribute vs block syntax)
  - P1-5: WAF default_action input silently ignored (always emits allow {})
  - P1-6: consumer_invoke_policy.json has placeholder account ID (needs substitution)
  - P1-7: L2 composition outputs section not implemented in resolver
  - P1-8: terraform/spike/*.tf overwritten by run_platform.sh (state contamination)
  - P1-9: GitHub API URLs hardcoded (Gitea deployments silently fail)

P2 nits fixed (2 of 8):
  - P2-2: tagging-standard.json description referenced .yaml instead of .py
  - P2-3: unused graph_resource_name_utils import removed

Tests: 275 passed (was 272; +3 caller identity validation tests).
2026-07-22 21:09:52 +00:00
Jon Chery a4b17d0f26 fix(P26): resolve multi-resource L1 ref ids in contract resolver
---ci---
project: acdl
phase: 26
milestone: v1.7
status: execute
---/ci---

The microservice pattern (and any L2 referencing multi-resource L1s like
vpc) failed at the adapter stage because the resolver emitted refs using
the child id (e.g. 'vpc') instead of the expanded sub-resource id (e.g.
'vpc-subnet'). The adapter's type_by_id table only knows the sub-resource
ids, so ref:vpc.subnet_ids was an unknown resource id.

Fix:
- contract_resolver.py: child_outputs now maps {outputName -> resourceId}
  instead of just the interface outputs dict. For multi-resource L1s, the
  ref uses the sub-resource id that produces the output. For single-resource
  L1s, the resourceId == childId (unchanged behavior).
- vpc interface.json: the subnet sub-resource output is 'subnet_ids'
  (matching the interface-level output name) instead of 'subnet_id'.
- adapter.py OUTPUT_MAP: aws:ec2:subnet now maps both 'subnet_ids' and
  'subnet_id' to 'id'.

Verification:
  - microservice pattern check-only: PASS (11 resources)
  - static-assets pattern check-only: PASS (4 resources)
  - platform check-only: PASS
  - full test suite: 266 passed
2026-07-22 20:15:59 +00:00
Jon Chery 4fe794c7a4 feat(P25): deploy outputs (SSM + PR comment) + error reporting via Lambda + stage comments
---ci---
project: acdl
phase: 25
milestone: v1.7
status: execute
---/ci---
2026-07-22 20:08:30 +00:00
Jon Chery 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---
2026-07-22 20:04:10 +00:00
Jon Chery a90a7562b9 verify(P21): code review — 1 P0 auto-fixed, 3 P1+ flagged
acdl-ci / Lint (push) Successful in 8s
acdl-ci / Test (push) Successful in 18s
acdl-ci / Platform check-only (offline) (push) Successful in 9s
---ci---
phase: 21
milestone: v1.6
status: verify
lessons:
  - P0 fix: docs/_config.yml had conflicting theme + remote_theme (would
    break the GitHub Pages build). Removed the conflicting theme: key,
    kept remote_theme: minimal-mistakes.
  - P2 fix: removed unused json + os imports from core/environment_check.py.
---/ci---

Multi-persona review of v1.6 phase 21 (docs restructure + core rename +
environments scaffold).

P0 (blocking) — AUTO-FIXED:
- M1: docs/_config.yml declared both  and
  . Jekyll rejects using
  both; the Pages build would fail. Fixed: removed the  line,
  kept  (minimal-mistakes, which provides the
  layout the defaults reference).

P2 (nits) — AUTO-FIXED:
- M2: core/environment_check.py imported  +  but never used
  them. Removed.

P1 (important) — FLAGGED FOR POST-HOC REVIEW (do not block ship):
- C1 (pre-existing, from v1.5 review C2): .github/workflows/deploy.yml
  checks out the platform repo at , but no floating  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 (or v1.6.0 now that it exists). The
  consumer guide + sample contract also reference @v1.4.
- C2: docs/_config.yml  key is not a standard minimal-mistakes
  navigation config (that theme reads _data/navigation.yml). The
  key is harmless metadata but won't render a real nav. Recommend adding
  docs/_data/navigation.yml for the theme, or switching to a theme that
  reads  from _config.yml. Non-blocking for the docs content.
- S1 (pre-existing, from v1.5 review S1): the static-key override in
  deploy.yml sets ACDL_AWS_ACCESS_KEY_ID/ACDL_AWS_SECRET_ACCESS_KEY as env
  vars on the configure-aws-credentials step, but that action reads AWS_*
  or its own access-key/secret-key inputs, not ACDL_AWS_*. The override
  is not actually wired. Phase 21 did not touch this step.

Verified: byte-identical workflows (CI + deploy); dev.json valid JSON;
all core Python compiles; path-traversal on --env is safe (no file match
-> onboarding prompt, exit 1); all docs internal links resolve; 166
tests pass; run_ci.sh green. The run_platform.sh env-check ordering is
correct (default contract is assigned before the env check runs).
2026-07-22 18:41:41 +00:00
Jon Chery 4e495e5648 feat(P21): environments concept + onboarding scaffold (REQ-61)
---ci---
project: acdl
phase: 21
milestone: v1.6
status: execute
---/ci---

Introduce platform-managed environments: a consumer does not provide an
AWS account, VPC, subnet, S3 state bucket, or runner key. A named
environment is a platform-owned bundle of account + network + state
backend + IAM role (surfaced via ABAC), selected by name in the contract.

Scaffold:
- core/environments/dev.json (sample dev env definition)
- core/environments/README.md (how envs are used + how to add one)
- core/environment_check.py (reads a contract's environment field,
  looks up core/environments/<name>.json, prints a friendly onboarding
  prompt when none exists, exits non-zero)
- scripts/run_platform.sh: Step 0 calls environment_check.py before
  contract validation; a missing env halts the pipeline with the
  onboarding prompt
- tests/test_environment_check.py: 12 tests (dev bound, missing env ->
  onboarding prompt, onboarding message lists provisions, contract
  paths, wire-in, check-only still passes)

Tests: 166 pass (154 + 12 new).
2026-07-22 18:24:02 +00:00
Jon Chery b758a7c242 refactor(P21): rename acdl_platform/ -> core/ (REQ-53)
---ci---
project: acdl
phase: 21
milestone: v1.6
status: execute
---/ci---

Rename the acdl_platform/ package to core/ across the directory, all
imports in tests/scripts/pipelines/workflows, and doc references. The
package is imported as core.confidence_signal / core.contract_resolver /
core.outbox_writer. The deploy workflow's platform-repo checkout dir is
renamed acdl-platform/ -> platform/ (workspace path, not the python
package). Both .gitea + .github workflows stay byte-identical.

Note: the original target name 'platform/' shadows Python's stdlib
platform module (pytest's import uuid -> platform.system() fails when
the repo root is on sys.path, which every test does). 'core/' avoids
the clash while honoring the intent (drop the verbose acdl_platform).

Tests: 154 pass. run_ci.sh green.
2026-07-22 18:21:12 +00:00