ship: phase-11 v1.2-research-and-readme (v1.2.1)
---ci--- project: acdl phase: 11 milestone: v1.2 status: shipped release: tag: v1.2.1 requirements: covered: [REQ-29] ---/ci--- Phase 11 shipped: v1.2 research addendum + README rewrite. REQ-29 verified. #36988 re-checked (still open) -> D-047. NFR audit + simplification (D-048) + ECS L1 scoping (D-049) complete. Phase 12 (nfr-harden-and-simplify) next.
This commit is contained in:
+36
-132
@@ -1,145 +1,49 @@
|
|||||||
---
|
---
|
||||||
phase: 10
|
phase: 11
|
||||||
name: v1-spike-l2-and-contract-e2e
|
name: v1.2-research-and-readme
|
||||||
milestone: v1.1
|
milestone: v1.2
|
||||||
milestone_type: feature
|
requirements: [REQ-29]
|
||||||
status: planned
|
type: docs
|
||||||
requirements: [REQ-25, REQ-27, REQ-28]
|
branch: phase/11-v1.2-research-and-readme
|
||||||
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 10 — v1-spike-l2-and-contract-e2e PLAN
|
# Phase 11 — v1.2-research-and-readme (v1.2) PLAN
|
||||||
|
|
||||||
## Goal
|
## Goal
|
||||||
|
|
||||||
The milestone capstone. Implement `l2-static-asset` (thin-composition
|
Re-evaluate go-gitea/gitea#36988 (OIDC); audit the v1.1 spike for NFR gaps
|
||||||
referencing `l1-s3` only, depth 1), the contract→IR resolution, and one
|
+ simplification opportunities; rewrite README.md to reflect v1.1 complete
|
||||||
end-to-end contract submission flowing through: contract schema
|
+ the actual platform flow + v1.2 objective.
|
||||||
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).
|
|
||||||
|
|
||||||
After Phase 10 ships + verifies: the COMPLETE gate (review → ship v1.2.0
|
## Tasks (single wave — docs territory)
|
||||||
→ audit).
|
|
||||||
|
|
||||||
## 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)
|
### T-11.2 — README.md rewrite (REQ-29)
|
||||||
- **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)
|
- Status section: v1.2 active, v1.1 complete (tag v1.2.0), v1.0 archived.
|
||||||
- **REQ-28** → T-10.9 (verify_phase10.sh with the substrate-agnostic grep check)
|
- "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
|
||||||
|
|
||||||
```
|
- `grep -c "v1.2" README.md` ≥ 5 (v1.2 referenced throughout).
|
||||||
Wave 1 (platform-engineer) — T-10.1 composition.json, T-10.2 registry, T-10.3 README, T-10.6 adapter L2 extension
|
- `grep "v1.1 (active)" README.md` returns nothing (no stale framing).
|
||||||
Wave 2 (backend-engineer) — T-10.4 spike.yaml, T-10.5 contract_resolver.py, T-10.7 outbox_writer.py
|
- `.ciagent/RESEARCH.md` has a "## v1.2 Research Addendum" section.
|
||||||
Wave 3 (backend+security) — T-10.8 run_spike_e2e.sh
|
- D-047/D-048/D-049 present in the addendum's decisions table.
|
||||||
Wave 4 (lead-developer) — T-10.9 verify_phase10.sh
|
- `scripts/verify_phase11.sh` (authored in the verify step).
|
||||||
Wave 5 (lead, EXECUTE-only) — run e2e + verify + traceability + ship
|
|
||||||
```
|
|
||||||
|
|
||||||
Dependencies: Wave 2 → Wave 1 (resolver reads composition.json); Wave 3 → Wave 1+2; Wave 4 → all prior; Wave 5 EXECUTE-only.
|
## Ship
|
||||||
|
|
||||||
---
|
Merge `phase/11-v1.2-research-and-readme` → `main` (--no-ff). Tag `v1.2.1`.
|
||||||
|
|
||||||
### 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": "<iso8601>", "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 <contract.yaml> <out_ir.json>`. 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 <event.json>`. 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.
|
|
||||||
@@ -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`.*
|
*End of RESEARCH.md. Path: `/root/acdl/.ciagent/RESEARCH.md`.*
|
||||||
+52
-161
@@ -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**
|
**Verdict: Phase 11: VERIFIED**
|
||||||
**Tag: v1.1.5** — milestone capstone shipped.
|
**Tag: v1.2.1**
|
||||||
|
**Date: 2026-07-21**
|
||||||
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).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 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 |
|
### 1. Structural
|
||||||
|------|--------|-------|
|
|
||||||
| `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/<stack_name>/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 |
|
|
||||||
|
|
||||||
### Tags + .gitignore
|
- `README.md` exists (52 → 144 lines, +92).
|
||||||
- Tags `v1.1.0`..`v1.1.5` all present.
|
- `.ciagent/RESEARCH.md` has a `## v1.2 Research Addendum (Phase 11, 2026-07-21)` section (Targets 9–13 + decisions table).
|
||||||
- `.gitignore` line 14: `terraform/spike/.terraform.lock.hcl` (P1-2 fix from P10 prep 798f430).
|
- `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.**
|
||||||
|
|
||||||
---
|
### 2. Behavioral (`scripts/verify_phase11.sh`)
|
||||||
|
|
||||||
## Layer 2 — Behavioral: PASS
|
|
||||||
|
|
||||||
### Gate re-run (real AWS)
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ bash scripts/verify_phase10.sh
|
=== Phase 11 verification ===
|
||||||
ok: composition.json: l2-static-asset references l1-s3 only (depth 1)
|
README.md: OK (v1.2 framing, platform flow, how-to-run, credentials)
|
||||||
ok: contracts/spike.yaml validates against the contract schema
|
RESEARCH.md: OK (v1.2 addendum, Targets 9-13, D-047/D-048/D-049)
|
||||||
ok: contract_resolver.py resolves spike.yaml to an IR-schema-valid instance
|
.ciagent/ files: OK (v1.2 milestone consistent across all 5 files)
|
||||||
ok: adapter.py compiles L2 IR to terraform with aws_s3_bucket
|
#36988 re-check: OK (date + D-039 extension recorded)
|
||||||
ok: run_spike_e2e.sh completes the full pipeline end-to-end
|
|
||||||
ok: confidence band is pass for dev
|
=== Phase 11: VERIFIED ===
|
||||||
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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
### 3. Security
|
||||||
`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**.
|
|
||||||
|
|
||||||
### Resolver cross-check
|
- 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.
|
||||||
`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).
|
- 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
|
### 4. Quality
|
||||||
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). ✅
|
|
||||||
|
|
||||||
### E2E pipeline (Wave 5, real AWS)
|
- 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`).
|
||||||
`run_spike_e2e.sh` exits 0 and prints:
|
- 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`).
|
||||||
- `terraform plan OK (1 to add, 0 to change, 0 to destroy expected)` — real AWS plan succeeds.
|
- 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.
|
||||||
- `checkov: 6 failed, 5 passed` → `PolicyCheckResult: 12 record(s)` (incl. ACDL_TAG_NAMING SKIPPED per D-043).
|
- D-047/D-048/D-049 are consistent with the PROJECT.md decisions table and the ROADMAP.md phase descriptions.
|
||||||
- `confidence: score=0.8 band=pass` (dev threshold ≥ 0.50).
|
- **PASS.**
|
||||||
- `outbox: contractId= 11111111-... hash= 6e4711b9...` — DynamoDB `put_item` to `acdl-outbox`.
|
|
||||||
- Final line: `=== SPIKE E2E OK ===`.
|
|
||||||
|
|
||||||
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:
|
## Conclusion
|
||||||
- `.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)
|
|
||||||
|
|
||||||
No `.env*`, no `*.tfstate`, no `*_key*`, no `tfplan`, no `.terraform.lock.hcl` (the latter is gitignored via line 14).
|
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
|
||||||
### AKIA scan
|
NFR gaps are catalogued (Phase 12 scope), the simplification plan is
|
||||||
`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.
|
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.
|
||||||
**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.
|
|
||||||
@@ -8,45 +8,159 @@ a production deployment by declaring intent, without authoring a workflow,
|
|||||||
a configuration file, or a Terraform module.
|
a configuration file, or a Terraform module.
|
||||||
|
|
||||||
- **Vision** (the why): [`docs/vision.md`](docs/vision.md)
|
- **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)
|
- **Decisions**: [`.ciagent/PROJECT.md`](.ciagent/PROJECT.md)
|
||||||
- **Target architecture**: [`.ciagent/ARCHITECTURE.md`](.ciagent/ARCHITECTURE.md)
|
|
||||||
- **Phase plan**: [`.ciagent/ROADMAP.md`](.ciagent/ROADMAP.md)
|
- **Phase plan**: [`.ciagent/ROADMAP.md`](.ciagent/ROADMAP.md)
|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
- **v1.1 (active):** architecture finalization + v1 spike. Finalize the
|
- **v1.2 (active):** platform hardening + first real consumer deployment.
|
||||||
architecture to v1.0 (resolve the 11 open design decisions) and prove the
|
Harden the v1.1 spike's NFRs, simplify the setup, rewrite the docs, and
|
||||||
locked commitments with one end-to-end implementation spike
|
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`
|
(`l1-s3` + `l2-static-asset` + Terraform adapter → real `terraform plan`
|
||||||
against AWS).
|
against AWS). Gitea release id 202.
|
||||||
- **v1.0 demo (complete, archived):** tag `v1.1.0`. The 30-minute
|
- **v1.0 demo (complete, archived under `demo/`, tag `v1.1.0`):** the
|
||||||
stub-driven executive demo is preserved under `demo/` as the intent
|
30-minute stub-driven executive demo. Preserved as the intent reference;
|
||||||
reference; it is not the platform.
|
it is not the platform.
|
||||||
|
|
||||||
## Repository layout
|
## How the platform works
|
||||||
|
|
||||||
| Path | Purpose | Populated |
|
The platform is **four layers + six cross-cutting concerns**, bound by the
|
||||||
|------|---------|-----------|
|
vision's "Two Consumer Surfaces, One Platform" tenet: technical developers
|
||||||
| `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+ |
|
(L3A) and non-technical consumers (L3B) converge on the same contract
|
||||||
| `schemas/` | JSON Schemas: IR, PolicyCheckResult, contract | Phase 07 |
|
schema, the same policy envelope, and the same evidence stream.
|
||||||
| `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 |
|
|
||||||
|
|
||||||
## 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
|
||||||
bash demo/scripts/run_demo.sh --no-upload
|
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
|
entirely on local stubs — no AWS, no AI — and shows intent and safety
|
||||||
behavior rather than provisioning real cloud resources. It is the reference
|
behavior rather than provisioning real cloud resources.
|
||||||
of intent for the real platform; it is not the platform itself.
|
|
||||||
|
## 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).
|
||||||
Executable
+51
@@ -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
|
||||||
Reference in New Issue
Block a user