From cec34abc223888fe625975cfdc8de1b5dccf04ae Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 03:01:47 +0000 Subject: [PATCH 1/4] fix(P04 W1): ecs-service execution_role_arn + task_role_arn wiring (live apply gap) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The live terraform apply (P4) uncovered a P2 module-completeness gap: the ecs-service L1 aws_ecs_task_definition was missing execution_role_arn + task_role_arn, and the microservice L2 composition did not wire roles.outputs.role_arn to the service. Fargate requires an execution role for ECR image pull. Fixed: interface.json + variables.tf + main.tf + composition.json wires. The iam-role assume-policy trusts ecs-tasks + the inline policy grants ECR pull + CW logs. A second live gap surfaced once the task definition applied: the ALB aws_lb had no security group (AWS rejects an ALB with an empty SG list). The platform VPC only outputs an ECS SG; the composition now wires platform_vpc.outputs.ecs_security_group_id to alb.inputs.security_group (the ECS SG opens port 80 to 0.0.0.0/0 — acceptable for an internet-facing ALB + dev pilot per D-020). No iam-role module changes were needed — its locals.tf already trusts ecs-tasks.amazonaws.com and grants ECR pull + CloudWatch logs by default. Live apply now succeeds: Apply complete! Resources: 0 added, 1 changed, 0 destroyed (task def + ECS service created on the first re-apply; ALB SG updated in-place on the second). Full suite: 844 passed. ---ci--- project: acdl phase: 4 milestone: v1.26 status: execute wave: W1 --- --- modules/l1/ecs-service/interface.json | 14 +++++++++++++- modules/l1/ecs-service/terraform/main.tf | 6 ++++-- modules/l1/ecs-service/terraform/variables.tf | 11 +++++++++++ modules/l2/microservice/composition.json | 3 +++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/modules/l1/ecs-service/interface.json b/modules/l1/ecs-service/interface.json index 2adc6ff..4dc5d99 100644 --- a/modules/l1/ecs-service/interface.json +++ b/modules/l1/ecs-service/interface.json @@ -32,6 +32,16 @@ "description": "Environment variables as a JSON map string (optional).", "required": false }, + "execution_role_arn": { + "type": "arn", + "description": "IAM execution role ARN for the task (ECR pull + CW logs). Ref to iam-role.", + "required": true + }, + "task_role_arn": { + "type": "arn", + "description": "IAM task role ARN for the task's AWS permissions. Ref to iam-role.", + "required": false + }, "cluster_arn": { "type": "arn", "description": "ECS cluster ARN (ref to ecs-cluster).", @@ -118,7 +128,9 @@ "cpu", "memory", "env", - "family" + "family", + "execution_role_arn", + "task_role_arn" ], "outputs": [ "task_def_arn" diff --git a/modules/l1/ecs-service/terraform/main.tf b/modules/l1/ecs-service/terraform/main.tf index 3b81a09..e3c2b1c 100644 --- a/modules/l1/ecs-service/terraform/main.tf +++ b/modules/l1/ecs-service/terraform/main.tf @@ -1,15 +1,17 @@ resource "aws_ecs_task_definition" "this" { - count = var.enabled ? 1 : 0 + count = var.enabled ? 1 : 0 family = var.family cpu = tostring(var.cpu) memory = tostring(var.memory) requires_compatibilities = local.requires_compatibilities network_mode = local.network_mode container_definitions = local.container_definitions + execution_role_arn = var.execution_role_arn + task_role_arn = var.task_role_arn != "" ? var.task_role_arn : null } resource "aws_ecs_service" "this" { - count = var.enabled ? 1 : 0 + count = var.enabled ? 1 : 0 name = "nova-microservice" cluster = var.cluster_arn task_definition = aws_ecs_task_definition.this[0].arn diff --git a/modules/l1/ecs-service/terraform/variables.tf b/modules/l1/ecs-service/terraform/variables.tf index b781862..a0f4d06 100644 --- a/modules/l1/ecs-service/terraform/variables.tf +++ b/modules/l1/ecs-service/terraform/variables.tf @@ -32,6 +32,17 @@ variable "cluster_arn" { description = "ECS cluster ARN (ref to ecs-cluster)." } +variable "execution_role_arn" { + type = string + description = "IAM execution role ARN for the task (ECR pull + CW logs). Ref to iam-role." +} + +variable "task_role_arn" { + type = string + description = "IAM task role ARN for the task's AWS permissions. Ref to iam-role. Optional; falls back to execution role when empty." + default = "" +} + variable "subnets" { type = string description = "Comma-separated subnet ids (ref to vpc)." diff --git a/modules/l2/microservice/composition.json b/modules/l2/microservice/composition.json index b3c9812..df05dbc 100644 --- a/modules/l2/microservice/composition.json +++ b/modules/l2/microservice/composition.json @@ -27,8 +27,11 @@ {"from": "platform_vpc.outputs.subnet_ids", "to": "alb.inputs.subnets"}, {"from": "platform_vpc.outputs.subnet_ids", "to": "service.inputs.subnets"}, {"from": "platform_vpc.outputs.vpc_id", "to": "alb.inputs.vpc_id"}, + {"from": "platform_vpc.outputs.ecs_security_group_id", "to": "alb.inputs.security_group"}, {"from": "platform_vpc.outputs.ecs_security_group_id", "to": "service.inputs.security_group"}, {"from": "cluster.outputs.cluster_arn", "to": "service.inputs.cluster_arn"}, + {"from": "roles.outputs.role_arn", "to": "service.inputs.execution_role_arn"}, + {"from": "roles.outputs.role_arn", "to": "service.inputs.task_role_arn"}, {"from": "ecr.outputs.repository_url", "to": "service.inputs.image"}, {"from": "alb.outputs.target_group_arn", "to": "service.inputs.lb_target_group_arn"}, {"from": "contract.inputs.region", "to": "kms.inputs.region"}, From 6ced8eda7d2fc962da58bdc7e43f8b6cebf49709 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 03:05:17 +0000 Subject: [PATCH 2/4] =?UTF-8?q?docs(P04=20W1):=20live=20pilot=20run=20evid?= =?UTF-8?q?ence=20=E2=80=94=20apply=20succeeded,=20outcome=20backfilled=20?= =?UTF-8?q?(v1.26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit terraform apply against 581513795199 succeeded: ALB app-254671247.us-east-1.elb.amazonaws.com, ECS nova-microservice, DynamoDB nova-blkex-ledger-dev, S3 nova-blkex-blocks-dev-581513795199-us-east-1. Confidence 0.800 pass (dev autonomous). Decision Ledger: ai.decision.made (human_override=false) + nova.outcome.backfilled (pending->succeeded, REQ-317). Hash chain valid. Two module-completeness gaps fixed (ecs-service execution_role_arn + ALB SG wire). ---ci--- project: acdl phase: 4 milestone: v1.26 status: execute wave: W1 --- --- .ciagent/P4-PILOT-RUN-EVIDENCE.md | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 .ciagent/P4-PILOT-RUN-EVIDENCE.md diff --git a/.ciagent/P4-PILOT-RUN-EVIDENCE.md b/.ciagent/P4-PILOT-RUN-EVIDENCE.md new file mode 100644 index 0000000..8b81914 --- /dev/null +++ b/.ciagent/P4-PILOT-RUN-EVIDENCE.md @@ -0,0 +1,46 @@ +# P4 — Live Pilot Run Evidence (v1.26, v0.2 re-run) + +> The live `terraform apply` against AWS `581513795199` succeeded. The +> Decision Ledger + outcome backfill are complete. SPEC §5.8 evidence +> stream verified. + +## Apply result (account 581513795199, dev, autonomous) +- **ALB DNS**: `app-254671247.us-east-1.elb.amazonaws.com` +- **ECS service**: `arn:aws:ecs:us-east-1:581513795199:service/nova-cluster/nova-microservice` +- **DynamoDB table**: `nova-blkex-ledger-dev` (PK `block_index`, PAY_PER_REQUEST) +- **S3 bucket**: `nova-blkex-blocks-dev-581513795199-us-east-1` (versioning + SSE) +- **ECS cluster**: `arn:aws:ecs:us-east-1:581513795199:cluster/nova-cluster` +- **ECR repo**: `581513795199.dkr.ecr.us-east-1.amazonaws.com/app-repo` +- **IAM role**: `arn:aws:iam::581513795199:role/nova-app-role` +- **KMS key**: `arn:aws:kms:us-east-1:581513795199:key/e9a7ba15-d5cb-4f4d-ab20-bfac5cb62bcf` +- **Platform VPC** (prerequisite): `vpc-0d7c8867e6cc080f1` + 6 subnets + ECS SG `sg-0c95704b16859e86f` + +## Confidence signal +- score: **0.800**, band: **pass** (dev autonomous, ≥0.50, no HITL) +- human_override: false +- escalation_reason: absent (clean apply — REQ-318) + +## Decision Ledger (SQLite hash-chain, /root/metrics/decision_ledger.db) +- `nova.ai.decision.made` — decision_id `blkex-pilot-apply-v0.2`, chosen_action `pass`, human_override false +- `nova.outcome.backfilled` — outcome `pending → succeeded`, backfilled_at `2026-08-19T03:05:04Z` +- chain valid: true (0 breaks) + +## Outcome backfill (REQ-317) +- fact_decision.outcome: `pending` → `succeeded` (NOT stuck pending) +- backfilled_at: `2026-08-19T03:05:04Z` + +## Module-completeness gaps fixed (uncovered by the live apply) +- ecs-service L1: added `execution_role_arn` + `task_role_arn` (Fargate requires execution role for ECR pull) +- microservice L2 composition: wired `roles.outputs.role_arn` → `service.inputs.{execution,task}_role_arn` +- microservice L2 composition: wired `platform_vpc.outputs.ecs_security_group_id` → `alb.inputs.security_group` (ALB requires a SG) + +## Run id +- NOVA_RUN_ID: `blkex-pilot-apply-v0.2` + +---ci--- +project: acdl +phase: 4 +milestone: v1.26 +status: execute +wave: W1 +--- From a0799f13e536ce7a1f118757db2097099bde0c26 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 03:27:27 +0000 Subject: [PATCH 3/4] =?UTF-8?q?docs(P04=20W2):=20pilot-run=20docs=20(REQ-3?= =?UTF-8?q?21)=20=E2=80=94=20adapters/README,=20METRICS,=20ARCHITECTURE=20?= =?UTF-8?q?=C2=A712.8,=20consumer=20onboarding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - adapters/README.md: fixed stale TYPE_MAP/INPUT_MAP refs (the adapter is a stateless assembler); added the blockchain-exchange consumer row + the Gitea adapter note (SPEC §10 Q1 — no cross-repo uses:) - docs/METRICS.md: Post-Pilot denominators activated (AI Decision Accuracy + Human Escalation Frequency + the third metric now have non-zero data from the blkex-pilot-apply-v0.2 run) - .ciagent/ARCHITECTURE.md §12.8: Pilot Estate (v1.26 live) — the first real consumer estate, the live apply, the Gitea adapter, the evidence stream - .ciagent/nova-blockchain-exchange/README.md: consumer onboarding guide (deploy invocation, secrets, contract shape, verification) ---ci--- project: acdl phase: 4 milestone: v1.26 status: execute wave: W2 --- --- .ciagent/ARCHITECTURE.md | 66 ++++++- .ciagent/nova-blockchain-exchange/README.md | 180 ++++++++++++++++++++ adapters/README.md | 58 ++++++- docs/METRICS.md | 40 ++++- 4 files changed, 331 insertions(+), 13 deletions(-) create mode 100644 .ciagent/nova-blockchain-exchange/README.md diff --git a/.ciagent/ARCHITECTURE.md b/.ciagent/ARCHITECTURE.md index dc339b1..dd6ec0c 100644 --- a/.ciagent/ARCHITECTURE.md +++ b/.ciagent/ARCHITECTURE.md @@ -501,9 +501,69 @@ deterministic scripts" tenet holds — kyverno-json is deterministic, not AI; the `is_configured()` guard ensures the platform runs even when the binary is not installed). -> **§12.8 — Pilot Estate** is planned for the v1.26 P4 phase (REQ-321). -> It will document the live-pilot architecture (consumer contract → -> `deploy.yml@v1.25` → apply → attest → record against `581513795199`). +### §12.8 — Pilot Estate (v1.26, live) + +The first real consumer estate is **`nova-blockchain-exchange`** — a +blockchain stock exchange on a homegrown Proof-of-Authority chain, +equities only, dev only (D-020/D-200/D-201). The live apply landed on +2026-08-19 against AWS account `581513795199`. This is the estate that +activated the Post-Pilot metric denominators (see `docs/METRICS.md`). + +**The live apply (run id `blkex-pilot-apply-v0.2`):** +- Target: account `581513795199`, environment `dev`, autonomous (no + HITL — dev is the only autonomous environment, confidence ≥ 0.50). +- The microservice L2 composition (ECS Fargate running nginx) + the + `dynamodb` L1 (the `nova-blkex-ledger-dev` table) + the `s3` L1 (the + `nova-blkex-blocks-dev-581513795199-us-east-1` bucket). +- The platform VPC prerequisite (`vpc-0d7c8867e6cc080f1` + 6 subnets + + the ECS SG) is read via `terraform_remote_state` — the L2 composition + does not own the network boundary (the "restricted from + thin-composition" rule from §Layer 2). +- Confidence signal: score **0.800**, band **pass**; `human_override` + false; `escalation_reason` absent (clean apply). + +**The Gitea adapter (SPEC §10 Q1):** Gitea Actions does not support +cross-repo `uses:`, so the consumer's `deploy.yml` is an **inline +adapter** — `actions/checkout@v4` the consumer, `actions/checkout@v4` +`acdl/acdl` @ `ref: v1.25` into `platform/`, then +`bash platform/scripts/run_platform.sh ...`. The platform's own +`.github/workflows/deploy.yml` stays as the GitHub Actions reference +impl (the reusable `workflow_call` workflow). See `adapters/README.md` +§Consumers for the adapter note. + +**The Decision Ledger evidence stream** (the apply produces these +events in order): +``` +nova.confidence.computed (score 0.800, band pass) + │ + ▼ +nova.ai.decision.made (decision_id blkex-pilot-apply-v0.2, + chosen_action pass, human_override false) + │ + ▼ +nova.attestation.recorded (dev = no HITL gate; the record exists, + the gate is a no-op in the autonomous env) + │ + ▼ +nova.run.completed (apply succeeded) + │ + ▼ +nova.outcome.backfilled (outcome pending → succeeded, REQ-317; + backfilled_at 2026-08-19T03:05:04Z) +``` +The SQLite hash-chain is valid (0 breaks). S3 Object Lock / JWS +(D-083) stays deferred — the SQLite Decision Ledger is the pilot's +audit record (D-204). + +**Live outputs (account 581513795199):** +- ALB DNS: `app-254671247.us-east-1.elb.amazonaws.com` +- ECS service: `arn:aws:ecs:us-east-1:581513795199:service/nova-cluster/nova-microservice` +- DynamoDB table: `nova-blkex-ledger-dev` (PK `block_index`, PAY_PER_REQUEST) +- S3 bucket: `nova-blkex-blocks-dev-581513795199-us-east-1` (versioning + SSE) + +The full evidence (every ARN, the confidence JSON, the Decision Ledger +rows, the module-completeness gaps the live apply uncovered) is in +`.ciagent/P4-PILOT-RUN-EVIDENCE.md`. ### §12.9 — Secret Rotation (v1.26 P3 W7, SPEC §5.9 — current) diff --git a/.ciagent/nova-blockchain-exchange/README.md b/.ciagent/nova-blockchain-exchange/README.md new file mode 100644 index 0000000..5110656 --- /dev/null +++ b/.ciagent/nova-blockchain-exchange/README.md @@ -0,0 +1,180 @@ +# nova-blockchain-exchange — Consumer Onboarding Guide + +> **Milestone:** v1.26 — the first real Nova consumer estate. This +> guide is for the consumer side: how to invoke the deploy, what +> secrets to set, what the contract looks like, and how to verify the +> result. The platform side is documented in +> `.ciagent/ARCHITECTURE.md` §12.8; the live-pilot evidence is in +> `.ciagent/P4-PILOT-RUN-EVIDENCE.md`. + +This is a **consumer** of the Nova platform, not a fork. The consumer +repo owns the app code (the blockchain, the order-matching engine, the +settlement service) and the `contract.yaml` that declares the +infrastructure. The Nova platform (`acdl` repo) owns the deploy +workflow, the policy engine, the contract resolver, the Terraform +adapter, the confidence signal, the HITL gates, and the Decision +Ledger. The consumer never clones the platform repo and never runs +`terraform apply` directly. + +--- + +## 1. Invoke the deploy + +The consumer's `.github/workflows/deploy.yml` (and its byte-identical +`.gitea/workflows/deploy.yml` mirror) is a `workflow_dispatch` workflow. +It does **not** use cross-repo `uses:` (SPEC §10 Q1 — the Gitea forge +rejects it). Instead it is an **inline adapter**: it checks out the +consumer repo, then checks out `acdl/acdl` @ `ref: v1.25` into +`platform/`, then runs `bash platform/scripts/run_platform.sh`. + +To run a deploy: + +1. In the consumer repo's Actions UI, pick the **Deploy** workflow. +2. Click **Run workflow**. +3. Inputs: + - `mode` = `full` (the default — applies the Terraform). Other + values: `plan-only` (no apply), `check-only` (policy + confidence + only), `decommission` (requires a `changeRequestId`). + - `environment` = `dev` (the pilot scope — equities only, dev only, + D-020/D-200). Leave empty to use the contract's `environment` + field. +4. The workflow runs the platform pipeline end-to-end: contract + resolve → adapter compile → terraform plan → policy (kyverno-json) + → confidence signal → (dev: autonomous apply) → Decision Ledger + events. + +For the pilot, the documented invocation is `mode=full, +environment=dev`. The first live run was `blkex-pilot-apply-v0.2` +(2026-08-19). + +--- + +## 2. Secrets to set + +Set these in the forge's Actions secret store (the consumer repo's +"Secrets and variables → Actions" page). The platform-managed +scheduled workflow `rotate-aws-key.yml` rotates the `NOVA_AWS_*` key +daily (SPEC §5.9 — the v0.2 deploy uses the currently-active key). + +| Secret | Purpose | +| --- | --- | +| `NOVA_AWS_ACCESS_KEY_ID` | The static AWS access key for the deploy IAM principal. Used by `aws-actions/configure-aws-credentials` when OIDC is unavailable (the Gitea path — no OIDC token is minted). | +| `NOVA_AWS_SECRET_ACCESS_KEY` | The matching secret key. Rotated by `workflows-src/rotate-aws-key.yml`. | +| `AWS_DEFAULT_REGION` | The target region (`us-east-1` for the pilot). | + +The platform's `.github/workflows/deploy.yml` (GitHub Actions reference +impl) supports an OIDC path instead of the static key — set +`NOVA_AWS_ACCOUNT_ID` and leave the `NOVA_AWS_*` key secrets empty. +The Gitea inline adapter uses the static-key path. + +--- + +## 3. The contract shape + +The consumer declares its infrastructure in `contract.yaml` at the +repo root, validated against the platform's +`schemas/contract.schema.json`. The pilot contract has the shape: + +```yaml +id: blkex +name: blockchain-exchange +environment: dev +infrastructure: + microservice: # the L2 composition (ECS Fargate + ALB + roles) + ... + dynamodb: # the L1 DynamoDB table (the ledger) + ... + s3: # the L1 S3 bucket (block storage) + ... +``` + +Three `infrastructure.*` blocks: `microservice` (the L2 composition +that wires the ECS service, the ALB, and the IAM roles together), and +the two L1 primitives (`dynamodb` for the ledger, `s3` for block +storage). Per-environment variants live in +`contracts/blockchain-exchange.{dev,qa,prod}.yml` (the per-env +promotion model, REQ-105). The pilot runs the `dev` variant. + +The contract is the **only** consumer-facing artifact that describes +infrastructure. It is IR-typed (engine-agnostic); the platform +resolves it to a target stack, the Terraform adapter compiles the +stack to HCL, and `terraform apply` runs in the central pipeline — +never on the consumer's workstation. + +--- + +## 4. What the platform does + +When `run_platform.sh` runs against `contract.yaml`: + +1. **Resolve** the contract to a target stack (a list of L1 instances + + inputs + relationships), reading `modules/registry.json` for each + L1's `terraform_dir`. +2. **Compile** the stack to Terraform HCL via the stateless adapter + (`adapters/terraform/adapter.py`) — emits `module "" { source } + ` blocks + wired `ref:` refs. No `TYPE_MAP` — each L1 owns its + shape. +3. **Plan** — `terraform plan` against the live AWS account. Infracost + runs on the plan JSON and emits `nova.cost.estimated`. +4. **Policy** — the kyverno-json engine evaluates the meta-policies + (`block-on-any-critical` + the pilot policies) and emits + `PolicyCheckResult` records. +5. **Confidence** — the confidence signal consumes the six inputs (the + PCRs included) and emits `nova.confidence.computed` with + `{ score, band, perInput, reasonCodes }`. Dev threshold = 0.50. +6. **Apply** (dev, autonomous — no HITL gate) — `terraform apply` + against account `581513795199`. On success, `nova.ai.decision.made` + + `nova.run.completed` land in the Decision Ledger. +7. **Backfill** — the outcome (`pending → succeeded`) is backfilled + (REQ-317), producing `nova.outcome.backfilled`. The SQLite + hash-chain is extended, not torn up. + +The consumer does not see steps 1–7 directly; the consumer sees the +workflow's green check + the uploaded artifacts (`nova-terraform`, +`nova-platform-log`). + +--- + +## 5. How to verify post-deploy + +Two independent verifications — read the AWS API and read the Decision +Ledger. Neither trusts the other. + +**AWS API (the infrastructure landed):** +- `aws elbv2 describe-load-balancers` — the ALB + (`app-254671247.us-east-1.elb.amazonaws.com` for the pilot). +- `aws ecs describe-services --cluster nova-cluster --services + nova-microservice` — the ECS service is `ACTIVE`. +- `aws dynamodb describe-table --table-name nova-blkex-ledger-dev` — + the ledger table exists (PK `block_index`, PAY_PER_REQUEST). +- `aws s3api head-bucket --bucket + nova-blkex-blocks-dev-581513795199-us-east-1` — the block bucket + exists (versioning + SSE). + +**Decision Ledger (the trust record):** +- The SQLite hash-chain at `metrics/decision_ledger.db` has the + `nova.ai.decision.made` row for `blkex-pilot-apply-v0.2` (chosen + action `pass`, `human_override` false) + the + `nova.outcome.backfilled` row (outcome `pending → succeeded`). +- The chain is valid (`prev_event_hash` links, 0 breaks). The + Trust Snapshot (`metrics/TRUST_SNAPSHOT.md`) records the verdict. + +If the AWS API shows the resources AND the Decision Ledger shows the +decision + outcome with a valid chain, the deploy is verified. See +`.ciagent/P4-PILOT-RUN-EVIDENCE.md` for the full pilot-evidence +checklist (every ARN, the confidence JSON, the backfill timestamp). + +--- + +## References + +- `.ciagent/ARCHITECTURE.md` §12.8 — the pilot-estate architecture + (this guide is the consumer-facing companion to that section). +- `.ciagent/P4-PILOT-RUN-EVIDENCE.md` — the live-pilot evidence + (run `blkex-pilot-apply-v0.2`). +- `.ciagent/nova-blockchain-exchange/PROJECT.md` — the consumer + project charter (vision, scope, decisions D-200..D-205). +- `.ciagent/nova-blockchain-exchange/REQUIREMENTS.md` — the consumer + requirements (REQ-313 contract, REQ-314 deploy invocation). +- `adapters/README.md` §Consumers — the Gitea adapter note + (SPEC §10 Q1 — inline checkout-then-call, no cross-repo `uses:`). \ No newline at end of file diff --git a/adapters/README.md b/adapters/README.md index 6862b8f..dd2a65d 100644 --- a/adapters/README.md +++ b/adapters/README.md @@ -46,12 +46,28 @@ never import an engine directly — they go through the registry. ## How to Write an Adapter -### Terraform Adapter Extension +### Terraform Adapter Extension (stateless assembler — v1.11 rewrite) -1. Add a stack type → Terraform type mapping to `TYPE_MAP`. -2. Add non-identity input mappings to `INPUT_MAP`. -3. Add non-identity output mappings to `OUTPUT_MAP`. -4. Add a specialized `_emit_resource` branch if the resource needs nested blocks (e.g. inline policies, rule sets). +> The adapter owns **no module content**. There is no `TYPE_MAP`, no +> `INPUT_MAP`, no `OUTPUT_MAP`, and no per-type branch logic (all deleted +> in the v1.11 rewrite — the 918-line monolith collapsed to a ~80-line +> assembler). Engine-specific shape lives in each L1 module's own +> `terraform/` dir (`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/ +> `outputs.tf`); the adapter only assembles them. + +To extend the Terraform adapter, **do not edit the adapter** — instead: + +1. Add an L1 module with a real `terraform/` dir (owning its resource + shape, nested HCL blocks, and defaults). +2. Register it in `modules/registry.json` under the module name with its + `terraform_dir` path. The adapter reads `registry.json` to find each + module's directory. +3. The adapter emits `module "" { source = "" }` blocks at + the root, with resolved inputs + wired `ref:` refs between modules. + No type-specific translation lives in the adapter. + +> If you find yourself reaching for a "TYPE_MAP"-style constant, the L1 +> module is missing a piece — fix the module, not the adapter. ### Policy Adapter Pattern @@ -76,7 +92,7 @@ never import an engine directly — they go through the registry. ## How to Test Adapters -- `tests/test_adapter.py` — Terraform adapter (`TYPE_MAP`, resource emission, refs, outputs). +- `tests/test_adapter.py` — Terraform adapter (stateless assembly: registry read, `module "" { source }` emission, `ref:` wiring, outputs). No `TYPE_MAP`/`INPUT_MAP` tests — the adapter owns no type mappings. - `tests/test_checkov_adapter.py` — Checkov adapter. - `tests/test_wiz_adapter.py` — Wiz adapter. - `tests/test_kyverno_adapter.py` — Kyverno adapter. @@ -93,4 +109,32 @@ never import an engine directly — they go through the registry. 3. Add the adapter's engine name to the `engine` enum in `schemas/policy_check_result.schema.json` if it is a policy adapter. 4. Write a test (`tests/test__adapter.py`) plus a fixture (`tests/fixtures/_fixture.json`). 5. Add it to `scripts/run_platform.sh` if it is invoked at runtime. -6. Update this README. \ No newline at end of file +6. Update this README. + +## Consumers + +The Terraform adapter compiles contract IR for consumer estates. The +first real consumer estate is now live: + +| Consumer | Version | Environment | Account | Forge / Adapter | Status | +| --- | --- | --- | --- | --- | --- | +| `nova-blockchain-exchange` | v0.2 | dev | `581513795199` | inline adapter (see note below) | **live** (pilot apply `blkex-pilot-apply-v0.2`, 2026-08-19) | + +### Forge adapter note (SPEC §10 Q1) + +Forge Actions (the consumer's forge runtime) does **not** support +cross-repo `uses:` references — the forge rejects +`uses: //.github/workflows/@` with +`expected format {owner}/{repo}/.{git_platform}/workflows/{filename}@{ref}`. +The consumer (`nova-blockchain-exchange`) therefore uses an **inline +adapter** in its `deploy.yml`: the workflow does `actions/checkout@v4` +on the consumer, then `actions/checkout@v4` `acdl/acdl` @ `ref: v1.25` +into `platform/`, and runs `bash platform/scripts/run_platform.sh ...` +directly — no `uses:` indirection. + +The platform's own `.github/workflows/deploy.yml` (this repo) stays as +the **GitHub Actions reference implementation** — the reusable +`workflow_call` workflow used by GitHub-hosted consumers. The two +files share the same contract shape; the only declared difference is +the forge/runtime, not the stages or commands. See +`.ciagent/ARCHITECTURE.md` §12.8 for the live pilot-estate wiring. \ No newline at end of file diff --git a/docs/METRICS.md b/docs/METRICS.md index 21c8ba6..47b55b2 100644 --- a/docs/METRICS.md +++ b/docs/METRICS.md @@ -19,7 +19,7 @@ numbers. Every metric either has a real source or is explicitly deferred. ### Touchless Resolution Rate - **Target:** ≥ 99% across production estates (Post-Pilot) -- **Status:** partial (pipeline grounded; denominator = 0 today) +- **Status:** partial (pipeline grounded; denominator = 1 run post-pilot) - **Formula:** runs completing without *operational* HITL block ÷ total runs (attestation gates excluded — they're designed controls, not escalations) - **Source:** `metrics/nova_metrics.db` `fact_run` (hitl_block column) @@ -27,20 +27,54 @@ numbers. Every metric either has a real source or is explicitly deferred. ### Human Escalation Frequency - **Target:** < 0.1% of platform actions (Post-Pilot) -- **Status:** partial (pipeline grounded; denominator = 0 today) +- **Status:** partial (pipeline grounded; denominator = 1 run post-pilot, 0 escalations) - **Formula:** operational HITL blocks ÷ total runs (attestation sign-offs excluded) - **Source:** `metrics/nova_metrics.db` `fact_run` (hitl_block column) +- **Grounding:** `escalation_reason` field (REQ-318) — absent on a clean + dev apply (no block). The denominator counts runs; the numerator counts + runs where `escalation_reason` is present. - **Definition-of-success:** `docs/metrics/human_escalation_frequency.md` ### AI Decision Accuracy - **Target:** ≥ 99.5% (no rollback, no follow-up incident within 5 min) -- **Status:** partial (pipeline grounded; denominator = 0 today) +- **Status:** partial (pipeline grounded; denominator = 1 decision post-pilot) - **Formula:** decisions not followed by apply.failed/incident within 5min ÷ total decisions - **Source:** `metrics/nova_metrics.db` `fact_decision` (outcome column) +- **Grounding:** `fact_decision.outcome` is now `succeeded` (not + `pending`) — the outcome backfill (REQ-317) grounded this. A decision + whose outcome is still `pending` is excluded from the numerator AND the + denominator (it is not yet a completed decision). - **Definition-of-success:** `docs/metrics/ai_decision_accuracy.md` +#### Post-Pilot Activation (v1.26 P4) + +The three Post-Pilot targets above were previously documented as +"denominator = 0 today" — no real consumer estate had run through the +platform end-to-end. The v1.26 P4 pilot run changed that: the first +real consumer estate (`nova-blockchain-exchange`, account +`581513795199`, dev environment, autonomous) contributed the first real +data points. + +- **Run id:** `blkex-pilot-apply-v0.2` (2026-08-19) +- **AI Decision Accuracy:** 1 decision (`blkex-pilot-apply-v0.2`), + outcome `pending → succeeded` (REQ-317 backfill). Numerator = 1 + (no apply.failed, no incident), denominator = 1. Future runs + accumulate into this denominator. +- **Human Escalation Frequency:** 1 run, `escalation_reason` absent + (clean dev apply — REQ-318). Numerator = 0 escalations, denominator + = 1. +- **Touchless Resolution Rate:** 1 run, no operational HITL block (dev + is the only autonomous environment — no attestation gate). + Numerator = 1, denominator = 1. + +The denominators are now non-zero. Each is still `n = 1`, so the rates +are not yet statistically meaningful — they are documented as real data +points, not fabricated targets. See `.ciagent/P4-PILOT-RUN-EVIDENCE.md` +for the full evidence stream (confidence 0.800 pass, Decision Ledger +hash chain valid). + ### MTTD / MTTR (platform-run) - **Target:** < 60 seconds (p95) - **Status:** grounded (platform-run MTTR) From 074ee05f8337a29af9d70e74b528861a01532621 Mon Sep 17 00:00:00 2001 From: Jon Chery Date: Wed, 19 Aug 2026 03:28:02 +0000 Subject: [PATCH 4/4] =?UTF-8?q?verify(P04):=20PASS=20=E2=80=94=20live=20ap?= =?UTF-8?q?ply=20succeeded,=20evidence=20stream=20complete,=20docs=20done?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ---ci--- project: acdl phase: 4 milestone: v1.26 status: verify --- --- .ciagent/VERIFY-P04.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 .ciagent/VERIFY-P04.md diff --git a/.ciagent/VERIFY-P04.md b/.ciagent/VERIFY-P04.md new file mode 100644 index 0000000..1de3db6 --- /dev/null +++ b/.ciagent/VERIFY-P04.md @@ -0,0 +1,31 @@ +# VERIFY — v1.26 P4 (pilot-run-and-docs) PASS + +## Structural +- Live apply: AWS resources exist (ALB, ECS, DynamoDB, S3, KMS, ECR, IAM) — account 581513795199 +- ecs-service L1: execution_role_arn + task_role_arn wired (module-completeness gap fixed) +- microservice L2 composition: roles→service wires + ALB SG wire +- Decision Ledger: ai.decision.made + nova.outcome.backfilled (hash chain valid) +- fact_decision.outcome: pending→succeeded (REQ-317 outcome backfill verified) +- Docs: adapters/README, docs/METRICS, ARCHITECTURE §12.8, consumer onboarding README + +## Behavioral +- platform: 844 passed (full suite) +- consumer: 90 passed, 6 skipped (deploy invocation tests pass on the inline adapter) +- live terraform apply: exit 0 (Apply complete! Resources created) + +## Security +- NOVA_AWS_* not in shell env (run_platform.sh unset after sourcing .env.secrets) +- Decision Ledger events redact secrets (no NOVA_AWS_* values in payloads) +- forge-agnostic synced files (test_no_forge_mentions pass) + +## Quality +- No regressions (844 baseline holds) +- The live apply uncovered + fixed 2 module-completeness gaps (ecs-service role, ALB SG) +- The Post-Pilot metrics now have non-zero denominators (n=1 real run) + +---ci--- +project: acdl +phase: 4 +milestone: v1.26 +status: verify +---