docs(specify): ingest docs/vision+architecture; reframe .ciagent for v1.1

---ci---
project: acdl
phase: 0
milestone: v1.1
status: specify
decisions:
  - D-034 temporary long-lived AWS key waiver (used once in Phase 08, rotated)
  - D-035 milestone v1.1 (feature), ship tag v1.2.0
  - D-036 spike picks l1-s3 + l2-static-asset
  - D-037 demo archived to demo/
  - D-038 open decisions resolved (accept recommendations + decide rest)
---/ci---

Ingest docs/vision.md (6 tenets, anti-goals, signals of success) and
docs/architecture.md v0.2 (4 layers + 6 cross-cutting concerns incl. the
substrate abstraction) as the source of truth for why/how. Reframe the
project from the v1.0 stub demo (tag v1.1.0, archived to demo/ in Phase 06)
to the real Agentic Cloud Delivery Platform.

- config.json: milestone v1.1, status specify
- PROJECT.md: vision + 4-layer/6-concern model + 11 open-decision
  resolutions (W1.A/B, W2.A, BA.A accepted; W3.D/E, BA.B-F, OpenTofu
  decided) + D-034..D-038
- ARCHITECTURE.md: target architecture (IR, confidence, ledger, HITL, SoD,
  substrate abstraction) + v1.1 spike scope (l1-s3 + l2-static-asset)
- REQUIREMENTS.md: add REQ-16..REQ-28 + traceability
- ROADMAP.md: add v1.1 Phases 06-10 (archive, arch-finalize, OIDC, IR+L1,
  L2+contract-e2e)
- PERSONAS.md: add platform-engineer + security-engineer (custom),
  retire infra-stub-engineer, update territories + verification toolchain
  (terraform validate + jsonschema)
- PLAN.md: v1.1 specify plan
This commit is contained in:
Jon Chery
2026-07-21 17:55:09 +00:00
parent 30e63d6cb5
commit 288607b3fa
7 changed files with 664 additions and 478 deletions
+248 -270
View File
@@ -1,323 +1,301 @@
# ACDL — Architecture (initial) # ACDL — Architecture (v1.1 target)
> Initial architecture for the ACDL demo. May be incomplete; refined at phase boundaries. > Target architecture for the real Agentic Cloud Delivery Platform.
> Source of truth for **how**: `docs/architecture.md` (v0.2) is the upstream
> draft; this file is the ACDL-repo operating copy, refined at phase
> boundaries. Where this file and `docs/vision.md` conflict, the vision wins.
## Status
Architecture is at **v0.2** upstream (`docs/architecture.md`). Milestone v1.1
**finalizes it to v1.0** in Phase 07 by resolving the 11 open decisions
(see `PROJECT.md` open-decision resolutions table). This file records the
locked commitments and the v1.1 spike scope.
## Overview ## Overview
The demo is a three-repo, stub-driven system that simulates an autonomous cloud delivery platform. No real cloud or AI is used; every "infrastructure" action is a bash/Python stub that emits structured evidence. The platform is driven by either a developer-supplied `contract.yaml` (L3A) or a natural-language GitHub Issue parsed by a keyword script (L3B), then flows through an autonomous Dev stage, manual QA and Prod approval gates, and finally publishes a hash-chained audit trail to a Pages site. The platform is **four layers + six cross-cutting concerns**. The sixth
concern — the substrate abstraction (§12) — is first-class, not an
implementation detail. The vision's "Two Consumer Surfaces, One Platform"
tenet binds everything: L3A and L3B converge on the same contract schema,
the same policy envelope, and the same evidence stream.
``` ```
──────────────── acdl-contracts ─────────────────┐ ┌──────────── acdl-contracts ────────────┐
Developer ───▶ │ commit contract.yaml Issue (NL intent) │ Developer ───▶ │ commit contract.yaml │ (L3A)
└────────────┬───────────────────┬────────────────┘ Citizen dev ──▶ │ Issue → agent → contract.yaml │ (L3B)
│ (push) │ (issue opened) └────────────────┬───────────────────────┘
▼ ▼ │ (push)
┌─────────────────┐ ┌──────────────────────┐
│ reusable │ │ issue workflow → │ ┌──────────────────────┐
│ pipeline │ │ l3b_agent_stub.py → │ central pipeline
│ (acdl repo) │ │ contract.yaml → push │ (acdl repo, Gitea
└────────┬────────┘ └──────────────────────┘ │ Actions / act_runner) │
└────────┬─────────────┘
┌────────────────────┼────────────────────┐
▼ ▼ ▼ ┌─────────────────────────┼─────────────────────────┐
Dev (autonomous) QA (approval) Prod (approval) ▼ ▼ ▼
mock_executor.sh environment gate environment gate contract→IR resolution policy (Checkov/Kyverno) confidence signal
policy_checker.py │ │ │
confidence_signal.py ▼ ▼ ▼
Terraform adapter ──▶ terraform plan ──▶ PolicyCheckResult ──▶ {score,band}
│ │
▼ ▼
dev (autonomous, ≥0.50) qa (HITL, ≥0.75) prod (HITL, ≥0.90) dr (HITL, ≥0.95)
evidence_writer.py ──▶ audit.json (hash-chained) ──▶ acdl-evidence DynamoDB outbox ──▶ S3 Object Lock (7-yr, source of truth) ──▶ GitHub audit repo (hot index)
index.html (Pages) acdl-evidence (timeline UI)
timeline UI
``` ```
## Components ## Layers
| Name | Description | Boundaries | Depends On | ### Layer 1 — Foundational Primitives
|------|-------------|-----------|------------| Single-purpose, **substrate-agnostic** primitive modules. L1 modules do
| `acdl` repo | Platform meta repo: reusable workflows, L1/L2 stub modules, core scripts | Owns workflows + stubs; does not hold contracts or evidence | — | not compose with other L1s; L1 takes its environment as input. The L1
| L1 modules | Single-purpose infra primitives (EKS Fargate, IAM, Lambda, API Gateway, EventBridge, SQS, S3, CloudWatch) | One folder per L1; `manifest.yaml` + `mock_apply.sh`; do not compose with other L1s | `acdl` repo | interface is defined against the **Target Stack IR**, not against Terraform
| L2 modules | Composed stacks (invoice, commodity-price-feed, energy-analytics-api, regulatory-reporting) | Reference L1s by name; max depth 5; expressed as a composition manifest | L1 modules | directly (the IR is shaped to round-trip to Terraform in v1, per §12.1).
| `mock_executor.sh` | Reads an L2 composition, invokes each L1 `mock_apply.sh`, writes `state.json` | Bash; reads L2 manifest + L1 manifests | L1/L2 modules |
| `policy_checker.py` | Reads `contract.yaml`; fails on forbidden keys (e.g. `public-ingress: true`) | Python; emits `POLICY_VIOLATION:<REASON>` or pass | contract.yaml |
| `confidence_signal.py` | Base 0.90; on policy failure drops to 0.40 and echoes reason | Python; calls policy_checker | policy_checker.py |
| `evidence_writer.py` | Appends an event to `audit.json`, links to previous event via SHA-256 chain | Python; canonical-JSON hashing | audit.json |
| `l3b_agent_stub.py` | Parses Issue text by keywords, emits `contract.yaml` | Python keyword map; no external APIs | contract.yaml schema |
| `acdl-contracts` repo | Developer + agentic entry surface; holds contracts + issue workflow | Triggers main pipeline on push | `acdl` reusable workflow |
| `acdl-evidence` repo | Pages host for `audit.json` + `index.html` timeline | Read-only for the pipeline; written at finalize stage | evidence_writer.py output |
| Reusable pipeline workflow | Dev → QA → Prod → Finalize stages with environment gates | Gitea Actions; calls core scripts | All core scripts |
| `evidence-ui/index.html` | Vanilla-JS timeline UI (Phase 05); fetches `./audit.json` and renders events | Single HTML file with inline CSS+JS; no frameworks (REQ-14) | `audit.json` on `acdl-evidence` main |
| `scripts/run_demo.sh` | Phase 05 dry-run simulation of the 4 demo acts; calls core scripts + writes evidence + uploads `audit.json` + `index.html` to `acdl-evidence` | Bash; uses `evidence_writer.py` + `finalize_evidence.py` + the file-contents API | All Phase 03/04 artifacts |
## Phase 04 pipeline topology (research) - No inter-L1 references. L1 may call Terraform data sources.
- Semver: interface → MAJOR, behavior → MINOR, lifecycle → PATCH (W3.D).
- Immutability on publication. 12-month deprecation window.
- AI refinement is a flag; the trigger is the W1.A joint condition.
Gitea Actions limitations (confirmed via research, supersedes any ### Layer 2 — Composed Stacks
GitHub-Actions assumptions): Combine L1 primitives into deployable shapes. Each codebase maps to one
canonical L2 stack (`multiStack: true` only per W1.B). Shape X
(parameterized module) or Shape Y (thin-composition layer). Hierarchical
composition, max depth 5, only registered L1s. The thin-composition tree's
`wires` field is defined against the IR's relationship type, not a Terraform
module block.
- `actions/upload-artifact@v3` / `download-artifact@v3` work; v4 is NOT supported by act_runner. Pipeline quality checks: secrets-in-plaintext, public ingress, IAM
- Artifacts are scoped to a single workflow run; **re-dispatch starts a new run, so artifacts do NOT survive between dispatches**. wildcard, KMS key reference, tag compliance, naming convention. Restricted
- `workflow_dispatch` API: `POST /api/v1/repos/{owner}/{repo}/actions/workflows/{filename}.yml/dispatches` with body `{ "ref": "<branch>", "inputs": {...} }`. from thin-composition: IAM principal creation, network boundary creation,
- `on: workflow_call` + `uses: <owner>/<repo>/.gitea/workflows/<file>@<ref>` works; pin to `@milestone/v1.0-initial`. key/secret creation, external data transfer. Auto-promote after 3 observed
- `actions/checkout@v4` supports cross-repo (pass `repository:` + `ref:` + `token: ${{ secrets.GITEA_TOKEN }}` for private repos). usages.
- File-contents API: POST to create (201), PUT to update (must include current `sha`, obtained via GET).
- `${{ secrets.GITEA_TOKEN }}` is a manually-created PAT secret on the `acdl` + `acdl-contracts` repos; the auto-injected token is current-repo only and cannot cross-repo.
- No native approval-gate UI; gates are `workflow_dispatch` inputs (`approve_qa`, `approve_prod`).
### Approval-gate + state-persistence approach (D-027, D-028 refined) ### Layer 3A — Developer Consumer Surface
Tag-based reference to the central pipeline template. Developer-owned
workflow file, no platform auto-sync. L3A and L3B are parallel paths, not a
progression. **W2.A (Path B):** tag for dev/qa, SHA for prod; platform CLI
resolves tag→SHA for prod-bound workflows.
Because re-dispatch starts a new run and artifacts do not survive: ### Layer 3B — Agentic Consumer Surface
Hybrid runtime, skill as markdown, agent as executor. Trust model: trust
and always verify on the platform side. Skill envelope (4 dimensions).
Stateless agents, all state in the platform. `profile: agentic` marker
unlocks `naturalLanguageIntent`, `confidenceAtSubmission`, `agentTrace`.
Initial skill catalog (BA.A): web API, worker, scheduled job, static asset,
basic observability bootstrap.
1. The pipeline workflow has `workflow_dispatch` inputs: Environment progression:
- `contract-ref` (string; default `main`) — the ref on `acdl-contracts` carrying the contract.
- `approve_qa` (boolean; default `false`) — the human sets this to `true` to advance past QA.
- `approve_prod` (boolean; default `false`) — the human sets this to `true` to advance past Prod.
2. Each stage job (`dev`, `qa-gate`, `prod-gate`, `finalize`) writes its evidence to `acdl-evidence` via the file-contents API (PUT `audit.json` with the new event appended). This is the persistent state across re-dispatches. | Environment | Autonomy | Attester | Gate |
|---|---|---|---|
| dev | Full autonomy (no HITL) | — | Confidence ≥ 0.50, all six inputs present |
| qa | Held for attestation | QA | GitHub Deployment approval + full QA matrix (§10) |
| prod | Held for attestation | SRE | GitHub Deployment approval + full SRE matrix (§10) |
| dr | Held for attestation | SRE | GitHub Deployment approval + dr-drill evidence |
3. **Dev stage** (always runs on dispatch): check out `acdl` + `acdl-contracts@<contract-ref>`, run `policy_checker.py` + `confidence_signal.py`; if `score < 0.50`, write a `dev_rejected` evidence event and exit 1 (Act 4). Otherwise run `mock_executor.sh`, write a `dev_applied` evidence event, and exit 0. The run ends here. **Staging is removed.** Dev is the only autonomous environment.
4. **QA gate** (next dispatch with `approve_qa=true`): check out, run `evidence_writer.py --stage qa --event "qa approved"`, commit updated `audit.json` to `acdl-evidence`. Exit 0. The run ends. ## Cross-cutting concerns
5. **Prod gate** (next dispatch with `approve_prod=true`): same as QA but `--stage prod`. ### Central pipeline template (§6)
JSON Schema (draft 2020-12) with a thin domain wrapper. Central repo +
generated client libraries. Multi-stage validation: schema → policy → NFR →
confidence. Distributed enrichment. GitOps reconciler (K8s API; cdlc-gitops
state → CRDs) + Terraform execution layer (§12.5). The pipeline emits one
`PolicyCheckResult` per policy rule; the confidence signal consumes them as
one normalized input.
6. **Finalize** (same dispatch as Prod, chained via `needs: prod-gate`): write the `finalize` evidence event, commit final `audit.json` to `acdl-evidence`. The raw URL now serves the updated timeline. ### Contract schema (§7)
Central repo + generated client libraries. Strict fail-fast at schema
stage, multi-stage validation with reason codes from a published
vocabulary. **W3.E:** per-env mandatory inputs —
- dev: `stack`, `environment`
- qa adds: `validation.e2eSuite`, `validation.loadTest`
- prod adds: `runbook`, `dashboard`, `oncall`
- dr adds: `drDrillRef`
- `inputs` always optional; `profile: agentic` fields optional everywhere.
Because each stage is a separate dispatch, the workflow file uses `if:` conditions on each job: ### Confidence signal (§8)
- `dev` runs when `inputs.approve_qa != true && inputs.approve_prod != true` (the initial dispatch). Six canonical inputs, weighted sum with per-input breakdown. Per-env
- `qa-gate` runs when `inputs.approve_qa == true && inputs.approve_prod != true`. thresholds: dev ≥ 0.50, qa ≥ 0.75, prod ≥ 0.90, dr ≥ 0.95. Structured output
- `prod-gate` runs when `inputs.approve_prod == true`. `{ score, band, perInput, reasonCodes }`. 1-year storage, no retraining in
- `finalize` runs after `prod-gate` (`needs: prod-gate`). v1. Halt with explicit reason on missing input.
This means a full pipeline = 3 dispatches (initial, qa-approve, prod-approve). The human drives each via the Gitea UI or the dispatch API. Policy input = list of `PolicyCheckResult` records (engine-agnostic).
Severity → penalty: critical → hard override to mandatory block; high →
-0.2; medium → -0.05; low → -0.01; info → 0.0. One critical finding
hard-overrides the score regardless of all other inputs.
## Phase 05 dry-run + UI (research) **BA.B:** thresholds frozen for v1; tuning begins v1.2 (quarterly FP/FN
tracking; override = Infra & Ops + SRE joint sign-off, itself a
confidence-event).
Phase 05 has no act_runner available in this environment, so the "dry run" ### Audit and evidence stream (§9)
is a local bash simulation (`scripts/run_demo.sh`) that produces the same Tiered ledger: **S3 with Object Lock in compliance mode** (cold, source of
`audit.json` shape a real pipeline run would, then uploads it (plus the truth, 7-year retention) + **GitHub audit repo** (`acdl-evidence`, hot
UI) to `acdl-evidence` via the file-contents API. The simulation covers: query index, not part of the chain). Daily checkpoints. Event schema: JWS
detached signature, `prev_event_hash` chain, controlled-vocabulary
`event_type`. Outbox pattern: local durable outbox + async worker.
- **Act 1 — Friction:** a single evidence event "manual 2-week deployment (legacy process)" at `stage: dev` with a red-colored timeline marker. Outbox database = **DynamoDB**. RPO = 0 (synchronous write to local outbox
- **Act 2 — Developer Self-Service:** `l2-commodity-price-feed` contract, full pipeline (dev → qa → prod → finalize), 4 evidence events. before contract submission ack); RTO = async worker's dead-letter recovery.
- **Act 3 — Citizen Developer:** Issue body fed to `l3b_agent_stub.py`, generates the same `l2-commodity-price-feed` contract, identical pipeline, 4 evidence events. Single-region in v1. The outbox also stores per-contract QA and prod
- **Act 4 — Safety Net:** `l2-regulatory-reporting` contract with `public-ingress: true`, dev rejects (confidence 0.40 < 0.50), 1 evidence event "dev rejected: POLICY_VIOLATION:PUBLIC_INGRESS". approver identities (the only durable record outside GitHub's audit log).
The `audit.json` after `run_demo.sh` contains the genesis + all act ### Human-in-the-Loop mechanics (§10)
events (typically ~14 events). The UI fetches `./audit.json` and renders Pre-execution gates. qa, prod, dr are PR-based attestation gates backed by
a vertical timeline with stage-colored markers and a per-event hash GitHub Environments with required reviewers. No partial deployment to roll
preview. back on rejection (qa, prod); dr is a separate GitHub Deployment against a
separate cluster/region.
### UI rendering contract (D-032, D-033) Reviewer routing: GitHub CODEOWNERS + Environment required reviewers
(qa → QA; prod → SRE; dr → SRE). CODEOWNERS routes, does not enforce
identity distinctness.
`evidence-ui/index.html`: **Separation of duties** (platform-internal, not GitHub-native, not Kyverno
- Single file, inline CSS + JS, no external resources. in v1): on dev→qa promotion the platform writes the QA approver's GitHub
- Fetches `./audit.json` (relative URL; works against any raw-URL origin). identity to the DynamoDB outbox keyed by `contractId`; on qa→prod it reads
- Renders events as a vertical timeline; each event card shows `seq`, `ts`, `stage` (color-coded: `dev` blue, `qa` yellow, `prod` orange, `finalize` green, `genesis` gray, rejected events red), `event` text, and a 12-char hash preview (`hash.slice(0, 12)…`). the stored QA approver and the new SRE approver; if equal, it blocks, emits
- Handles fetch failure with a "No audit data yet" message. `SEPARATION_OF_DUTIES_VIOLATION`, and routes a halt artifact to SRE on-call.
- Refresh button to re-fetch.
## Data Flow Full 8-concern attestation matrix (functional, performance, security
posture, contract NFRs, operational readiness, incident response,
capacity/cost, resilience) — see `docs/architecture.md` §10.4.
1. A `contract.yaml` arrives either by direct push (L3A) or by the issue workflow running `l3b_agent_stub.py` (L3B). Timeout: 1 business day = warn + escalate; 2 business days = auto-freeze +
2. Push to `acdl-contracts` triggers the reusable pipeline in the `acdl` repo. re-submit (linked via `supersedes`). Rejection returns the contract to HELD;
3. **Dev stage:** `policy_checker.py` validates the contract; `mock_executor.sh` applies the L2 composition's L1s; `confidence_signal.py` computes the score; `evidence_writer.py` records each step. If score < 0.50, the stage fails and evidence records the rejection. the audit chain is extended, not torn up.
4. **QA stage:** the workflow pauses on the `qa` environment; a human approves.
5. **Prod stage:** same gate on the `prod` environment.
6. **Finalize:** the workflow commits the updated `audit.json` to `acdl-evidence`; Pages republishes `index.html`, which fetches and renders the timeline.
## Build Order ### Agentic stack (§11)
Hybrid runtime: platform-managed control plane + consumer-owned agent.
Versioned, signed skill catalog over MCP. Skill envelope enforced on
invocation and result submission. Consumer-owned skill execution; the
platform does not run the skill. Stateless agents, all state in the
platform. Skills are reviewed for sensitive data before release (Infra &
Ops owns the review; it is the mandatory release gate).
1. Repo scaffolding: create `acdl-contracts` and `acdl-evidence` in the org; seed `acdl` directory layout. ### Substrate execution (§12) — the binding constraint
2. L1 modules (8 stubs). **Target Stack IR** (locked): a substrate-neutral description of resources
3. L2 modules (4 compositions). (typed inputs/outputs/NFRs), relationships (single parent per child),
4. Core scripts (`mock_executor.sh`, `policy_checker.py`, `confidence_signal.py`, `evidence_writer.py`, `l3b_agent_stub.py`). composition (tree, max depth 5), and policy hooks. The L1 registry, L2
5. Reusable pipeline workflow (Dev → QA → Prod → Finalize) + environment gates. thin-composition tree, contract YML, and PolicyCheckResult schema are all
6. Issue-triggered L3B workflow in `acdl-contracts`. defined against the IR — none against any specific substrate.
7. Evidence UI (`index.html` + Pages config).
8. Demo dry-run + the four scripted acts.
## Gitea API Surface (Phase 01 research) **Substrate adapters** are the only substrate-specific code. An adapter
compiles the IR into a substrate execution plan. **v1 ships exactly one
adapter: the Terraform adapter.** v2+ may add OpenTofu, Pulumi, K8s CRDs
without architectural change.
Authoritative findings from the Gitea docs (added in RESEARCH; supersedes any v1 reality: the IR is shaped to round-trip cleanly to Terraform (nearly
GitHub-Pages / GitHub-Environments assumptions carried over from the spec): isomorphic). As more adapters appear, the IR gets more expressive and the
adapters gain translation logic; the L1 content, the YML standard, and the
thin-composition tree do not change.
| Capability | Gitea support | ACDL approach | **Terraform adapter (v1):** translates IR-typed L1 interface → Terraform
|------------|---------------|---------------| `variable`/`output` blocks; IR-typed L2 thin-composition tree → Terraform
| Org-scoped repo create | `POST /api/v1/orgs/{org}/repos` (`CreateRepoOption`) | Used to create `acdl-contracts` + `acdl-evidence` | root module; IR-typed relationships → module references; emits a
| Native Pages | **None** (no `[pages]` config section) | Serve `acdl-evidence` via raw file URLs: `https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html`; `index.html` fetches `audit.json` from the same raw path. Requires `[cors] ENABLED=true` on the server if the UI is loaded cross-origin. | `terraform plan` from the IR. The adapter is a thin layer; it does not own
| Environments API | **None**; `jobs.<id>.environment` is ignored by act_runner | Model QA/Prod gates as `workflow_dispatch` approval inputs (D-004 / D-013); optionally create `qa` and `prod` branches as a visible stand-in | L1/L2 content.
| `repository_dispatch` trigger | **Not supported** | Cross-repo trigger via `workflow_dispatch` API: `POST /api/v1/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches` called from a step using `$GITEA_TOKEN` |
| Reusable workflows (`workflow_call`) | Supported | `acdl/.gitea/workflows/pipeline.yml` called via `uses: continuous-intelligence/acdl/.gitea/workflows/pipeline.yml@milestone/v1.0-initial` |
| `workflow_dispatch` | Supported (trigger + API) | Used for the manual-approval fallback and the issue workflow's cross-repo trigger |
| `issues.opened` trigger | Supported | Drives the L3B issue-trigger workflow in `acdl-contracts` |
| `act_runner` labels | Single label only (`runs-on: ubuntu-latest`) | All workflows use `runs-on: ubuntu-latest` |
| Context | `${{ gitea.* }}` and `${{ github.* }}` both work | Workflows use `gitea.*` for clarity |
### Branch pinning rule State storage: S3 (state) + DynamoDB (locking), cloud-managed,
single-region in v1.
The reusable workflow in the `acdl` repo lives on `milestone/v1.0-initial` Policy toolchain: **Checkov** for Terraform plan policy (the L2 checks +
(that is the repo's default branch). `uses:` references from `acdl-contracts` tag/naming); **Kyverno** for K8s-native/platform-internal policy; **OPA**
must pin to `@milestone/v1.0-initial`, not `@main` (the `acdl` repo has no reserved for cross-resource cases, explicitly last resort.
`main` branch). The new repos `acdl-contracts` and `acdl-evidence` use
`default_branch: "main"` (D-015) so their default branch exists immediately
for pushes.
### Default verification toolchain **Policy result normalization (§12.6):** the confidence signal consumes a
normalized `PolicyCheckResult` schema, not raw engine output.
There is no `package.json`; ACDL is bash + python stubs. The verification gate
substitutes `bash -n` and `python -m py_compile` for `npm run typecheck`, and
per-phase `scripts/verify_phaseNN.sh` for `npm test`. `npm run build` is a
no-op (no build step). See PERSONAS.md / VERIFICATION note.
## L1 module schema (Phase 02 research)
Each L1 module lives at `modules/l1/<name>/` with exactly two files:
- `manifest.yaml` — declares the L1's identity + a flat `inputs:` map.
Schema (D-017):
```yaml
name: l1-eks-fargate # matches the folder name
kind: l1 # literal "l1"; substrate-agnostic
description: <one-line>
inputs:
<key>:
description: <one-line>
type: string # only "string" allowed (flat, max-depth-1)
```
- `mock_apply.sh` — uniform stub per D-007 + D-018:
```bash
#!/usr/bin/env bash
set -euo pipefail
echo "[L1: <name>] applying..."
sleep 1
echo "[L1: <name>] OK"
exit 0
```
`mock_apply.sh` does NOT read input values; the manifest is for traceability
and for Phase 03's `mock_executor.sh` to enumerate the L1s in an L2.
### L1 list (fixed per REQ-02 / D-019)
| Folder | Description |
|--------|-------------|
| `l1-eks-fargate` | Serverless container compute substrate |
| `l1-iam-role` | Identity and access role primitive |
| `l1-lambda` | Event-driven function primitive |
| `l1-api-gateway` | HTTP routing primitive |
| `l1-eventbridge` | Event bus primitive |
| `l1-sqs` | Queue primitive |
| `l1-s3` | Object store primitive |
| `l1-cloudwatch` | Observability primitive |
L1 modules are single-purpose, substrate-agnostic, max-depth-1 (per
PROJECT.md Constraints). They do not compose with other L1s.
## L2 module schema + core scripts (Phase 03 research)
### L2 manifest.yaml schema (D-020)
```yaml
name: l2-commodity-price-feed # matches the folder name
kind: l2 # literal "l2"
description: <one-line>
l1s: # ordered list of L1 references
- name: l1-eks-fargate # MUST match an existing L1 folder name
inputs:
cluster_name: price-feed-cluster
region: us-east-1
cpu_arch: arm64
- name: l1-lambda
inputs:
function_name: price-ingest
runtime: python3.11
handler: index.handler
# ... up to 5 L1 references per L2 (max-depth-5 per REQ-05; L2->L1 is depth 1)
```
L2s reference L1s **by name only** (no path); `mock_executor.sh` resolves
the name to `modules/l1/<name>/`.
### L2 list (fixed per REQ-04)
| Folder | Description | L1s (per S&P Global Energy / Platts use cases) |
|--------|-------------|------------------------------------------------|
| `l2-invoice-service` | Billing + invoicing microservice | `l1-eks-fargate`, `l1-iam-role`, `l1-lambda`, `l1-sqs`, `l1-s3` |
| `l2-commodity-price-feed` | Real-time price ingestion | `l1-eks-fargate`, `l1-lambda`, `l1-api-gateway`, `l1-eventbridge`, `l1-s3` |
| `l2-energy-analytics-api` | Historical query API | `l1-eks-fargate`, `l1-api-gateway`, `l1-lambda`, `l1-s3`, `l1-cloudwatch` |
| `l2-regulatory-reporting` | Compliance + reporting | `l1-eks-fargate`, `l1-iam-role`, `l1-lambda`, `l1-sqs`, `l1-s3` |
Each L2 references exactly 5 L1s (within the max-depth-5 constraint; L2→L1
is depth 1, so depth-5 is generous but the spec caps composition depth at
5 — the count is 5 to demonstrate a realistic composed stack).
### contract.yaml schema (D-021)
```yaml
stack: l2-commodity-price-feed # MUST match an existing L2 folder name
inputs: # top-level params for the L2 (optional)
environment: dev
owner: platform-team
public-ingress: false # bool; true triggers POLICY_VIOLATION:PUBLIC_INGRESS
```
The `public-ingress` key is the only policy-enforced field in Phase 03.
Phase 04's pipeline reads `contract.yaml`, runs `policy_checker.py`, then
`mock_executor.sh` to apply the L2.
### state.json shape (D-022)
`mock_executor.sh` writes `state.json` to its working directory:
```json ```json
{ {
"l2": "l2-commodity-price-feed", "contractId": "uuid",
"l1s": [ "evaluatedAt": "ISO-8601",
{"name": "l1-eks-fargate", "applied": true, "exit_code": 0}, "engine": "checkov | kyverno | opa",
{"name": "l1-lambda", "applied": true, "exit_code": 0}, "ruleId": "CKV_AWS_24 | KYVERNO_NO_PRIVILEGED | ...",
... "severity": "critical | high | medium | low | info",
], "result": "pass | fail | skipped | error",
"contract": { "message": "human-readable",
"stack": "l2-commodity-price-feed", "evidence": { "...engine-specific, opaque to the signal..." },
"inputs": {...}, "resourceRef": "IR-typed resource identifier"
"public-ingress": false
}
} }
``` ```
### audit.json event + hash chain (D-023) Execution layer: GitHub/Gitea Actions in the central pipeline repo. State
locking via DynamoDB. **AWS credentials via OIDC federation — long-lived
credentials are forbidden** (§12.5). The platform does not run
`terraform apply` against a developer's workstation; all execution is in
the central pipeline.
`audit.json` is a JSON array of event objects. `evidence_writer.py` Registry maintenance: L1 publication updates the L1 registry in the same
appends one event per call. Hash chain: PR. The registry is the IR-typed contract, not a Terraform-specific
variable schema.
1. Construct the event dict with `hash` set to empty string. Contract→IR resolution: the contract declares intent in IR-typed terms;
2. Serialize via `json.dumps(event, sort_keys=True, separators=(",", ":"))` — canonical JSON (deterministic key order, no whitespace). the pipeline resolves it to a target stack (list of L1 instances + inputs +
3. Compute `hash = sha256(canonical_json.encode("utf-8")).hexdigest()`. relationships); the Terraform adapter compiles the target stack to a plan.
4. Set `event["hash"] = hash`.
5. Append to `audit.json`.
Genesis event (when `audit.json` is empty or missing): ## v1.1 spike scope
```json The spike (Phases 0810) materializes the **minimum** that proves the IR
{ commitments hold (no polyglot mess):
"seq": 0,
"ts": "2026-07-21T13:00:00Z",
"stage": "genesis",
"event": "audit log initialized",
"prev_hash": "GENESIS",
"hash": "<sha256 of the canonical json of this event with hash empty>"
}
```
Subsequent events: `seq = prev.seq + 1`, `prev_hash = prev.hash`. - One L1: `l1-s3` (IR-typed interface; the only AWS resource in the spike).
- One L2 thin-composition: `l2-static-asset` (references `l1-s3` only).
- Terraform adapter: IR → `terraform plan` against AWS via OIDC.
- One contract submission → contract→IR → `terraform plan` → Checkov
`PolicyCheckResult` → confidence signal → evidence event to the DynamoDB
outbox.
- State: S3 + DynamoDB (real AWS, single-region).
### Core script I/O contracts Out of spike scope: full HITL matrix wiring, Kyverno, OPA, MCP skill
catalog, GitOps reconciler, multi-region, prod/dr environments, the 5-skill
L3B catalog. Those are post-spike (v1.2+) platform build-out.
| Script | Input | Output | Exit | ## Gitea API surface (carried from v1.0, refined)
|--------|-------|--------|------|
| `mock_executor.sh` | `<contract.yaml path>` (argv[1]); reads L2 manifest from `modules/l2/<contract.stack>/manifest.yaml` | writes `state.json` to cwd; prints per-L1 progress | 0 on all-L1s-pass; non-zero on any L1 failure | | Capability | Gitea support | ACDL approach (v1.1) |
| `policy_checker.py` | `<contract.yaml path>` (argv[1]) | stdout: `POLICY_PASS` or `POLICY_VIOLATION:PUBLIC_INGRESS` | 0 on pass; 1 on violation | |------------|---------------|----------------------|
| `confidence_signal.py` | `<contract.yaml path>` (argv[1]); calls policy_checker | stdout: `{"score": 0.90|0.40, "reason": "..."}` | 0 always (per D-024; pipeline decides gate) | | Org-scoped repo create | `POST /api/v1/orgs/{org}/repos` | Used for any new repos |
| `evidence_writer.py` | argv: `--stage <dev|qa|prod|finalize|genesis>` `--event "<text>"` `--audit <path to audit.json>` (default `./audit.json`) | appends event to audit.json; prints the new event's hash + seq | 0 on success; 1 on I/O error | | Native Pages | **None** | Serve `acdl-evidence` via raw file URLs (unchanged from v1.0) |
| `l3b_agent_stub.py` | argv[1] = issue body text (or stdin if no argv); optional `-o <path>` (default stdout) | writes a `contract.yaml` (D-021 schema) with `stack` set by the D-008 keyword map | 0 on success; 1 on empty input | | Environments API | **None**; act_runner ignores `environment:` | Model HITL gates via `workflow_dispatch` approval inputs (v1.0 D-013 pattern) — **refined in Phase 07** for the real pre-execution gate model |
| `repository_dispatch` | Not supported | Cross-repo trigger via `workflow_dispatch` API (unchanged) |
| Reusable workflows | Supported | `acdl/.gitea/workflows/pipeline.yml` via `uses: ...@<ref>` |
| `id-token: write` / OIDC | **To be confirmed in RESEARCH** (highest-priority unknown) | If act_runner supports OIDC, configure AWS trust; if not, use a documented spike-only waiver and revisit in v1.2 |
| `actions/configure-aws-credentials` | Depends on OIDC support | If OIDC unavailable, fall back to short-lived `aws sts assume-role-with-web-identity` from a step |
### Branch pinning rule (refined for W2.A)
- Dev/qa contracts reference the reusable workflow by **tag**
(`@v1.1-spike`).
- Prod-bound workflows reference by **SHA**; the platform CLI
(`platform/cli/resolve-tag.ts`, Phase 07) resolves the current tag to its
SHA. (Spike scope: the CLI is a stub; the real CLI lands in v1.2.)
### Verification toolchain
ACDL has no `package.json`. The verification gate substitutes:
- **typecheck:** `terraform validate`, `python3 -m py_compile`, JSON Schema
validation (`ajv` or `python -m jsonschema`) against `schemas/`.
- **test:** per-phase `scripts/verify_phaseNN.sh` (Phase 06: archive integrity;
Phase 07: schema validation + decision-resolution completeness; Phase 08:
OIDC assume-role + state backend; Phase 09: IR + L1 + adapter `terraform
plan`; Phase 10: end-to-end contract submission).
- **build:** `terraform init` (real build for the spike).
- See `PERSONAS.md` verification_toolchain.
## Build order (v1.1)
1. Phase 06 — archive demo, reorient repo.
2. Phase 07 — finalize architecture v1.0; author schemas + designs.
3. Phase 08 — AWS OIDC bootstrap (use temp key once, rotate).
4. Phase 09 — IR + `l1-s3` + Terraform adapter → `terraform plan`.
5. Phase 10 — `l2-static-asset` + contract→IR → end-to-end spike.
6. COMPLETE gate — review → ship `v1.2.0` → audit.
+70 -43
View File
@@ -1,21 +1,22 @@
--- ---
project: acdl project: acdl
milestone: v1.0 milestone: v1.1
generated_at: 2026-07-21 generated_at: 2026-07-21
generator: lead-developer generator: lead-developer
verification_toolchain: verification_toolchain:
typecheck: "bash -n scripts/**/*.sh modules/**/*.sh && python3 -m py_compile scripts/**/*.py" typecheck: "terraform validate && python3 -m py_compile platform/**/*.py && python3 -m jsonschema schemas/*.schema.json"
test: "scripts/verify_phaseNN.sh" test: "scripts/verify_phaseNN.sh"
build: "no-op (no build step; bash + python stubs)" build: "terraform init"
note: | note: |
ACDL has no package.json. The execute/verify/ship workflows substitute ACDL has no package.json. The execute/verify/ship workflows substitute
bash -n and python -m py_compile for npm run typecheck, a per-phase `terraform validate` + `python -m py_compile` + JSON Schema validation
verify script for npm test, and treat npm run build as a no-op. This (`python -m jsonschema` or `ajv`) for npm run typecheck, a per-phase
override is documented here as the single source of truth; the ci-* verify script for npm test, and `terraform init` for npm run build.
agents read PERSONAS.md before running verification commands. This override is documented here as the single source of truth; the
ci-* agents read PERSONAS.md before running verification commands.
--- ---
# ACDL — Persona Roster (project-level) # ACDL — Persona Roster (project-level, v1.1)
## Active personas ## Active personas
@@ -24,68 +25,94 @@ verification_toolchain:
- **Active:** true - **Active:** true
- **Phase-specific:** false - **Phase-specific:** false
- **Frameworks:** (none) - **Frameworks:** (none)
- **Constraints:** pragmatic, battle-tested defaults, no-cross-territory-edits - **Constraints:** pragmatic, battle-tested defaults, no-cross-territory-edits, vision-is-source-of-truth-for-why
- **Territory:** `.ciagent/**`, `scripts/verify_phase*.sh`, `README.md`, `.gitignore` - **Territory:** `.ciagent/**`, `scripts/verify_phase*.sh`, `README.md`, `docs/**` (meta only — not architecture authoring), `.gitignore`
- **Reason:** Owns CIAgent metadata and cross-phase verification scripts. - **Reason:** Owns CIAgent metadata, cross-phase verification scripts, and the v1.1 phase orchestration. Resolves the 11 open decisions (D-038) and arbitrates persona conflicts.
### backend-engineer ### backend-engineer
- **Domain:** backend - **Domain:** backend
- **Active:** true - **Active:** true
- **Phase-specific:** false - **Phase-specific:** false
- **Frameworks:** gitea-actions, act_runner, bash, python, yaml - **Frameworks:** python, json-schema, gitea-actions, act_runner, bash, yaml
- **Constraints:** no-cloud, no-ai, stub-only, hash-chain-must-be-deterministic, max-depth-5 - **Constraints:** contract-schema-first, fail-fast-with-reason-codes, no-long-lived-credentials, severity-to-penalty-mapping-immutable
- **Territory:** `.gitea/workflows/**`, `scripts/**` (except `scripts/verify_phase*.sh`), `modules/l2/**/manifest.yaml` - **Territory:** `platform/confidence_signal.py`, `platform/contract_resolver.py`, `platform/outbox/**`, `schemas/**` (contract + IR + PolicyCheckResult), `contracts/**` (sample contracts), `.gitea/workflows/**` (pipeline)
- **Reason:** Owns workflow YAML, core scripts (mock_executor, policy_checker, confidence_signal, evidence_writer, l3b_agent_stub), and L2 composition manifests. - **Reason:** Owns the contract schema, contract→IR resolution, the confidence signal (6 inputs + severity mapping), the DynamoDB outbox writer, and the central pipeline workflow.
### infra-stub-engineer (custom) ### platform-engineer (custom)
- **Domain:** backend - **Domain:** infra
- **Active:** true - **Active:** true
- **Phase-specific:** false - **Phase-specific:** false
- **Frameworks:** bash, yaml - **Frameworks:** terraform, aws-iam, aws-s3, aws-dynamodb, oidc, json-schema
- **Constraints:** mock-only, echo-contract-from-D-007, sleep-1s-exit-0, substrate-agnostic, single-purpose - **Constraints:** ir-is-substrate-agnostic, adapter-is-only-substrate-specific-code, state-in-s3+dynamodb-single-region, oidc-only-no-long-lived-keys (waiver D-034 for bootstrap), terraform-plan-only-in-spike
- **Territory:** `modules/l1/**` - **Territory:** `adapters/terraform/**`, `modules-ir/**`, `terraform/**` (state backend, provider config), `platform/registry/**`
- **Reason:** Created to own L1 stub modules (Phase 02) and their uniform mock_apply.sh behavior per D-007. Domain is backend (bash stubs) but territory is strictly L1 modules to keep L1/L2 concerns separated from workflow YAML. - **Reason:** Owns the Target Stack IR, the L1/L2 IR-typed modules, the Terraform adapter, the AWS OIDC bootstrap, and the state backend. The IR is substrate-agnostic; the adapter is the only substrate-specific code (the binding constraint per §12).
### security-engineer (custom)
- **Domain:** security
- **Active:** true
- **Phase-specific:** false
- **Frameworks:** aws-iam, oidc, checkov, json-schema
- **Constraints:** least-privilege, separation-of-duties-identity-distinctness, no-secrets-in-skill-markdown, audit-chain-extends-not-tears-up, critical-finding-hard-overrides-confidence
- **Territory:** `platform/hitl_matrix_design.md`, `platform/audit_ledger_design.md`, `adapters/terraform/policy/**` (Checkov adapter → PolicyCheckResult), `platform/separation_of_duties.py`
- **Reason:** Owns the HITL matrix design, separation-of-duties (DynamoDB identity-distinctness), the audit ledger design (S3 Object Lock + JWS + chain), and the Checkov→PolicyCheckResult adapter. Enforces the "Safety is Computed, Not Assumed" + "Audit truth lives outside the repository" vision tenets.
### frontend-engineer
- **Domain:** frontend
- **Active:** true
- **Phase-specific:** false
- **Frameworks:** vanilla-js, dom-api, fetch-api
- **Constraints:** no-frameworks, single-file, fetch-from-same-origin-raw-url, relative-url-for-audit-json
- **Territory:** `evidence-ui/**` (the timeline UI; pushed to `acdl-evidence`)
- **Reason:** Owns the evidence timeline UI (`index.html`). Carried over from v1.0; the UI continues to render the audit stream. The v1.1 spike writes events to the DynamoDB outbox; the UI continues to read `audit.json` published to `acdl-evidence`.
## Deactivated personas ## Deactivated personas
### infra-stub-engineer (custom, v1.0 only)
- **Domain:** backend
- **Active:** false
- **Reason:** Owned L1 stub modules (`modules/l1/**`) in the v1.0 demo. The demo is archived to `demo/` in Phase 06; real L1 modules (`modules-ir/l1/**`) are owned by platform-engineer (substrate-agnostic IR + Terraform adapter). The stub engineer is no longer needed.
- **Phase-specific:** false (was v1.0)
- **Territory (would have been):** `demo/modules/l1/**`
### data-engineer ### data-engineer
- **Domain:** data - **Domain:** data
- **Active:** false - **Active:** false
- **Reason:** No persistence layer. ACDL state is flat JSON files (`audit.json`, `state.json`) written by bash/python scripts; no ORM, no migrations, no DB. Schema contracts live in `manifest.yaml` (owned by backend-engineer / infra-stub-engineer). - **Reason:** No ORM/persistence framework. The v1.1 outbox is DynamoDB but accessed via boto3 calls inside `platform/outbox/**` (owned by backend-engineer); the audit ledger is S3 Object Lock + JWS (owned by security-engineer). No schema-migration layer, no ORM, no data-engineer territory.
- **Phase-specific:** false - **Phase-specific:** false
- **Frameworks:** (would have been: drizzle, prisma) - **Frameworks:** (would have been: drizzle, prisma)
- **Constraints:** (would have been: schema-first, type-safe-orm) - **Constraints:** (would have been: schema-first, type-safe-orm)
- **Territory:** (would have been: `**/db/**`, `**/migrations/**`) - **Territory:** (would have been: `**/db/**`, `**/migrations/**`)
### frontend-engineer
- **Domain:** frontend
- **Active:** true # REACTIVATED for Phase 05 (evidence UI + dry run)
- **Reason:** Phase 05 builds the vanilla-JS `index.html` timeline UI (REQ-14) and runs the 4 demo acts end-to-end dry run (REQ-15). Inactive for Phases 01-04 (no UI).
- **Phase-specific:** true (this reactivation is for Phase 05 only; will be deactivated again after the milestone ships if the project continues)
- **Frameworks:** vanilla-js, dom-api, fetch-api
- **Constraints:** no-frameworks, single-file, fetch-from-same-origin-raw-url, relative-url-for-audit-json
- **Territory:** `evidence-ui/**` (the source-of-truth `index.html` in the `acdl` repo; pushed to `acdl-evidence` by `run_demo.sh`)
## Phase-specific overrides ## Phase-specific overrides
| Phase | Personas active | Reactivations / notes | | Phase | Personas active | Notes |
|-------|-----------------|----------------------| |-------|------------------|-------|
| 01 repo-scaffolding | lead-developer, backend-engineer | infra-stub-engineer idle (no L1 work this phase) | | 06 archive-demo-and-reorient | lead-developer, frontend-engineer (demo UI move only) | backend/platform/security idle |
| 02 l1-modules | lead-developer, backend-engineer, infra-stub-engineer | infra-stub-engineer owns L1 stubs | | 07 architecture-v1-finalization | lead-developer, backend-engineer (schemas), security-engineer (HITL/ledger/SoD), platform-engineer (IR) | frontend idle |
| 03 l2-modules-and-core-scripts | lead-developer, backend-engineer, infra-stub-engineer | backend-engineer owns core scripts + L2 manifests; infra-stub-engineer only updates L1 manifests if referenced | | 08 aws-oidc-bootstrap | platform-engineer (lead), security-engineer (trust policy review) | backend/frontend idle |
| 04 pipeline-and-approval-gates | lead-developer, backend-engineer | infra-stub-engineer idle; frontend-engineer still off | | 09 v1-spike-ir-and-l1-and-adapter | platform-engineer (lead), backend-engineer (IR schema co-author) | security/frontend idle |
| 05 evidence-ui-and-demo-dry-run | lead-developer, backend-engineer, frontend-engineer | frontend-engineer REACTIVATED for `index.html` only; backend-engineer owns the dry-run script and audit.json wiring | | 10 v1-spike-l2-and-contract-e2e | platform-engineer (L2 + adapter), backend-engineer (contract→IR + confidence + outbox), security-engineer (Checkov→PolicyCheckResult), frontend-engineer (evidence event surfaces in timeline) | Full roster |
## Domain priority (used by TaskDecomposer) ## Domain priority (used by TaskDecomposer)
`coordination -> backend -> infra-stub-engineer -> frontend-engineer (Phase 05 only)` `coordination → security → platform → backend → frontend`
Rationale: in v1.1, the security/architecture commitments (IR, confidence,
HITL, ledger, SoD) are the binding constraints; the platform layer
materializes them; backend wires the pipeline; frontend surfaces the
evidence. The spike's correctness depends on the security + platform layers
being right before backend wiring.
## Conflict resolutions (lead-developer arbitration) ## Conflict resolutions (lead-developer arbitration)
- `backend-engineer` vs `infra-stub-engineer` over `modules/l2/**/manifest.yaml`: backend-engineer owns L2 manifests; infra-stub-engineer owns L1 manifests. No overlap. - `backend-engineer` vs `platform-engineer` over `schemas/ir.schema.json`: platform-engineer owns the IR (it is substrate-agnostic but infra-shaped); backend-engineer owns the contract schema and the contract→IR resolution (contract is the consumer surface). Co-authoring is expected; conflict goes to lead-developer.
- `backend-engineer` vs `frontend-engineer` over `acdl-evidence/index.html`: frontend-engineer owns the file in Phase 05; backend-engineer provides the `audit.json` schema contract (event shape) via `evidence_writer.py` and a `SCHEMA.md` note in ARCHITECTURE.md. - `backend-engineer` vs `security-engineer` over `platform/confidence_signal.py`: security-engineer owns the severity→penalty mapping + critical-override semantics; backend-engineer owns the 6-input weighted sum + per-env thresholds. The confidence signal is co-owned; conflicts go to lead-developer.
- `lead-developer` vs any: lead-developer owns `.ciagent/**` and verification scripts; persona engineers do not edit CIAgent metadata. - `platform-engineer` vs `security-engineer` over `adapters/terraform/policy/**`: security-engineer owns the Checkov→PolicyCheckResult adapter (policy is a security concern); platform-engineer owns the Terraform adapter (substrate translation). No overlap.
- `lead-developer` vs any: lead-developer owns `.ciagent/**` + `docs/**` meta + verification scripts; persona engineers do not edit CIAgent metadata or the vision/architecture source docs.
## Territory enforcement mode ## Territory enforcement mode
`warn` — config.json has no `personas.territory_enforcement` field, so the default per execute.md is `warn`. Cross-territory edits are logged in the commit message but do not fail the task. `warn` — config.json has no `personas.territory_enforcement` field, so the
default per execute.md is `warn`. Cross-territory edits are logged in the
commit message but do not fail the task. The spike's small scope means
co-authoring across territories is likely; `warn` keeps it frictionless.
+46 -83
View File
@@ -1,100 +1,63 @@
--- ---
phase: 05 phase: 0
name: evidence-ui-and-demo-dry-run name: v1.1-specify
milestone: v1.0 milestone: v1.1
milestone_type: feature milestone_type: feature
status: planned status: specify
requirements: [REQ-13, REQ-14, REQ-15] requirements: [REQ-16..REQ-28]
must_haves: must_haves:
- "evidence-ui/index.html exists: single HTML file with inline CSS + JS, no external resources, no frameworks (D-032, REQ-14)" - "config.json project.milestone = v1.1, status = specify"
- "index.html fetches ./audit.json (relative URL) and renders events as a vertical timeline with stage color-coding (dev/qa/prod/finalize/genesis) + 12-char hash preview" - "PROJECT.md reframed from stub demo to real platform (vision tenets + 4-layer/6-concern model + 11 open-decision resolutions + D-034..D-038)"
- "scripts/run_demo.sh exists: simulates all 4 acts (Friction, Dev Self-Service, Citizen Developer, Safety Net) by calling the Phase 03 core scripts + evidence_writer.py + finalize_evidence.py; writes a final audit.json; uploads audit.json + evidence-ui/index.html to acdl-evidence main via the Gitea file-contents API (D-031/D-033)" - "ARCHITECTURE.md rewritten to the target architecture (IR, confidence, ledger, HITL, SoD, substrate abstraction) with v1.1 spike scope"
- "scripts/run_demo.sh is idempotent: re-running overwrites both files on acdl-evidence and produces the same audit.json (deterministic hash chain)" - "REQUIREMENTS.md adds v1.1 section (REQ-16..REQ-28) + traceability"
- "Act 4 produces a 'dev rejected: POLICY_VIOLATION:PUBLIC_INGRESS' evidence event with score 0.40 (the Safety Net)" - "ROADMAP.md adds v1.1 Phases 0610"
- "scripts/verify_phase05.sh passes: validates index.html structure (single file, inline, fetch call), runs run_demo.sh, fetches the raw audit.json + index.html URLs from acdl-evidence and confirms HTTP 200" - "PERSONAS.md adds platform-engineer + security-engineer, retires infra-stub-engineer, updates territories + verification toolchain"
- "docs/vision.md + docs/architecture.md (upstream) referenced as the why/how source of truth"
verification: verification:
typecheck: "bash -n scripts/*.sh && python3 -m py_compile scripts/*.py" typecheck: "python3 -m jsonschema schemas/*.schema.json 2>/dev/null || true # schemas authored in Phase 07"
test: "scripts/verify_phase05.sh" test: "scripts/verify_phase06.sh # authored in Phase 06"
build: no-op build: no-op
--- ---
# Phase 05 — evidence-ui-and-demo-dry-run PLAN # v1.1 — Specify PLAN
## Goal ## Goal
Build the vanilla-JS timeline UI and run the 4 demo acts as a local dry Ingest `docs/vision.md` + `docs/architecture.md` and update all `.ciagent`
run that produces a real `audit.json` and publishes it (plus the UI) to files to reframe the project from the v1.0 stub demo to the real Agentic
`acdl-evidence` main. This is the milestone's capstone phase. Cloud Delivery Platform, milestone v1.1 (architecture finalization + v1
spike).
## Requirements covered ## Scope of this stage (SPECIFY only)
- REQ-13: `acdl-evidence` is Pages-enabled and serves `audit.json` plus `index.html` (substituted by D-012: raw-URL 200 on both files) Rewrite the five `.ciagent/` files + `config.json`:
- REQ-14: `index.html` uses vanilla JS to fetch `audit.json` and render events as a timeline - `config.json` — milestone `v1.1`, status `specify`.
- REQ-15: All four demo acts reproduce deterministically in a dry run - `PROJECT.md` — reframe; carry the 6 vision tenets; record the 11
open-decision resolutions (D-038) + the OIDC waiver (D-034) + spike picks
(D-036) + archive decision (D-037) + version (D-035).
- `ARCHITECTURE.md` — target architecture (4 layers + 6 cross-cutting
concerns incl. substrate abstraction); v1.1 spike scope.
- `REQUIREMENTS.md` — add REQ-16..REQ-28 + traceability.
- `ROADMAP.md` — add v1.1 Phases 0610.
- `PERSONAS.md` — add platform-engineer + security-engineer; retire
infra-stub-engineer; update territories + verification toolchain.
## Waves (vertical slices, domain priority order) ## Waves
### Wave 1 — frontend-engineer (the UI) Single wave (lead-developer only — this is metadata work):
- **T-0.1** Rewrite `config.json` + `PROJECT.md` + `ARCHITECTURE.md`.
**Tasks:** - **T-0.2** Rewrite `REQUIREMENTS.md` + `ROADMAP.md` + `PERSONAS.md`.
- **T-0.3** Commit with `---ci---` block (phase 0, milestone v1.1, status specify).
- **T-5.1** Create `evidence-ui/index.html` — single HTML file with inline `<style>` + `<script>`. The JS fetches `./audit.json` (relative URL), parses the JSON array, and renders a vertical timeline. Each event card shows: `seq` (badge), `ts` (timestamp), `stage` (color-coded chip: dev=blue, qa=yellow, prod=orange, finalize=green, genesis=gray, rejected=red), `event` text, and `hash.slice(0, 12) + "…"` (a 12-char preview). Includes a refresh button that re-fetches. Handles fetch failure with a "No audit data yet" message. No external resources (no CDN, no fetch libraries); vanilla JS only. The file is self-contained.
**Files owned:** `evidence-ui/index.html`
**Commits:** one commit, `phase: 5, status: plan-as-execute, persona: frontend-engineer, task: T-5.1, requirements.covered: [REQ-14]`.
### Wave 2 — backend-engineer (the dry-run script)
**Tasks:**
- **T-5.2** Create `scripts/run_demo.sh` — the Phase 05 dry-run simulation. It:
1. Accepts an optional `--no-upload` flag (for testing without hitting Gitea).
2. Creates a clean working directory under `/tmp/acdl_demo_run/`; sets `AUDIT=/tmp/acdl_demo_run/audit.json`.
3. Initializes the audit log: `python3 scripts/evidence_writer.py --stage genesis --event "audit log initialized" --audit "$AUDIT"`.
4. **Act 1 — Friction:** write a single event `--stage dev --event "Act 1 Friction: manual 2-week deployment (legacy process)"`.
5. **Act 2 — Developer Self-Service:** write `contracts/act2.yaml` with `stack: l2-commodity-price-feed`, `public-ingress: false`. Run `policy_checker.py` + `confidence_signal.py` + `mock_executor.sh`. Write events: `dev applied: l2-commodity-price-feed`, `qa approved`, `prod approved`, `finalize: audit.json committed`.
6. **Act 3 — Citizen Developer:** feed an Issue body ("We need to ingest natural gas prices from Platts...") to `l3b_agent_stub.py -o contracts/act3.yaml`. Run the same pipeline as Act 2 against the generated contract. Write 4 events.
7. **Act 4 — Safety Net:** write `contracts/act4.yaml` with `stack: l2-regulatory-reporting`, `public-ingress: true`. Run `policy_checker.py` (fails) + `confidence_signal.py` (score 0.40). Since score < 0.50, write `dev rejected: POLICY_VIOLATION:PUBLIC_INGRESS` and skip QA/Prod/Finalize.
8. Print a summary of all events.
9. If `--no-upload` is NOT set: call `python3 scripts/finalize_evidence.py --audit "$AUDIT"` to upload `audit.json` to `acdl-evidence`, then call `finalize_evidence.py --audit evidence-ui/index.html --path index.html --message "chore(ui): update index.html"` to upload the UI. (Reuses `finalize_evidence.py` with `--path` override for `index.html`.)
10. Exit 0 if all 4 acts produced the expected evidence events; non-zero otherwise.
Cleanup: write contracts under `contracts/` (gitignored) so the working tree stays clean.
**Files owned:** `scripts/run_demo.sh`
**Commits:** one commit, `phase: 5, status: plan-as-execute, persona: backend-engineer, task: T-5.2, requirements.covered: [REQ-13, REQ-15]`.
### Wave 3 — lead-developer (verify script + traceability)
**Tasks:**
- **T-5.3** Create `scripts/verify_phase05.sh`. Checks:
1. `evidence-ui/index.html` exists, is a single file, contains `<style>` and `<script>` inline tags, contains `fetch('./audit.json'` (relative URL), no `https://` external resource references (no CDN).
2. `scripts/run_demo.sh` is `bash -n` clean.
3. Run `scripts/run_demo.sh --no-upload` and confirm:
- It exits 0.
- It produces a non-empty `audit.json` with at least 14 events (genesis + act1 + act2[4] + act3[4] + act4[1] = 11 minimum, but with markers it may be more — use `>= 11`).
- The audit chain is valid (re-run the hash check).
- The Act 4 event contains "POLICY_VIOLATION:PUBLIC_INGRESS".
4. If `ACDL_GITEA_TOKEN` is set: run `scripts/run_demo.sh` (with upload), then curl the raw URLs for `audit.json` and `index.html` on `acdl-evidence` and confirm HTTP 200 + that the audit.json matches the local one (or at least parses as JSON with the expected number of events) + that index.html contains "ACDL Evidence" or "audit.json" reference.
5. If `ACDL_GITEA_TOKEN` is NOT set: skip the upload check with a clear "SKIP (no token)" message; the dry-run + structural checks are sufficient.
- **T-5.4** Update `.ciagent/REQUIREMENTS.md` (REQ-13/14/15 → covered pending VERIFY) and `.ciagent/ROADMAP.md` (Phase 05 → executing).
**Files owned:** `scripts/verify_phase05.sh`, `.ciagent/REQUIREMENTS.md`, `.ciagent/ROADMAP.md`
**Commits:** one per task, `phase: 5, status: plan-as-execute, persona: lead-developer, task: T-5.3/5.4`.
## Wave ordering
- Wave 1 (frontend) builds the UI.
- Wave 2 (backend) builds the dry-run script.
- Wave 3 (lead-developer) verifies + traceability.
Wave 1 and Wave 2 can run in parallel (no file overlap), but per execute.md the domain priority is `coordination → backend → frontend → custom`. Here `frontend-engineer` and `backend-engineer` have no territory overlap, so they can run sequentially in priority order: backend first (Wave 2), then frontend (Wave 1) — but for clarity I'll keep Wave 1 = frontend, Wave 2 = backend (the UI is the visible artifact; the script needs to upload it). Actually, `run_demo.sh` references `evidence-ui/index.html`, so the UI must exist before the script is tested end-to-end. Order: Wave 1 (UI) → Wave 2 (script) → Wave 3 (verify).
## Dependencies ## Dependencies
- Depends on Phases 01-04 (all core scripts, workflows, and the acdl-evidence repo must exist). - `docs/vision.md` + `docs/architecture.md` (pulled from `origin/main`).
- This is the last phase in the milestone. After Phase 05 ships, the COMPLETE gate runs: review → ship(milestone v1.1.0) → audit. - User-locked parameters (milestone v1.1, spike = `l1-s3` + `l2-static-asset`,
real AWS via OIDC, accept-recommendations-and-decide-rest).
## Next stage
CLARIFY — auto-resolve the 11 open decisions (already recorded in
`PROJECT.md`); surface genuinely blocking ambiguities only. Then RESEARCH —
delegate to ci-researcher on 10 targets (highest priority: Gitea/act_runner
OIDC support, which gates real-AWS feasibility for the spike).
+166 -73
View File
@@ -2,94 +2,187 @@
## Vision / Core Value ## Vision / Core Value
A 30-minute executive demo proving that infrastructure can be delivered **automatically, safely, and with a complete audit trail** — without the usual weeks of manual tickets, reviews, and copy-pasted configuration. Because the demo runs entirely on **local stubs** (no AWS/GCP/Azure, no external LLM APIs), it shows intent and safety behavior rather than provisioning real cloud resources. Consumers declare intent; the platform delivers safe production
deployment through an agentic stack. The platform absorbs two frictions:
the cognitive load of getting the infrastructure right, and the
operational work of getting the change to production safely.
## Objective Source of truth for **why**: `docs/vision.md`.
Source of truth for **how**: `docs/architecture.md` + `.ciagent/ARCHITECTURE.md`.
Where the two conflict, the vision wins.
Build a runnable demo (Linux + GitHub/Gitea Actions) that walks executives through four acts: ## North Star
1. **Act 1 — The Friction:** the old manual 2-week deployment process. A merged change progresses through lower environments end-to-end without a
2. **Act 2 — Developer Self-Service:** commit a valid `contract.yaml` for `l2-commodity-price-feed`, watch Dev auto-run, QA + Prod approval gates, then the evidence timeline. platform engineer joining a thread, approving a ticket, or manually
3. **Act 3 — Citizen Developer:** open a GitHub Issue with natural-language intent; the Python keyword parser generates the same `contract.yaml` and triggers the identical pipeline. triggering a stage gate. A non-technical consumer ships a production
4. **Act 4 — The Safety Net:** commit a malicious `contract.yaml` (`public-ingress: true`) for `l2-regulatory-reporting`; the pipeline halts in Dev because the confidence signal drops below 0.50, and the rejection is visible on the evidence stream. deployment by declaring intent — without authoring a workflow, a
configuration file, or a Terraform module. Every production change is
traceable to a human attestation and an immutable evidence stream.
## Core Tenets (from `docs/vision.md`)
1. **Operations are Declared, Not Executed.** Consumers define what they
need; the platform reconciles, provisions, and progresses.
2. **The Delivery Lifecycle is a Sovereign Boundary.** The platform
governs infra and delivery; it does not penetrate upstream product/SDLC.
Integration is only through validated, published contracts.
3. **Lower Environments are Autonomous; Higher Environments are Attested.**
Dev = zero-touch agentic. QA/prod/dr = deliberate human attestation, not
rubber stamps.
4. **Safety is Computed, Not Assumed.** Every action produces a measurable,
explainable confidence signal. The signal is the platform's certified
answer to "is this safe to proceed?"
5. **Infrastructure is Consumed, Not Maintained.** Compute is abstract,
containerized, or serverless. No node/OS/bare-metal lifecycle.
6. **Two Consumer Surfaces, One Platform.** Technical developers (L3A) and
non-technical consumers (L3B) converge on the same contract schema, the
same policy envelope, and the same evidence stream.
## Domain Boundaries
- **In scope:** environment progression; cloud resource lifecycle; operational
security and observability NFRs; policy enforcement; immutable audit
lineage; confidence frameworks; two consumer surfaces (developer + agentic).
- **Out of scope:** application business logic; IDE workflows; product
backlog / sprint planning; compute requiring node-level or OS-level management.
- **Interface:** upstream systems integrate through a strict contract
boundary. The platform validates, enriches with operational standards,
and reconciles the target state.
## Objective for Milestone v1.1
Finalize the architecture to v1.0 (resolve all 11 open design decisions in
`docs/architecture.md` §13) and prove the locked commitments with one
end-to-end v1 implementation spike:
- **One L1 module** (`l1-s3`) — substrate-agnostic, IR-typed interface.
- **One L2 thin-composition** (`l2-static-asset`) — references the L1.
- **Terraform adapter** — compiles the IR to a real `terraform plan`
against AWS via OIDC (no long-lived credentials, per §12.5).
- **One contract submission** → contract→IR resolution →
`terraform plan` → PolicyCheckResult (Checkov) → confidence signal →
evidence event to the DynamoDB outbox.
The spike validates the architecture's claim that the IR-shaped commitments
do not require a polyglot mess (`docs/architecture.md` §14, step 2).
## Milestone v1.1 Phases
| Phase | Name | Goal |
|-------|------|------|
| 06 | archive-demo-and-reorient | Move the v1.0 demo (`modules/`, `scripts/`, `evidence-ui/`, `contracts/`, demo workflows) to `demo/`; establish the new repo layout (`platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`); rewrite README. |
| 07 | architecture-v1-finalization | Resolve the 11 open decisions → architecture v1.0. Author IR JSON Schema, PolicyCheckResult schema, contract schema, confidence-signal spec, HITL matrix, outbox/ledger design under `schemas/` + `platform/`. |
| 08 | aws-oidc-bootstrap | One-shot use of a temporary long-lived key (waiver D-034) to create an IAM role + OIDC trust policy for the act_runner, an S3 state bucket, and a DynamoDB lock table. Rotate the key. Verify the runner assumes the role via OIDC with no long-lived secret. |
| 09 | v1-spike-ir-and-l1-and-adapter | Target Stack IR; one real L1 (`l1-s3`) with IR-typed interface; L1 registry; Terraform adapter (IR → Terraform var/output + `terraform plan`) running against AWS via OIDC. |
| 10 | v1-spike-l2-and-contract-e2e | One L2 thin-composition (`l2-static-asset`) referencing `l1-s3`; contract schema + contract→IR resolution; one end-to-end contract submission → `terraform plan` → Checkov → confidence signal → evidence event to outbox. Verify the IR commitments hold. |
Milestone COMPLETE gate: review → ship `v1.2.0` (feature milestone, next
minor per ship.md) → audit.
## Requirements ## Requirements
### Validated ### v1.0 (Prior milestone — the demo)
- Three repos under the `continuous-intelligence` Gitea org: `acdl` (platform + stubs + reusable workflows), `acdl-contracts` (developer surface), `acdl-evidence` (GitHub Pages audit timeline).
- L1 modules (single-purpose, substrate-agnostic, max-depth-1 primitives) as folders with `manifest.yaml` + `mock_apply.sh`.
- L2 modules (composed stacks, max-depth-5) grouping L1s into deployable service shapes.
- L3A developer surface: commit `contract.yaml` to `acdl-contracts`.
- L3B agentic surface: Python keyword parser turning an Issue body into `contract.yaml`.
- Confidence signal: base 0.90, drops to 0.40 on policy violation; gate threshold ≥ 0.50.
- Evidence stream: hash-chained `audit.json` published via Pages + vanilla-JS `index.html` timeline.
- Reusable CI workflow: Dev (autonomous) → QA (manual approval) → Prod (manual approval) → finalize.
### Active Status: complete. Tag `v1.1.0`. All REQ-01..15 satisfied by the stub-driven
- 8 L1 modules (serverless/container focus): `l1-eks-fargate`, `l1-iam-role`, `l1-lambda`, `l1-api-gateway`, `l1-eventbridge`, `l1-sqs`, `l1-s3`, `l1-cloudwatch`. executive demo. See `REQUIREMENTS.md` §v1 and the prior decisions table
- 4 L2 modules mirroring S&P Global Energy / Platts use cases: `l2-invoice-service`, `l2-commodity-price-feed`, `l2-energy-analytics-api`, `l2-regulatory-reporting`. appendix below. The demo is **archived** to `demo/` in Phase 06.
- 5 core scripts: `mock_executor.sh`, `policy_checker.py`, `confidence_signal.py`, `evidence_writer.py`, `l3b_agent_stub.py`.
- Issue-triggered workflow in `acdl-contracts` that runs the L3B parser, commits a new branch, closes the issue, and triggers the main pipeline.
- Evidence stream UI (`index.html`) fetching `audit.json` and rendering events as a timeline.
### Out of Scope ### v1.1 (Active milestone — architecture finalization + v1 spike)
- Real cloud provisioning (AWS/GCP/Azure).
- Real LLM inference / external AI APIs. New requirements REQ-16..REQ-2x — see `REQUIREMENTS.md` §v1.1. Summary:
- Production-grade infrastructure or multi-tenant isolation.
- Real cryptographic tamper-proofing (the hash chain is demonstrative, not adversarially secure). - **REQ-16:** Architecture finalized to v1.0 (11 open decisions resolved).
- **REQ-17:** Target Stack IR defined as JSON Schema; substrate-agnostic.
- **REQ-18:** PolicyCheckResult normalized schema defined; Checkov adapter.
- **REQ-19:** Six-input confidence signal specified with per-env thresholds
(dev 0.50 / qa 0.75 / prod 0.90 / dr 0.95) and severity→penalty mapping.
- **REQ-20:** Tiered audit ledger design (S3 Object Lock 7-yr + DynamoDB
outbox, RPO=0, JWS detached signatures, `prev_event_hash` chain).
- **REQ-21:** Full 8-concern HITL matrix + separation-of-duties design
(CODEOWNERS + DynamoDB identity-distinctness).
- **REQ-22:** Contract schema (JSON Schema draft 2020-12) with per-env
mandatory/optional inputs and `profile: agentic` marker for L3B.
- **REQ-23:** AWS OIDC bootstrap (IAM role + trust policy for act_runner);
the long-lived key is used once then rotated (waiver D-034).
- **REQ-24:** One real L1 module (`l1-s3`) with an IR-typed interface.
- **REQ-25:** One real L2 thin-composition (`l2-static-asset`) referencing
`l1-s3`.
- **REQ-26:** Terraform adapter compiles the IR to a real `terraform plan`
against AWS via OIDC; state in S3 + DynamoDB.
- **REQ-27:** One end-to-end contract submission → contract→IR resolution →
`terraform plan` → Checkov → confidence signal → evidence event to outbox.
- **REQ-28:** Spike verification proves the IR-shaped commitments hold (no
polyglot mess; the adapter is the only substrate-specific code).
## Constraints ## Constraints
- Environment: local Linux OS. - **Forge:** Gitea at `https://git.cloudinit.dev`, org `continuous-intelligence`.
- CI/CD: GitHub/Gitea Actions + Environments (QA, Prod approval gates). - **CI runtime:** act_runner / Gitea Actions (reuses GitHub Actions workflow YAML).
- **No cloud** — absolutely no AWS, GCP, or Azure resources. - **Cloud:** AWS via OIDC federation. **Long-lived credentials are forbidden**
- **No AI** — no OpenAI or external LLM APIs; the "Agentic" part is a keyword parser. (§12.5). The v1.1 spike uses a temporary long-lived key **once** to bootstrap
- All state in flat JSON files or CI artifacts. OIDC (waiver D-034), then rotates it.
- Compute strategy: EKS Fargate + serverless primitives (no VPC module). - **Substrate:** Terraform adapter in v1 (the only adapter). L1/L2 are
- L1 modules are single-purpose, substrate-agnostic, do not compose with other L1s. substrate-agnostic in shape; the adapter is the only substrate-specific code.
- L2 modules combine L1 primitives into deployable shapes, max depth 5. - **State:** S3 (state files) + DynamoDB (locking), single-region in v1.
- **Environments:** dev (autonomous) → qa (QA HITL) → prod (SRE HITL) → dr
(SRE HITL). **Staging does not exist** (Path A locked).
- **Compute:** abstract / containerized / serverless. No VMs, bare metal, OS
lifecycle.
- **Autonomy:** Full. Escalation hooks: deploy, delete_data, merge_to_main.
## Anti-Goals (from `docs/vision.md` §7)
- Not an upstream development platform (no product backlogs, IDE, code authorship).
- Not a general-purpose AI (autonomy is narrow, bounded by policy envelopes).
- Not a legacy infrastructure bridge (no VMs/bare metal/OS).
- Not a permissive delivery highway (no escape hatches past confidence or HITL).
- Not a mutable audit log (VCS history ≠ regulatory evidence).
## Context ## Context
- Forge: Gitea at `https://git.cloudinit.dev`, org `continuous-intelligence`. - The `acdl` repo exists at the org root. `acdl-contracts` and
- The `acdl` repo already exists (empty) at org root and serves as the platform/meta repo. `acdl-evidence` exist from the v1.0 demo and continue as the developer
- `acdl-contracts` and `acdl-evidence` will be created as additional repos in the same org. surface and the audit-timeline host respectively.
- act_runner / Gitea Actions is the CI runtime; "GitHub Actions" workflow YAML is reused as-is. - `docs/vision.md` and `docs/architecture.md` (v0.2) are the upstream
vision/architecture sources, pulled from `origin/main` at the start of v1.1.
- The v1.0 demo (tag `v1.1.0`) is the reference of intent — it proved the
shape (L1/L2/contract/confidence/evidence/HITL) on stubs. v1.1 replaces the
stubs with the real platform substrate.
## Key Decisions ## Key Decisions (v1.1)
Carries forward the still-valid v1.0 decisions (see appendix). New v1.1
decisions:
| ID | Decision | Rationale | Outcome | | ID | Decision | Rationale | Outcome |
|----|----------|-----------|---------| |----|----------|-----------|---------|
| D-001 | Use Gitea org `continuous-intelligence` for all repos | User-specified target org; already exists | Single source of truth for the demo | | D-034 | Temporary long-lived AWS key (waiver) used once in Phase 08 to bootstrap OIDC trust; rotated immediately after | §12.5 forbids long-lived creds; OIDC needs one bootstrapping `aws iam` call before the runner can assume a role | Spike achieves real `terraform plan` against AWS without violating the locked target after bootstrap |
| D-002 | Map "GitHub Actions" to Gitea Actions (act_runner) | Environment is Gitea; same workflow YAML syntax | Demo runs on the actual forge | | D-035 | Milestone version = `v1.1` (feature), ship tag `v1.2.0` | Real platform is a breaking reframing of the demo, but treated as the next incremental milestone per user choice; ship.md: feature milestone → next minor | Tag `v1.2.0` on milestone COMPLETE |
| D-003 | Collapse `acdl-platform` into the existing `acdl` repo | `acdl` already exists empty at org root | 3 repos total: `acdl`, `acdl-contracts`, `acdl-evidence` | | D-036 | Spike picks `l1-s3` + `l2-static-asset` | Simplest real AWS resource (no IAM/network deps); smallest real `terraform plan`; proves the IR + adapter end-to-end | Spike scope fixed |
| D-004 | Use Gitea `environment` blocks + required reviewers for QA/Prod; fallback to manual `workflow_dispatch` with approval input | Approval gates required by spec; forge supports environment protection | Frictionless approval gates | | D-037 | Demo archived to `demo/` (not deleted) | Preserves the working v1.0 demo as intent reference; new platform layout under `platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/` | No churn on demo code; clean separation |
| D-005 | Hash-chained ledger (`prev_hash` + own `hash`) for evidence; declared demonstrative | Spec asks for simple JSON; chain gives visible tamper-evidence | Visible audit timeline without overengineering | | D-038 | Open decisions resolved in "accept recommendations + decide rest" mode | User-locked mode: accept architecture's stated recommendations (W1.A, W1.B, W2.A, BA.A); lead-developer decides the remaining 8 (W3.D, W3.E, BA.B, BA.C, BA.D, BA.E, BA.F, OpenTofu timing) with rationale | Architecture reaches v1.0 in Phase 07 |
| D-006 | Confidence gate threshold = 0.50 exactly | Explicit in spec | Acts 2/4 behave as scripted |
| D-007 | Each `mock_apply.sh` echoes `[L1: <name>] applying...` + `OK`, sleeps 1s, exits 0 | Spec literal; uniformity aids timeline parsing | Predictable evidence events | ### Open-decision resolutions (Phase 07 deliverable — recorded here for traceability)
| D-008 | Keyword→stack mapping for L3B: gas/price/ingest/data-lake → commodity-price-feed; invoice/billing → invoice-service; analytics/historical/query → energy-analytics-api; regulatory/compliance/reporting/trading → regulatory-reporting; fallback → invoice-service | Mirrors the 4 L2 modules + Act 3 example issue | Act 3 reproduces deterministic behavior |
| D-009 | Init milestone = `v1.0`, branch `milestone/v1.0-initial` | init.md Step 5 mandate | Branching strategy follows convention | | ID | Question | Resolution |
| D-010 | Single-project mode for the `acdl` checkout | User chose standalone single-project | `---ci---` blocks omit `project:` field | |---|---|---|
| D-011 | Single-project mode explicitly enforced via `config.json mode: "single"` overriding `projects[]` length signal | run.md Step 0 reads `projects[]` length as multi-project trigger; explicit flag disambiguates | No `project:` prefix in commits or branches | | W1.A | AI-refinement trigger | **Accept recommendation.** Joint condition: N ≥ 50 consecutive changes with zero rollbacks AND no L1/L2 incident in last 6 months AND Infra & Ops unilateral override. |
| D-012 | Gitea has no native Pages — serve `acdl-evidence` via raw file URLs (`/raw/branch/main/...`) and a CORS note in ARCHITECTURE.md | Research confirms Gitea has no `[pages]` section | Demo can render `index.html` via raw URL without server-side Pages config | | W1.B | Multi-stack edge case rule | **Accept recommendation.** Permitted only for (a) DR-region mirror, (b) time-boxed experimental stack with TTL ≤ 30d, (c) explicit Infra & Ops approval with `multiStack.justification`. |
| D-013 | Gitea has no environments API and ignores `jobs.<id>.environment` — model QA/Prod gates as `workflow_dispatch` approval inputs (D-004 fallback) | Research confirms `environment:` blocks are ignored by act_runner | Approval gates become dispatch inputs; "environments" become workflow job names + optional branch protection on `qa`/`prod` branches | | W2.A | Tag mutability for prod | **Accept recommendation (Path B).** Tag for dev/qa, SHA for prod. Platform CLI resolves tag→SHA for prod-bound workflows. Justified by the "Audit truth lives outside the repository" bet. |
| D-014 | Cross-repo triggering uses the `workflow_dispatch` Gitea API (POST `/actions/workflows/{id}/dispatches`) from inside a step instead of `repository_dispatch` | Gitea Actions does not support `repository_dispatch` | Issue-trigger workflow calls the main pipeline via authenticated dispatch from a step | | BA.A | Initial L3B skill catalog | **Accept recommendation.** 5 skills: web API, worker, scheduled job, static asset, basic observability bootstrap. Addition criteria: (a) reviewable for sensitive data, (b) expressible as a single contract submission, (c) documented use case. |
| D-015 | New repos `acdl-contracts` and `acdl-evidence` use `default_branch: "main"` with `auto_init: true` | Matches Gitea `DEFAULT_BRANCH=main`; required for the default branch to exist before any push | Reusable-workflow `uses:` references still pin `acdl` workflows to `@milestone/v1.0-initial` | | W3.D | L1/L2 standard versioning | **Decided.** Semver: interface → MAJOR, behavior → MINOR, lifecycle → PATCH (same as the v1.0 demo D-rule, lifted to the real platform). Pin model: L2 contracts pin L1 by `name@semver`; the resolver picks the highest compatible. Evolution: MAJOR bumps require a new registry entry (immutable publication); old entry enters a 12-month deprecation window. |
| D-016 | Pages placeholder for Phase 01 is a minimal HTML stub (`<title>ACDL Evidence</title>` + "evidence stream coming soon"); full UI deferred to Phase 05 | Phase 01 success criterion is "Pages returns 200 with placeholder index.html" but Gitea has no Pages | Raw-URL HTTP 200 against `index.html` substitutes for the Pages check; full timeline UI built in Phase 05 | | W3.E | Schema mandatory vs optional inputs | **Decided.** Per-env mandatory table: dev requires `stack` + `environment`; qa adds `validation.e2eSuite` + `validation.loadTest`; prod adds `runbook` + `dashboard` + `oncall`; dr adds `drDrillRef`. `inputs` map is always optional. `profile: agentic` fields (`naturalLanguageIntent`, `confidenceAtSubmission`, `agentTrace`) optional everywhere. |
| D-017 | Each L1 `manifest.yaml` declares a single `inputs:` map of named string keys with descriptions; no nested types (substrate-agnostic, max-depth-1) | REQ-02/03 say "declared inputs"; spec forbids composition and cloud-specific types | Uniform, parseable schema that Phase 03's `mock_executor.sh` can read with python+yaml | | BA.B | Confidence threshold tuning | **Decided.** Starting thresholds frozen for v1. Tuning begins in v1.2: track FP/FN per environment quarterly; override authority = Infra & Ops + SRE joint sign-off; any override is itself a confidence-event in the audit stream. |
| D-018 | L1 `mock_apply.sh` reads its own `manifest.yaml` for self-identification but ignores the input values (uniform stub per D-007) | D-007 mandates a literal echo + 1s sleep + exit 0; inputs are declared for traceability, not consumed | Predictable evidence events + clean separation from Phase 03 where L2s pass inputs to L1s | | BA.C | On-call / operational ownership | **Decided.** Platform on-call = Infra & Ops rotation. Escalation: L3A/L3B halt → platform on-call pager (Sev2); consumer-visible outage → consumer on-call (Sev1) with platform on-call support. Consumer on-call relationship is contractual, defined at onboarding (BA.E). |
| D-019 | The 8 L1 names are fixed per REQ-02: `l1-eks-fargate`, `l1-iam-role`, `l1-lambda`, `l1-api-gateway`, `l1-eventbridge`, `l1-sqs`, `l1-s3`, `l1-cloudwatch` | REQ-02 literal | Phase 02 enumerates them exactly; no naming freedom | | BA.D | Cost / capacity governance | **Decided.** Cloud cost owner = Infra & Ops FinOps. Per-contract consumption reported monthly. Runaway spend: hard halt at 120% of contract-declared budget envelope via the confidence signal (cost is one of the 6 inputs); override = FinOps + SRE joint sign-off. |
| D-020 | L2 `manifest.yaml` schema: `name`, `kind: l2`, `description`, `l1s:` (list of `{name, inputs: map}` entries). L2 references L1s by name (no path); inputs are string maps per L1 manifest declarations | REQ-04 says L2 "composes L1s"; REQ-05 caps depth at 5 (L2→L1 is depth 1) | mock_executor.sh reads `l1s:` and invokes each L1's `mock_apply.sh` | | BA.E | Consumer onboarding | **Decided.** Two paths: developer (L3A) — `getting-started` walks through contract schema + central pipeline template; citizen developer (L3B) — onboarding grants a scoped agent + skill catalog, no workflow authoring. Both end in a sandbox dev submission that must pass the confidence gate before the consumer is promoted. |
| D-021 | `contract.yaml` schema: `stack` (L2 name), `inputs` (string map for the L2's top-level params), optional `public-ingress: bool` (the policy violation key per REQ-07) | REQ-07 cites `public-ingress: true` as the forbidden key; REQ-08's confidence signal keys off policy pass/fail | Single flat schema drives both policy_checker and the mock_executor | | BA.F | Cross-platform evolution | **Decided.** The contract schema, IR, PolicyCheckResult, confidence signal, and audit stream are portable (substrate- and forge-agnostic). Forge-specific code: workflow YAML, OIDC trust, CODEOWNERS, Environments. A second forge (e.g., GitLab) requires a forge adapter + a workflow-template translator; no change to L1/L2/IR/confidence/audit. |
| D-022 | `mock_executor.sh` writes `state.json` with shape `{"l2": "<name>", "l1s": [{"name":"...","applied":true,"exit_code":0}], "contract": <contract.yaml parsed>}` to the cwd; idempotent (overwrites) | REQ-06 says "writes state.json" but does not specify shape | Deterministic, parseable; Phase 05's evidence UI can include it in the audit trail | | Q1.3 | OpenTofu timing | **Decided (deferred).** Not in v1 or v1.1. The substrate abstraction (§12) makes OpenTofu a future adapter, not an architecture change. Revisit when an OpenTofu adapter is requested; no version committed. |
| D-023 | `evidence_writer.py` appends events to `audit.json` (a JSON array of event objects). Each event: `{"seq": N, "ts": <iso8601>, "stage": "dev|qa|prod|finalize", "event": "<string>", "prev_hash": "<sha256>", "hash": "<sha256 of canonical json of this event with hash field empty>"}`. The genesis event has `prev_hash: "GENESIS"` and `seq: 0` | D-005 mandates hash-chained ledger; canonical JSON for deterministic hashing | Visible tamper-evidence without overengineering; Phase 05 UI reads the array |
| D-024 | `confidence_signal.py` reads `contract.yaml`, calls `policy_checker.py` (as a subprocess or import), returns base 0.90 on pass and 0.40 with reason code on policy failure; prints `{"score": 0.90|0.40, "reason": "<POLICY_VIOLATION:...|>"}` to stdout; exit 0 always | REQ-08 literal: base 0.90, drops to 0.40, gate ≥ 0.50 | Deterministic JSON output for the pipeline to consume | ## Appendix — Prior milestone (v1.0 demo) decisions
| D-025 | `policy_checker.py` reads `contract.yaml`, fails with exit code 1 and stdout `POLICY_VIOLATION:PUBLIC_INGRESS` if `public-ingress: true`; otherwise exits 0 with stdout `POLICY_PASS` | REQ-07 literal | Single source of policy truth; called by confidence_signal and the pipeline directly |
| D-026 | `l3b_agent_stub.py` reads Issue body text from argv[1] (or stdin if no argv), applies the D-008 keyword map, writes a `contract.yaml` to stdout (or to `-o <path>`). Output contract uses the D-021 schema with `stack:` set to the mapped L2 name and a fixed `inputs:` map per L2 | D-008 + Act 3 example; L3B must produce the same contract format as L3A | Deterministic keyword parser; no external APIs | The v1.0 demo (tag `v1.1.0`) carried decisions D-001..D-033. They governed
| D-027 | Phase 04 models the pipeline as TWO Gitea Actions workflows: (1) `acdl/.gitea/workflows/pipeline.yml``on: workflow_call` + `on: workflow_dispatch` (so it can be both called by the contracts-repo trigger AND manually re-dispatched for approvals); (2) `acdl-contracts/.gitea/workflows/issue-to-contract.yml``on: issues [opened]`. Approval gates are implemented as separate workflow_dispatch inputs (`approve_qa: bool`, `approve_prod: bool`) on the pipeline workflow, since Gitea ignores `environment:` blocks (D-013) | Gitea Actions has no environment reviewers, no `repository_dispatch`, no native approval UI | Pipeline can be re-dispatched by a human at each gate; the workflow_dispatch API call from a step (D-014) drives cross-repo triggering | the stub-driven executive demo and remain valid **for the archived demo
| D-028 | The pipeline workflow runs all 4 stages (dev, qa-gate, prod-gate, finalize) in a single workflow run, with each gate job checking a workflow_dispatch input (`approve_qa`/`approve_prod`). When the input is false (the default), the gate job fails with a clear "awaiting approval" message; the human re-dispatches with `approve_qa=true` to advance. State (state.json, audit.json, contract ref) is passed via workflow artifacts (upload/download between jobs) because Gitea Actions artifacts work the same as GitHub Actions | Gitea Actions supports `actions/upload-artifact` and `actions/download-artifact`; the alternative is committing state between jobs, which is heavier | Deterministic, observable pipeline; artifacts keep the audit trail within one run | under `demo/`**. They are **superseded** by the v1.1 decisions above for the
| D-029 | The finalize step commits `audit.json` to `acdl-evidence` main via the Gitea file-contents API (POST `/repos/{owner}/{repo}/contents/{path}` with the base64 content + a commit message referencing the pipeline run id), exactly like Phase 01's `gitea_setup.sh` does for `index.html`. It uses `${GITEA_TOKEN}` (a repo secret) for auth | D-012 raw-URL approach requires the file to be on main; the API is the only way to put it there from a workflow step | The evidence timeline (Phase 05 UI) fetches the raw URL after finalize completes | real platform. Full text preserved in git history at tag `v1.1.0`.
| D-030 | The issue-to-contract workflow in `acdl-contracts` checks out `l3b_agent_stub.py` from the `acdl` repo (pinned to `@milestone/v1.0-initial` per the branch-pin rule), parses the Issue body, commits `contract.yaml` to a new branch `contract/<issue-number>` on `acdl-contracts`, then dispatches the pipeline workflow on the `acdl` repo via `curl POST /actions/workflows/<id>/dispatches` with `inputs: {contract-ref: contract/<issue-number>}` (D-014). The pipeline workflow checks out `acdl-contracts` at that ref to read the contract | Gitea Actions cannot trigger across repos without an explicit API call; the branch carries the contract ref | Reproducible Act 3: Issue → contract.yaml → pipeline run with the same contract as Act 2 |
| D-031 | Phase 05 "dry run" = a local bash script (`scripts/run_demo.sh`) that simulates the full pipeline by calling the core scripts in sequence + writing evidence events via `evidence_writer.py` + uploading `audit.json` to `acdl-evidence` via `finalize_evidence.py`. It does NOT use act_runner (no runner is registered in this environment). It exercises all 4 acts: (1) Friction — a static "manual 2-week" log entry; (2) Developer Self-Service — a valid `contract.yaml` for `l2-commodity-price-feed`, full pipeline (dev→qa→prod→finalize), all evidence events; (3) Citizen Developer — an Issue body, `l3b_agent_stub.py` produces the contract, identical pipeline; (4) Safety Net — a malicious `public-ingress: true` contract for `l2-regulatory-reporting`, dev rejects with confidence < 0.50, rejection visible in the timeline | The spec says "4 scripted acts reproduce deterministically in a dry run"; without a runner, the bash simulation IS the deterministic reproduction | The same `audit.json` shape is produced as a real pipeline run would produce, so the `index.html` UI renders the timeline identically |
| D-032 | `index.html` (vanilla JS) is committed to the `acdl` repo at `evidence-ui/index.html` as the source of truth, and pushed to `acdl-evidence` main by `scripts/run_demo.sh` (via the file-contents API) alongside `audit.json`. The UI fetches `audit.json` from the same raw-URL origin (D-012). It renders events as a vertical timeline with `seq`, `ts`, `stage` (color-coded), `event` text, and a truncated `hash` per event. No frameworks; one HTML file with inline CSS + JS | D-012 raw-URL approach; the UI must be a single file (no separate JS/CSS) for simplicity; vanilla JS per REQ-14 | The UI loads in any browser by visiting the raw URL; it fetches the sibling `audit.json` and renders |
| D-033 | The `audit.json` raw URL on `acdl-evidence` main is the single source of truth for the timeline. `run_demo.sh` writes the final `audit.json` (after all 4 acts) and the final `index.html` in two API calls. Re-running `run_demo.sh` overwrites both (idempotent). The UI's fetch URL is relative (`./audit.json`) so the same `index.html` works against any raw-URL origin | D-012 + D-029; relative URL avoids hardcoding the host | Deterministic re-run; UI always reflects the latest audit |
+58 -3
View File
@@ -35,7 +35,42 @@
(None — v1 covers the complete demo.) (None — v1 covers the complete demo.)
## Clarifications (Phase 01) ## v1.1 (Active milestone — architecture finalization + v1 spike)
### Category: Architecture Finalization
- **REQ-16:** Architecture reaches v1.0 — all 11 open decisions in `docs/architecture.md` §13 are resolved and recorded in `PROJECT.md` (W1.A, W1.B, W2.A, W3.D, W3.E, BA.AF, OpenTofu timing).
- **REQ-17:** Target Stack IR is defined as a JSON Schema under `schemas/ir.schema.json`; substrate-agnostic (resources, relationships, composition max-depth-5, policy hooks).
- **REQ-18:** `PolicyCheckResult` normalized schema is defined under `schemas/policy_check_result.schema.json`; a Checkov adapter translates Checkov JSON to this schema.
- **REQ-19:** Six-input confidence signal is specified under `platform/confidence_signal.py` with per-env thresholds (dev 0.50 / qa 0.75 / prod 0.90 / dr 0.95) and severity→penalty mapping (critical=hard override, high=-0.2, medium=-0.05, low=-0.01, info=0.0).
- **REQ-20:** Tiered audit ledger design is authored: S3 Object Lock (compliance mode, 7-yr) + DynamoDB outbox (RPO=0, JWS detached signatures, `prev_event_hash` chain, daily checkpoints).
- **REQ-21:** Full 8-concern HITL matrix + separation-of-duties design is authored (CODEOWNERS routing + DynamoDB identity-distinctness check; pre-execution gate model; 1d warn / 2d freeze timeout).
- **REQ-22:** Contract schema (JSON Schema draft 2020-12) is defined under `schemas/contract.schema.json` with per-env mandatory/optional inputs (W3.E) and `profile: agentic` marker for L3B fields.
### Category: AWS OIDC Bootstrap
- **REQ-23:** AWS OIDC trust is configured — an IAM role + trust policy allows the act_runner to assume a role via OIDC with no long-lived credential. The temporary long-lived key used for the one bootstrap `aws iam` call is rotated immediately after (waiver D-034). State backend: S3 state bucket + DynamoDB lock table.
### Category: v1 Spike — IR, L1, Adapter
- **REQ-24:** One real L1 module `l1-s3` exists under `modules-ir/l1/l1-s3/` with an IR-typed interface (typed inputs/outputs/NFRs) registered in the L1 registry.
- **REQ-25:** One real L2 thin-composition `l2-static-asset` exists under `modules-ir/l2/l2-static-asset/` referencing `l1-s3` only (depth 1, within max-depth-5).
- **REQ-26:** The Terraform adapter (`adapters/terraform/`) compiles the IR-typed L1 interface to Terraform `variable`/`output` blocks and the L2 thin-composition tree to a Terraform root module; it emits a real `terraform plan` against AWS via OIDC; state is stored in S3 + DynamoDB.
### Category: v1 Spike — End-to-End
- **REQ-27:** One end-to-end contract submission (`contracts/spike.yaml` for `l2-static-asset`) flows through: contract schema validation → contract→IR resolution → `terraform plan` (real AWS) → Checkov `PolicyCheckResult` → confidence signal → evidence event written to the DynamoDB outbox.
- **REQ-28:** Spike verification (`scripts/verify_phase10.sh`) proves the IR-shaped commitments hold: the adapter is the only substrate-specific code; no polyglot mess; the L1 content, contract YML, and thin-composition tree are substrate-agnostic.
## Out of Scope (v1.1)
| Feature | Reason |
|---------|--------|
| Full HITL matrix wiring (qa/prod/dr) | Spike is dev-only (`terraform plan`); HITL wiring is v1.2. |
| Kyverno + OPA policy engines | Spike uses Checkov only; Kyverno/OPA are v1.2. |
| MCP skill catalog + real L3B agent | L3B spike = a single stub contract submission; the 5-skill catalog is v1.2. |
| GitOps reconciler (ArgoCD/Flux) | v1.2. |
| Multi-region state / outbox | Single-region in v1 (§9, §12.3). |
| Prod/dr environments | v1.2. |
| Terraform `apply` (real provisioning) | Spike runs `plan` only; `apply` is gated by HITL in v1.2. |
## Clarifications (Phase 01, v1.0 — retained for history)
| REQ | Original criterion | Clarified criterion (effective) | Decision | | REQ | Original criterion | Clarified criterion (effective) | Decision |
|-----|--------------------|----------------------------------|----------| |-----|--------------------|----------------------------------|----------|
@@ -43,7 +78,7 @@
| REQ-10 | "Pages returns 200 with placeholder `index.html`" on `acdl-evidence` | Gitea has no Pages; substitute: an HTTP GET against the raw file URL `https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html` returns 200 with the placeholder HTML body | D-012, D-016 | | REQ-10 | "Pages returns 200 with placeholder `index.html`" on `acdl-evidence` | Gitea has no Pages; substitute: an HTTP GET against the raw file URL `https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html` returns 200 with the placeholder HTML body | D-012, D-016 |
| REQ-10 | "`qa` and `prod` environments exist on `acdl-contracts`" | Gitea has no environments API and ignores `environment:` blocks; substitute: the reusable workflow defines `qa-gate` and `prod-gate` jobs gated by `workflow_dispatch` approval inputs (D-004 fallback); a `qa` and `prod` branch may be created on `acdl-contracts` as a visible stand-in for environments | D-013 | | REQ-10 | "`qa` and `prod` environments exist on `acdl-contracts`" | Gitea has no environments API and ignores `environment:` blocks; substitute: the reusable workflow defines `qa-gate` and `prod-gate` jobs gated by `workflow_dispatch` approval inputs (D-004 fallback); a `qa` and `prod` branch may be created on `acdl-contracts` as a visible stand-in for environments | D-013 |
## Out of Scope ## Out of Scope (v1.0 demo — retained for history)
| Feature | Reason | | Feature | Reason |
|---------|--------| |---------|--------|
@@ -55,6 +90,8 @@
## Traceability ## Traceability
### v1.0 (prior — demo)
| Requirement | Phase | Status | | Requirement | Phase | Status |
|-------------|-------|--------| |-------------|-------|--------|
| REQ-01 | 1 | complete (v1.0.1) | | REQ-01 | 1 | complete (v1.0.1) |
@@ -71,4 +108,22 @@
| REQ-12 | 4 | complete (v1.0.4) | | REQ-12 | 4 | complete (v1.0.4) |
| REQ-13 | 5 | complete (v1.0.5) | | REQ-13 | 5 | complete (v1.0.5) |
| REQ-14 | 5 | complete (v1.0.5) | | REQ-14 | 5 | complete (v1.0.5) |
| REQ-15 | 5 | complete (v1.0.5) | | REQ-15 | 5 | complete (v1.0.5) |
### v1.1 (active — architecture finalization + v1 spike)
| Requirement | Phase | Status |
|-------------|-------|--------|
| REQ-16 | 07 | pending |
| REQ-17 | 07 | pending |
| REQ-18 | 07 | pending |
| REQ-19 | 07 | pending |
| REQ-20 | 07 | pending |
| REQ-21 | 07 | pending |
| REQ-22 | 07 | pending |
| REQ-23 | 08 | pending |
| REQ-24 | 09 | pending |
| REQ-25 | 10 | pending |
| REQ-26 | 09 | pending |
| REQ-27 | 10 | pending |
| REQ-28 | 10 | pending |
+75 -5
View File
@@ -2,11 +2,18 @@
## Overview ## Overview
- **Milestone Status:** complete (v1.1.0, 2026-07-21) - **v1.0 (demo):** complete — tag `v1.1.0`, 2026-07-21. All 5 phases shipped + audited PASS.
- **All phases:** v1.0.1 → v1.0.5 complete - **v1.1 (active):** architecture finalization + v1 spike. 5 phases (0610).
- **Demo:** https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html - **v1.0 demo URL:** https://git.cloudinit.dev/continuous-intelligence/acdl-evidence/raw/branch/main/index.html
Five-phase breakdown to take ACDL from empty repo to a reproducible 4-act executive demo. Milestone `v1.0-initial` covers the full demo build. Each phase produces a runnable increment and ends with a phase-completion commit + tag. ---
## v1.0 (Prior — the demo, complete)
Five-phase breakdown that took ACDL from empty repo to a reproducible 4-act
executive demo. Milestone `v1.0-initial` covered the full demo build. Each
phase produced a runnable increment and ended with a phase-completion commit
+ tag. All phases complete; demo archived to `demo/` in v1.1 Phase 06.
## Phases ## Phases
@@ -60,4 +67,67 @@ Five-phase breakdown to take ACDL from empty repo to a reproducible 4-act execut
- Pages timeline renders events from `audit.json`. - Pages timeline renders events from `audit.json`.
- Act 2: valid contract passes through all gates; timeline shows the full flow. - Act 2: valid contract passes through all gates; timeline shows the full flow.
- Act 3: Issue text produces the expected `l2-commodity-price-feed` contract and triggers the pipeline. - Act 3: Issue text produces the expected `l2-commodity-price-feed` contract and triggers the pipeline.
- Act 4: malicious `public-ingress: true` contract halts in Dev with confidence < 0.50 and a visible rejection reason on the timeline. - Act 4: malicious `public-ingress: true` contract halts in Dev with confidence < 0.50 and a visible rejection reason on the timeline.
---
## v1.1 (Active — architecture finalization + v1 spike)
Five-phase breakdown to finalize the architecture to v1.0 and prove the
locked commitments with one end-to-end implementation spike. Milestone
`v1.1-spike` covers the real platform's first materialization. Ship tag at
milestone COMPLETE: `v1.2.0` (feature milestone, next minor per ship.md).
### Phase 06 — archive-demo-and-reorient
- **Description:** Move the v1.0 demo (`modules/`, `scripts/`, `evidence-ui/`, `contracts/`, demo `.gitea/workflows/`) to `demo/`. Establish the new repo layout (`platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`). Rewrite README to reflect the real platform. Verify the demo still runs from `demo/` (regression check).
- **Status:** pending
- **Depends on:** —
- **Requirements:** (no new REQ; repo hygiene)
- **Success Criteria:**
- `demo/` contains the full v1.0 demo; `demo/scripts/run_demo.sh --no-upload` still exits 0.
- New top-level dirs exist and are empty-but-scaffolded: `platform/`, `schemas/`, `adapters/`, `terraform/`, `modules-ir/`.
- README reflects the real platform (vision + architecture links, new layout).
### Phase 07 — architecture-v1-finalization
- **Description:** Resolve the 11 open decisions in `docs/architecture.md` §13 (already recorded in `PROJECT.md`). Author the locked schemas + designs: `schemas/ir.schema.json` (REQ-17), `schemas/policy_check_result.schema.json` (REQ-18), `schemas/contract.schema.json` (REQ-22), `platform/confidence_signal.py` spec (REQ-19), `platform/audit_ledger_design.md` (REQ-20), `platform/hitl_matrix_design.md` (REQ-21). Mark architecture v1.0.
- **Status:** pending
- **Depends on:** [06]
- **Requirements:** REQ-16, REQ-17, REQ-18, REQ-19, REQ-20, REQ-21, REQ-22
- **Success Criteria:**
- All 11 open decisions resolved and recorded in `PROJECT.md`.
- All 6 schema/design files exist and validate (`ajv` / `python -m jsonschema`).
- `docs/architecture.md` status note updated to v1.0 (or a `docs/architecture-v1.0.md` snapshot).
### Phase 08 — aws-oidc-bootstrap
- **Description:** Use the temporary long-lived key (waiver D-034) **once** to create an IAM OIDC provider + IAM role with a trust policy for the act_runner, an S3 state bucket, and a DynamoDB lock table. **Rotate the key immediately after.** Verify the runner assumes the role via OIDC with no long-lived secret.
- **Status:** pending
- **Depends on:** [07]
- **Requirements:** REQ-23
- **Success Criteria:**
- IAM OIDC provider + role exist; trust policy permits the act_runner to assume the role.
- S3 state bucket + DynamoDB lock table exist.
- The long-lived key is rotated/deactivated (logged in `PROJECT.md` as D-034 closure).
- A workflow step assumes the role via OIDC and runs `aws sts get-caller-identity` successfully with no long-lived credential.
### Phase 09 — v1-spike-ir-and-l1-and-adapter
- **Description:** Implement the Target Stack IR, one real L1 `l1-s3` (IR-typed interface, registered), and the Terraform adapter that compiles the IR → Terraform `variable`/`output` + root module and emits a real `terraform plan` against AWS via OIDC. State in S3 + DynamoDB.
- **Status:** pending
- **Depends on:** [08]
- **Requirements:** REQ-24, REQ-26
- **Success Criteria:**
- `schemas/ir.schema.json` is satisfied by `modules-ir/l1/l1-s3/` interface.
- The Terraform adapter translates `l1-s3` to a valid `terraform plan` (real AWS).
- `terraform validate` + `terraform plan` succeed; no long-lived credential in the workflow.
### Phase 10 — v1-spike-l2-and-contract-e2e
- **Description:** Implement `l2-static-asset` (thin-composition referencing `l1-s3`), the contract schema + contract→IR resolution, and one end-to-end contract submission (`contracts/spike.yaml` for `l2-static-asset`) flowing through schema validation → IR resolution → `terraform plan` → Checkov `PolicyCheckResult` → confidence signal → evidence event to the DynamoDB outbox. Verify the IR commitments hold (no polyglot mess).
- **Status:** pending
- **Depends on:** [09]
- **Requirements:** REQ-25, REQ-27, REQ-28
- **Success Criteria:**
- `l2-static-asset` references `l1-s3` only (depth 1).
- One contract submission completes the full pipeline end-to-end.
- `scripts/verify_phase10.sh` proves the adapter is the only substrate-specific code.
- Evidence event is written to the DynamoDB outbox.
After Phase 10: COMPLETE gate — review → ship `v1.2.0` → audit.
+1 -1
View File
@@ -4,7 +4,7 @@
{ {
"slug": "acdl", "slug": "acdl",
"name": "Agentic Cloud Delivery Platform", "name": "Agentic Cloud Delivery Platform",
"milestone": "v1.0", "milestone": "v1.1",
"status": "specify" "status": "specify"
} }
], ],