# Phase 07 — architecture-v1-finalization (v1.1.2) Verification Verifying Phase 07 on `main` (HEAD `8723206`, tag `v1.1.2`). Phase branch `phase/07-architecture-v1-finalization` deleted after squash merge. Phase 07 was a **design-authoring phase**: it locked the ACDL architecture to v1.0 by authoring 9 deliverable files (6 REQ-mapped schema/design files + the Checkov adapter + the SoD module + the architecture-v1.0 snapshot) that resolve all 11 open decisions in `docs/architecture.md` §13 (W1.A, W1.B, W2.A, W3.D, W3.E, BA.A, BA.B, BA.C, BA.D, BA.E, BA.F + the OpenTofu timing sub-decision Q1.3), recorded in `PROJECT.md` under decisions D-034..D-046. Verification layers: structural, behavioral, security, quality. All layers PASS. Final verdict: **Phase 07: VERIFIED**. ## Layer 1 — Structural: PASS ### 1.1 All 9 deliverable files exist ``` $ ls -la docs/architecture-v1.0.md schemas/ir.schema.json \ schemas/policy_check_result.schema.json schemas/contract.schema.json \ adapters/terraform/policy/checkov_adapter.py platform/confidence_signal.py \ platform/audit_ledger_design.md platform/hitl_matrix_design.md \ platform/separation_of_duties.py scripts/verify_phase07.sh -rw-r--r-- 1 root root 30167 Jul 21 18:48 docs/architecture-v1.0.md -rw-r--r-- 1 root root 5538 Jul 21 18:48 schemas/ir.schema.json -rw-r--r-- 1 root root 2484 Jul 21 18:48 schemas/policy_check_result.schema.json -rw-r--r-- 1 root root 3924 Jul 21 18:48 schemas/contract.schema.json -rw-r--r-- 1 root root 3578 Jul 21 18:48 adapters/terraform/policy/checkov_adapter.py -rw-r--r-- 1 root root 5787 Jul 21 18:48 platform/confidence_signal.py -rw-r--r-- 1 root root 5036 Jul 21 18:48 platform/audit_ledger_design.md -rw-r--r-- 1 root root 6321 Jul 21 18:48 platform/hitl_matrix_design.md -rw-r--r-- 1 root root 1835 Jul 21 18:48 platform/separation_of_duties.py -rwxr-xr-x 1 root root 3831 Jul 21 18:48 scripts/verify_phase07.sh ``` All 9 REQ-mapped files + the verify script are present (sizes non-zero). ### 1.2 `docs/architecture-v1.0.md` status line is v1.0 (not v0.2) ``` $ grep -n "Status:" docs/architecture-v1.0.md | head -3 14: Status: **v1.0** (snapshot taken in ACDL Phase 07, milestone v1.1). All 11 429: Status: **v1.0**. All 11 open items in §13 are resolved. ... ``` Status line at L14 says `v1.0` (Phase 07 bump); the upstream `v0.2` status does not survive into the snapshot's status line. ### 1.3 All 11 open-decision IDs + Q1.3 appear in the snapshot ``` $ grep -cE "W1\.A|W1\.B|W2\.A|W3\.D|W3\.E|BA\.A|BA\.B|BA\.C|BA\.D|BA\.E|BA\.F|Q1\.3" \ docs/architecture-v1.0.md 33 ``` Every one of the 11 IDs + Q1.3 appears in both the resolution log table (L26–37) and the §13 "✅ RESOLVED (see PROJECT.md)" markers (L75–425). Each row carries the resolution text + a pointer to `PROJECT.md`. ### 1.4 `gitea-runner` rename (D-046) applied; `act_runner` only in "formerly" note ``` $ grep -n "act_runner\|gitea-runner" docs/architecture-v1.0.md 9: > `act_runner` → `gitea-runner` rename (D-046, 2026-04 in gitea/runner#850) 10: > is applied; `act_runner` appears only in a "formerly" note. 352: > gitea-runner v2.1.0 (formerly `act_runner`, renamed 2026-04 in ``` `gitea-runner` is the body name; `act_runner` only appears in the header note + the "formerly" parenthetical at L352. D-046 satisfied. ### 1.5 §15 table lists all 6 REQ-mapped files ``` $ sed -n '441,455p' docs/architecture-v1.0.md ## 15. Phase 07 authored artifacts ... | REQ | File | Owner persona | |-----|------|--------------| | REQ-17 | `schemas/ir.schema.json` | platform-engineer | | REQ-18 | `schemas/policy_check_result.schema.json` + `adapters/terraform/policy/checkov_adapter.py` | security-engineer | | REQ-19 | `platform/confidence_signal.py` | backend-engineer + security-engineer (co-authored) | | REQ-20 | `platform/audit_ledger_design.md` | security-engineer | | REQ-21 | `platform/hitl_matrix_design.md` + `platform/separation_of_duties.py` | security-engineer | | REQ-22 | `schemas/contract.schema.json` | backend-engineer | ``` All 6 REQ rows + the 8 underlying files are listed. All §15 files exist on disk (cross-checked with `os.path.exists` for every entry). ### 1.6 The 3 JSON Schemas declare Draft 2020-12 + required fields ``` $ grep -n '\$schema\|draft/2020-12' schemas/*.schema.json schemas/ir.schema.json:2: "$schema": "https://json-schema.org/draft/2020-12/schema", schemas/policy_check_result.schema.json:2: "$schema": "https://json-schema.org/draft/2020-12/schema", schemas/contract.schema.json:2: "$schema": "https://json-schema.org/draft/2020-12/schema", ``` Per-file required-fields check: - `schemas/ir.schema.json`: `required: [version, stack, resources]`; `stack.depth` max 5; `stack.name` pattern `^l[12]-[a-z][a-z0-9-]*$`; `stack.kind` enum `[l1, l2]`; `resource.module` pattern `^l1-[a-z][a-z0-9-]*@\d+\.\d+\.\d+$` (W3.D name@semver); `relationship.kind` enum `[parent, depends_on, uses_output]`; `shared_keyword` reserved (present, unused). ✅ - `schemas/policy_check_result.schema.json`: `required: [contractId, evaluatedAt, engine, ruleId, severity, result, message, resourceRef]`; `engine` enum `[checkov, kyverno, opa]`; `severity` enum `[critical, high, medium, low, info]`; `result` enum `[pass, fail, skipped, error]`; `evidence` optional with `additionalProperties: true`. ✅ - `schemas/contract.schema.json`: top `required: [stack, environment]`; `stack` pattern `^l2-[a-z][a-z0-9-]*$`; `environment` enum `[dev, qa, prod, dr]` (no `staging`); `profile` enum `[developer, agentic]` default `developer`; `allOf` conditionals present: qa→`[validation]`, prod→`[runbook, dashboard, oncall]`, dr→`[drDrillRef]`, agentic→`[naturalLanguageIntent]`. ✅ Substrate-agnostic invariant for IR schema: the only occurrence of `aws_s3_bucket` is in the `$comment` (L6) and a `description` (L61) where it is explicitly called out as the *non*-IR / *Terraform* type to avoid. No Terraform-block keywords (`variable`/`output` as JSON keys, `tf_block`) appear in the schema body. Invariant satisfied. ### 1.7 The 3 .py files have expected module docstrings + public functions - `platform/confidence_signal.py` (REQ-19, T-7.9): module docstring (L1–32) enumerates the 6 inputs + weights + severity→penalty + per-env thresholds. Public surface: `WEIGHTS`, `PENALTY`, `THRESHOLDS`, `Signal` dataclass, `_per_input_score`, `compute`, `__main__` CLI. ✅ - `platform/separation_of_duties.py` (REQ-21, T-7.8): module docstring (L1–12) explains `qaApprover != prodApprover` + outbox read + spike dev-only note. Public surface: `check(outbox_client, contract_id, current_prod_approver)`, `route_halt_artifact(...)`. ✅ - `adapters/terraform/policy/checkov_adapter.py` (REQ-18, T-7.5): module docstring (L1–11) "Translate Checkov JSON output to ACDL PolicyCheckResult records". Public surface: `RULE_MAP`, `_iso8601_now`, `_to_pcr`, `_emit_tag_naming_skipped`, `adapt`, `__main__` CLI. ✅ ### 1.8 History preservation — `docs/architecture-v1.0.md` is a new file ``` $ git log --follow --oneline docs/architecture-v1.0.md 92d4535 phase: 7, status: plan-as-execute, persona: lead-developer, task: T-7.1 ``` Single creation commit (T-7.1, 92d4535). As expected for a new file — the upstream `docs/architecture.md` (52665b8 → b84a8a2) history is preserved on the upstream file itself; the snapshot is intentionally a new file, not a copy-with-rename. ### 1.9 Tags v1.1.0, v1.1.1, v1.1.2 all exist ``` $ git tag --list 'v1.1*' v1.1.0 v1.1.1 v1.1.2 ``` ## Layer 2 — Behavioral: PASS ### 2.1 `scripts/verify_phase07.sh` exits 0 with the expected final line ``` $ bash scripts/verify_phase07.sh ok: all 9 deliverable files exist ok: 3 JSON Schemas validate as Draft 2020-12 ok: 3 .py files py_compile ok: 3 .md design files non-empty ok: all 11 decision IDs + OpenTofu present in PROJECT.md ok: docs/architecture-v1.0.md status is v1.0 ok: D-040..D-044 present in PROJECT.md ok: spike contract validates against contract schema ok: minimal IR validates against IR schema VERIFIED — Phase 07: architecture v1.0 finalized; 6 files authored + 11 decisions resolved $ echo $? 0 ``` All 9 assertions in the script pass; final line matches PLAN.md spec. ### 2.2 Typecheck gate ``` $ bash -n scripts/verify_phase07.sh && \ python3 -m py_compile platform/confidence_signal.py \ platform/separation_of_duties.py \ adapters/terraform/policy/checkov_adapter.py TYPECHECK_OK ``` `bash -n` (syntax) + `py_compile` (byte-compile) all pass. ### 2.3 Schema cross-checks (PLAN.md self-verify test instances) Run from `/tmp` to avoid the repo `platform/` package shadowing stdlib `platform` (see Layer 3 §3.3): ``` OK: qa without validation fails OK: prod without runbook fails OK: dr without drDrillRef fails OK: agentic without NLI fails OK: agentic with NLI passes OK: staging rejected OK: valid PCR passes ALL_SCHEMA_CROSSCHECKS_OK ``` `environment` enum confirmed `[dev, qa, prod, dr]` — no `staging` (Path A locked, ARCHITECTURE.md §5). Per-env mandatory conditionals all fire correctly. ### 2.4 Confidence signal behavioral spot-checks (REQ-19) Run from `/tmp` with `sys.path.insert(0, '/root/acdl')`: - **Missing input → block + INPUT_MISSING:** `compute('cid','dev', inputs)` with `nfrs` omitted returns `Signal(0.0, "block", {}, ["INPUT_MISSING:nfrs"])`. ✅ - **Critical fail → 0.0 block + CRITICAL_OVERRIDE:** `compute('cid','dev', inputs)` with one `severity:"critical", result:"fail"` PolicyCheckResult returns `Signal(0.0, "block", per_input, ["CRITICAL_OVERRIDE:CKV_AWS_X"])` regardless of other inputs. ✅ - **Cold-start dev → pass ≥ 0.50:** `compute('cid','dev', inputs)` with the ACDL_TAG_NAMING `skipped` PolicyCheckResult + all validation true + neutral 0.5 for freshness/source/history/nfrs returns `Signal(0.950, "pass", ...)`. ✅ (0.95 ≥ 0.50 dev threshold). Note: the `WEIGHTS` dict keys are `policy / validation / freshness / source / history / nfrs` (the canonical names), not the docstring's `policy_results` label — the docstring describes the input's *type* ("list[PolicyCheckResult]"); the `compute()` loop iterates `WEIGHTS.items()` and reads `inputs.get("policy")` (the key). This is consistent with the Wave 4 self-verify ("`policy_results`" in the docstring is the descriptive label, `policy` is the dict key — verified at runtime). ### 2.5 Separation-of-duties behavioral spot-checks (REQ-21) - `check(None, "cid", "anyone")` → `(True, "no outbox client (dev-only spike)")`. ✅ - `check(stub_returning_None, "cid", "anyone")` → `(True, "no prior approver (first promotion)")`. ✅ - `check(stub_with_approver_qa("alice"), "cid", "alice")` → `(False, "SEPARATION_OF_DUTIES_VIOLATION: qaApprover==prodApprover==alice")`. ✅ - `check(stub_with_approver_qa("alice"), "cid", "bob")` → `(True, "distinct")`. ✅ - `check(stub_with_empty_approver_qa, "cid", "alice")` → `(True, "no QA approver recorded (dev-only spike)")`. ✅ All SoD branches match PLAN.md T-7.8 spec. ### 2.6 Checkov adapter behavioral spot-check (REQ-18 adapter) Synthetic Checkov JSON with one failed `CKV_AWS_24`: ``` $ python3 adapters/terraform/policy/checkov_adapter.py fixture.json test-contract-id [ { "contractId": "test-contract-id", "evaluatedAt": "2026-07-21T18:49:11Z", "engine": "checkov", "ruleId": "CKV_AWS_24", "severity": "medium", ← per RULE_MAP (public-ingress SG 0.0.0.0/0) "result": "fail", "message": "SG 0.0.0.0/0 on 22", "evidence": {"file_path": null, "resource": "aws_security_group.r1", "resource_address": "aws_security_group.r1", "code_block": null}, "resourceRef": "aws_security_group.r1" }, { "contractId": "test-contract-id", "ruleId": "ACDL_TAG_NAMING", ← D-043 appended SKIPPED record "severity": "info", "result": "skipped", "message": "tag/naming check deferred to v1.2 (D-043)", "evidence": {}, "resourceRef": "" } ] ``` Severity correctly defaulted to `medium` for `CKV_AWS_24` from `RULE_MAP`; `ACDL_TAG_NAMING` SKIPPED record appended (D-043). Both records validate against `schemas/policy_check_result.schema.json`. ## Layer 3 — Security: PASS ### 3.1 No credentials/secrets introduced Files touched by Phase 07 (v1.1.1..v1.1.2): ``` $ git log v1.1.1..v1.1.2 --diff-filter=A --name-only --pretty=format: | sort -u .ciagent/PLAN.md .ciagent/REQUIREMENTS.md .ciagent/ROADMAP.md .ciagent/VERIFY.md adapters/terraform/policy/__init__.py adapters/terraform/policy/checkov_adapter.py docs/architecture-v1.0.md platform/__init__.py platform/audit_ledger_design.md platform/confidence_signal.py platform/hitl_matrix_design.md platform/separation_of_duties.py schemas/contract.schema.json schemas/ir.schema.json schemas/policy_check_result.schema.json scripts/verify_phase07.sh ``` No `.env`, no `*.tfstate`, no `*_key*`, no `credentials*` files. Phase 07 is design authoring + stdlib-only Python — no AWS/TF runtime calls, no boto3 imports (the spike passes a duck-typed `outbox_client`). ### 3.2 LSP diagnostic on `platform/confidence_signal.py:148` is a false positive ``` $ python3 -m py_compile platform/confidence_signal.py $ python3 -c "import ast; ast.parse(open('platform/confidence_signal.py').read()); print('AST parse OK')" AST parse OK ``` At L148, `p = PENALTY.get(sev, 0.0)` — `sev` comes from `pcr.get("severity")` where `pcr` is `Dict[str, Any]`. The LSP ("No overloads for `get` match the provided arguments") is a known false-positive when `.get()` is called on a `Dict[str, Any]` value in some pyright configurations. `py_compile` passes; runtime behavior is verified correct in §2.4 (the critical-override branch returns the expected `CRITICAL_OVERRIDE:` and the `None` sentinel correctly short-circuits via `if p is None:` at L149). Not a real bug. ### 3.3 `platform/` package shadows stdlib `platform` — documented + worked around The repo's `platform/` Python package (our code) shadows the stdlib `platform` module when the repo root is on `sys.path[0]` (which a `python3 -c` invocation from repo root triggers). `jsonschema` imports `uuid` → `uuid` imports `platform.system()` → fails with `AttributeError: module 'platform' has no attribute 'system'`. `scripts/verify_phase07.sh` documents this and works around it by running all `jsonschema`-invoking python from `/tmp` with absolute paths to the schemas: ``` $ grep -n "platform\|cd /tmp\|sys.path\|shadow" scripts/verify_phase07.sh 24: # Run python from /tmp so the repo's `platform/` package does not shadow the 25: # stdlib `platform` module (jsonschema imports uuid -> platform.system(); 26: # our platform/ shadows it when cwd is repo root and on sys.path[0]). 28: ( cd /tmp && python3 -c "..." ) 70: ( cd /tmp && python3 -c "..." ) 78: ( cd /tmp && python3 -c "..." ) ``` The workaround is correct: `cwd=/tmp` puts `/tmp` at `sys.path[0]`, so `import platform` resolves to the stdlib, not our package; the schemas are passed by absolute path. The verify script passes (§2.1), and my inline behavioral spot-checks (§2.3–2.5) reproduced the workaround by running from `/tmp` + `sys.path.insert(0, '/root/acdl')` to import our modules explicitly. **P1 — flag for post-hoc cleanup:** a v1.2 rename of `platform/` to `acdl_platform/` (or moving the package under a `src/` layout) would avoid the shadowing entirely, removing the need for the `/tmp` dance in every jsonschema-invoking test. This is out of Phase 07 scope (Phase 07 must ship the `platform/` layout the README + PLAN.md committed to). Flagged for v1.2. ### 3.4 No `import boto3` in the Phase 07 .py files ``` $ grep -nE "^import |^from " platform/confidence_signal.py \ platform/separation_of_duties.py \ adapters/terraform/policy/checkov_adapter.py platform/confidence_signal.py:34: from dataclasses import dataclass, asdict platform/confidence_signal.py:35: from typing import List, Literal, Optional, Dict, Any platform/confidence_signal.py:36: import json platform/confidence_signal.py:37: import sys platform/separation_of_duties.py:14: from typing import Optional, Tuple adapters/terraform/policy/checkov_adapter.py:13: import datetime adapters/terraform/policy/checkov_adapter.py:14: import json adapters/terraform/policy/checkov_adapter.py:15: import sys ``` Stdlib only across all 3 modules. The SoD `check()` signature receives a duck-typed `outbox_client` (has `.get(pk)`); the pipeline step owns the boto3 client. PLAN.md T-7.8 spec satisfied. ## Layer 4 — Quality: PASS ### 4.1 README layout table still matches reality ``` $ grep -n "platform/\|schemas/\|adapters/" README.md 31: | `platform/` | Platform code: confidence signal, contract resolver, outbox, HITL/ledger designs | Phase 07+ | 32: | `schemas/` | JSON Schemas: IR, PolicyCheckResult, contract | Phase 07 | 33: | `adapters/` | Substrate adapters (Terraform adapter in v1; the only substrate-specific code per §12) | Phase 09 | ``` README's "Phase 07+" and "Phase 07" annotations are now accurate — `platform/` and `schemas/` are populated with the 9 deliverable files (no longer just `.gitkeep`'d). `adapters/` is annotated "Phase 09" which is also accurate: only the `policy/` subdirectory is populated in Phase 07 (the Checkov adapter, REQ-18), and the rest of the adapter surface (the IR→Terraform module compiler) is Phase 09. ### 4.2 Phase 07 commit messages all carry `---ci---` blocks Phase 07 commits on main (v1.1.1..v1.1.2): ``` 8723206 ship: phase-07 architecture-v1-finalization (v1.1.2) 412e1ef phase: 7, status: plan-as-execute, persona: lead-developer, task: T-7.10 68d90c0 phase: 7, status: plan-as-execute, persona: backend-engineer+security-engineer, task: T-7.9 6ed93f0 phase: 7, status: plan-as-execute, persona: security-engineer, task: T-7.4..T-7.8 f8e99ed phase: 7, status: plan-as-execute, persona: platform-engineer+backend-engineer, task: T-7.2+T-7.3 92d4535 phase: 7, status: plan-as-execute, persona: lead-developer, task: T-7.1 b40aadd docs(P07): create Phase 07 plan (architecture-v1-finalization) ``` Each execute commit + the ship commit carries a `---ci---` block with `project / phase / milestone / status / persona / task` (execute commits) or `release.tag` (ship commit). Verified by inspecting commit bodies (`git log v1.1.1..v1.1.2 --pretty=format:'%H %s%n---%n%b%n---'`): T-7.1, T-7.2+7.3, T-7.4..7.8, T-7.9, T-7.10, and the merge all include well-formed `---ci---` blocks. ✅ ### 4.3 `ROADMAP.md` Phase 07 status = "complete (v1.1.2)" ``` $ grep -n "Phase 07\|complete.*v1.1.2\|status" .ciagent/ROADMAP.md | head -5 91: ### Phase 07 — architecture-v1-finalization 93: - **Status:** complete (v1.1.2) ``` ### 4.4 `REQUIREMENTS.md` traceability — REQ-16..22 complete (v1.1.2) ``` $ grep -n "REQ-1[6-9]\|REQ-2[0-2]" .ciagent/REQUIREMENTS.md | tail -7 117: | REQ-16 | 07 | complete (v1.1.2) | 118: | REQ-17 | 07 | complete (v1.1.2) | 119: | REQ-18 | 07 | complete (v1.1.2) | 120: | REQ-19 | 07 | complete (v1.1.2) | 121: | REQ-20 | 07 | complete (v1.1.2) | 122: | REQ-21 | 07 | complete (v1.1.2) | 123: | REQ-22 | 07 | complete (v1.1.2) | ``` All 7 Phase 07 requirements marked complete at v1.1.2. ### 4.5 `docs/architecture-v1.0.md` internal consistency - The §15 table's 6 files (8 underlying paths) all exist on disk (verified via `os.path.exists` for every entry). ✅ - The 11 resolutions in the §13 markers (L75–425) match the `PROJECT.md` "Open-decision resolutions" table (L178–189) verbatim (W1.A, W1.B, W2.A, W3.D, W3.E, BA.A, BA.B, BA.C, BA.D, BA.E, BA.F) + the Q1.3-OpenTofu sub-decision. ✅ - Decisions D-034..D-046 all present in `PROJECT.md` (the decision table at L160–172). ✅ ## Issues found ### P0 (blocking) — none No P0 issues. All must-haves from PLAN.md are satisfied; the phase gate `scripts/verify_phase07.sh` is green; behavioral spot-checks all pass. ### P1 (post-hoc cleanup, out of Phase 07 scope) - **P1-1: `platform/` package shadows stdlib `platform`.** The repo's `platform/` directory is a Python package that shadows the stdlib `platform` module when the repo root is on `sys.path` (any `python3 -c` from repo root). This breaks `jsonschema` (which imports `uuid` → `platform.system()`). `scripts/verify_phase07.sh` works around it by running jsonschema-invoking python from `/tmp`, but the workaround is brittle — every future test script that imports `jsonschema` (or any stdlib module that transitively imports `platform`) from repo root will hit the same shadow. Recommended v1.2 fix: rename `platform/` → `acdl_platform/` (or adopt a `src/` layout) so the package no longer collides with the stdlib name. Out of Phase 07 scope (the layout is locked by Phase 06 + README + PLAN.md). - **P1-2: LSP false positive on `platform/confidence_signal.py:148`.** The "No overloads for `get` match the provided arguments" diagnostic is a pyright false positive triggered by `Dict[str, Any]` typing on `pcr`. `py_compile` passes; runtime behavior is correct (verified in §2.4). No fix needed in Phase 07; if a v1.2 type tightening pass happens, replacing `Dict[str, Any]` with a `TypedDict` for `PolicyCheckResult` would silence the LSP and improve type safety. ## Requirement coverage summary | REQ | File(s) | Status | |-----|---------|--------| | REQ-16 | `docs/architecture-v1.0.md` | covered (v1.1.2) | | REQ-17 | `schemas/ir.schema.json` | covered (v1.1.2) | | REQ-18 | `schemas/policy_check_result.schema.json` + `adapters/terraform/policy/checkov_adapter.py` | covered (v1.1.2) | | REQ-19 | `platform/confidence_signal.py` | covered (v1.1.2) | | REQ-20 | `platform/audit_ledger_design.md` | covered (v1.1.2) | | REQ-21 | `platform/hitl_matrix_design.md` + `platform/separation_of_duties.py` | covered (v1.1.2) | | REQ-22 | `schemas/contract.schema.json` | covered (v1.1.2) | All 7 Phase 07 requirements covered. No partials. ## Final verdict Phase 07: VERIFIED