Files
acdl/.ciagent/PERSONAS.md
T
Jon Chery ca7d41c1ad docs(P00): research findings — v1.17 telemetry signal inventory + reference architecture + metric scorecard + deck research
---ci---
project: acdl
phase: 0
milestone: v1.17
status: research
---/ci---
2026-08-04 19:12:18 +00:00

26 KiB
Raw Permalink Blame History

project, milestone, generated_at, generator, verification_toolchain
project milestone generated_at generator verification_toolchain
acdl v1.17 2026-08-04 lead-developer
typecheck test build note
terraform validate && python3 -m py_compile core/**/*.py && python3 -m jsonschema schemas/*.schema.json bash scripts/run_regression.sh # 22-capability gate (D-091/D-118) + CAP-023/024 (v1.17) bash scripts/run_ci.sh # full local CI reproduction (lint+test+check-only) v1.17 adds a telemetry/observability layer (metrics emitters, SQLite cold store, PowerBI export, Decision Ledger) + a unified narrative deck + a durable NORTH_STAR.md. Three active personas: lead-developer (coordination + deck narrative co-author), backend-engineer (event emitters, outbox_writer extension, Infracost adapter), data-engineer (SQLite store, schemas, PowerBI views, metrics collector). frontend- engineer stays deactivated (no Nova web UI — dashboards are PowerBI, not a Nova-built frontend; decks are markdown = lead-developer territory). No new custom personas needed — the metrics domain maps cleanly to data-engineer (schema/store/export) + backend-engineer (emitters/instrumentation).

ACDL — Persona Roster (project-level, v1.11 RESTART)

v1.11 is a restart (D-097). The v1.9 roster is superseded. Three structural corrections: (1) stateless adapter (D-098), (2) terraform owns lifecycle (D-101), (3) pipeline-driven testing (D-102). The roster is simplified to the three active domains: data (terraform foundation), backend (adapter/resolver), general (pipelines/workflows).

Active personas

lead-developer

  • Domain: coordination
  • Active: true
  • Phase-specific: false
  • Reason: Owns CIAgent metadata, cross-phase verification scripts, the v1.11 phase orchestration (D-107: P56a + P56b split), and arbitrates persona conflicts. Resolves the milestone decomposition and the STANDARDS.md §8 rewrite (the adapter extension pattern is replaced by the per-module terraform subdir pattern).

backend-engineer

  • Domain: backend
  • Active: true
  • Phase-specific: false
  • Reason: Owns the adapter rewrite (D-098: stateless assembler — deletes TYPE_MAP/INPUT_MAP/OUTPUT_MAP + 39 type-specific branches, becomes a ~80-line assembler that emits module "x" { source = "..." ... } blocks) and the contract resolver env-aware state keys (D-106: spike/{id}/{env}/terraform.tfstate). The adapter holds no module content; the engine binding lives in the per-module terraform/ subdir. Co-authoring expected on the adapter + run_platform.sh boundary (general adds --apply/--destroy modes that invoke the adapter).
  • Territory: adapters/terraform/adapter.py (rewrite to stateless assembler), core/contract_resolver.py (env-aware state keys, deterministic composition), schemas/stack.schema.json (if the stack instance shape changes), tests/test_adapter*.py (regression baseline — the s3 instance.json round-trip must still pass).

data-engineer

  • Domain: data
  • Active: true
  • Phase-specific: false
  • Reason: Reactivated for v1.11. Owns the heaviest territory: the per-module terraform/ subdirs (D-098/D-099/D-100 — the engine binding) for all 12 L1 modules, plus the single platform VPC (D-105: terraform/platform owns ONE VPC; the microservice composition drops its vpc child and references the platform VPC via data source). Each L1 module ships a real terraform module dir (versions/variables/locals/main/outputs.tf) owning its resource shape, nested blocks, and defaults. locals.tf is used heavily to centralize default interpolation (D-099). Multi-resource modules get the full 5-file split; trivial single-resource modules may inline locals in main.tf. This is the binding constraint — the stateless adapter cannot be written until the reference s3 module exists (D-107: P56a proves the design with s3 first).
  • Territory: terraform/ (platform VPC, D-105), modules/l1/*/terraform/ (per-module terraform subdirs — the engine binding), modules/l1/*/interface.json (defaults move from adapter to interface inputs), modules/registry.json (terraform_dir field), modules/l2/microservice/composition.json (drop the vpc child, D-105), modules/STANDARDS.md §8 (rewrite the adapter extension pattern → per-module terraform subdir pattern).

general (lead-developer + backend-engineer pipeline work)

  • Domain: coordination + pipelines
  • Active: true
  • Phase-specific: false
  • Reason: Owns the pipeline-driven testing (D-102/D-103/D-104) and the terraform lifecycle modes (D-101). The modules-lifecycle pipeline (Gitea + GitHub, byte-identical) matrix-runs each L1 module's examples/{simple,complex}.yml contracts through apply→modify→destroy against live AWS. run_platform.sh gains --apply and --destroy modes; Python never runs terraform. verify_deploy_microservice.py is deleted (D-101). Co-authoring expected on the run_platform.sh boundary (backend-engineer rewrites the adapter that run_platform.sh invokes).
  • Territory: pipelines/modules-lifecycle.yml, .gitea/workflows/modules-lifecycle.yml + .github/workflows/modules-lifecycle.yml (byte-identical, D-102), scripts/run_platform.sh (--apply/--destroy modes, D-101), scripts/run_primitive_plan.sh (if extended for lifecycle), scripts/run_pattern_plan.sh (if extended), pipelines/README.md (document the new pipeline), schemas/deploy-pipeline.schema.json (if the lifecycle stages are added to the contract).

Deactivated personas

lambda-engineer (custom, v1.9 — deactivated for v1.11)

  • Domain: serverless
  • Active: false
  • Phase-specific: false
  • Reason: No per-module Python this milestone (D-102: testing is pipeline-driven, not pytest). The v1.9 Lambda (core/lambda/contract_ingestor.py) and the terraform/platform/main.tf Lambda/DynamoDB/KMS/Secrets definitions persist from v1.9 but are not touched in v1.11. The acdl-sod-halt SNS topic and the attestation matrix are out of scope. Removed from the roster for v1.11; reactivates if a future milestone touches the Lambda.

platform-engineer (custom, v1.9 — folded into data-engineer for v1.11)

  • Domain: infra
  • Active: false
  • Phase-specific: false
  • Reason: The v1.11 scope (D-097..D-107) is terraform module authoring + adapter rewrite + pipelines — not the v1.9-era L1/L2 IR-typed module authoring or the AWS OIDC bootstrap. The platform-engineer's v1.9 territory (adapters/terraform/**, modules/**, terraform/**) is split: the adapter goes to backend-engineer (rewrite), the per-module terraform subdirs + platform VPC go to data-engineer (the heaviest v1.11 work). Folded into data-engineer for v1.11; reactivates if a future milestone does IR-shaped module authoring or OIDC bootstrap work.

security-engineer (custom, v1.9 — deactivated for v1.11)

  • Domain: security
  • Active: false
  • Phase-specific: false
  • Reason: The v1.11 scope does not touch Wiz/Kyverno/Checkov adapters, the HITL matrix, separation-of-duties, or the audit ledger. The security-engineer's v1.9 territory persists but is not touched. Removed from the roster for v1.11; reactivates if a future milestone touches security adapters or HITL gates.

frontend-engineer

  • Domain: frontend
  • Active: false
  • Phase-specific: false
  • Reason: The evidence timeline UI (evidence-ui/**) is unchanged from v1.0 and not touched in v1.11. Removed from the active roster; reactivates if a future milestone touches the timeline UI.

data-engineer (v1.9 — was deactivated, reactivated for v1.11)

  • Domain: data
  • Active: true (reactivated)
  • Phase-specific: false
  • Reason: See the active data-engineer entry above. The v1.9 deactivation rationale ("No ORM/persistence framework") no longer applies — v1.11's data-engineer owns terraform module authoring, not a data persistence layer.

infra-stub-engineer (custom, v1.0 only)

  • Domain: backend
  • Active: false
  • Reason: Owned L1 stub modules in the v1.0 demo. The demo is archived to demo/; real L1 modules are owned by data-engineer (v1.11). Not reactivated.

Phase-specific overrides

Phase Personas active Notes
56a adapter-rewrite-and-s3-reference-module data-engineer (lead: s3 reference terraform module — proves the design), backend-engineer (lead: stateless adapter rewrite — emits module blocks for s3), general (run_platform.sh --apply/--destroy skeleton) security/lambda/frontend idle
56b remaining-11-l1-module-terraform-subdirs data-engineer (lead: author 11 L1 module terraform subdirs — vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf, rds, kms-key, uptime), backend-engineer (adapter: confirm each module round-trips through the assembler), general (modules-lifecycle pipeline wiring) security/lambda/frontend idle
(modules-lifecycle pipeline) general (lead: byte-identical Gitea+GitHub workflow + matrix apply→modify→destroy), data-engineer (examples/{simple,complex}.yml contracts as the modify variants), backend-engineer (adapter confirms the lifecycle cells resolve) security/lambda/frontend idle
(platform VPC + composition drop) data-engineer (lead: terraform/platform VPC + microservice composition drops vpc child, D-105), backend-engineer (resolver: env-aware state keys, D-106) general/security/lambda/frontend idle
verify lead-developer (lead: 4-layer verification), all active personas (review their territory)
review-audit-complete lead-developer (lead: review + audit + milestone completion), all active personas (review participation)

Domain priority (used by TaskDecomposer)

data → backend → general

Rationale: in v1.11, the terraform foundation (per-module terraform/ subdirs + platform VPC) is the binding constraint — the stateless adapter cannot be written until the reference s3 module exists (D-107: P56a proves the design with s3 first). Backend (adapter/resolver) follows once the module shape is proven. General (pipelines/workflows) wires the lifecycle modes last, once the adapter + modules produce valid terraform.

Conflict resolutions (lead-developer arbitration)

  • backend-engineer vs data-engineer over modules/l1/*/interface.json: data-engineer owns the interface defaults (defaults move from the adapter to the interface inputs, D-100); backend-engineer owns the adapter that reads them. Co-authoring is expected; conflict goes to lead-developer.
  • backend-engineer vs general over scripts/run_platform.sh: backend-engineer rewrites the adapter that run_platform.sh invokes; general adds the --apply/--destroy modes. The interface (the CLI flags + the adapter invocation) is co-authored; conflicts go to lead-developer.
  • data-engineer vs general over modules/l1/*/examples/: data-engineer owns the example contracts (the modify variants, D-103); general owns the pipeline that matrix-runs them. Co-authoring is expected; conflicts go to lead-developer.
  • lead-developer vs any: lead-developer owns .ciagent/** + docs/** meta + verification scripts + modules/STANDARDS.md §8 rewrite; persona engineers do not edit CIAgent metadata or the vision/architecture source docs.

Territory enforcement mode

warn — config.json has no personas.territory_enforcement field, so the default per execute.md is warn. Cross-territory edits are logged in the commit message but do not fail the task. v1.11's scope means co-authoring across territories is likely (e.g. backend + general on the adapter + run_platform.sh boundary; data + general on the examples + pipeline boundary); warn keeps it frictionless.

v1.15 Persona Addendum — Nova Rebrand (2026-07-30)

Milestone: v1.15-Nova. The roster carries forward from v1.11/v1.14 unchanged — the rebrand touches existing territories, no new domains. frontend-engineer remains deactivated (no UI; decks are markdown = lead-developer territory). No security-engineer persona is activated — the ABAC session-policy + tag-key migration (REQ-162) is data-engineer territory (terraform IAM) with lead-developer review.

v1.15 territory assignments

Phase Lead Contributors Territory
P1 docs-decks-prose lead-developer README.md, docs/**, .ciagent/*.md, deck .md/-marp.md/-talking-points.md/.html, docs/presentations/assets/mmd/*.mmd (+ PNG re-export), pyproject.toml, schemas/*.schema.json $id (D-110), docs/NOVA_MIGRATION.md, .github/workflows/release.yml title, modules/STANDARDS.md
P2 code-envvars-consumer-path backend-engineer lead-developer (docs/runbook) core/env.py (NEW dual-read helper, D-108), core/*.py (call-site migration), scripts/*.py + *.sh, adapters/**, tests/**, .gitea/workflows/** + .github/workflows/**, .env + .env.secrets (key rename), schemas/tagging-standard.json, adapters/terraform/policy/custom_rules/acdl_tagging.pynova_tagging.py (D-109: warn mode)
P3 ssm-tagkeys data-engineer backend-engineer (readers) core/output_publisher.py (SSM path /nova/), core/contract_resolver.py (SSM reads), scripts/migrate_ssm_paths.py (NEW), terraform/** (tag keys nova:*), adapters/terraform/policy/custom_rules/nova_tagging.py (D-109: hard mode), ABAC session-policy terraform
P4 aws-resource-migration data-engineer lead-developer (runbook) terraform/platform/main.tf, terraform/microservice/main.tf, terraform/ci-vpc/main.tf, terraform/bootstrap/**, modules/l1/alb/instance.json, scripts/migrate_dynamodb_data.py (NEW), docs/NOVA_AWS_MIGRATION.md (NEW runbook), core/lambda/contract_ingestor.py (default table names → nova-*, D-111)
P5 final-review-ship lead-developer all active (review) .ciagent/** (REQUIREMENTS/ROADMAP/PROJECT complete), core/env.py (remove dual-read fallback), nova_tagging.py (hard-fail acdl:*), review + audit

v1.15 domain priority

lead → backend → data (inverted from v1.11)

Rationale: the rebrand is docs/prose-first (P1 establishes the vocabulary, no runtime impact), then code/env-vars/consumer-path (P2), then SSM/tag-keys (P3), then the heavy terraform/AWS migration (P4). Lead-developer owns the docs + runbooks + verification + final ship; backend-engineer owns the dual-read helper + call-site migration + contract resolver; data-engineer owns the terraform resource/tag/SSM migration (the heaviest terraform territory). Co-authoring expected at: core/env.py + core/*.py boundary (backend + lead on the helper design), nova_tagging.py + schemas/tagging-standard.json boundary (backend authors the rule, data-engineer owns the tag-key schema), core/output_publisher.py SSM path + terraform outputs boundary (backend writes the reader, data-engineer owns the terraform that produces the outputs).

v1.15 verification toolchain (unchanged from v1.14)

typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
test:      bash scripts/run_regression.sh   # 16-capability gate
build:     bash scripts/run_ci.sh           # full local CI reproduction

The regression gate (CAP-001..CAP-016) must stay 16/16 Verified throughout the rebrand — the rebrand must not regress any capability. P2/P3/P4 update test fixtures that reference ACDL/acdl so the gate stays green.

v1.16 Persona Addendum — Nova Simplification (2026-07-30)

Milestone: v1.16-Nova-Simplification (NFR). Roster carries forward unchanged — NFR work touches existing territories, no new domains. The onboarding request-path (P18P20) is backend-engineer (Lambda action + onboarding.py) + data-engineer (cross-account Terraform) territory. frontend-engineer remains deactivated. No security-engineer persona — the ingestor defense-in-depth (P10) is backend-engineer with lead-developer review; IAM/ABAC (P20) is data-engineer territory.

v1.16 territory assignments

Phase Lead Contributors Territory
P1 state-bucket+kyverno fix backend-engineer data-engineer (kyverno policy) adapters/terraform/adapter.py:117, adapters/kyverno/policies/require-resource-labels.yml
P2 user-facing brand sweep lead-developer backend-engineer core/environment_check.py, core/lambda/contract_ingestor.py, scripts/post_stage_comment.sh, scripts/run_ci.sh, module docstrings, adapters/README.md
P3 dead-code+stale-prefix lead-developer scripts/run_platform.sh, core/local_emulators.py, core/regression_verify.py, lifecycle scripts
P4 migrate-ssm except backend-engineer scripts/migrate_ssm_paths.py
P5 regression-verify dedup backend-engineer core/regression_verify.py
P6 run-platform deadcode+hitl-fn lead-developer scripts/run_platform.sh
P7 contract-resolver envloader+kind backend-engineer core/contract_resolver.py, modules/registry.json
P8 workflow generator lead-developer backend-engineer (test) scripts/sync_workflows.py (NEW), tests/test_pipeline_contract.py, .gitea/workflows/**, .github/workflows/**
P9 run-platform split lead-developer scripts/run_platform.sh, scripts/run_decommission.sh (NEW), scripts/run_uptime.sh (NEW)
P10 ingestor defense-in-depth backend-engineer lead-developer (review) core/lambda/contract_ingestor.py, core/environments/
P11 ingestor payload validation backend-engineer core/lambda/contract_ingestor.py
P12 split contract-resolver backend-engineer core/contract_resolver.pycore/contract_resolve.py + core/decommission_transform.py + core/contract_resolver_cli.py
P13 split regression-verify backend-engineer core/regression_verify.py → split modules
P14 schema-driven outputs+cache backend-engineer data-engineer (interface.json) core/output_publisher.py, core/contract_resolver.py, modules/l1/*/interface.json
P15 run-platform --help+flags lead-developer scripts/run_platform.sh, README.md
P16 workflows README catalog lead-developer .github/workflows/README.md (NEW)
P17 getting-started consolidation lead-developer README.md
P18 onboarding schema+lambda backend-engineer lead-developer (schema) schemas/onboarding.schema.json (NEW), core/lambda/contract_ingestor.py
P19 onboarding envfile autogen backend-engineer lead-developer (docs) core/onboarding.py (NEW), core/environment_check.py, core/environments/README.md
P20 cross-account role offline data-engineer backend-engineer (ABAC) terraform/onboarding/ (NEW), terraform/platform/main.tf
P21 final-review-ship lead-developer all active (review) .ciagent/**, review + audit + ship

v1.16 domain priority

backend → lead → data (the simplification + security + ingestor work is backend-heavy; lead-developer owns docs/DX/splits; data-engineer owns the P20 cross-account Terraform only).

v1.16 verification toolchain

typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
test:      bash scripts/run_regression.sh   # 22-capability gate (D-118: P9 + P21)
build:     bash scripts/run_ci.sh           # full local CI reproduction

The regression gate (22 capabilities) must stay 22/22 Verified throughout v1.16 — simplification must not regress any capability (D-118). P9 (end of Wave 2) and P21 (milestone complete) run the gate; P14 (end of Wave 3) is an offline mid-milestone checkpoint.


v1.17 Persona Roster — Strategic Direction, Leadership Metrics & Unified Story

v1.17 adds a telemetry/observability layer (P1P3), a metrics catalog

  • NORTH_STAR integration (P4), a unified narrative deck (P5), a regression capability (P6), and a final review/ship (P7). Three active personas; frontend-engineer stays deactivated (no Nova web UI — dashboards are PowerBI, not a Nova-built frontend).

Active personas

lead-developer

  • Domain: coordination + deck narrative
  • Active: true
  • Phase-specific: false
  • Reason: Owns CIAgent metadata, the NORTH_STAR.md authoring process (P0), the milestone decomposition, the unified narrative deck co-authoring (P5 — the deck is markdown, which is lead-developer territory per the established convention), and the final review/ship (P7). Arbitrates persona conflicts (e.g., backend vs data on the emitter/store boundary).
  • Territory: .ciagent/NORTH_STAR.md, .ciagent/PROJECT.md, .ciagent/REQUIREMENTS.md, .ciagent/PLAN.md, .ciagent/RESEARCH.md, .ciagent/ARCHITECTURE.md, docs/presentations/nova-no-humans-platform.md (NEW — unified deck source of truth), docs/presentations/nova-no-humans-platform-marp.md, docs/presentations/nova-no-humans-platform-talking-points.md, docs/METRICS.md, docs/metrics/*.md (per-KPI definition docs).

backend-engineer

  • Domain: backend (event emitters + instrumentation)
  • Active: true
  • Phase-specific: false
  • Reason: Owns the event emitters (P1): the CloudEvents envelope, the per-run manifest writer, the outbox_writer.py extension to the SQLite Decision Ledger, the Infracost post-processor, the hitl_gates.py attestation event emission, the confidence_signal.py decision event emission, the checkov_adapter.py policy event emission, and the pytest --junitxml addopts change. Also owns the regression_verify.py CAP-023/024 additions (P6). The emitter work is the bridge between existing Nova components and the new metrics layer — it touches the code paths that already exist.
  • Territory: core/metrics/event_envelope.py (NEW), core/metrics/run_manifest.py (NEW), core/metrics/infracost_adapter.py (NEW), core/metrics/decision_ledger.py (NEW — extends outbox_writer), core/outbox_writer.py (extend to SQLite), core/hitl_gates.py (emit attestation.recorded), core/confidence_signal.py (emit ai.decision.made), adapters/terraform/policy/checkov_adapter.py (emit policy.evaluated), scripts/run_platform.sh (invoke manifest writer + Infracost), core/regression_verify.py (CAP-023/024), pyproject.toml (addopts --junitxml), tests/test_metrics_emitters.py (NEW), tests/test_decision_ledger.py (NEW).

data-engineer

  • Domain: data (schema, SQLite store, PowerBI export)
  • Active: true
  • Phase-specific: false
  • Reason: Reactivated with a new territory for v1.17: the metrics collector (P2) and the PowerBI export (P3). Owns the schema design (metrics_*.schema.json), the SQLite cold store (nova_metrics.db), the fact/dimension table design, the 8 deferred placeholder views, and the CSV/JSON export. The data-engineer's schema-first constraint applies: all event types and fact/dim tables have JSON Schema definitions before any code is written. The collector reads files + events → SQLite; the export reads SQLite → CSV/JSON. This is the heaviest data-territory work since v1.11's terraform modules.
  • Territory: core/metrics/collector.py (NEW), core/metrics/powerbi_export.py (NEW), schemas/metrics_*.schema.json (NEW — event + fact/dim schemas), metrics/nova_metrics.db (NEW — SQLite cold store), metrics/powerbi/ (NEW — CSV/JSON export dir), docs/METRICS_VIEWS.md (NEW — schema doc for PowerBI views), tests/test_metrics_collector.py (NEW), tests/test_powerbi_export.py (NEW).

Deactivated personas

frontend-engineer

  • Domain: frontend
  • Active: false
  • Phase-specific: false
  • Reason: v1.17 has no Nova web UI. The leadership dashboards are PowerBI (an external tool that ingests CSV/JSON files), not a Nova-built frontend. The decks are markdown (lead-developer territory). frontend-engineer stays deactivated, consistent with v1.11v1.16. Reactivates if a future milestone builds a Nova web UI.

lambda-engineer, platform-engineer, security-engineer

  • Active: false (carried forward from v1.11)
  • Reason: v1.17 does not touch the Lambda (beyond emitting events from the existing hitl_gates/attestation_matrix), does not do IR- shaped module authoring, and does not touch security adapters beyond emitting policy.evaluated events. The existing components are instrumented, not rewritten.

v1.17 phase assignment

Phase Primary persona Supporting Territory
P0 pre-execution lead-developer .ciagent/NORTH_STAR.md, PROJECT.md, REQUIREMENTS.md, RESEARCH.md, ARCHITECTURE.md, PERSONAS.md, PLAN.md
P1 event-emitters backend-engineer data-engineer (schemas) core/metrics/event_envelope.py, run_manifest.py, decision_ledger.py, infracost_adapter.py, outbox_writer.py, hitl_gates.py, confidence_signal.py, checkov_adapter.py, run_platform.sh, pyproject.toml
P2 metrics-collector data-engineer backend-engineer (event formats) core/metrics/collector.py, schemas/metrics_*.schema.json, metrics/nova_metrics.db
P3 powerbi-export data-engineer core/metrics/powerbi_export.py, metrics/powerbi/, docs/METRICS_VIEWS.md
P4 metrics-catalog + north-star-integration lead-developer data-engineer (metric definitions) docs/METRICS.md, docs/metrics/*.md, PROJECT.md, ARCHITECTURE.md, config.json
P5 deck-rebuild lead-developer docs/presentations/nova-no-humans-platform*.md, retire old decks
P6 regression-capability backend-engineer data-engineer (CAP-023 schema) core/regression_verify.py (CAP-023, CAP-024)
P7 final-review-ship lead-developer all active (review) .ciagent/**, review + audit + ship

v1.17 domain priority

backend → data → lead (the emitter work in P1 is the foundation; data-engineer's collector + export in P2P3 depends on P1's event formats; lead-developer's catalog + deck in P4P5 depends on the metrics being grounded).

v1.17 verification toolchain

typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
test:      bash scripts/run_regression.sh   # 22-capability gate + CAP-023/024 (v1.17)
build:     bash scripts/run_ci.sh           # full local CI reproduction

The regression gate (22 capabilities + CAP-023 metrics collector + CAP-024 deck structure) must pass at P6 and P7. CAP-009 (offline pytest suite) must remain Verified after the --junitxml addopts change (assumption A5).