Files
acdl/.ciagent/RESEARCH.md
T
Jon Chery 707d8a1e39 docs(P00): research findings — v1.26 (PoA blockchain, deploy model, DynamoDB gap, personas)
---ci---
project: acdl
phase: 0
milestone: v1.26
status: research
requirements: [REQ-310..REQ-322]
personas: [lead-developer, backend-engineer, data-engineer, policy-engineer, blockchain-engineer]
---/ci---
2026-08-12 21:16:42 +00:00

12 KiB

Nova — v1.26 Research Findings

Phase: research (pre-execution). Milestone: v1.26 (Live Pilot Estate Activation). Status: research. Researcher: ci-researcher. Autonomy: full.


1. Domain — Homegrown PoA Blockchain for Securities Settlement

1.1 Why a homegrown chain (not Ethereum/Solana/Hyperledger)

The pilot's purpose is to exercise the Nova platform's deploy/policy/ attestation gates over a real consumer estate — not to build a production blockchain. A homegrown PoA ledger is the minimal viable chain: append-only blocks, single validator (pilot), SHA-256 hash chain, deterministic block production. It records every order, match, and settlement as transactions; settlement finality = block commit. This is sufficient to demonstrate that Nova's policy engine (kyverno-json) can assert settlement finality declaratively (REQ-315) and that the Decision Ledger captures the apply decision.

A production chain (Ethereum/Solana/Hyperledger) would be the consumer app's choice, not the platform's. The platform is chain-agnostic — it deploys whatever the consumer's contract.yaml declares. For the pilot, the homegrown chain is the simplest way to produce a real consumer estate without a heavyweight external dependency.

1.2 PoA consensus — single validator (pilot)

Proof-of-Authority with a single validator is the minimal consensus model: the validator proposes + commits blocks. No Byzantine fault tolerance (single validator = no forks). Deterministic block production: same ordered transactions → same block (same hash). This makes the chain auditable (the hash chain is verifiable) and reproducible (a replay produces the same chain). Multi-validator BFT is a future milestone (D-201).

1.3 T+1 settlement finality

Equities settle T+1 (trade date + 1 business day). The pilot's settlement service records matches as transactions on the chain; a settlement is final when its block is committed. The settlement-finality kyverno-json policy (REQ-315) asserts all_committed: true before any promotion (qa→prod) — the declarative gate that turns settlement finality into a policy artifact. This is the securities-specific extension of v1.25's policy engine: the same KyvernoJsonEngine evaluates a policy over a new payload shape (settlement-service status JSON).

1.4 Equities-only scope (D-200)

Bonds (T+2), derivatives (varying), and options (exercise models) have different settlement models. A pilot should demonstrate the Nova platform's gates over the simplest case (equities T+1) before expanding. "All types of securities" is the product vision; v1.26 is the pilot (equities first). Future milestones add other security types with their settlement models.


2. Nova Consumer Deploy Model

2.1 The reusable deploy.yml@v1.25 workflow

The platform's .github/workflows/deploy.yml is a workflow_call — a reusable workflow that a consumer repo invokes via uses: acdl/.github/workflows/deploy.yml@v1.25. Inputs: contract (default .nova/contract.yml), mode (default full; enum full|plan-only|check-only|decommission), environment (override). The workflow checks out the consumer repo + the platform repo, runs scripts/run_platform.sh, and records the apply decision + attestation in the Decision Ledger. Secrets: NOVA_AWS_* (account + access key + secret) + NOVA_LAMBDA_URL (error reporting).

The pilot consumer (nova-blockchain-exchange) invokes this workflow with mode: full for dev (D-209). The .gitea/workflows/deploy.yml mirror is byte-identical (the platform's deploy workflow is forge-agnostic — Gitea + GitHub).

2.2 run_platform.sh --apply path (confirmed)

scripts/run_platform.sh:431-455 — the --apply (or mode: full) path runs terraform apply -auto-approve after the HITL gate (:438). For dev (autonomous, no HITL gate), the apply proceeds directly. The apply records the env via core/env_transition.py record (:450). The full pipeline (no --apply flag) continues to Step 7 (confidence signal) + Step 8 (outbox write).

Gap (noted in RESEARCH §4): the --apply path exits before the outbox write (Step 8). The pilot runs the full pipeline (not --apply alone), so the outbox write happens. The run.completed event lands in the JSONL Decision Ledger (not the DynamoDB outbox) — this is by design (the outbox is the platform-run evidence stream; the Decision Ledger is the cold store for metrics).

2.3 Contract schema — multi-module manifest

schemas/contract.schema.json:7,24-48 — required fields: id, name, environment, infrastructure. The infrastructure block is minProperties: 1 with patternProperties accepting any module name key. Multi-module manifest is supported: one contract can declare infrastructure: { microservice: {...}, dynamodb: {...}, s3: {...} }. The constraint is the modules/registry.json (the module must be registered), not the schema.


3. Platform Module Readiness (the critical finding)

3.1 The adapter is stateless (v1.11 rewrite)

adapters/terraform/adapter.py:1-11 — the adapter is a "STATELESS ASSEMBLER" that owns no module content. There is no TYPE_MAP, INPUT_MAP, or OUTPUT_MAP (deleted in the v1.11 stateless rewrite; modules/STANDARDS.md:212-214 confirms). A new stack type requires a new L1 module (modules/l1/<name>/ with interface.json + terraform/main.tf + README.md + instance.json) + a modules/registry.json entry — not an adapter change.

3.2 ECS — ready

modules/l1/ecs-service/terraform/main.tf:1,11aws_ecs_task_definition + aws_ecs_service. interface.json:5-6type: aws:ecs:task_definition. registry.json:29-37 — registered. Tests: test_adapter.py:164-185,257-360, test_contract_resolver.py:61-92. The microservice L2 (modules/l2/microservice/composition.json) references 6 L1 children (ecs-cluster, ecr, iam-role, alb, ecs-service, kms-key) — the ECS pattern is fully wired end-to-end.

3.3 S3 — ready

modules/l1/s3/terraform/main.tf:1aws_s3_bucket (+ versioning + SSE). interface.json:5-6type: aws:s3:bucket. registry.json:2-10 — registered. Tests: test_adapter.py:56-110,241-257, test_contract_resolver.py:36-51,92-130.

3.4 DynamoDB — GAP (REQ-322)

No modules/l1/dynamodb/ directory, no registry.json key, no interface.json, no terraform/, no tests. The blockchain exchange's ledger table needs this primitive. REQ-322 authors it: interface.json (stack type aws:dynamodb:table), terraform/main.tf (aws_dynamodb_table with PK + optional SK, PAY_PER_REQUEST default, encryption + PITR enabled per v1.8 NFR defaults), README.md, instance.json, + registry.json entry. The adapter needs no change (stateless); the contract's infrastructure.dynamodb block references this primitive. This is the single platform-side module build-out for the milestone.

3.5 Stale doc (not a blocker)

adapters/README.md:49-54 references the deleted TYPE_MAP/ INPUT_MAP/OUTPUT_MAP — contradicts adapter.py:1-11 + modules/STANDARDS.md:212-214. REQ-321 (docs) should fix this.


4. Metric Pipeline Grounding (Post-Pilot targets)

4.1 AI Decision Accuracy — outcome backfill (REQ-317)

core/metrics/decision_ledger.py:210-211 documents the event chain: confidence.computed → ai.decision.made → attestation.recorded → run.completed/failed. collector.py:262 inserts fact_decision.outcome as "pending"there is no outcome-backfill step wiring run.completed/run.failed back into fact_decision.outcome. The AI Decision Accuracy metric (trust_snapshot.py:70-85, _get_ai_decision_accuracy) reads decisions WHERE outcome='succeeded' ÷ total — so it reads 0% today (all pending). REQ-317 adds core/metrics/outcome_backfill.py that reads run-manifest events and updates fact_decision.outcome + fact_decision.backfilled_at. The PCR schema is unchanged (D-211).

4.2 Human Escalation Frequency — reason='confidence' tag (REQ-318)

core/confidence_signal.py:184 — a block band sets human_override=True in the ai.decision.made event. run_platform.sh:636 fails the pipeline on block. The Human Escalation Frequency metric (docs/metrics/human_escalation_frequency.md:11-12) is defined as count(runs WHERE hitl_block=1 AND reason='confidence') ÷ total runs. The reason='confidence' discriminator is not currently storedhitl_block is a boolean from the manifest. REQ-318 adds escalation_reason: 'confidence' to the ai.decision.made event when band == 'block' + persists it into fact_run via the collector.

4.3 Touchless Resolution Rate — denominator activates post-pilot

docs/metrics/touchless_resolution_rate.md:12-15 — defined as a SQL query over fact_run (runs WHERE hitl_block=0 ÷ total runs). The data lands in fact_run.hitl_block via collector.py:216-227. No dedicated emitter computes the ratio — it's a downstream query. The denominator is 0 today (no consumer runs). The pilot run activates the denominator.


5. kyverno-json Policy Extensibility

adapters/kyverno-json/kyverno_json_engine.py:74-80 — the engine is policy-dir agnostic: it loads whatever subdir the caller passes. Existing subdirs: contract/, stack-ir/, plan-json/, meta/, regression/. Adding a new subdir (e.g. pilot-readiness/, settlement-finality/) requires: (1) mkdir adapters/kyverno-json/policies/<name>/, (2) drop ValidatingPolicy YAML/JSON files, (3) wire a caller. No engine code change needed. Test pattern: one test file per subdir (tests/test_<name>_policies.py).

The pilot adds two new policy subdirs: pilot-readiness/ (REQ-320, no-placeholder-account) + settlement-finality/ (REQ-315, all-matches-committed). Both follow the established pattern.


6. Env-JSON Wiring Reconciliation (REQ-319)

core/environments/dev.json:4account_id: "000000000000" (placeholder). core/environment_check.py:48-53 warns (non-fatal) when account_id is placeholder + env != dev. adapters/terraform/adapter.py:116-117 — computes the state bucket as nova-tfstate-<AWS_ACCOUNT_ID>-us-east-1 from the AWS_ACCOUNT_ID env var, not from the env JSON's state_backend.bucket. This is the wiring gap: the env JSON's state_backend field is currently unused by the live apply path. REQ-319 makes the adapter read env.state_backend.bucket when present (falling back to the computed name for backwards compat) + updates dev.json to the real account 581513795199 + real bucket nova-tfstate-581513795199-us-east-1.


7. Risk Analysis

Risk Likelihood Impact Mitigation
NOVA_AWS_* key lacks a needed IAM permission mid-pilot Low (bootstrap succeeded → root-equivalent) High (blocks apply) D-207; the key has root-equivalent perms (empirically confirmed).
DynamoDB primitive takes longer than expected (new module) Medium Medium REQ-322 is the single platform-side build-out; the s3/rds primitives are the template — straightforward.
Homegrown chain has a correctness bug (hash chain breaks) Low High REQ-310 tests cover chain integrity, hash determinism, genesis, append/verify.
deploy.yml@v1.25 ref doesn't resolve (floating tag) Low High The platform's release.yml creates + force-moves the v1.25 + v1 floating tags on merge to main. The pilot contract uses @v1.25.
Settlement-finality policy false-negatives (blocks a valid promotion) Medium Medium REQ-315 tests cover passing + failing fixtures; the policy is skip-when-kj-absent (graceful).
D-083 deferral challenged (audit ledger not tamper-evident) Low Low D-204; the SQLite hash-chain + DynamoDB outbox is the pilot's audit record. Tamper-evidence is a future milestone.

8. Persona Assessment

See PERSONAS.md (next section, produced by the lead-developer at the end of RESEARCH). The active roster: backend-engineer (blockchain core

  • settlement + outcome backfill), data-engineer (DynamoDB primitive + metrics cold store), policy-engineer (kyverno-json policies), + blockchain-engineer (custom, phase-specific — chain consensus, order matching, settlement finality). frontend-engineer is deactivated (no UI in the pilot).