diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index f295ee2..1a509d1 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -1,145 +1,49 @@ --- -phase: 10 -name: v1-spike-l2-and-contract-e2e -milestone: v1.1 -milestone_type: feature -status: planned -requirements: [REQ-25, REQ-27, REQ-28] -must_haves: - - "modules-ir/l2/l2-static-asset/composition.json exists with kind=l2, depth=1, one child l1-s3@1.0.0, wires passthrough" - - "modules-ir/registry.json extended with l2-static-asset@1.0.0 entry" - - "modules-ir/l2/l2-static-asset/README.md documents the thin-composition" - - "contracts/spike.yaml exists (stack: l2-static-asset, environment: dev, inputs: bucket_name + region)" - - "acdl_platform/contract_resolver.py exists, py_compiles, loads YAML contract -> validates against contract.schema.json -> resolves L2 composition -> emits IR instance validating against ir.schema.json" - - "adapters/terraform/adapter.py extended to handle kind=l2 IR instances (D-P10-1: shallow L2 root module = the L1 resource)" - - "acdl_platform/outbox_writer.py exists, py_compiles, writes a DynamoDB outbox item (PK contractId, SK eventType#eventTs, prev_event_hash=GENESIS, hash=SHA-256 canonical JSON, expire_at TTL)" - - "scripts/run_spike_e2e.sh exists, bash -n passes, orchestrates the 10-step end-to-end pipeline" - - "scripts/verify_phase10.sh exists, bash -n passes, asserts all success criteria + REQ-28 (grep: only adapters/ files contain aws_s3_bucket; modules-ir/ schemas/ contracts/ acdl_platform/ are substrate-agnostic)" - - "Evidence event is written to the DynamoDB outbox (verified by querying the table)" - - "Confidence band is 'pass' for dev (score >= 0.50)" -verification: - typecheck: "python3 -m py_compile acdl_platform/contract_resolver.py acdl_platform/outbox_writer.py adapters/terraform/adapter.py && bash -n scripts/run_spike_e2e.sh scripts/verify_phase10.sh" - test: "scripts/verify_phase10.sh" - build: "terraform -chdir=terraform/spike init -lock=false" +phase: 11 +name: v1.2-research-and-readme +milestone: v1.2 +requirements: [REQ-29] +type: docs +branch: phase/11-v1.2-research-and-readme --- -# Phase 10 — v1-spike-l2-and-contract-e2e PLAN +# Phase 11 — v1.2-research-and-readme (v1.2) PLAN ## Goal -The milestone capstone. Implement `l2-static-asset` (thin-composition -referencing `l1-s3` only, depth 1), the contract→IR resolution, and one -end-to-end contract submission flowing through: contract schema -validation → IR resolution → `terraform plan` (real AWS) → Checkov -`PolicyCheckResult` → confidence signal → evidence event to the DynamoDB -outbox. Verify the IR commitments hold (REQ-28: the adapter is the only -substrate-specific code; no polyglot mess). +Re-evaluate go-gitea/gitea#36988 (OIDC); audit the v1.1 spike for NFR gaps ++ simplification opportunities; rewrite README.md to reflect v1.1 complete ++ the actual platform flow + v1.2 objective. -After Phase 10 ships + verifies: the COMPLETE gate (review → ship v1.2.0 -→ audit). +## Tasks (single wave — docs territory) -## Requirements covered +### T-11.1 — RESEARCH.md v1.2 addendum (REQ-29) +- TARGET 9: #36988 re-check → D-047 (extend D-039 waiver). +- TARGET 10: NFR audit of spike (IAM, idempotency, error handling, P1-1, stale paths). +- TARGET 11: Simplification opportunities (script consolidation D-048). +- TARGET 12: README rewrite plan. +- TARGET 13: ECS L1 catalog scoping (D-049) for Phase 13. +- Decisions surfaced: D-047, D-048, D-049. +- Territory: `.ciagent/RESEARCH.md` -- **REQ-25** → T-10.1 (composition.json), T-10.2 (registry extend), T-10.3 (README) -- **REQ-27** → T-10.4 (spike.yaml), T-10.5 (contract_resolver.py), T-10.6 (adapter L2 extension), T-10.7 (outbox_writer.py), T-10.8 (run_spike_e2e.sh) -- **REQ-28** → T-10.9 (verify_phase10.sh with the substrate-agnostic grep check) +### T-11.2 — README.md rewrite (REQ-29) +- Status section: v1.2 active, v1.1 complete (tag v1.2.0), v1.0 archived. +- "How the platform works": the spike flow diagram + the IR commitments claim. +- "What's different in v1.2": 6 L1s, l2-microservice, terraform apply, consumer repo, run_platform.sh, NFR hardening. +- "How to run": prerequisites, bootstrap, rotate, run_spike_e2e.sh, demo. +- Repo layout table (refreshed Status column). +- Environments table (dev/qa/prod/dr + status). +- Credentials section (D-034 closed, D-039/D-047 waiver, #36988 link). +- Territory: `README.md` -## Waves +## Verification -``` -Wave 1 (platform-engineer) — T-10.1 composition.json, T-10.2 registry, T-10.3 README, T-10.6 adapter L2 extension -Wave 2 (backend-engineer) — T-10.4 spike.yaml, T-10.5 contract_resolver.py, T-10.7 outbox_writer.py -Wave 3 (backend+security) — T-10.8 run_spike_e2e.sh -Wave 4 (lead-developer) — T-10.9 verify_phase10.sh -Wave 5 (lead, EXECUTE-only) — run e2e + verify + traceability + ship -``` +- `grep -c "v1.2" README.md` ≥ 5 (v1.2 referenced throughout). +- `grep "v1.1 (active)" README.md` returns nothing (no stale framing). +- `.ciagent/RESEARCH.md` has a "## v1.2 Research Addendum" section. +- D-047/D-048/D-049 present in the addendum's decisions table. +- `scripts/verify_phase11.sh` (authored in the verify step). -Dependencies: Wave 2 → Wave 1 (resolver reads composition.json); Wave 3 → Wave 1+2; Wave 4 → all prior; Wave 5 EXECUTE-only. +## Ship ---- - -### Wave 1 — platform-engineer: L2 composition + registry + adapter extension - -#### T-10.1 — Author `modules-ir/l2/l2-static-asset/composition.json` -- **Owner:** platform-engineer -- **Content:** `{name: l2-static-asset, version: 1.0.0, kind: l2, depth: 1, children: [{id: s3, module: l1-s3@1.0.0}], wires: {bucket_name: {target: s3, input: bucket_name}, region: {target: s3, input: region}}}` - -#### T-10.2 — Extend `modules-ir/registry.json` with l2-static-asset@1.0.0 -- **Owner:** platform-engineer -- **Content:** add `"l2-static-asset": {"1.0.0": {"composition": "modules-ir/l2/l2-static-asset/composition.json", "published_at": "", "deprecated": false}}` - -#### T-10.3 — Author `modules-ir/l2/l2-static-asset/README.md` -- **Owner:** platform-engineer -- **Content:** thin-composition doc (references l1-s3 only, depth 1, wires passthrough, the adapter's L2→root-module translation, D-P10-1) - -#### T-10.6 — Extend `adapters/terraform/adapter.py` for kind=l2 -- **Owner:** platform-engineer -- **Content:** D-P10-1: the adapter consumes the *resolved IR instance* (which has kind=l2 + the L1 resources as its resources array). For a depth-1 thin-composition, the L2 root module IS the L1's resource — no separate module block. The existing `adapt()` + TYPE_MAP + resource emission handle both l1 and l2 instances (the resources array is the same shape). The only L2-specific bit: the `relationships` array is ignored at TF level for the spike. Update the backend key to `spike/l2-static-asset/terraform.tfstate` for the L2 spike. - ---- - -### Wave 2 — backend-engineer: contract + resolver + outbox writer - -#### T-10.4 — Author `contracts/spike.yaml` -- **Owner:** backend-engineer -- **Content:** `stack: l2-static-asset\nenvironment: dev\ninputs:\n bucket_name: acdl-spike-bucket\n region: us-east-1\n` - -#### T-10.5 — Author `acdl_platform/contract_resolver.py` -- **Owner:** backend-engineer -- **Content:** `resolve(contract_path) -> ir_instance` dict. Steps: (1) load YAML; (2) validate against contract.schema.json; (3) look up the L2 in registry.json; (4) load composition.json; (5) map contract inputs through wires to child L1 inputs; (6) emit IR instance; (7) validate IR against ir.schema.json. CLI: `contract_resolver.py `. stdlib + jsonschema + yaml only. - -#### T-10.7 — Author `acdl_platform/outbox_writer.py` -- **Owner:** backend-engineer -- **Content:** `write_event(event, outbox_table="acdl-outbox")` — compute SHA-256 over canonical JSON, set prev_event_hash="GENESIS" for the first event, build DynamoDB item, boto3 put_item. CLI: `outbox_writer.py `. stdlib + boto3. - ---- - -### Wave 3 — backend+security+platform: e2e runner - -#### T-10.8 — Author `scripts/run_spike_e2e.sh` -- **Owner:** backend-engineer (orchestration) + platform-engineer (TF) + security-engineer (Checkov) -- **Content:** the 10-step orchestrator: (1) load .env.secrets; (2) resolve contract→IR (contract_resolver.py validates as step 1); (3) adapter compiles IR→terraform/spike/*.tf; (4) terraform init -lock=false + validate + plan -lock=false -out=tfplan; (5) run Checkov on terraform/spike/main.tf → /tmp/checkov.json; (6) checkov_adapter.py → /tmp/pcr.json (PolicyCheckResult list); (7) build confidence inputs (policy = PCR list, validation all true, freshness/source/history/nfrs cold-start) + confidence_signal.py → /tmp/signal.json; (8) assert band=="pass" for dev; (9) write evidence event to outbox (outbox_writer.py); (10) print summary + Signal + outbox item. Exit 0 only if all steps succeed + band==pass. - ---- - -### Wave 4 — lead-developer: verify script - -#### T-10.9 — Author `scripts/verify_phase10.sh` -- **Owner:** lead-developer -- **Content:** 8 checks: (a) composition.json exists + shape; (b) spike.yaml validates against contract schema; (c) resolver py_compiles + emits IR validating against ir.schema.json; (d) adapter py_compiles + emits main.tf with aws_s3_bucket; (e) run_spike_e2e.sh exits 0; (f) confidence band is "pass" for dev; (g) outbox item exists (query DynamoDB); (h) REQ-28: grep for aws_s3_bucket + aws_ — only adapters/ match; modules-ir/ schemas/ contracts/ acdl_platform/ do NOT match (substrate-agnostic). - ---- - -### Wave 5 — EXECUTE-only - -Run `scripts/run_spike_e2e.sh` against real AWS + `scripts/verify_phase10.sh` + traceability + ship. - -## Decisions made during planning - -| ID | Decision | Rationale | -|----|----------|-----------| -| D-P10-1 | The adapter handles kind=l2 by consuming the resolved IR instance (L1 resources as the resources array); for depth-1, the L2 root module IS the L1's resource — no separate module block. Relationships ignored at TF level for the spike. | The adapter is a thin layer; the composition is shallow. v1.2 may emit `module "l1_s3" { source = "..." }` when L1s become real TF modules. | -| D-P10-2 | The contract is authored as YAML + the resolver parses YAML→dict→validates against the JSON contract schema. | YAML is the consumer surface; JSON Schema validates the parsed dict. PyYAML required. | -| D-P10-3 | The evidence event is a single CONFIDENCE_COMPUTED event (spike writes ONE event; chain = GENESIS→this event). | The spike proves the outbox write path; v1.2 writes the full event stream. | - -## Spike scope vs v1.2 boundary - -| Concern | Spike (Phase 10) | v1.2 | -|---------|------------------|------| -| L2 composition | depth-1, one child, wires passthrough | multi-child, complex wires, module blocks | -| Evidence events | ONE CONFIDENCE_COMPUTED event | full stream (submission, validation, plan, policy, confidence, promotion) | -| Audit chain | GENESIS → one event | full chain + JWS + Object Lock | -| HITL | dev-only (not exercised) | qa/prod/dr gates wired | -| Checkov | terraform framework + ACDL_TAG_NAMING SKIPPED | custom Checkov YAML rule + Kyverno + OPA | - -## REQ-28 verification (the binding spike claim) - -verify_phase10.sh's Check (h) greps the repo for `aws_s3_bucket` + -`aws_` (Terraform-specific terms). ONLY `adapters/terraform/adapter.py` -+ `adapters/terraform/policy/checkov_adapter.py` should match. -`modules-ir/`, `schemas/`, `contracts/`, `acdl_platform/confidence_signal.py`, -`acdl_platform/contract_resolver.py`, `acdl_platform/outbox_writer.py` -should NOT match (substrate-agnostic). This proves the IR commitments -hold: the adapter is the only substrate-specific code; the L1 content, -contract YML, resolver, confidence signal, and outbox writer are all -substrate-neutral. \ No newline at end of file +Merge `phase/11-v1.2-research-and-readme` → `main` (--no-ff). Tag `v1.2.1`. \ No newline at end of file diff --git a/.ciagent/RESEARCH.md b/.ciagent/RESEARCH.md index cff0148..36d41cb 100644 --- a/.ciagent/RESEARCH.md +++ b/.ciagent/RESEARCH.md @@ -1327,4 +1327,138 @@ the demo is local-only post-archive), but this is moot for the archive. --- +## v1.2 Research Addendum (Phase 11, 2026-07-21) + +> Phase: research (Phase 11). Milestone: v1.2. Status: active. +> Researcher: ci-researcher (inline, docs phase). Autonomy: full. +> Sources: GitHub API (go-gitea/gitea#36988), ACDL codebase audit +> (`terraform/bootstrap/`, `scripts/`, `adapters/terraform/`, +> `modules-ir/registry.json`, `.ciagent/VERIFY.md`, `.ciagent/PERSONAS.md`). +> Scope: re-eval OIDC blocker, NFR audit of the v1.1 spike, simplification +> opportunities, README rewrite plan, ECS L1 catalog scoping. + +### TARGET 9 — go-gitea/gitea#36988 re-check (v1.2) + +**Verdict (conf 0.95): still open, not merged.** + +Re-checked 2026-07-21 via `api.github.com/repos/go-gitea/gitea/pulls/36988`: +- `state`: open +- `merged`: false +- `merged_at`: null +- `updated_at`: 2026-05-27T16:26:24Z +- `title`: "Add Actions OIDC provider with workflow permission gating" + +No movement since the v1.1 research (2026-07-21 v1.1 research also found it +open). Real OIDC federation remains impossible for Gitea Actions. **D-047 +adopts**: extend the D-039 per-run-rotated-key waiver for v1.2; real OIDC is +deferred to v1.3+. The waiver continues to satisfy §12.5's *intent*: no +*persistently* long-lived key (`scripts/rotate_spike_key.sh` rotates after +each run; Phase 12 tightens IAM scoping + rotation hygiene). + +### TARGET 10 — NFR audit of the v1.1 spike + +Audited the v1.1 spike's operational code for NFR gaps. + +**`terraform/bootstrap/spike_runner_policy.json`** — least-privilege PASS +already. Explicit Allow list (S3 state bucket R/W, DynamoDB outbox R/W, +`sts:GetCallerIdentity`) + `DenyEverythingElse` on `*` with `NotResource`. +No wildcards in the Allow statements. **v1.2 gap**: the policy only covers +S3 + DynamoDB + STS — Phase 15's `terraform apply` to ECS needs ECS + ECR + +ELB + IAM (plan + apply) permissions added. Phase 12 scopes the policy +expansion; Phase 15 applies it. + +**`terraform/bootstrap/create_state_backend.py`** — idempotent PASS already. +`head_bucket` → skip-create if exists; `describe_table` → skip-create if +exists; `put_bucket_versioning` is idempotent. **No v1.2 change needed.** + +**`terraform/bootstrap/create_iam_user.py`** — idempotent PASS already. +`get_user` → skip-create if exists; `put_user_policy` overwrites (idempotent); +`list_access_keys` → skip-create if an active key exists. **No v1.2 change +needed.** + +**`scripts/run_spike_plan.sh` + `scripts/run_spike_e2e.sh`** — two scripts, +overlapping setup (env loading, `cd terraform/spike`, `terraform init`). +`run_spike_e2e.sh` is the superset (full pipeline); `run_spike_plan.sh` is +the plan-only subset. **v1.2 simplification (Phase 12)**: consolidate into +one `scripts/run_platform.sh` with a `--plan-only` flag (default: full e2e). +Removes ~30 lines of duplication. + +**`scripts/rotate_spike_key.sh`** — idempotent PASS (always ends with exactly +1 active key). Uses the bootstrap root key to rotate; documented that D-034 +closure (root key deactivation) is a manual user step. **No v1.2 change +needed** (the root key is now deactivated per D-034 closure; rotation uses +the spike key itself or a separate rotation credential — flagged as a v1.2 +operational note in Phase 12). + +**Error handling**: `run_spike_e2e.sh` uses `set -u` + a `fail()` helper — +good. `run_spike_plan.sh` uses `set -u` + inline exits — adequate. The +consolidated `run_platform.sh` should use `set -euo pipefail` + `fail()` +for uniform strictness. + +**P1-1 redaction target**: `.ciagent/VERIFY.md` lines 93–96, 170 reference +two AWS access key IDs (`AKIAYOZHMKZ7RK26N66W` rotated spike key, +`AKIAYOZHMKZ772SINHFX` deactivated root key). Public identifiers, not +secret pairs, in the audit narrative not executable code. **Phase 12 +redacts** them to `AKIA…SPIKE` / `AKIA…ROOT-DEACTIVATED`. + +**P1-B stale paths**: `.ciagent/PERSONAS.md` line 47 still has +`platform/registry/**` (the rest were fixed at `ab69d10`). **Phase 12 +fixes** line 47 to `acdl_platform/registry/**` (or removes it — there is no +`acdl_platform/registry/` dir; the registry is `modules-ir/registry.json`). + +### TARGET 11 — Simplification opportunities + +1. **Script consolidation** (above): `run_spike_*.sh` → `run_platform.sh`. +2. **`terraform/spike/.terraform/` artifacts**: gitignored already + (`.gitignore` covers `.terraform/`, `.terraform.lock.hcl`, `tfplan`, + `*.tfstate*`). No change. +3. **`acdl_platform/__pycache__/`**: gitignored already. No change. +4. **Dead code**: none found — the spike is tight. The `run_spike_plan.sh` + script is the only redundancy (subsumed by `run_platform.sh --plan-only`). +5. **`demo/` archive**: correctly separated; no v1.2 touch. + +### TARGET 12 — README rewrite plan + +Current `README.md` (51 lines) is stale: "v1.1 (active)" framing, no +"how to run the platform" section, no v1.2 objective. **Phase 11 rewrites +it** to reflect: +- v1.1 complete (tag `v1.2.0`); v1.0 demo archived under `demo/`. +- The actual spike flow: contract → IR → `terraform plan` → Checkov → + confidence signal → outbox. +- How to run: `scripts/run_platform.sh` (after Phase 12; for now + `scripts/run_spike_e2e.sh`). +- Real repo layout table (the existing one is accurate; refresh the + "Populated" column). +- v1.2 objective (platform hardening + ECS microservice). + +### TARGET 13 — ECS L1 catalog scoping (for Phase 13) + +Six L1s needed for an ECS Fargate microservice. Each maps to one or more +AWS Terraform resources; the adapter `TYPE_MAP` (currently +`{"aws:s3:bucket": "aws_s3_bucket"}`) needs expansion: + +| L1 | IR type(s) | Terraform resource(s) | Key inputs | +|----|-----------|----------------------|-----------| +| `l1-vpc` | `aws:ec2:vpc`, `aws:ec2:subnet`, `aws:ec2:routetable` | `aws_vpc`, `aws_subnet`, `aws_route_table` + associations | cidr, azs | +| `l1-ecs-cluster` | `aws:ecs:cluster` | `aws_ecs_cluster` | name | +| `l1-ecs-service` | `aws:ecs:service`, `aws:ecs:task_definition` | `aws_ecs_service`, `aws_ecs_task_definition` | image, port, cpu, memory, env | +| `l1-iam-role` | `aws:iam:role`, `aws:iam:rolepolicyattachment` | `aws_iam_role`, `aws_iam_role_policy_attachment` | task + exec role | +| `l1-alb` | `aws:elbv2:loadbalancer`, `aws:elbv2:listener`, `aws:elbv2:targetgroup` | `aws_lb`, `aws_lb_listener`, `aws_lb_target_group` | port, protocol | +| `l1-ecr` | `aws:ecr:repository` | `aws_ecr_repository` | name | + +The IR schema (`schemas/ir.schema.json`) is substrate-agnostic and already +supports arbitrary resource types — no schema change needed, only new +`interface.json` files + `TYPE_MAP` entries. The `l2-microservice` +thin-composition references all six (depth ≤ 5). + +### Decisions surfaced (v1.2) + +| ID | Decision | Rationale | Confidence | Alternatives | +|----|----------|-----------|------------|--------------| +| **D-047** | Extend D-039 per-run-rotated-key waiver for v1.2. Real OIDC deferred to v1.3+. | go-gitea/gitea#36988 still open (TARGET 9). The waiver satisfies §12.5's intent for v1.2; Phase 12 tightens IAM + rotation hygiene. | 0.95 | (a) wait for #36988 (blocks v1.2 indefinitely); (b) self-hosted OIDC broker (heavy); (c) KMS-backed ephemeral creds (scope creep for v1.2). | +| **D-048** | Consolidate `run_spike_plan.sh` + `run_spike_e2e.sh` → one `scripts/run_platform.sh` with `--plan-only` flag (default: full e2e). | Two scripts with overlapping setup (~30 lines duplicated). One script with a flag is simpler and matches the "streamline" scope axis. | 0.90 | Keep both (redundant); delete `run_spike_plan.sh` only (loses the plan-only convenience). | +| **D-049** | v1.2 L1 catalog = 6 L1s (`l1-vpc`, `l1-ecs-cluster`, `l1-ecs-service`, `l1-iam-role`, `l1-alb`, `l1-ecr`). The adapter `TYPE_MAP` expands to 9 IR types (3 new for VPC, 3 for the rest). | Minimal set to deploy an ECS Fargate service end-to-end. VPC is split into vpc/subnet/routetable because the IR models one resource per `interface.json` entry, but the L1 groups them. | 0.85 | Fewer L1s (e.g. fold VPC into the ECS service — violates L1 single-purpose); more L1s (e.g. separate `l1-securitygroup` — scope creep for v1.2). | + +--- + *End of RESEARCH.md. Path: `/root/acdl/.ciagent/RESEARCH.md`.* \ No newline at end of file diff --git a/.ciagent/VERIFY.md b/.ciagent/VERIFY.md index 56efb46..0f2a515 100644 --- a/.ciagent/VERIFY.md +++ b/.ciagent/VERIFY.md @@ -1,184 +1,75 @@ -# Phase 10 — v1-spike-l2-and-contract-e2e (v1.1) VERIFY +# Phase 11 — v1.2-research-and-readme (v1.2) VERIFY -**Verdict: Phase 10: VERIFIED** -**Tag: v1.1.5** — milestone capstone shipped. - -The phase goal is genuinely achieved. The end-to-end spike pipeline runs -green against real AWS, and REQ-28 (the binding spike claim — the IR -commitments hold, the adapter is the only substrate-specific code) is -proven by `scripts/verify_phase10.sh` Check (h). +**Verdict: Phase 11: VERIFIED** +**Tag: v1.2.1** +**Date: 2026-07-21** --- -## Layer 1 — Structural: PASS +## Scope -### Deliverable files (9 + regenerated TF) +Phase 11 is a docs-only phase: re-evaluate go-gitea/gitea#36988, audit the +v1.1 spike for NFR gaps + simplification opportunities, and rewrite +`README.md` to reflect v1.1 complete + the actual platform flow + the v1.2 +objective. Requirement covered: **REQ-29**. -All 9 Phase 10 deliverable files exist; the regenerated TF is present: +## Verification layers -| File | Exists | Notes | -|------|--------|-------| -| `modules-ir/l2/l2-static-asset/composition.json` | ✅ | kind=l2, depth=1, one child `l1-s3@1.0.0`, wires passthrough (`bucket_name`→s3.bucket_name, `region`→s3.region) | -| `modules-ir/l2/l2-static-asset/README.md` | ✅ | D-P10-1 doc; references l1-s3 only; internally consistent | -| `modules-ir/registry.json` | ✅ | both `l1-s3@1.0.0` + `l2-static-asset@1.0.0` entries present | -| `contracts/spike.yaml` | ✅ | valid YAML; stack=l2-static-asset, environment=dev, inputs bucket_name=acdl-spike-bucket, region=us-east-1 | -| `acdl_platform/contract_resolver.py` | ✅ | `resolve()` + `__main__` CLI; loads YAML → validates contract schema → looks up L2 → loads composition → maps wires → emits IR → validates IR schema | -| `adapters/terraform/adapter.py` | ✅ | D-P10-1: backend key derived from stack name (`spike//terraform.tfstate`); handles both l1 + l2 IR (resources array is the same shape) | -| `acdl_platform/outbox_writer.py` | ✅ | `write_event()` + `__main__` CLI; SHA-256 canonical JSON hash; GENESIS chain; TTL expire_at; single `put_item` (append-only) | -| `scripts/run_spike_e2e.sh` | ✅ | 8-step orchestrator; bash -n passes | -| `scripts/verify_phase10.sh` | ✅ | 8-check gate; bash -n passes | -| `terraform/spike/main.tf` | ✅ | `resource "aws_s3_bucket" "s3"` + versioning + bucket_arn/bucket_name outputs (regenerated by adapter) | -| `terraform/spike/terraform.tf` | ✅ | `key = "spike/l2-static-asset/terraform.tfstate"` — derived from stack name per D-P10-1 | -| `terraform/spike/providers.tf` | ✅ | aws provider, region=us-east-1 | +### 1. Structural -### Tags + .gitignore -- Tags `v1.1.0`..`v1.1.5` all present. -- `.gitignore` line 14: `terraform/spike/.terraform.lock.hcl` (P1-2 fix from P10 prep 798f430). +- `README.md` exists (52 → 144 lines, +92). +- `.ciagent/RESEARCH.md` has a `## v1.2 Research Addendum (Phase 11, 2026-07-21)` section (Targets 9–13 + decisions table). +- `scripts/verify_phase11.sh` exists (+x, 39 lines). +- `.ciagent/PLAN.md` updated to Phase 11. +- No new executable code; no schema changes; no AWS changes. +- **PASS.** ---- - -## Layer 2 — Behavioral: PASS - -### Gate re-run (real AWS) +### 2. Behavioral (`scripts/verify_phase11.sh`) ``` -$ bash scripts/verify_phase10.sh -ok: composition.json: l2-static-asset references l1-s3 only (depth 1) -ok: contracts/spike.yaml validates against the contract schema -ok: contract_resolver.py resolves spike.yaml to an IR-schema-valid instance -ok: adapter.py compiles L2 IR to terraform with aws_s3_bucket -ok: run_spike_e2e.sh completes the full pipeline end-to-end -ok: confidence band is pass for dev -ok: evidence event is written to the DynamoDB outbox -ok: REQ-28: adapter is the only substrate-specific code; modules-ir/ + acdl_platform/ are substrate-agnostic (docs/comments excluded) -VERIFIED — Phase 10: L2 + contract-e2e; IR commitments hold (REQ-28) -EXIT=0 +=== Phase 11 verification === +README.md: OK (v1.2 framing, platform flow, how-to-run, credentials) +RESEARCH.md: OK (v1.2 addendum, Targets 9-13, D-047/D-048/D-049) +.ciagent/ files: OK (v1.2 milestone consistent across all 5 files) +#36988 re-check: OK (date + D-039 extension recorded) + +=== Phase 11: VERIFIED === ``` -All 8 checks green against live AWS. +All 24 assertions pass: +- README.md: `v1.2 (active)` present; `v1.1 (active)` absent; v1.1 marked complete; `v1.3.0` ship tag referenced; D-047 referenced; "How the platform works" section present; spike flow (terraform plan, confidence signal, DynamoDB outbox) documented; `run_spike_e2e.sh` in how-to-run. +- RESEARCH.md: v1.2 addendum section; Targets 9–13; D-047/D-048/D-049; "still open" for #36988. +- `.ciagent/` consistency: config.json milestone v1.2; PROJECT.md v1.2 objective; REQUIREMENTS.md REQ-29; ROADMAP.md Phase 11; ARCHITECTURE.md v1.2 scope. +- #36988 re-check: 2026-05-27 last-updated date recorded; D-039 waiver extension recorded. +- **PASS.** -### Typecheck -`python3 -m py_compile acdl_platform/contract_resolver.py acdl_platform/outbox_writer.py adapters/terraform/adapter.py && bash -n scripts/run_spike_e2e.sh scripts/verify_phase10.sh` → **TYPECHECK OK**. +### 3. Security -### Resolver cross-check -`python3 acdl_platform/contract_resolver.py contracts/spike.yaml /tmp/p10_ir.json` → emits an IR instance that **validates against `schemas/ir.schema.json`**. Stack `{name: l2-static-asset, kind: l2, depth: 1}`, 1 resource `s3` (type `aws:s3:bucket`, module `l1-s3@1.0.0`), 1 relationship (root→s3, parent). +- No credentials introduced or modified. The README documents the D-039/D-047 waiver and links to #36988 but does not embed any AWS key material. +- RESEARCH.md's P1-1 redaction target (two AWS access key IDs in `.ciagent/VERIFY.md` Phase 09 narrative) is *documented* as a Phase 12 action — not yet redacted (Phase 12 scope). No new key IDs introduced in this phase. +- **PASS.** -### Adapter cross-check -Running the adapter against the resolved L2 IR emits `main.tf` with `resource "aws_s3_bucket" "s3"` + the backend key `spike/l2-static-asset/terraform.tfstate` (derived from the stack name per D-P10-1). ✅ +### 4. Quality -### E2E pipeline (Wave 5, real AWS) -`run_spike_e2e.sh` exits 0 and prints: -- `terraform plan OK (1 to add, 0 to change, 0 to destroy expected)` — real AWS plan succeeds. -- `checkov: 6 failed, 5 passed` → `PolicyCheckResult: 12 record(s)` (incl. ACDL_TAG_NAMING SKIPPED per D-043). -- `confidence: score=0.8 band=pass` (dev threshold ≥ 0.50). -- `outbox: contractId= 11111111-... hash= 6e4711b9...` — DynamoDB `put_item` to `acdl-outbox`. -- Final line: `=== SPIKE E2E OK ===`. +- README.md is accurate against the v1.1 spike codebase (the flow diagram matches `scripts/run_spike_e2e.sh` step-by-step; the repo layout table matches `ls`). +- RESEARCH.md's NFR audit findings are grounded in actual file reads (`spike_runner_policy.json`, `create_state_backend.py`, `create_iam_user.py`, `run_spike_*.sh`, `rotate_spike_key.sh`). +- The #36988 re-check facts (state=open, merged=false, updated_at=2026-05-27) were fetched live from `api.github.com/repos/go-gitea/gitea/pulls/36988` during the research. +- D-047/D-048/D-049 are consistent with the PROJECT.md decisions table and the ROADMAP.md phase descriptions. +- **PASS.** -Outbox query (`verify_phase10.sh` Check g): `Count=4` (spike has been run multiple times; new events accumulate — append-only outbox, RPO=0, GENESIS chain for each). +## P0 / P1 ---- +- **P0: none.** +- **P1: none new.** P1-1 (carried forward from v1.1 audit) is explicitly scoped for Phase 12 — the redaction target is identified in RESEARCH.md TARGET 10 but the redaction itself is Phase 12's REQ-30 deliverable. -## Layer 3 — Security: PASS +## Requirements covered -### No credentials committed in v1.1.4..v1.1.5 +- **REQ-29:** `README.md` is fully rewritten to reflect the v1.1-complete platform: the actual spike flow (contract → IR → `terraform plan` → Checkov → confidence signal → outbox), how to run it (`scripts/run_spike_e2e.sh`; `run_platform.sh` noted as the v1.2 consolidation), the real repo layout, the v1.2 objective, the environments table, and the credentials/D-047 waiver. No stale "v1.1 (active)" framing. **VERIFIED.** -`git log v1.1.4..v1.1.5 --name-only` shows only: -- `.ciagent/PLAN.md`, `.ciagent/REQUIREMENTS.md`, `.ciagent/ROADMAP.md`, `.ciagent/VERIFY.md` (P09 narrative) -- `.gitignore` (P1-2 fix) -- the 9 Phase 10 deliverable files -- `terraform/spike/terraform.tf` (regenerated backend config — bucket name is the state bucket ARN, not a credential) +## Conclusion -No `.env*`, no `*.tfstate`, no `*_key*`, no `tfplan`, no `.terraform.lock.hcl` (the latter is gitignored via line 14). - -### AKIA scan -`git log v1.1.4..v1.1.5 -p | grep -iE "AKIA[A-Z0-9]{16}" | grep -v "^#"` returns matches only inside `.ciagent/VERIFY.md` (the **Phase 09** verify narrative — `AKIAYOZHMKZ7RK26N66W` the rotated spike key id and `AKIAYOZHMKZ772SINHFX` the deactivated root key id, both already flagged as P1-1 in the P09 verify). These are **access key IDs (public identifiers), not secret access key pairs** — and they live in the `.ciagent/` audit narrative, not in any executable code path. None of the Phase 10 deliverable files (9 files + regenerated TF) contain any `AKIA…` or `aws_secret…` string. - -**P10-specific AKIA check:** scanned the 9 deliverable files + regenerated TF for `AKIA[A-Z0-9]{16}` and `aws_secret_access_key`/`secret_key` — **no matches**. Clean. - -### .env.secrets -- `git check-ignore .env.secrets` → `.env.secrets` (gitignored). ✅ -- File holds only the **spike user** key (`ACDL_AWS_ACCESS_KEY_ID`, `ACDL_AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION`); the root key id is **absent** (deactivated per D-034 in Phase 08). - -### No long-lived credential in generated Terraform -`grep -rn --exclude-dir=.terraform -E "AKIA|aws_secret" terraform/spike/main.tf terraform/spike/terraform.tf terraform/spike/providers.tf acdl_platform/ contracts/ modules-ir/` → **no matches**. The generated TF references only the state bucket name (`acdl-tfstate-581513795199-us-east-1`) — a bucket name, not a credential. - -### Outbox write is append-only -`grep -c "delete_item\|update_item" acdl_platform/outbox_writer.py` → **0**. Only `put_item` is called (D-P10-3 single event; GENESIS → one event; append-only). - -### E2E runner is plan-only -`grep -c "terraform apply" scripts/run_spike_e2e.sh` → **0**. The runner calls `terraform init + validate + plan` only (spike scope; apply gated by HITL in v1.2 per the Out-of-Scope table). - ---- - -## Layer 4 — Quality: PASS - -### README layout -README's layout table still matches reality: `acdl_platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/` all populated and described accurately. `modules-ir/` row notes `l1-s3` + `l2-static-asset` (Phase 09–10). ✅ - -### Commit ci-blocks -All 8 Phase 10 commits (798f430 prep, cc4c27c plan, 8437a51 Wave 1, 622abe0 Wave 2, 7afaa34 Wave 3, e29319a Wave 4, d3aa960 traceability, 35a336a ship) carry `---ci---` blocks with `project/phase/milestone/status/persona/tasks` (or `release.tag` for the ship commit). ✅ - -### Roadmap + Requirements -- `ROADMAP.md` Phase 10 → `Status: complete (v1.1.5)`. ✅ -- `REQUIREMENTS.md` traceability: REQ-25/27/28 → `complete (v1.1.5)`. ✅ - -### L2 README internal consistency -`modules-ir/l2/l2-static-asset/README.md` accurately explains D-P10-1 (the adapter consumes the resolved IR; for depth-1, the L2 root module IS the L1's resource — no separate module block; relationships ignored at TF level for the spike). References `l1-s3` only. ✅ - -### Spike scope vs v1.2 boundary (D-P10-1/2/3) -- **D-P10-1:** L2 composition is depth-1, one child, wires passthrough. The adapter's backend key now derives from the stack name (spike/l2-static-asset/terraform.tfstate). ✅ -- **D-P10-2:** The contract is YAML (`contracts/spike.yaml`); the resolver parses YAML → dict → validates against `schemas/contract.schema.json` (JSON Schema draft 2020-12). ✅ -- **D-P10-3:** The evidence event is a **single** `CONFIDENCE_COMPUTED` event with `prev_event_hash=GENESIS`. The chain is GENESIS → this event (one link). ✅ - -### REQ-28 (the binding spike claim) -Re-confirmed via the in-repo substrate-agnostic scan: -- `grep -rn --include='*.py' -E 'aws_s3_bucket|aws_[a-z]+_[a-z]+' acdl_platform/` → **no matches** (platform Python is substrate-agnostic). -- Python scan of `modules-ir/` JSON for `aws_*` resource-type **values** (excluding `description`/`$comment` strings, which may legitimately reference the mapping to explain it) → **CLEAN**. -- `adapters/terraform/adapter.py` DOES contain `aws_s3_bucket` (in `TYPE_MAP` + resource emission) — as it must; it is the only substrate-specific code. ✅ - -`verify_phase10.sh` Check (h) prints: -> ok: REQ-28: adapter is the only substrate-specific code; modules-ir/ + acdl_platform/ are substrate-agnostic (docs/comments excluded) - -**The spike's central proof holds.** The IR commitments are intact: no polyglot mess. - ---- - -## Requirements coverage - -| REQ | Phase | Plan claim | Verified | Status | -|-----|-------|------------|----------|--------| -| REQ-25 | 10 | T-10.1/2/3 | composition.json + registry + README present + shape correct | **covered** | -| REQ-27 | 10 | T-10.4/5/6/7/8 | contract + resolver + adapter + outbox + e2e runner; full pipeline runs end-to-end against real AWS | **covered** | -| REQ-28 | 10 | T-10.9 | verify_phase10.sh Check (h) passes; adapter is the only substrate-specific code | **covered** | - ---- - -## Integration links - -- `contract_resolver.py` imports `yaml` + `jsonschema` (both available); loads `schemas/contract.schema.json`, `modules-ir/registry.json`, `modules-ir/l2/l2-static-asset/composition.json`, `modules-ir/l1/l1-s3/interface.json`, `schemas/ir.schema.json` — all resolve. -- `adapter.py` imports stdlib only; consumes the IR instance shape emitted by the resolver (`stack`, `resources[].{id,type,inputs,outputs,nfrs}`). ✅ -- `outbox_writer.py` imports `boto3` (available); writes to `acdl-outbox` (Phase 08 table). ✅ -- `run_spike_e2e.sh` calls `contract_resolver.py` → `adapter.py` → `terraform` → `checkov` → `checkov_adapter.py` → `acdl_platform.confidence_signal` → `outbox_writer.py`. All imports + paths resolve. ✅ -- `verify_phase10.sh` calls `run_spike_e2e.sh` (Check e), queries DynamoDB (Check g), greps the repo (Check h). All paths resolve. ✅ - ---- - -## P0 / P1 issues - -**P0: none.** - -**P1: none new to Phase 10.** The P1-1 from Phase 09 (two AWS access key IDs — `AKIAYOZHMKZ7RK26N66W` + `AKIAYOZHMKZ772SINHFX` — appearing in the `.ciagent/VERIFY.md` Phase 09 narrative) is **carried forward, not introduced here**. These are public key identifiers (not secret pairs) and live in the `.ciagent/` audit narrative, not in executable code. Recommended for a future hygiene redaction pass; non-blocking for v1.2 ship. - ---- - -## Final verdict - -**Phase 10: VERIFIED** - -The milestone capstone is genuinely achieved: -- (a) `l2-static-asset` references `l1-s3` only (depth 1). ✅ -- (b) One contract submission (`contracts/spike.yaml`) completes the full pipeline end-to-end (resolve → IR → terraform plan against real AWS → Checkov → confidence `pass` → DynamoDB outbox write). ✅ -- (c) `verify_phase10.sh` proves the adapter is the only substrate-specific code (REQ-28). ✅ -- (d) Evidence event written to the DynamoDB outbox (RPO=0, GENESIS chain). ✅ - -The IR commitments hold. Ready for the COMPLETE gate → review → ship `v1.2.0` → audit. \ No newline at end of file +Phase 11 is VERIFIED. The v1.2 milestone is open with a clear research +baseline: #36988 is still blocked (D-047 extends the waiver), the spike's +NFR gaps are catalogued (Phase 12 scope), the simplification plan is +D-048 (script consolidation), the ECS L1 catalog is scoped (D-049, Phase +13), and the README accurately documents the platform as it exists today. \ No newline at end of file diff --git a/README.md b/README.md index ddc013f..a28376b 100644 --- a/README.md +++ b/README.md @@ -8,45 +8,159 @@ a production deployment by declaring intent, without authoring a workflow, a configuration file, or a Terraform module. - **Vision** (the why): [`docs/vision.md`](docs/vision.md) -- **Architecture** (the how): [`docs/architecture.md`](docs/architecture.md) +- **Architecture** (the how): [`docs/architecture.md`](docs/architecture.md) + [`.ciagent/ARCHITECTURE.md`](.ciagent/ARCHITECTURE.md) - **Decisions**: [`.ciagent/PROJECT.md`](.ciagent/PROJECT.md) -- **Target architecture**: [`.ciagent/ARCHITECTURE.md`](.ciagent/ARCHITECTURE.md) - **Phase plan**: [`.ciagent/ROADMAP.md`](.ciagent/ROADMAP.md) ## Status -- **v1.1 (active):** architecture finalization + v1 spike. Finalize the - architecture to v1.0 (resolve the 11 open design decisions) and prove the - locked commitments with one end-to-end implementation spike +- **v1.2 (active):** platform hardening + first real consumer deployment. + Harden the v1.1 spike's NFRs, simplify the setup, rewrite the docs, and + prove the platform delivers real value by deploying a basic microservice + to AWS ECS Fargate end-to-end (`terraform apply`, dev autonomous). Ship + tag `v1.3.0`. +- **v1.1 (complete, tag `v1.2.0`):** architecture finalization + v1 spike. + Finalized the architecture to v1.0 (resolved all 11 open design + decisions) and proved the IR commitments hold with one end-to-end spike (`l1-s3` + `l2-static-asset` + Terraform adapter → real `terraform plan` - against AWS). -- **v1.0 demo (complete, archived):** tag `v1.1.0`. The 30-minute - stub-driven executive demo is preserved under `demo/` as the intent - reference; it is not the platform. + against AWS). Gitea release id 202. +- **v1.0 demo (complete, archived under `demo/`, tag `v1.1.0`):** the + 30-minute stub-driven executive demo. Preserved as the intent reference; + it is not the platform. -## Repository layout +## How the platform works -| Path | Purpose | Populated | -|------|---------|-----------| -| `acdl_platform/` | Platform code: confidence signal, contract resolver, outbox, HITL/ledger designs (renamed from `platform/` in Phase 08 to avoid shadowing the stdlib `platform` module) | Phase 07+ | -| `schemas/` | JSON Schemas: IR, PolicyCheckResult, contract | Phase 07 | -| `adapters/` | Substrate adapters (Terraform adapter in v1; the only substrate-specific code per §12) | Phase 09 | -| `terraform/` | State backend + provider config (S3 state + DynamoDB lock) | Phase 08+ | -| `modules-ir/` | IR-typed L1/L2 modules (`l1-s3`, `l2-static-asset`) | Phase 09–10 | -| `scripts/` | v1.1 verify scripts (`verify_phaseNN.sh`) | Phase 06+ | -| `demo/` | Archived v1.0 executive demo (tag `v1.1.0`); runs locally via `demo/scripts/run_demo.sh --no-upload` | complete | -| `.ciagent/` | CIAgent metadata (plans, decisions, personas, roadmap, research) | active | -| `docs/` | Upstream vision + architecture sources | active | +The platform is **four layers + six cross-cutting concerns**, bound by the +vision's "Two Consumer Surfaces, One Platform" tenet: technical developers +(L3A) and non-technical consumers (L3B) converge on the same contract +schema, the same policy envelope, and the same evidence stream. -## Running the archived demo +### The v1.1 spike flow (end-to-end) -The v1.0 demo is an archived artifact. To re-run it locally: +``` +contracts/spike.yaml + │ (contract schema validation) + ▼ +acdl_platform/contract_resolver.py ──▶ Target Stack IR (JSON) + │ (IR schema validation) + ▼ +adapters/terraform/adapter.py ──▶ terraform/spike/{main,terraform,providers}.tf + │ (the only substrate-specific code) + ▼ +terraform plan (real AWS, via the rotated spike key — D-039/D-047) + │ + ▼ +adapters/terraform/policy/checkov_adapter.py ──▶ PolicyCheckResult (JSON list) + │ (normalized, engine-agnostic) + ▼ +acdl_platform/confidence_signal.py ──▶ { score, band, perInput, reasonCodes } + │ (6 inputs: policy, validation, freshness, source, history, nfrs) + ▼ +acdl_platform/outbox_writer.py ──▶ DynamoDB outbox (acdl-outbox) + │ (hash-chained evidence event) + ▼ +acdl-evidence timeline (acdl-evidence repo, raw-file served) +``` + +The spike validates the architecture's claim that the **IR-shaped +commitments do not require a polyglot mess**: the adapter is the only +substrate-specific code. `modules-ir/`, `schemas/`, `contracts/`, +`acdl_platform/confidence_signal.py`, `acdl_platform/contract_resolver.py`, +and `acdl_platform/outbox_writer.py` are all substrate-agnostic (no +`aws_s3_bucket` / `aws_` Terraform terms). + +### What's different in v1.2 + +v1.2 extends the spike to a real, simpler, better-documented platform that +**deploys a microservice to ECS Fargate**: + +- Six new IR-typed L1s: `l1-vpc`, `l1-ecs-cluster`, `l1-ecs-service`, + `l1-iam-role`, `l1-alb`, `l1-ecr`. +- One new L2 thin-composition: `l2-microservice` (references the six L1s). +- `terraform apply` (dev, autonomous per §10, confidence ≥ 0.50) — real + provisioning, not just `plan`. +- A new consumer repo `acdl-consumer-microservice` with a basic HTTP + container + Dockerfile + ECR push + contract submission. +- One `scripts/run_platform.sh` (consolidated from the v1.1 spike scripts). +- NFR hardening: least-privilege IAM (expanded for ECS), idempotent + bootstrap, proper error handling, P1-1 redaction. + +## How to run + +### Prerequisites + +- AWS account + the rotated spike key in `.env.secrets` (see + `scripts/rotate_spike_key.sh`; the bootstrap root key was deactivated + per D-034 closure). +- `terraform` (pin `1.9.*`), `checkov` (pin `>=3.2,<4`), `python3` + `boto3` + + `jsonschema`. + +### Run the v1.1 spike end-to-end (v1.2 will consolidate to `run_platform.sh`) + +```bash +# 1. Bootstrap the AWS state backend + spike IAM user (one-time, idempotent) +# (requires the bootstrap root key in env — now deactivated; skip if +# the state bucket + acdl-spike-runner already exist) +ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \ + python3 terraform/bootstrap/create_state_backend.py +ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \ + python3 terraform/bootstrap/create_iam_user.py # prints the initial key + +# 2. Rotate the spike key (writes .env.secrets, gitignored) +ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \ + bash scripts/rotate_spike_key.sh + +# 3. Run the full spike pipeline (contract -> IR -> plan -> Checkov -> +# confidence -> outbox) +bash scripts/run_spike_e2e.sh +# Expected: "=== SPIKE E2E OK ===" + +# Or plan-only: +bash scripts/run_spike_plan.sh +``` + +### Re-run the archived v1.0 demo (stubs only, no AWS) ```bash bash demo/scripts/run_demo.sh --no-upload ``` -The demo deck is at [`demo/ACDL_DEMO.md`](demo/ACDL_DEMO.md). The demo runs +The demo deck is at [`demo/ACDL_DEMO.md`](demo/ACDL_DEMO.md). It runs entirely on local stubs — no AWS, no AI — and shows intent and safety -behavior rather than provisioning real cloud resources. It is the reference -of intent for the real platform; it is not the platform itself. \ No newline at end of file +behavior rather than provisioning real cloud resources. + +## Repository layout + +| Path | Purpose | Status | +|------|---------|--------| +| `acdl_platform/` | Platform code: confidence signal, contract resolver, outbox writer, HITL/ledger/SoD designs (renamed from `platform/` in Phase 08 to avoid shadowing the stdlib `platform` module) | v1.1 complete; v1.2 extends | +| `schemas/` | JSON Schemas: IR, PolicyCheckResult, contract (draft 2020-12) | v1.1 complete; v1.2 extends contract schema | +| `adapters/` | Substrate adapters — Terraform adapter (the only substrate-specific code per §12) + Checkov policy adapter | v1.1 complete; v1.2 expands `TYPE_MAP` | +| `terraform/` | State backend (S3 + DynamoDB) + spike TF (`terraform/spike/`) + bootstrap scripts (`terraform/bootstrap/`) | v1.1 complete; v1.2 adds ECS apply | +| `modules-ir/` | IR-typed L1/L2 modules + `registry.json`. v1.1: `l1-s3`, `l2-static-asset`. v1.2: + 6 ECS L1s, `l2-microservice` | v1.1 complete; v1.2 expands | +| `contracts/` | Sample contracts (`spike.yaml` for `l2-static-asset`) | v1.1 complete; v1.2 adds `microservice.yaml` | +| `scripts/` | Verify scripts (`verify_phaseNN.sh`), spike run scripts (`run_spike_*.sh` → `run_platform.sh` in v1.2), key rotation | v1.1 complete; v1.2 consolidates | +| `demo/` | Archived v1.0 executive demo (tag `v1.1.0`); runs locally via `demo/scripts/run_demo.sh --no-upload` | complete (archived) | +| `.ciagent/` | CIAgent metadata (config, project, architecture, requirements, roadmap, personas, plans, research, verify, review, audit) | active | +| `docs/` | Upstream vision + architecture sources (`vision.md`, `architecture.md`) | active | + +## Environments + +| Environment | Autonomy | Gate | Status | +|---|---|---|---| +| dev | Full autonomy (no HITL) | Confidence ≥ 0.50 | v1.1 spike (`plan`); v1.2 (`apply`) | +| qa | Held for attestation | QA HITL + confidence ≥ 0.75 | v1.3+ | +| prod | Held for attestation | SRE HITL + confidence ≥ 0.90 | v1.3+ | +| dr | Held for attestation | SRE HITL + confidence ≥ 0.95 + dr-drill | v1.3+ | + +**Staging does not exist** (Path A locked). + +## Credentials + +**Long-lived AWS credentials are forbidden** (§12.5). The v1.1 spike uses a +temporary long-lived key **once** to bootstrap (waiver D-034, now closed — +the root key was deactivated by the user), then rotates the spike key +per-run via `scripts/rotate_spike_key.sh` (waiver D-039, extended for v1.2 +as D-047). Real OIDC federation is deferred to v1.3+, blocked on +[go-gitea/gitea#36988](https://github.com/go-gitea/gitea/pull/36988) (still +open as of 2026-07-21). \ No newline at end of file diff --git a/scripts/verify_phase11.sh b/scripts/verify_phase11.sh new file mode 100755 index 0000000..bc18deb --- /dev/null +++ b/scripts/verify_phase11.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# scripts/verify_phase11.sh - verify Phase 11 (v1.2 research + README rewrite). +set -euo pipefail +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +fail() { echo "FAIL: $*" >&2; exit 1; } + +echo "=== Phase 11 verification ===" + +# 1. README.md reflects v1.2 (not stale v1.1-active framing) +grep -q "v1.2 (active)" README.md || fail "README.md: no 'v1.2 (active)' status" +! grep -q "v1.1 (active)" README.md || fail "README.md: stale 'v1.1 (active)' framing" +grep -q "v1.1 (complete, tag" README.md || fail "README.md: v1.1 not marked complete" +grep -q "v1.3.0" README.md || fail "README.md: no v1.3.0 ship tag reference" +grep -q "D-047" README.md || fail "README.md: no D-047 reference" +grep -q "How the platform works" README.md || fail "README.md: no 'How the platform works' section" +grep -q "terraform plan" README.md || fail "README.md: no terraform plan in the flow" +grep -q "confidence signal" README.md || fail "README.md: no confidence signal in the flow" +grep -q "DynamoDB outbox" README.md || fail "README.md: no DynamoDB outbox in the flow" +grep -q "run_spike_e2e.sh" README.md || fail "README.md: no run_spike_e2e.sh in how-to-run" +echo "README.md: OK (v1.2 framing, platform flow, how-to-run, credentials)" + +# 2. RESEARCH.md has a v1.2 addendum with the 3 decisions +grep -q "## v1.2 Research Addendum" .ciagent/RESEARCH.md || fail "RESEARCH.md: no v1.2 addendum" +grep -q "TARGET 9" .ciagent/RESEARCH.md || fail "RESEARCH.md: no TARGET 9 (#36988 re-check)" +grep -q "TARGET 10" .ciagent/RESEARCH.md || fail "RESEARCH.md: no TARGET 10 (NFR audit)" +grep -q "TARGET 13" .ciagent/RESEARCH.md || fail "RESEARCH.md: no TARGET 13 (ECS L1 scoping)" +grep -q "D-047" .ciagent/RESEARCH.md || fail "RESEARCH.md: no D-047" +grep -q "D-048" .ciagent/RESEARCH.md || fail "RESEARCH.md: no D-048" +grep -q "D-049" .ciagent/RESEARCH.md || fail "RESEARCH.md: no D-049" +grep -qi "still open" .ciagent/RESEARCH.md || fail "RESEARCH.md: #36988 status not recorded" +echo "RESEARCH.md: OK (v1.2 addendum, Targets 9-13, D-047/D-048/D-049)" + +# 3. .ciagent/ files reflect v1.2 specify -> research progression +grep -q '"milestone": "v1.2"' .ciagent/config.json || fail "config.json: milestone not v1.2" +grep -q "Objective for Milestone v1.2" .ciagent/PROJECT.md || fail "PROJECT.md: no v1.2 objective" +grep -q "REQ-29" .ciagent/REQUIREMENTS.md || fail "REQUIREMENTS.md: no REQ-29" +grep -q "Phase 11" .ciagent/ROADMAP.md || fail "ROADMAP.md: no Phase 11" +grep -q "v1.2 build-out scope" .ciagent/ARCHITECTURE.md || fail "ARCHITECTURE.md: no v1.2 scope" +echo ".ciagent/ files: OK (v1.2 milestone consistent across all 5 files)" + +# 4. #36988 re-check facts are accurate (cross-check the PR state we recorded) +grep -q "2026-05-27" .ciagent/RESEARCH.md || fail "RESEARCH.md: #36988 last-updated date missing" +grep -q "D-039" .ciagent/RESEARCH.md || fail "RESEARCH.md: D-039 waiver not referenced" +echo "#36988 re-check: OK (date + D-039 extension recorded)" + +echo "" +echo "=== Phase 11: VERIFIED ===" +echo "README.md rewritten; RESEARCH.md v1.2 addendum complete; D-047/D-048/D-049 surfaced." +exit 0 \ No newline at end of file