merge(phase/04): v1.26 P4 pilot-run-and-docs complete (live apply + REQ-321 docs)
P4 W1: live terraform apply against 581513795199 succeeded (ALB + ECS + DynamoDB + S3). Decision Ledger: ai.decision.made + nova.outcome.backfilled (outcome pending->succeeded). 2 module-completeness gaps fixed (ecs-service execution_role_arn, ALB SG). P4 W2: docs (adapters/README, METRICS, ARCHITECTURE §12.8, consumer onboarding). 844 platform + 90 consumer tests green. ---ci--- project: acdl phase: 4 milestone: v1.26 status: complete ---
This commit is contained in:
@@ -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
|
AI; the `is_configured()` guard ensures the platform runs even when the
|
||||||
binary is not installed).
|
binary is not installed).
|
||||||
|
|
||||||
> **§12.8 — Pilot Estate** is planned for the v1.26 P4 phase (REQ-321).
|
### §12.8 — Pilot Estate (v1.26, live)
|
||||||
> It will document the live-pilot architecture (consumer contract →
|
|
||||||
> `deploy.yml@v1.25` → apply → attest → record against `581513795199`).
|
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)
|
### §12.9 — Secret Rotation (v1.26 P3 W7, SPEC §5.9 — current)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
---
|
||||||
@@ -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
|
||||||
|
---
|
||||||
@@ -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 "<rid>" { 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:`).
|
||||||
+50
-6
@@ -46,12 +46,28 @@ never import an engine directly — they go through the registry.
|
|||||||
|
|
||||||
## How to Write an Adapter
|
## 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`.
|
> The adapter owns **no module content**. There is no `TYPE_MAP`, no
|
||||||
2. Add non-identity input mappings to `INPUT_MAP`.
|
> `INPUT_MAP`, no `OUTPUT_MAP`, and no per-type branch logic (all deleted
|
||||||
3. Add non-identity output mappings to `OUTPUT_MAP`.
|
> in the v1.11 rewrite — the 918-line monolith collapsed to a ~80-line
|
||||||
4. Add a specialized `_emit_resource` branch if the resource needs nested blocks (e.g. inline policies, rule sets).
|
> 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 "<rid>" { source = "<path>" }` 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
|
### Policy Adapter Pattern
|
||||||
|
|
||||||
@@ -76,7 +92,7 @@ never import an engine directly — they go through the registry.
|
|||||||
|
|
||||||
## How to Test Adapters
|
## 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 "<rid>" { 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_checkov_adapter.py` — Checkov adapter.
|
||||||
- `tests/test_wiz_adapter.py` — Wiz adapter.
|
- `tests/test_wiz_adapter.py` — Wiz adapter.
|
||||||
- `tests/test_kyverno_adapter.py` — Kyverno adapter.
|
- `tests/test_kyverno_adapter.py` — Kyverno adapter.
|
||||||
@@ -94,3 +110,31 @@ never import an engine directly — they go through the registry.
|
|||||||
4. Write a test (`tests/test_<name>_adapter.py`) plus a fixture (`tests/fixtures/<name>_fixture.json`).
|
4. Write a test (`tests/test_<name>_adapter.py`) plus a fixture (`tests/fixtures/<name>_fixture.json`).
|
||||||
5. Add it to `scripts/run_platform.sh` if it is invoked at runtime.
|
5. Add it to `scripts/run_platform.sh` if it is invoked at runtime.
|
||||||
6. Update this README.
|
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: <owner>/<repo>/.github/workflows/<file>@<ref>` 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.
|
||||||
+37
-3
@@ -19,7 +19,7 @@ numbers. Every metric either has a real source or is explicitly deferred.
|
|||||||
|
|
||||||
### Touchless Resolution Rate
|
### Touchless Resolution Rate
|
||||||
- **Target:** ≥ 99% across production estates (Post-Pilot)
|
- **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
|
- **Formula:** runs completing without *operational* HITL block ÷ total runs
|
||||||
(attestation gates excluded — they're designed controls, not escalations)
|
(attestation gates excluded — they're designed controls, not escalations)
|
||||||
- **Source:** `metrics/nova_metrics.db` `fact_run` (hitl_block column)
|
- **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
|
### Human Escalation Frequency
|
||||||
- **Target:** < 0.1% of platform actions (Post-Pilot)
|
- **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
|
- **Formula:** operational HITL blocks ÷ total runs (attestation sign-offs
|
||||||
excluded)
|
excluded)
|
||||||
- **Source:** `metrics/nova_metrics.db` `fact_run` (hitl_block column)
|
- **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`
|
- **Definition-of-success:** `docs/metrics/human_escalation_frequency.md`
|
||||||
|
|
||||||
### AI Decision Accuracy
|
### AI Decision Accuracy
|
||||||
- **Target:** ≥ 99.5% (no rollback, no follow-up incident within 5 min)
|
- **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
|
- **Formula:** decisions not followed by apply.failed/incident within 5min
|
||||||
÷ total decisions
|
÷ total decisions
|
||||||
- **Source:** `metrics/nova_metrics.db` `fact_decision` (outcome column)
|
- **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`
|
- **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)
|
### MTTD / MTTR (platform-run)
|
||||||
- **Target:** < 60 seconds (p95)
|
- **Target:** < 60 seconds (p95)
|
||||||
- **Status:** grounded (platform-run MTTR)
|
- **Status:** grounded (platform-run MTTR)
|
||||||
|
|||||||
@@ -32,6 +32,16 @@
|
|||||||
"description": "Environment variables as a JSON map string (optional).",
|
"description": "Environment variables as a JSON map string (optional).",
|
||||||
"required": false
|
"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": {
|
"cluster_arn": {
|
||||||
"type": "arn",
|
"type": "arn",
|
||||||
"description": "ECS cluster ARN (ref to ecs-cluster).",
|
"description": "ECS cluster ARN (ref to ecs-cluster).",
|
||||||
@@ -118,7 +128,9 @@
|
|||||||
"cpu",
|
"cpu",
|
||||||
"memory",
|
"memory",
|
||||||
"env",
|
"env",
|
||||||
"family"
|
"family",
|
||||||
|
"execution_role_arn",
|
||||||
|
"task_role_arn"
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
"task_def_arn"
|
"task_def_arn"
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
resource "aws_ecs_task_definition" "this" {
|
resource "aws_ecs_task_definition" "this" {
|
||||||
count = var.enabled ? 1 : 0
|
count = var.enabled ? 1 : 0
|
||||||
family = var.family
|
family = var.family
|
||||||
cpu = tostring(var.cpu)
|
cpu = tostring(var.cpu)
|
||||||
memory = tostring(var.memory)
|
memory = tostring(var.memory)
|
||||||
requires_compatibilities = local.requires_compatibilities
|
requires_compatibilities = local.requires_compatibilities
|
||||||
network_mode = local.network_mode
|
network_mode = local.network_mode
|
||||||
container_definitions = local.container_definitions
|
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" {
|
resource "aws_ecs_service" "this" {
|
||||||
count = var.enabled ? 1 : 0
|
count = var.enabled ? 1 : 0
|
||||||
name = "nova-microservice"
|
name = "nova-microservice"
|
||||||
cluster = var.cluster_arn
|
cluster = var.cluster_arn
|
||||||
task_definition = aws_ecs_task_definition.this[0].arn
|
task_definition = aws_ecs_task_definition.this[0].arn
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ variable "cluster_arn" {
|
|||||||
description = "ECS cluster ARN (ref to ecs-cluster)."
|
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" {
|
variable "subnets" {
|
||||||
type = string
|
type = string
|
||||||
description = "Comma-separated subnet ids (ref to vpc)."
|
description = "Comma-separated subnet ids (ref to vpc)."
|
||||||
|
|||||||
@@ -27,8 +27,11 @@
|
|||||||
{"from": "platform_vpc.outputs.subnet_ids", "to": "alb.inputs.subnets"},
|
{"from": "platform_vpc.outputs.subnet_ids", "to": "alb.inputs.subnets"},
|
||||||
{"from": "platform_vpc.outputs.subnet_ids", "to": "service.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.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": "platform_vpc.outputs.ecs_security_group_id", "to": "service.inputs.security_group"},
|
||||||
{"from": "cluster.outputs.cluster_arn", "to": "service.inputs.cluster_arn"},
|
{"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": "ecr.outputs.repository_url", "to": "service.inputs.image"},
|
||||||
{"from": "alb.outputs.target_group_arn", "to": "service.inputs.lb_target_group_arn"},
|
{"from": "alb.outputs.target_group_arn", "to": "service.inputs.lb_target_group_arn"},
|
||||||
{"from": "contract.inputs.region", "to": "kms.inputs.region"},
|
{"from": "contract.inputs.region", "to": "kms.inputs.region"},
|
||||||
|
|||||||
Reference in New Issue
Block a user