Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2efed26bb6 | |||
| 5c07e29b90 | |||
| aa868c97ef | |||
| e4a9915891 | |||
| 0ca383dae6 | |||
| ed5ea90654 | |||
| 2273009b95 | |||
| 0d2cbdb423 | |||
| b418d429b5 | |||
| dcba380b52 | |||
| f0bc3be92c | |||
| 0b79b16715 | |||
| 90624be63f | |||
| be51fc15fa | |||
| e3f4ce17d4 | |||
| e0d01ad2ef | |||
| a4c5f332f6 | |||
| 9e20b7ba95 | |||
| 6da538c936 | |||
| 4e03817ea6 | |||
| 951ad56576 | |||
| d882cf0c6e | |||
| 564d4a4ca3 | |||
| c524ad731e | |||
| 8bcf7296d5 | |||
| 81c7a22ddd | |||
| 2c08c778a9 | |||
| 6ffcbe8283 | |||
| 5775a97388 | |||
| b3c75ccec1 | |||
| e891496163 | |||
| 382944c055 | |||
| 71b6a4fa91 | |||
| 0f677641ee | |||
| e3ebbc4978 | |||
| 37b6b6fc14 | |||
| d61a3d1a2f | |||
| 4c8b2b77fc | |||
| 1daae0ac0a | |||
| d048460abf | |||
| 0ad6a88c4b | |||
| eb5b24b88d | |||
| cb1a7071a7 | |||
| e4adb3f09e |
@@ -1,13 +1,11 @@
|
||||
{
|
||||
"phase": 0,
|
||||
"stage": "complete",
|
||||
"milestone": "v1.17",
|
||||
"stage": "plan",
|
||||
"milestone": "v1.21",
|
||||
"phase_role": "pre_execution",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-04T21:30:00Z",
|
||||
"updated_at": "2026-08-11T00:01:00Z",
|
||||
"milestone_complete": false,
|
||||
"tag": "v1.16.0",
|
||||
"release_id": 441,
|
||||
"requirements": ["REQ-185"],
|
||||
"notes": "Phase 0 complete. NORTH_STAR.md authored. 29 requirements (REQ-185..213). Telemetry reference architecture + metric scorecard. Deck rebuild plan (18 slides). Interactive GRILL: 12 binding decisions applied. Tag v1.16.0 pushed. Gitea release 441 created. Ready for execution phases P1..P7 + final P8."
|
||||
"requirements": ["REQ-245","REQ-246","REQ-247","REQ-248","REQ-249","REQ-250","REQ-251","REQ-252","REQ-253"],
|
||||
"notes": "v1.21 P0 plan stage complete. PLAN.md v1.21 section written. 5 execution phases (P1 strategic-docs, P2 slides, P3 marp+talking-points+README, P4 pipeline-hardening, P5 render+verify) + P6 final-review-ship. Wave 1 (P1/P2/P4 parallelizable), Wave 2 (P3), Wave 3 (P5), Wave 4 (P6). CLARIFY+RESEARCH minimal at full autonomy — domain known, requirements confirmed with user. Proceeding to P0 ship then execution."
|
||||
}
|
||||
+102
-351
@@ -1,33 +1,31 @@
|
||||
---
|
||||
project: acdl
|
||||
milestone: v1.17
|
||||
generated_at: 2026-08-04
|
||||
milestone: v1.18
|
||||
generated_at: 2026-08-06
|
||||
generator: lead-developer
|
||||
verification_toolchain:
|
||||
typecheck: "terraform validate && python3 -m py_compile core/**/*.py && python3 -m jsonschema schemas/*.schema.json"
|
||||
test: "bash scripts/run_regression.sh # 22-capability gate (D-091/D-118) + CAP-023/024 (v1.17)"
|
||||
build: "bash scripts/run_ci.sh # full local CI reproduction (lint+test+check-only)"
|
||||
typecheck: "python3 -m py_compile core/submission_readiness.py mcp/atelier/server.py && python3 -m jsonschema schemas/submission-readiness.schema.json"
|
||||
test: "pytest tests/test_submission_readiness.py tests/test_atelier_mcp.py # REQ-220 + REQ-225"
|
||||
build: "bash scripts/render_deck.sh docs/presentations/nova-no-humans-platform-marp.md # HTML + PPTX (D-142)"
|
||||
note: |
|
||||
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).
|
||||
v1.18 adds the Citizen Developer & Production-Grade Guidance surface:
|
||||
submission-readiness gate, Atelier-derived skills, the Atelier MCP server
|
||||
(plugin-registry, stdio), and PPTX-as-first-class-artifact deck automation.
|
||||
Three active personas: lead-developer (coordination + decks + RACI/scope
|
||||
docs), backend-engineer (MCP server + submission-readiness validator +
|
||||
render/attach scripts), data-engineer (submission-readiness schema if it
|
||||
touches contract storage / DynamoDB shape). frontend-engineer stays
|
||||
deactivated (v1.18 has no frontend; decks are markdown = lead-developer
|
||||
territory). The MCP plugin-registry is a backend pattern, so a separate
|
||||
mcp-engineer persona is NOT added — it folds into backend-engineer.
|
||||
---
|
||||
|
||||
# ACDL — Persona Roster (project-level, v1.11 RESTART)
|
||||
# ACDL — Persona Roster (v1.18 Citizen Developer & Production-Grade Guidance)
|
||||
|
||||
> 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).
|
||||
> v1.18 roster. Three active personas + one deactivated. The MCP server
|
||||
> plugin-registry (D-140) is a backend pattern, not a new persona — it
|
||||
> folds into backend-engineer. v1.17 precedent (frontend-engineer
|
||||
> deactivated, decks are markdown = lead-developer territory) is upheld.
|
||||
|
||||
## Active personas
|
||||
|
||||
@@ -35,351 +33,104 @@ verification_toolchain:
|
||||
- **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).
|
||||
- **Frameworks:** [] (no framework — owns process + narrative, not code)
|
||||
- **Constraints:** ["pragmatic", "battle-tested defaults", "no fabrication (NORTH_STAR honesty model)"]
|
||||
- **Territory:**
|
||||
- `docs/presentations/**` (Step 1/2/4 markdown + the deck automation trigger)
|
||||
- `.ciagent/**` (PROJECT, ROADMAP, REQUIREMENTS, RESEARCH, PLAN, GRILL, PERSONAS, REVIEW, CHECKPOINT)
|
||||
- `PROJECT.md` (RACI matrix + PDLC-scope statement, REQ-215/216)
|
||||
- `ROADMAP.md`
|
||||
- `REQUIREMENTS.md`
|
||||
- `docs/raci.md` (REQ-215)
|
||||
- `docs/scope.md` (REQ-216)
|
||||
- `docs/skills.md` (REQ-222 — the index page, not the skill files themselves)
|
||||
- `docs/submission-readiness.md` (REQ-219 — citizen-developer-facing copy; co-owned with backend-engineer for the reason-code catalog)
|
||||
- **Reason:** Owns CIAgent metadata, the milestone narrative, the RACI +
|
||||
PDLC-scope statements (REQ-215/216), the deck (21 slides, S&P theme
|
||||
regression check vs P1, CAP-024), the skills index page (REQ-222), and
|
||||
the citizen-developer-facing submission-readiness doc (REQ-219). Is
|
||||
the only persona that touches `.ciagent/**` and the deck markdown.
|
||||
- **Phase-specific flag:** none (active for all of P0–P7).
|
||||
|
||||
### 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).
|
||||
- **Frameworks:** ["mcp (Python SDK v2)", "pydantic", "jsonschema", "urllib"]
|
||||
- **Constraints:** ["api-first", "strict-typing", "plugin-registry extensible (D-140)", "stdio now / HTTP-ready (D-135)", "no stack traces to citizen developers (REQ-218)"]
|
||||
- **Territory:**
|
||||
- `mcp/atelier/server.py` (REQ-223)
|
||||
- `mcp/atelier/plugins/**/*.py` (REQ-223 — principles.py, validation.py)
|
||||
- `mcp/atelier/vendor/**` (REQ-224 — vendored Atelier snapshot)
|
||||
- `mcp/atelier/VERSION.md` + `mcp/atelier/README.md` (REQ-224)
|
||||
- `scripts/update_atelier_vendor.sh` (REQ-224)
|
||||
- `core/submission_readiness.py` (REQ-218 — the validator, invoked as `contract_ingestor.py --check-readiness`)
|
||||
- `scripts/render_deck.sh` (REQ-228 — HTML + PPTX render)
|
||||
- `scripts/attach_release_asset.py` (REQ-228 — Gitea release asset upload)
|
||||
- `tests/test_atelier_mcp.py` (REQ-225)
|
||||
- `tests/test_submission_readiness.py` (REQ-220)
|
||||
- `docs/submission-readiness.md` (REQ-219 — reason-code catalog section; co-owned with lead-developer for the narrative)
|
||||
- **Reason:** Owns the MCP server (plugin-registry, stdio, vendored
|
||||
Atelier), the submission-readiness validator (extends
|
||||
`contract_ingestor.py --check-readiness`, D-133), the render/attach
|
||||
scripts (D-142 trigger), and the two new test files. The MCP
|
||||
plugin-registry (D-140) is a backend pattern — no separate
|
||||
mcp-engineer persona is created; backend-engineer owns it.
|
||||
- **Phase-specific flag:** none (active for P1 deck-render, P3 validator,
|
||||
P5 MCP server, P6 scripts).
|
||||
|
||||
### 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.py` → `nova_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 (P18–P20) 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.py` → `core/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 (P1–P3), 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).
|
||||
- **Frameworks:** ["jsonschema", "dynamodb (item shape)"]
|
||||
- **Constraints:** ["schema-first", "superset-gate NOT duplicate (PROJECT.md hard constraint)", "W3.E per-env mandatory table is the source of truth"]
|
||||
- **Territory:**
|
||||
- `schemas/**` (REQ-217 — `submission-readiness.schema.json` is the new schema; existing schemas untouched)
|
||||
- `core/lambda/contract_ingestor.py` (the `--check-readiness` subcommand wiring, D-133 — the validator is in `core/submission_readiness.py` but the ingestor dispatches to it; co-owned with backend-engineer)
|
||||
- **Reason:** Owns the submission-readiness JSON Schema (REQ-217) — it
|
||||
is a schema artifact, data-engineer territory. The schema is a
|
||||
*superset gate above* `contract.schema.json`, not a duplicate (it
|
||||
references contract fields, does not redefine them). The
|
||||
per-env-mandatory table comes from W3.E (the locked decision). The
|
||||
ingestor wiring is co-owned with backend-engineer (the dispatch point
|
||||
is backend; the schema it validates against is data).
|
||||
- **Phase-specific flag:** none (active for P3 schema + ingestor wiring).
|
||||
|
||||
## 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.11–v1.16. Reactivates if a future milestone builds a Nova web UI.
|
||||
- **Domain:** frontend
|
||||
- **Frameworks:** ["react", "next.js"] (inert — no territory)
|
||||
- **Constraints:** ["component-first", "server-components", "minimal-client-js"] (inert)
|
||||
- **Territory:** [] (no territory in v1.18)
|
||||
- **Reason:** v1.18 has no frontend; decks are markdown (lead-developer
|
||||
territory); deactivated per PERSONAS.md v1.17 precedent. v1.18's
|
||||
observability stays PowerBI / external (Out of Scope: "A Nova-built
|
||||
frontend / dashboard"). The MCP server exposes tools to an AI agent,
|
||||
not a web UI. No reactivation trigger in this milestone.
|
||||
|
||||
### 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.
|
||||
## Roster decisions
|
||||
|
||||
## v1.17 phase assignment
|
||||
### D-143 (0.90): Fold mcp-engineer into backend-engineer
|
||||
The MCP plugin-registry (D-140: `plugins/<name>.py register(mcp)`) is a
|
||||
backend code pattern — Python modules, type hints, stdio transport,
|
||||
urllib for the Gitea asset API. It shares nothing with the data domain
|
||||
(schemas/DynamoDB) and is not a new engineering discipline. Creating a
|
||||
separate `mcp-engineer` persona would fragment ownership of the server +
|
||||
its tests + the render/attach scripts (all backend). **Decision:** fold
|
||||
into backend-engineer. backend-engineer's `frameworks` list gains
|
||||
`mcp (Python SDK v2)`. Confidence 0.90 — the only counter-argument is
|
||||
that MCP is a distinct protocol skill, but the SDK v2 API surface
|
||||
(`@mcp.tool()` + type hints) is small and well within backend-engineer's
|
||||
range (it's the same Pydantic/FastAPI-style pattern the persona already
|
||||
knows).
|
||||
|
||||
| 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 |
|
||||
### Territory-overlap resolution (co-ownership)
|
||||
|
||||
## v1.17 domain priority
|
||||
|
||||
`backend → data → lead` (the emitter work in P1 is the foundation;
|
||||
data-engineer's collector + export in P2–P3 depends on P1's event
|
||||
formats; lead-developer's catalog + deck in P4–P5 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).
|
||||
| Path | Primary | Co-owner | Why |
|
||||
|------|---------|----------|-----|
|
||||
| `docs/submission-readiness.md` | lead-developer (narrative + examples) | backend-engineer (reason-code catalog, REQ-218 codes) | The doc is citizen-developer-facing copy (lead) but the reason-code catalog (MISSING_TAGS, ENV_MISSING_MANDATORY, AGENTIC_MISSING_INTENT, MISSING_APP_SOURCE, POLICY_PRECONDITION_MISSING) is backend (it mirrors the validator's return codes). |
|
||||
| `core/lambda/contract_ingestor.py` | backend-engineer (dispatch wiring) | data-engineer (the schema it validates against) | D-133 places the `--check-readiness` subcommand on the ingestor (backend dispatch), but the readiness schema it loads is data-engineer territory. |
|
||||
| `schemas/submission-readiness.schema.json` | data-engineer (schema artifact) | backend-engineer (the validator must match it) | The schema is data-engineer's; the validator (REQ-218) is backend-engineer's and must stay in sync with it. |
|
||||
+1018
-1098
File diff suppressed because it is too large
Load Diff
+292
-7
@@ -58,6 +58,103 @@ traceable to a human attestation and an immutable evidence stream.
|
||||
boundary. The platform validates, enriches with operational standards,
|
||||
and reconciles the target state.
|
||||
|
||||
## Scope: Nova is Downstream of PDLC
|
||||
|
||||
> **Promoted from Core Tenet #2 + Anti-Goal #1 (v1.18, REQ-216).** This
|
||||
> is the unmissable scope statement — the PDLC is upstream, Nova is
|
||||
> downstream.
|
||||
|
||||
The **Product Development Lifecycle (PDLC)** — product backlog, code
|
||||
authorship, IDE workflows, sprint planning, application business logic —
|
||||
is **upstream** of Nova. Nova never penetrates the PDLC. Nova's domain is
|
||||
**infrastructure + delivery only**: environment progression, cloud
|
||||
resource lifecycle, operational security/observability NFRs, policy
|
||||
enforcement, immutable audit lineage, and the two consumer surfaces
|
||||
(technical developer + agentic).
|
||||
|
||||
Integration between the PDLC and Nova is **only** through the validated,
|
||||
published contract boundary (`schemas/contract.schema.json` +
|
||||
`schemas/submission-readiness.schema.json`). The citizen developer's AI
|
||||
coding agent, an upstream agentic SDLC platform, or any upstream
|
||||
development platform may all produce submissions — the source does not
|
||||
matter because all are subject to the same compliance standards (the
|
||||
submission-readiness gate, D-133). Nova validates, enriches with
|
||||
operational standards, and reconciles the target state. Nova never
|
||||
authors application code, manages product backlogs, or provides IDE
|
||||
workflows.
|
||||
|
||||
```
|
||||
PDLC (upstream) Nova (downstream)
|
||||
───────────────── ─────────────────
|
||||
product backlog contract ingestion
|
||||
code authorship (AI agent / IDE / SDLC) → submission-readiness gate
|
||||
sprint planning → policy enforcement
|
||||
application business logic → cloud resource lifecycle
|
||||
→ environment progression (dev→qa→prod→dr)
|
||||
→ immutable audit + attestation
|
||||
```
|
||||
|
||||
## RACI Matrix
|
||||
|
||||
> **Source of truth (v1.18, REQ-215, D-139).** Three roles clarify who
|
||||
> owns what across the Nova delivery lifecycle. The matrix is the
|
||||
> authoritative version; `docs/raci.md` is the citizen-developer-facing
|
||||
> copy.
|
||||
|
||||
### Roles
|
||||
|
||||
- **Citizen Developer (CD)** — the consumer (technical developer L3A or
|
||||
non-technical L3B). Responsible for all **Functional Requirements (FRs)**
|
||||
and **User Acceptance Testing (UAT)**. The FRs + UAT are produced via
|
||||
the citizen developer's AI coding agent, an upstream agentic SDLC, or
|
||||
an upstream development platform — **the source does not matter as all
|
||||
are subject to the same compliance standards** (the submission-readiness
|
||||
gate, D-133).
|
||||
- **Platform** — Nova. Responsible for all **Non-Functional Requirements
|
||||
(NFRs)**, **Infrastructure** (cloud resource lifecycle, state, IAM),
|
||||
**QA** (the platform-side quality checks: policy, confidence, schema),
|
||||
and **Production deployments to cloud** (the apply path, the pipeline,
|
||||
the release).
|
||||
- **Release Management (RM)** — **co-owned**. QA + SRE attestations are
|
||||
required by the actual release. The attestations are performed
|
||||
agentically (the platform runs the checks), but the release is
|
||||
**overseen and triggered by the Citizen Developer** — the human
|
||||
attestation at the stage gate (D-042, hitl_gates.py). The platform
|
||||
performs; the citizen developer authorizes.
|
||||
|
||||
### Matrix
|
||||
|
||||
| Work Category | Citizen Developer | Platform | Release Management |
|
||||
|---|---|---|---|
|
||||
| **Functional Requirements (FRs)** | **R/A** | C | I |
|
||||
| **User Acceptance Testing (UAT)** | **R/A** | C | I |
|
||||
| **Non-Functional Requirements (NFRs)** | I | **R/A** | C |
|
||||
| **Infrastructure (cloud, state, IAM)** | I | **R/A** | C |
|
||||
| **QA (policy, confidence, schema checks)** | C | **R/A** | I |
|
||||
| **Production deployment to cloud** | I | **R/A** | C |
|
||||
| **Release attestation (QA + SRE sign-off)** | **A** | R | **R** |
|
||||
|
||||
**Key: R** = Responsible (does the work) · **A** = Accountable (owns the
|
||||
outcome, sign-off) · **C** = Consulted · **I** = Informed.
|
||||
|
||||
**Compliance-standard equivalence note:** the citizen developer's FRs +
|
||||
UAT may originate from any upstream source — an AI coding agent, an
|
||||
agentic SDLC platform, or a traditional development platform. All are
|
||||
subject to the same compliance standards: the submission-readiness gate
|
||||
(`schemas/submission-readiness.schema.json`), the contract schema, the
|
||||
policy envelope, and the immutable audit stream. The platform does not
|
||||
differentiate by upstream source; it validates the submission, not the
|
||||
author.
|
||||
|
||||
**Co-ownership of Release Management:** the release is co-owned. The
|
||||
platform performs the QA + SRE attestations agentically (confidence signal,
|
||||
policy checks, separation-of-duties). The citizen developer oversees and
|
||||
triggers the actual release — the human attestation at the stage gate is
|
||||
the citizen developer's authorization, recorded with approver identity
|
||||
(D-042). The platform runs the checks; the citizen developer authorizes
|
||||
the promotion. This is the "autonomy in operations, human at stage gates"
|
||||
model from the NORTH_STAR.
|
||||
|
||||
## Capability Status (Re-Verified 2026-07-27)
|
||||
|
||||
> Source of truth: `.ciagent/CAPABILITY_INVENTORY.md` (Phase 54, D-093).
|
||||
@@ -592,12 +689,97 @@ DX: 16 total). Key changes:
|
||||
10. Old two-surfaces diagram replaced by scope boundary diagram.
|
||||
|
||||
Source markdown, talking points, and README all updated to mirror the new
|
||||
structure. Also includes scripts/sync_to_gl.sh (GitLab mirror sync
|
||||
utility, unrelated to presentations).
|
||||
structure. Also includes scripts/sync_to_nova.sh (manual-only "2nd release"
|
||||
into ~/nova — a separate GitLab consumer-facing repo with its own history;
|
||||
domain-based conventional commits, never triggered by CI; REQ-229).
|
||||
|
||||
No code changes; 494 tests pass; `run_ci.sh` + `run_platform.sh --check-only`
|
||||
green. PPTX files uploaded to Gitea release.
|
||||
|
||||
## Objective for Milestone v1.18 (active — Citizen Developer & Production-Grade Guidance)
|
||||
|
||||
v1.18 advances Nova from a platform that governs infrastructure delivery
|
||||
to one that **instructs the citizen developer on production-grade
|
||||
engineering** and defines a **clear, machine-checkable contract for what
|
||||
is acceptable to start**. Five user-directed inputs drive the milestone:
|
||||
|
||||
1. **S&P Global theme restoration.** The v1.17 P5 deck rebuild consolidated
|
||||
two decks into one unified narrative deck but lost the S&P Global Energy
|
||||
brand visual identity (introduced v1.9.2 / P45, commit `ae0cb58`). The
|
||||
Marp `style:` block (red-core `#D6002A`, grey-90 `#1B1B1B`, Akkurat Pro
|
||||
font, 8px top accent bar) is restored to the unified deck. The mermaid
|
||||
`sp-theme.json` survived; only the Marp CSS theme was lost.
|
||||
|
||||
2. **PDLC-upstream scope made explicit.** Core Tenet #2 already states the
|
||||
platform "does not penetrate upstream product/SDLC" and Anti-Goal #1 says
|
||||
"Not an upstream development platform." v1.18 promotes this from a
|
||||
buried tenet to a dedicated, unmissable scope statement in PROJECT.md +
|
||||
`docs/scope.md` + a deck slide: **the PDLC (Product Development
|
||||
Lifecycle — product backlog, code authorship, IDE) is upstream of Nova;
|
||||
Nova governs infra + delivery only; integration is through the validated
|
||||
contract boundary.**
|
||||
|
||||
3. **RACI matrix.** A three-role responsibility matrix clarifies who owns
|
||||
what: **Citizen Developer** (Responsible for all Functional Requirements
|
||||
+ User Acceptance Testing, via their AI coding agent / upstream agentic
|
||||
SDLC / upstream development platform — the source does not matter as all
|
||||
are subject to the same compliance standards), **Platform** (Responsible
|
||||
for all NFRs + Infrastructure + QA + Production deployments to cloud),
|
||||
**Release Management** (co-owned: QA + SRE attestations required by the
|
||||
actual release, performed agentically but overseen & triggered by the
|
||||
Citizen Developer). Source of truth in PROJECT.md + `docs/raci.md` + a
|
||||
deck slide.
|
||||
|
||||
4. **Nova input contract — "what is acceptable to start."** A JSON Schema
|
||||
(`schemas/submission-readiness.schema.json`) defines the
|
||||
acceptable-to-start gate as a superset *above* contract-schema validity:
|
||||
schema-valid contract + required Nova tags + per-env mandatory metadata
|
||||
(per W3.E) + declared policy preconditions + (for L3B) `profile:agentic`
|
||||
markers + `appSource` pointer. A validator (`core/submission_readiness.py`,
|
||||
invoked as `contract_ingestor.py --check-readiness`) returns a structured
|
||||
`ReadinessResult` with reason codes. On fail → citizen-developer-facing
|
||||
error (not a stack trace); on pass → proceeds to existing ingestion.
|
||||
|
||||
5. **Atelier integration — production-grade guidance + agentic validation.**
|
||||
Nova consumes `coreci/atelier` (a first-principles docs-as-code
|
||||
engineering framework — 8 core principles, 19 domains, 190 P-rules) via
|
||||
two surfaces: **skills** (markdown files under `skills/` keyed to Atelier
|
||||
domain paths, surfaced to the citizen developer's AI agent, extending the
|
||||
BA.A 5-skill catalog) and an **MCP server** (`mcp/atelier/server.py`,
|
||||
plugin-registry architecture, stdio transport, vendored Atelier snapshot
|
||||
for audit reproducibility) exposing tools for principle-lookup,
|
||||
domain-listing, matrix-lookup, and agentic validation against the
|
||||
Atelier agent-checklist — validation that goes beyond deterministic
|
||||
scanners (Wiz/Checkmarx/Mend) by catching correctness/clarity/simplicity/
|
||||
observability gaps.
|
||||
|
||||
**Deck automation (cross-cutting):** any phase modifying
|
||||
`docs/presentations/*-marp.md` or `docs/presentations/assets/` MUST
|
||||
re-render HTML + PPTX, **commit the PPTX to git** (binary, no LFS), and
|
||||
attach it to the phase's Gitea release. New scripts:
|
||||
`scripts/render_deck.sh` (HTML + PPTX render) and
|
||||
`scripts/attach_release_asset.py` (Gitea release asset upload).
|
||||
|
||||
**Milestone type:** Feature (P1 S&P theme restoration + P3 readiness
|
||||
schema/validator + P5 MCP server are new code/features). Tags run on the
|
||||
**v1.17.x** patch line (previous minor per branch-strategy): `v1.17.0` (P0)
|
||||
→ `v1.17.1..v1.17.6` (P1–P6) → `v1.17.7` (P7 final = milestone release).
|
||||
|
||||
**Phase count:** 8 (P0 pre-execution + 6 execution + 1 final).
|
||||
|
||||
**Hard constraints:**
|
||||
- DO NOT make anything up (NORTH_STAR.md honesty model).
|
||||
- The submission-readiness schema is a superset gate above
|
||||
`contract.schema.json`, NOT a duplicate — it references but does not
|
||||
redefine contract fields.
|
||||
- The MCP server is plugin-registry extensible (future capabilities drop
|
||||
in as new plugin files, no `server.py` edits).
|
||||
- Atelier is vendored (pinned tag) for audit reproducibility — an agentic
|
||||
validation result must be replayable against the exact principles that
|
||||
produced it.
|
||||
- PPTX is a first-class artifact: committed (history) + attached (download)
|
||||
— both always, not optional.
|
||||
|
||||
## Requirements
|
||||
|
||||
### v1.0 (Prior milestone — the demo)
|
||||
@@ -799,7 +981,7 @@ or user-directed scope). New v1.7 decisions:
|
||||
| W1.A | AI-refinement trigger | **Accept recommendation.** Joint condition: N ≥ 50 consecutive changes with zero rollbacks AND no L1/L2 incident in last 6 months AND Infra & Ops unilateral override. |
|
||||
| W1.B | Multi-stack edge case rule | **Accept recommendation.** Permitted only for (a) DR-region mirror, (b) time-boxed experimental stack with TTL ≤ 30d, (c) explicit Infra & Ops approval with `multiStack.justification`. |
|
||||
| W2.A | Tag mutability for prod | **Accept recommendation (Path B).** Tag for dev/qa, SHA for prod. Platform CLI resolves tag→SHA for prod-bound workflows. Justified by the "Audit truth lives outside the repository" bet. |
|
||||
| BA.A | Initial L3B skill catalog | **Accept recommendation.** 5 skills: web API, worker, scheduled job, static asset, basic observability bootstrap. Addition criteria: (a) reviewable for sensitive data, (b) expressible as a single contract submission, (c) documented use case. |
|
||||
| BA.A | Initial L3B skill catalog | **Accept recommendation.** 5 skills: web API, worker, scheduled job, static asset, basic observability bootstrap. Addition criteria: (a) reviewable for sensitive data, (b) expressible as a single contract submission, (c) documented use case. **Extended v1.18 (REQ-221/222):** the BA.A 5-skill catalog is extended with 9 Atelier-derived production-grade engineering skills under `skills/` (api, security, data, testing, observability, errors, devops, infrastructure-as-code, compliance), indexed by `docs/skills.md`. The Atelier skills extend, not replace, the BA.A catalog. |
|
||||
| W3.D | L1/L2 standard versioning | **Decided.** Semver: interface → MAJOR, behavior → MINOR, lifecycle → PATCH (same as the v1.0 demo D-rule, lifted to the real platform). Pin model: L2 contracts pin L1 by `name@semver`; the resolver picks the highest compatible. Evolution: MAJOR bumps require a new registry entry (immutable publication); old entry enters a 12-month deprecation window. |
|
||||
| W3.E | Schema mandatory vs optional inputs | **Decided.** Per-env mandatory table: dev requires `stack` + `environment`; qa adds `validation.e2eSuite` + `validation.loadTest`; prod adds `runbook` + `dashboard` + `oncall`; dr adds `drDrillRef`. `inputs` map is always optional. `profile: agentic` fields (`naturalLanguageIntent`, `confidenceAtSubmission`, `agentTrace`) optional everywhere. |
|
||||
| BA.B | Confidence threshold tuning | **Decided.** Starting thresholds frozen for v1. Tuning begins in v1.2: track FP/FN per environment quarterly; override authority = Infra & Ops + SRE joint sign-off; any override is itself a confidence-event in the audit stream. |
|
||||
@@ -1084,16 +1266,29 @@ P7 review+audit+ship). Tags on the v1.16.x line: `v1.16.0` (P0) →
|
||||
|
||||
- **Pillar A — Strategic Direction.** A durable, PO-authored
|
||||
`.ciagent/NORTH_STAR.md` encodes the platform's vision, 4 strategic
|
||||
objectives, 5 anti-goals, v1.17 non-goals, 12–18mo targets (with a
|
||||
objectives, anti-goals, v1.17 non-goals, 12–18mo targets (with a
|
||||
grounding column), and success criteria. CIAgent reads it in every
|
||||
future `/ci-run` so the direction survives across milestones. The
|
||||
attestation clarification is reflected: human attestation required at
|
||||
stage gates (QA for production, SRE for operational readiness); autonomy
|
||||
in operations, not in accountability.
|
||||
in operations, not in accountability. **v1.21 refinement:** Strategic
|
||||
Objective #4 reframed from "default substrate for agentic consumption" to
|
||||
integrating with externally owned PDLC/SDLC/Agentic/Citizen Developer
|
||||
platforms regardless of source (Nova provides skills + MCP endpoints;
|
||||
all prod intents go through the same controls). Objective #2 reworded:
|
||||
trust is established by deterministic scripts that calculate a score —
|
||||
the platform functions without AI. Objective #3 reworded with four
|
||||
CTO-grade metrics (Lead Time PR→Prod, Infrastructure Vulnerability
|
||||
Count trend, MTTR, Cloud Spend Reduction) all flowing into PowerBI.
|
||||
Anti-goals #1, #4, #5 removed; replaced with "not an upstream
|
||||
development platform" and "not a replacement for the PDLC".
|
||||
|
||||
- **Pillar B — Leadership Metrics + PowerBI.** Instrument Nova to
|
||||
collect, aggregate, and surface leadership-grade metrics that prove the
|
||||
"no-humans" autonomous-infrastructure value proposition. Nova-native
|
||||
"no-humans" autonomous-infrastructure value proposition (reframed in
|
||||
v1.21 to "autonomous cloud delivery" — professional framing; the
|
||||
platform delivers safe production deployment without an operator in
|
||||
the loop of normal operations). Nova-native
|
||||
minimal tech (CloudEvents 1.0 envelope, JSONL event log, SQLite cold
|
||||
store, hash-chained Decision Ledger via `outbox_writer.py` extension)
|
||||
+ Infracost for pre-apply cost estimates. Hybrid model: existing
|
||||
@@ -1133,4 +1328,94 @@ P7 review+audit+ship). Tags on the v1.16.x line: `v1.16.0` (P0) →
|
||||
| D-129 | PowerBI delivery = CSV/JSON files, folder connector. | Nova is offline-first; no live connector to a running service. PowerBI ingests via the folder connector. | P3 emits CSV/JSON to metrics/powerbi/. |
|
||||
| D-130 | Deck arc = Problem → Vision → How → Proof → Roadmap. | The unified narrative deck's 5-act structure. x3 arc at deck + slide level. Per-slide benefit callouts. Fluid transitions. Both old decks retired. | P5 builds the unified deck; old decks deleted. |
|
||||
| D-131 | MTTR scope = platform-run MTTR. | The <60s MTTR target refers to platform-run failures (apply.failed → successful retry), not infra-incident MTTR (no incident detection system). Infra-incident MTTR deferred. | P4 grounds platform-run MTTR. |
|
||||
| D-132 | Attestation instrumentation = emit attestation.recorded events. | The attestation system (hitl_gates.py + attestation_matrix.py + separation_of_duties.py) already exists. Instrument it: emit attestation.recorded events into the Decision Ledger + PowerBI. Attestation Coverage = 100% target grounded from outbox approver_* attributes. | P1 emits attestation events; P4 grounds Attestation Coverage. |
|
||||
| D-132 | Attestation instrumentation = emit attestation.recorded events. | The attestation system (hitl_gates.py + attestation_matrix.py + separation_of_duties.py) already exists. Instrument it: emit attestation.recorded events into the Decision Ledger + PowerBI. Attestation Coverage = 100% target grounded from outbox approver_* attributes. | P1 emits attestation events; P4 grounds Attestation Coverage. |
|
||||
|
||||
## Key Decisions (v1.18)
|
||||
|
||||
Resolved at the CLARIFY stage (full autonomy — all within locked
|
||||
constraints or user-directed scope). New v1.18 decisions:
|
||||
|
||||
| ID | Decision | Rationale | Outcome |
|
||||
|----|----------|-----------|---------|
|
||||
| D-133 | Submission-readiness validator location = extend `contract_ingestor.py --check-readiness`. | Adding a new CLI binary is unnecessary; the ingestor is the existing entry point for contract submission. The validator is a subcommand that runs before ingestion proceeds. No new binary, no new entry point to maintain. | P3 implements the subcommand; no new CLI binary. |
|
||||
| D-134 | Deck slide budget = 18 → 21 slides (no act restructure). | The 3 new slides (scope/RACI/atelier) are leadership-relevant and append after the existing 18. The 5-act arc (D-130) is preserved; the new slides are append-only context, not a new act. | P6 appends 3 slides → 21 total. |
|
||||
| D-135 | Atelier MCP transport = stdio now; HTTP-ready (same server object). | stdio is the local-agent transport (the citizen developer's AI agent spawns the server as a subprocess). The MCP Python SDK v2 supports Streamable HTTP on the same `MCPServer` object, so adding HTTP later is a transport-only change in `server.py`, not a rewrite. | P5 ships stdio; HTTP deferred (documented in README). |
|
||||
| D-136 | Atelier source = vendor pinned tag under `mcp/atelier/vendor/`. | An agentic validation result is only reproducible if the principles that produced it are pinned. Live-fetch breaks replayability (Atelier `main` drifts). Vendoring matches the v1.16 P15 offline-first precedent and the Nova thesis (provable trust). `mcp/atelier/vendor/VERSION.md` records the pinned tag; `scripts/update_atelier_vendor.sh` is the intentional upgrade path. | P5 vendors Atelier; live-fetch not implemented. |
|
||||
| D-137 | MCP server language = Python (MCP Python SDK v2, `modelcontextprotocol/python-sdk`). | Nova's `core/` is Python. The MCP Python SDK v2 (23.9k stars, MIT, stable) matches the codebase; type hints become JSON Schema automatically (`@mcp.tool()` decorator). | P5 uses Python SDK v2. |
|
||||
| D-138 | Skill catalog format = markdown files under `skills/` keyed to Atelier domain paths. | Markdown is the established Nova docs format (Jekyll Pages, 4-step deck process). Each skill file names the Atelier source path, distills the first-principles, links to agent-checklist triggers, and maps to the BA.A catalog. | P4 authors 9 markdown skill files. |
|
||||
| D-139 | RACI role names = Citizen Developer / Platform / Release Management (co-owned). | User-specified. The 3 roles are the columns of the RACI table. Release Management is co-owned: QA + SRE attestations are required by the actual release (performed agentically, overseen & triggered by the Citizen Developer). | P2 authors the RACI with these 3 roles. |
|
||||
| D-140 | MCP server extensibility = plugin-registry (`plugins/<name>.py` implementing `register(mcp)`). | Future capabilities (new scanners, policy evaluators, cost tools) drop in as new plugin files — no `server.py` edits. `server.py` scans `plugins/` and calls `register` on each. This is the extensibility insurance: plugins are decoupled from the server entrypoint. | P5 implements the plugin-registry; initial plugins are `principles.py` + `validation.py`. |
|
||||
| D-141 | PPTX storage = commit binary directly to `docs/presentations/` (no LFS). | Decks are small (~1-5 MiB); git handles binary blobs. LFS requires server-side support (unverified for git.cloudinit.dev) + client config. Committing directly is simplest and works without any repo/server config. Binary diffs are not delta-friendly, but deck changes are infrequent. | P1/P2/P6 commit .pptx directly. |
|
||||
| D-142 | Deck render trigger = any phase modifying `docs/presentations/*-marp.md` or `docs/presentations/assets/` must re-render HTML + PPTX, commit PPTX, and attach to the Gitea release. | PPTX was previously manual + release-only (not committed). v1.18 makes it a first-class artifact: committed (history) + attached (download), both always, not optional. Automated via `scripts/render_deck.sh` + `scripts/attach_release_asset.py`. | P1/P2/P6 run the render+commit+attach pipeline. |
|
||||
## Objective for Milestone v1.19 (complete — Nova 2nd-Release Sync)
|
||||
|
||||
> **NFR-only chore milestone.** Ships a patch on the v1.18.x line (tag
|
||||
> `v1.18.0`). Single execution phase. Establishes the manual-only "2nd
|
||||
> release" pipeline from `~/acdl` (CIAgent-managed source of truth, full audit
|
||||
> trail) into `~/nova` (GitLab `jonathanchery/nova` — separate repo, separate
|
||||
> history, consumer / platform-team audience).
|
||||
|
||||
### Why
|
||||
|
||||
`~/acdl` is the engineering source of truth and carries the full CIAgent
|
||||
audit trail (`.ciagent/`, milestone branches, `---ci---` blocks, Gitea
|
||||
releases). Consumers and the platform team should consume a clean,
|
||||
conventional-commit-shaped tree without the CIAgent plumbing. The old
|
||||
`scripts/sync_to_gl.sh` mirrored `~/acdl → ~/gl/acdl` with a single
|
||||
kitchen-sink `chore: sync from source mirror <ts>` commit — wrong audience,
|
||||
wrong commit standard, wrong repo.
|
||||
|
||||
### What
|
||||
|
||||
- **`scripts/sync_to_nova.sh`** replaces `scripts/sync_to_gl.sh`.
|
||||
- **Manual-only gate**: refuses without `--release` / `RELEASE_CONFIRMED=1`
|
||||
(exit 2). Never triggerable by CI.
|
||||
- **Consumer subset only**: excludes `.ciagent/`, `.gitea/`, `.env*`,
|
||||
`terraform/`, `demo/`, runtime metrics artifacts, and internal-only scripts
|
||||
(the `EXCLUDE_SCRIPTS` list — CIAgent/ops/release plumbing). Keeps
|
||||
consumer-facing runbooks (`run_ci.sh`, `run_platform.sh`, etc.) and the
|
||||
metrics export views (`metrics/README.md`, `powerbi/`, `TRUST_SNAPSHOT.md`).
|
||||
- **Destination history protected**: rsync `--filter=P .git` ensures
|
||||
`~/nova/.git` is never touched.
|
||||
- **Domain-based commits**: 13 fixed-order domains (config → core → adapters
|
||||
→ modules → contracts → schemas → pipelines → mcp → skills → scripts →
|
||||
tests → docs → workflows). Each changed domain gets its own conventional
|
||||
commit, supplied positionally via repeated `-m` flags. No kitchen-sink.
|
||||
- **Conventional-commit validation**: regex-enforced
|
||||
(`feat|fix|docs|chore|refactor|perf|test|build|ci|style|revert`); bypass via
|
||||
`--no-verify-format`.
|
||||
- **Modes**: `--list-domains` (print order), `--dry-run` (preview rsync +
|
||||
messages), `--no-push` (commit without pushing), `-v` (verbose).
|
||||
|
||||
### Out of Scope
|
||||
|
||||
- **coreci / Atelier review gate on the synced tree** — deferred. A future
|
||||
milestone may run a vendored-Atelier review pass before commit and block on
|
||||
P0 findings.
|
||||
- **Tagging releases on the `~/nova` side** — could add `--tag <semver>`
|
||||
later.
|
||||
- **Deleting `~/gl`** — the old mirror dir is left on disk; only the sync
|
||||
script targeting it is removed.
|
||||
|
||||
### Requirements
|
||||
|
||||
- **REQ-229** — `scripts/sync_to_nova.sh` replaces `sync_to_gl.sh` with the
|
||||
manual-only, consumer-subset, domain-committed 2nd-release pipeline
|
||||
described above. (Phase P1)
|
||||
|
||||
### Phase Plan
|
||||
|
||||
| Phase | Name | Status |
|
||||
|-------|------|--------|
|
||||
| P1 | nova-sync-script | complete |
|
||||
| P2 | final-review-ship | pending |
|
||||
|
||||
### Decisions
|
||||
|
||||
| ID | Decision | Rationale | Outcome |
|
||||
|----|----------|-----------|---------|
|
||||
| D-143 | 2nd release target = `~/nova` (separate GitLab repo), not `~/gl/acdl`. | `~/nova` is consumer/platform-team-facing with its own history; `~/gl/acdl` was an internal mirror with a kitchen-sink commit standard. Separate audience → separate repo → separate commit standard. | `sync_to_nova.sh` targets `~/nova`; `sync_to_gl.sh` removed. |
|
||||
| D-144 | Commit standard for `~/nova` = real conventional commits per domain (not the `---ci---` audit blocks used in `~/acdl`). | `~/acdl` commits carry CIAgent audit metadata (`---ci---` blocks) for the ciagent auditing workflow; that's noise for platform consumers. `~/nova` gets clean `feat/fix/docs/chore(scope): subject` commits grouped by domain. | Script validates conventional format; domain-based commits via positional `-m`. |
|
||||
| D-145 | Trigger = manual-only (`--release` / `RELEASE_CONFIRMED=1`). | The 2nd release is a deliberate human action, not a CI side-effect. The gate guarantees it can never fire from Gitea Actions, GitHub Actions, or accidental invocation. | Script exits 2 without `--release`. |
|
||||
| D-146 | Domain grouping = 13 fixed-order domains by path prefix; messages map positionally over CHANGED domains only. | Avoids the kitchen-sink commit; gives `~/nova` a reviewable, conventional history tailored to platform consumers. Positional-over-changed mapping lets the human supply exactly the messages needed, in domain order, without padding for unchanged domains. | `--list-domains` prints order; `--dry-run` previews; count-mismatch errors clearly. |
|
||||
| D-147 | coreci / Atelier review gate = deferred this milestone. | The vendored Atelier (`mcp/atelier/vendor`) could review the synced tree before commit and block on P0, but that's an additive hardening step, not part of establishing the pipeline. Deferred to a future milestone. | Sync ships consumer contents as-is; no review gate. |
|
||||
|
||||
+565
-29
@@ -1132,35 +1132,35 @@ with documented schemas.
|
||||
|
||||
| Requirement | Phase | Status |
|
||||
|-------------|-------|--------|
|
||||
| REQ-185 | P0 | in_progress |
|
||||
| REQ-186 | P4 | pending |
|
||||
| REQ-187 | P1 | pending |
|
||||
| REQ-188 | P1 | pending |
|
||||
| REQ-189 | P2 | pending |
|
||||
| REQ-190 | P3 | pending |
|
||||
| REQ-191 | P4 | pending |
|
||||
| REQ-192 | P4 | pending |
|
||||
| REQ-193 | P4 | pending |
|
||||
| REQ-194 | P4 | pending |
|
||||
| REQ-195 | P4 | pending |
|
||||
| REQ-196 | P5 | pending |
|
||||
| REQ-197 | P5 | pending |
|
||||
| REQ-198 | P6 | pending |
|
||||
| REQ-199 | P3 | pending |
|
||||
| REQ-200 | P2 | pending |
|
||||
| REQ-201 | P2 | pending |
|
||||
| REQ-202 | P5 | pending |
|
||||
| REQ-203 | P5 | pending |
|
||||
| REQ-204 | P4 | pending |
|
||||
| REQ-205 | P1+P2+P3 | pending |
|
||||
| REQ-206 | P1+P2 | pending |
|
||||
| REQ-207 | P2 | pending |
|
||||
| REQ-208 | P3 | pending |
|
||||
| REQ-209 | P3/P4 | pending |
|
||||
| REQ-210 | P4 | pending |
|
||||
| REQ-211 | P4 | pending |
|
||||
| REQ-212 | P4 | pending |
|
||||
| REQ-213 | P4/P5 | pending |
|
||||
| REQ-185 | P0 | complete |
|
||||
| REQ-186 | P4 | complete |
|
||||
| REQ-187 | P1 | complete |
|
||||
| REQ-188 | P1 | complete |
|
||||
| REQ-189 | P2 | complete |
|
||||
| REQ-190 | P3 | complete |
|
||||
| REQ-191 | P4 | complete |
|
||||
| REQ-192 | P4 | complete |
|
||||
| REQ-193 | P4 | complete |
|
||||
| REQ-194 | P4 | complete |
|
||||
| REQ-195 | P4 | complete |
|
||||
| REQ-196 | P5 | complete |
|
||||
| REQ-197 | P5 | complete |
|
||||
| REQ-198 | P6 | complete |
|
||||
| REQ-199 | P3 | complete |
|
||||
| REQ-200 | P2 | complete |
|
||||
| REQ-201 | P2 | complete |
|
||||
| REQ-202 | P5 | complete |
|
||||
| REQ-203 | P5 | complete |
|
||||
| REQ-204 | P4 | complete |
|
||||
| REQ-205 | P1+P2+P3 | complete |
|
||||
| REQ-206 | P1+P2 | complete |
|
||||
| REQ-207 | P2 | complete |
|
||||
| REQ-208 | P3 | complete |
|
||||
| REQ-209 | P3/P4 | complete |
|
||||
| REQ-210 | P4 | complete |
|
||||
| REQ-211 | P4 | complete |
|
||||
| REQ-212 | P4 | complete |
|
||||
| REQ-213 | P4/P5 | complete |
|
||||
|
||||
### Out of Scope (v1.17)
|
||||
|
||||
@@ -1180,3 +1180,539 @@ with documented schemas.
|
||||
- A third deck — the two existing decks merge into one; no new
|
||||
standalone metrics deck.
|
||||
- A Nova web UI — dashboards are PowerBI, not a Nova-built frontend.
|
||||
|
||||
---
|
||||
|
||||
## v1.18 — Citizen Developer & Production-Grade Guidance
|
||||
|
||||
> **Milestone type:** Feature. Tags run on the v1.17.x patch line (previous
|
||||
> minor per branch-strategy). `v1.17.0` (P0) → `v1.17.1..v1.17.6` (P1–P6) →
|
||||
> `v1.17.7` (P7 final = milestone release).
|
||||
> **Active milestone:** v1.18. **Branch:**
|
||||
> `milestone/v1.18-citizen-developer-guidance`.
|
||||
|
||||
### Requirements
|
||||
|
||||
- **REQ-214** — S&P Global Energy Marp theme restored in the unified deck
|
||||
(`docs/presentations/nova-no-humans-platform-marp.md`). The `style:` block
|
||||
from commit `ae0cb58` (v1.9.2 / P45) is ported: H1/H2 `#D6002A`
|
||||
(S&P red-core), title-slide bg `#1B1B1B` (grey-90) with 8px `#D6002A` top
|
||||
accent bar, body text `#1B1B1B`, blockquote border `#D6002A`,
|
||||
table headers `#F0F0F0`, font `'Akkurat Pro'` with web-safe fallbacks. The
|
||||
current Nova header/footer text is preserved (rebrand is not touched —
|
||||
only the visual theme is restored). HTML re-rendered with the S&P theme.
|
||||
|
||||
- **REQ-215** — RACI matrix authored in `PROJECT.md` (new `## RACI Matrix`
|
||||
section) and `docs/raci.md` (citizen-developer-facing copy). Three roles:
|
||||
**Citizen Developer** (Responsible for all Functional Requirements + User
|
||||
Acceptance Testing — via their AI coding agent / upstream agentic SDLC /
|
||||
upstream development platform; the source does not matter as all are
|
||||
subject to the same compliance standards), **Platform** (Responsible for
|
||||
all NFRs + Infrastructure + QA + Production deployments to cloud),
|
||||
**Release Management** (co-owned: QA + SRE attestations required by the
|
||||
actual release, performed agentically but overseen & triggered by the
|
||||
Citizen Developer). Rendered as a table: rows = work categories (FRs, UAT,
|
||||
NFRs, Infra, QA, Prod deploy, Release attestation), columns = R/A/C/I per
|
||||
role. Includes the compliance-standard-equivalence note.
|
||||
|
||||
- **REQ-216** — PDLC-upstream scope statement made explicit in `PROJECT.md`
|
||||
(new `## Scope: Nova is Downstream of PDLC` subsection under Domain
|
||||
Boundaries) and `docs/scope.md`. States that the PDLC (Product Development
|
||||
Lifecycle — product backlog, code authorship, IDE) is upstream of Nova;
|
||||
Nova governs infra + delivery only; integration is through the validated
|
||||
contract boundary. Promotes Core Tenet #2 + Anti-Goal #1 from buried
|
||||
tenets to a dedicated, unmissable scope statement.
|
||||
|
||||
- **REQ-217** — `schemas/submission-readiness.schema.json` (JSON Schema
|
||||
draft 2020-12) defines what is acceptable to start — a superset gate
|
||||
*above* `contract.schema.json` validity. Required fields: `contractId`
|
||||
(non-empty), `environment` (dev/qa/prod/dr) with the W3.E per-env mandatory
|
||||
table enforced (dev: stack+environment; qa: +validation.e2eSuite
|
||||
+validation.loadTest; prod: +runbook+dashboard+oncall; dr: +drDrillRef),
|
||||
`tags` (the 5 required Nova tags per D-054: `nova:owner`, `nova:contract`,
|
||||
`nova:environment`, `nova:cost-center`, `nova:ref`), `policyPreconditions`
|
||||
(declared policy expectations the platform will enforce, e.g.,
|
||||
`public-ingress: false`), `profile` (`developer` or `agentic`; if
|
||||
`agentic`, requires `naturalLanguageIntent`, `confidenceAtSubmission`,
|
||||
`agentTrace` per REQ-22 / W3.E), `appSource` (repo + ref pointer for
|
||||
runtime fetch).
|
||||
|
||||
- **REQ-218** — `core/submission_readiness.py` validator, invoked as
|
||||
`contract_ingestor.py --check-readiness` subcommand (decision D-133). Returns
|
||||
a structured `ReadinessResult` (pass/fail per check, with reason codes).
|
||||
On fail → the ingestor rejects with a citizen-developer-facing error
|
||||
(not a stack trace). On pass → proceeds to existing contract ingestion.
|
||||
Calls `contract.schema.json` validation first, then the readiness checks.
|
||||
Reason codes: `MISSING_TAGS`, `ENV_MISSING_MANDATORY:<env>:<field>`,
|
||||
`AGENTIC_MISSING_INTENT`, `MISSING_APP_SOURCE`, `POLICY_PRECONDITION_MISSING`.
|
||||
|
||||
- **REQ-219** — `docs/submission-readiness.md` citizen-developer-facing doc
|
||||
explaining what is acceptable to start, with good + rejected examples and
|
||||
the reason-code catalog. References `schemas/submission-readiness.schema.json`
|
||||
as the source of truth.
|
||||
|
||||
- **REQ-220** — `tests/test_submission_readiness.py` covers: good contract
|
||||
passes; missing tags fail with `MISSING_TAGS`; missing env mandatory fails
|
||||
with `ENV_MISSING_MANDATORY:<env>:<field>`; agentic profile missing intent
|
||||
fails with `AGENTIC_MISSING_INTENT`; missing appSource fails with
|
||||
`MISSING_APP_SOURCE`.
|
||||
|
||||
- **REQ-221** — `skills/` directory with 9 Atelier-derived skill files mapped
|
||||
to the BA.A citizen-developer catalog: `skills/api.md` (domains/api/),
|
||||
`skills/security.md` (domains/security/), `skills/data.md` (domains/data/),
|
||||
`skills/testing.md` (domains/testing/), `skills/observability.md`
|
||||
(domains/observability/), `skills/errors.md` (domains/errors/),
|
||||
`skills/devops.md` (domains/devops/), `skills/infrastructure-as-code.md`
|
||||
(domains/infrastructure-as-code/), `skills/compliance.md`
|
||||
(domains/compliance/). Each names the Atelier source path, distills the
|
||||
first-principles to the citizen-developer-relevant subset, links to
|
||||
agent-checklist triggers, and maps to the BA.A 5-skill catalog (web API,
|
||||
worker, scheduled job, static asset, basic observability bootstrap).
|
||||
|
||||
- **REQ-222** — `docs/skills.md` index page listing the skill catalog, the
|
||||
Atelier provenance, and how the citizen developer's AI agent consumes them
|
||||
(read before completing a task; run `review/agent-checklist.md` before
|
||||
finishing). `PROJECT.md` BA.A decision extended with the Atelier-derived
|
||||
skill catalog reference.
|
||||
|
||||
- **REQ-223** — `mcp/atelier/server.py` MCP server (stdio transport,
|
||||
decision D-135) with a **plugin-registry architecture** (decision D-140):
|
||||
`plugins/<name>.py` modules each expose `register(mcp: MCPServer) -> None`
|
||||
and call `@mcp.tool()` for their tools; `server.py` scans `plugins/` and
|
||||
calls `register` on each. Initial plugins: `principles.py`
|
||||
(`atelier.lookup_principle`, `atelier.list_domains`, `atelier.matrix_lookup`)
|
||||
and `validation.py` (`atelier.validate_against_principles` — agentic
|
||||
validation against the Atelier agent-checklist, beyond Wiz/Checkmarx/Mend).
|
||||
Uses the MCP Python SDK v2 (`modelcontextprotocol/python-sdk`).
|
||||
|
||||
- **REQ-224** — `mcp/atelier/vendor/` vendored Atelier snapshot (pinned tag,
|
||||
decision D-136) for audit reproducibility. `mcp/atelier/vendor/VERSION.md`
|
||||
records the pinned tag + a `scripts/update_atelier_vendor.sh` helper for
|
||||
intentional upgrades. `mcp/atelier/README.md` documents the server: how to
|
||||
run, transport, tool catalog, plugin-authoring guide, vendoring policy.
|
||||
|
||||
- **REQ-225** — `tests/test_atelier_mcp.py` covers: tool registration (all 4
|
||||
tools discoverable via `tools/list`), `atelier.lookup_principle` returns
|
||||
the principle text + core C-rule, `atelier.validate_against_principles`
|
||||
catches a planted C1 (correctness) + C7 (observability) violation in a
|
||||
known-bad snippet and passes a known-good snippet, `atelier.matrix_lookup`
|
||||
returns the domain→core mapping, plugin discovery loads all plugins in
|
||||
`plugins/`.
|
||||
|
||||
- **REQ-226** — 3 new deck slides added to the unified deck
|
||||
(`docs/presentations/nova-no-humans-platform-marp.md`) → 21 slides total:
|
||||
Slide 19 "Scope: Downstream of PDLC", Slide 20 "RACI: Who Owns What",
|
||||
Slide 21 "Production-Grade Guidance via Atelier". Arc Preview slide
|
||||
updated to reflect 21-slide count. Talking points
|
||||
(`nova-no-humans-platform-talking-points.md`) synced for the 3 new slides.
|
||||
S&P theme preserved (regression check vs P1). CAP-024 deck structure
|
||||
regression passes.
|
||||
|
||||
- **REQ-227** — `docs/presentations/README.md` slide count + deck table
|
||||
updated to reflect 21 slides + the 3 new slide titles.
|
||||
|
||||
- **REQ-228** — `scripts/render_deck.sh` (renders HTML + PPTX from a Marp
|
||||
deck, commits both to git) and `scripts/attach_release_asset.py` (uploads
|
||||
a file to a Gitea release via the API). Any phase modifying
|
||||
`docs/presentations/*-marp.md` or `docs/presentations/assets/` MUST
|
||||
re-render HTML + PPTX, commit the PPTX binary to `docs/presentations/`,
|
||||
and attach it to the phase's Gitea release. PPTX is stored as a committed
|
||||
binary (no LFS, decision D-141).
|
||||
|
||||
### Out of Scope (v1.18)
|
||||
|
||||
- **Streamable HTTP transport for the MCP server** — stdio ships now; HTTP
|
||||
is a future milestone (the SDK supports it on the same server object, so
|
||||
adding it later is a transport-only change, not a rewrite).
|
||||
- **A Nova-built frontend / dashboard** — observability stays PowerBI /
|
||||
external; no Nova web UI.
|
||||
- **Replacing the existing BA.A 5-skill catalog** — the Atelier-derived
|
||||
skills extend it, not replace it.
|
||||
- **Live AWS re-provisioning** (D-096, still deferred) — submission-readiness
|
||||
validates the contract shape, not a live AWS deployment.
|
||||
- **A second forge adapter** (GitLab) — BA.F cross-platform evolution is
|
||||
future work.
|
||||
- **Atelier live-fetch mode** — vendoring is the only mode this milestone;
|
||||
live-fetch (with its reproducibility trade-offs) is not implemented.
|
||||
|
||||
### v1.18 Traceability
|
||||
|
||||
| REQ | Phase | Status |
|
||||
|-----|-------|--------|
|
||||
| REQ-214 | P1 | complete |
|
||||
| REQ-215 | P2 | complete |
|
||||
| REQ-216 | P2 | complete |
|
||||
| REQ-217 | P3 | complete |
|
||||
| REQ-218 | P3 | complete |
|
||||
| REQ-219 | P3 | complete |
|
||||
| REQ-220 | P3 | complete |
|
||||
| REQ-221 | P4 | complete |
|
||||
| REQ-222 | P4 | complete |
|
||||
| REQ-223 | P5 | complete |
|
||||
| REQ-224 | P5 | complete |
|
||||
| REQ-225 | P5 | complete |
|
||||
| REQ-226 | P6 | complete |
|
||||
| REQ-227 | P6 | complete |
|
||||
| REQ-228 | P1/P2/P6 | complete |
|
||||
|
||||
## v1.19 — Nova 2nd-Release Sync (GitLab consumer mirror)
|
||||
|
||||
> **NFR-only chore milestone.** A single execution phase shipping a patch on
|
||||
> the v1.18.x line (tag `v1.18.0`). Establishes the manual-only "2nd release"
|
||||
> pipeline from `~/acdl` (CIAgent-managed source of truth) into `~/nova`
|
||||
> (GitLab `jonathanchery/nova` — a separate repo, separate history, consumer /
|
||||
> platform-team audience). `~/acdl` retains the full CIAgent audit trail;
|
||||
> `~/nova` receives only the consumer subset, committed with real
|
||||
> conventional commits per domain (no kitchen-sink "sync from source mirror").
|
||||
|
||||
- **REQ-229** — `scripts/sync_to_nova.sh` replaces `scripts/sync_to_gl.sh`.
|
||||
The script: (1) refuses to run without `--release` / `RELEASE_CONFIRMED=1`
|
||||
(manual-only — never triggerable by CI); (2) rsyncs the consumer subset of
|
||||
`~/acdl` into `~/nova`, excluding `.ciagent/`, `.gitea/`, `.env*`, `terraform/`,
|
||||
`demo/`, runtime metrics artifacts, and internal-only scripts (full list in
|
||||
`EXCLUDE_SCRIPTS`), while protecting `~/nova/.git` history via rsync
|
||||
`--filter=P .git`; (3) commits changes domain-by-domain in a fixed order
|
||||
(config → core → adapters → modules → contracts → schemas → pipelines →
|
||||
mcp → skills → scripts → tests → docs → workflows) using one
|
||||
conventional-commit message per changed domain passed via repeated `-m`
|
||||
flags (positional mapping over changed domains only — no kitchen-sink
|
||||
commit); (4) validates conventional-commit format (`feat|fix|docs|chore|…`)
|
||||
unless `--no-verify-format`; (5) pushes to the branch upstream unless
|
||||
`--no-push`. `--list-domains`, `--dry-run`, `-v` supported. The old
|
||||
`sync_to_gl.sh` is removed. (Phase P1)
|
||||
|
||||
### Out of Scope (v1.19)
|
||||
|
||||
- **coreci / Atelier review gate on the synced tree** — deferred; the sync
|
||||
ships consumer contents as-is. A future milestone may run a vendored-Atelier
|
||||
review pass before commit and block on P0 findings.
|
||||
- **Tagging releases on the `~/nova` side** — could add `--tag <semver>` later.
|
||||
- **Deleting `~/gl`** — the old GitLab `acdl` mirror is left on disk; only the
|
||||
sync script targeting it is removed.
|
||||
|
||||
### v1.19 Traceability
|
||||
|
||||
| REQ | Phase | Status |
|
||||
|-----|-------|--------|
|
||||
| REQ-229 | P1 | complete |
|
||||
|
||||
## v1.20 — Consumer Cleanup + Transparent Terraform + Slide Pipeline
|
||||
|
||||
> **Multi-concern milestone.** Four user-directed inputs spanning consumer
|
||||
> cleanup, infrastructure transparency, and presentation automation. Tags
|
||||
> run on the v1.19.x line (milestone v1.20 → tags v1.19.0, v1.19.1, …).
|
||||
>
|
||||
> **Input 1 — Gitea/GitLab removal:** Remove all mentions of `gitea` / `gitlab`
|
||||
> (case-insensitive) from every file synced to `~/nova`. The platform team
|
||||
> (consumer of `~/nova`) must never know about the dev forge or the GitLab
|
||||
> mirror. Genericize forge-detection code to `forge` / `generic_forge`.
|
||||
>
|
||||
> **Input 2 — Documentation simplification:** Radically simplify all synced
|
||||
> documentation. Anything the CIAgent needs to reference for itself lives in
|
||||
> `.ciagent/`. Everything else is tailored to the Platform Team audience.
|
||||
> Strip ciagent-internal provenance (REQ-/D-/P-/CAP- IDs, milestone headers,
|
||||
> `.ciagent/PROJECT.md` citations) from synced docs. Delete completed
|
||||
> migration guides. Move internal artifacts to `.ciagent/`.
|
||||
>
|
||||
> **Input 3 — Transparent terraform:** Move terraform `init` / `validate` /
|
||||
> `plan` / `apply` / `output` into native workflow steps (transparent, visible
|
||||
> in CI logs). Split `run_platform.sh` into `run_codegen.sh` (pre-TF) +
|
||||
> `run_postapply.sh` (post-TF). Add `var.enabled` feature flags to every L1
|
||||
> module + L2 composition toggles. Wire forge repo variables as per-client
|
||||
> feature flags — different clients test different functionality without
|
||||
> version upgrades.
|
||||
>
|
||||
> **Input 4 — Slide pipeline + product roadmap:** The slides have not adopted
|
||||
> the S&P Global theme fully. Create a dedicated render pipeline that builds
|
||||
> the slides (mermaid PNGs + Marp HTML/PPTX) with the S&P theme applied to all
|
||||
> slide chrome. Add a 12-month product roadmap (high-level, product-oriented
|
||||
> vs the technical roadmap in `.ciagent/ROADMAP.md`) to the deck.
|
||||
|
||||
- **REQ-230** — No `gitea` / `gitlab` string literal (case-insensitive) appears
|
||||
in any file synced to `~/nova`. Verified by
|
||||
`tests/test_no_forge_mentions.py` which scans the synced subset (same
|
||||
path rules as `sync_to_nova.sh`'s `DOMAINS` / `EXCLUDES`). Forge-detection
|
||||
code (`contract_ingestor.py`, `hitl_gates.py`, `run_platform.sh`) is
|
||||
genericized: `gitea` → `forge` / `generic_forge`, `GITEA_ACTOR` →
|
||||
`FORGE_ACTOR` (with `GITHUB_ACTOR` primary). (Phase P1)
|
||||
|
||||
- **REQ-231** — Synced documentation is tailored to the Platform Team
|
||||
audience. Ciagent-internal provenance (`v1.XX — Strategic Direction` headers,
|
||||
`REQ-NNN` / `D-NNN` / `P-NNN` / `CAP-NNN` IDs, `.ciagent/PROJECT.md`
|
||||
"source of truth" citations) is stripped from synced docs. (Phase P1)
|
||||
|
||||
- **REQ-232** — Completed/historical migration docs
|
||||
(`docs/NOVA_MIGRATION.md`, `docs/NOVA_AWS_MIGRATION.md`) removed from the
|
||||
synced tree. `docs/NO_HUMANS_THESIS.md` moved to `.ciagent/` (internal
|
||||
thesis-defense artifact). (Phase P1)
|
||||
|
||||
- **REQ-233** — Terraform `init` / `validate` / `plan` / `apply` / `output`
|
||||
run as native workflow steps in `deploy.yml` (transparent, named steps
|
||||
visible in CI logs), not buried inside `run_platform.sh`. (Phase P4)
|
||||
|
||||
- **REQ-234** — `run_platform.sh` is split: `run_codegen.sh` (pre-TF: env
|
||||
check, validate, resolve, adapt) + `run_postapply.sh` (post-TF: Checkov,
|
||||
confidence, HITL, outbox, SSM, comment, uptime). A thin `run_platform.sh`
|
||||
shim preserves backward compat for local-dev usage. (Phase P4)
|
||||
|
||||
- **REQ-235** — Every L1 module has `variable "enabled" { type = bool,
|
||||
default = true }` + `count = var.enabled ? 1 : 0` on its primary
|
||||
resource(s); declared in `interface.json`. The `uptime` module's
|
||||
`feature_flag_enabled` is renamed to `enabled` (with backward-compat alias).
|
||||
(Phase P4)
|
||||
|
||||
- **REQ-236** — L2 `composition.json` supports per-child `enabled` toggles
|
||||
driven by contract `inputs.enable_<child>`. The resolver skips children
|
||||
with `enabled: false`. (Phase P4)
|
||||
|
||||
- **REQ-237** — `deploy.yml` reads feature flags from forge repository
|
||||
variables (`vars.ENABLE_*`) and passes them as `-var` flags to terraform,
|
||||
enabling per-client feature toggles without version upgrades. (Phase P4)
|
||||
|
||||
- **REQ-238** — Stale artifact path `/tmp/acdl_platform_run_v18` in
|
||||
`deploy.yml` fixed to use `NOVA_WORK_DIR`. (Phase P4)
|
||||
|
||||
- **REQ-239** — A dedicated S&P Global theme CSS file
|
||||
(`docs/presentations/assets/nova-sp-theme.css`) is the Marp theme for all
|
||||
Nova presentation decks. The theme applies the S&P Red/Black/White palette
|
||||
(`#D6002A`, `#1B1B1B`, `#FFFFFF`) to all slide chrome (background,
|
||||
header/footer, pagination, tables, blockquotes), not just headings. (Phase P2)
|
||||
|
||||
- **REQ-240** — A dedicated render pipeline (`scripts/render_slides.sh`)
|
||||
builds the presentation deck end-to-end: (1) renders all
|
||||
`assets/mmd/*.mmd` → `assets/png/*.png` via `mermaid-cli --configFile
|
||||
sp-theme.json`; (2) renders the Marp deck → HTML + PPTX via `marp-cli`;
|
||||
(3) stages all rendered artifacts to git. Supersedes `render_deck.sh`.
|
||||
(Phase P2)
|
||||
|
||||
- **REQ-241** — A CI workflow (`workflows-src/slides.yml` +
|
||||
`.github/workflows/slides.yml`) runs `render_slides.sh` on any change to
|
||||
`docs/presentations/**` and commits the rendered HTML/PPTX/PNGs back. No
|
||||
manual re-render step; no artifact drift. (Phase P2)
|
||||
|
||||
- **REQ-242** — `tests/test_slides_pipeline.py` validates: (1) the Marp
|
||||
deck frontmatter references `nova-sp-theme.css`; (2) the CSS contains the
|
||||
S&P colors; (3) every `.mmd` has a corresponding `.png`; (4) the HTML
|
||||
exists and is newer than the Marp `.md`. (Phase P2)
|
||||
|
||||
- **REQ-243** — `docs/presentations/README.md` directory layout is updated
|
||||
to remove retired decks (`how-the-platform-works-*`,
|
||||
`the-developer-experience-*`) and document the render pipeline + theme CSS.
|
||||
(Phase P2)
|
||||
|
||||
- **REQ-244** — A 12-month product roadmap (4 quarters, product-outcome
|
||||
oriented, grounded in NORTH_STAR strategic objectives + deferred-metric
|
||||
candidate milestones) is added to the presentation deck as Slide 20 +
|
||||
Slide 21. The roadmap is distinct from Slide 15's deferred-metric unblock
|
||||
paths. A matching talking-points section is added. (Phase P3)
|
||||
|
||||
### Out of Scope (v1.20)
|
||||
|
||||
- **Multi-cloud (Azure/GCP) implementation** — deferred; only the product
|
||||
roadmap references it as a Q4 aspiration.
|
||||
- **ML anomaly-forecasting service** — deferred; only the product roadmap
|
||||
references it as a Q4 aspiration.
|
||||
- **Actual pilot estate activation** — deferred (requires live AWS
|
||||
re-provisioning, D-096 lift); the product roadmap references it as Q1.
|
||||
- **Token rotation for `NOVA_GITEA_TOKEN`** — out of scope; the `.env` files
|
||||
are correctly excluded from sync. Flagged for awareness only.
|
||||
|
||||
### v1.20 Traceability
|
||||
|
||||
| REQ | Phase | Status |
|
||||
|-----|-------|--------|
|
||||
| REQ-230 | P1 | complete |
|
||||
| REQ-231 | P1 | complete |
|
||||
| REQ-232 | P1 | complete |
|
||||
| REQ-233 | P4 | complete |
|
||||
| REQ-234 | P4 | complete |
|
||||
| REQ-235 | P4 | complete |
|
||||
| REQ-236 | P4 | complete |
|
||||
| REQ-237 | P4 | complete |
|
||||
| REQ-238 | P4 | complete |
|
||||
| REQ-239 | P2 | complete |
|
||||
| REQ-240 | P2 | complete |
|
||||
| REQ-241 | P2 | complete |
|
||||
| REQ-242 | P2 | complete |
|
||||
| REQ-243 | P2 | complete |
|
||||
| REQ-244 | P3 | complete |
|
||||
|
||||
## v1.21 — Nova Deck Refinement & Pipeline Hardening
|
||||
|
||||
> Leadership-deck refinement based on 33 review notes on the v1.20 deck
|
||||
> (v1.20 shipped as `nova-no-humans-platform*`). This milestone renames the
|
||||
> deck to the professional "Autonomous Cloud Delivery Platform" framing,
|
||||
> restructures the narrative (Problem → Solution → Proof → Roadmap + Ask),
|
||||
> removes internal provenance from audience-facing slides, hardens the
|
||||
> policy pipeline (Checkov before plan, Wiz-or-Checkov on plan), and moves
|
||||
> the strategic integration objective into the North Star.
|
||||
>
|
||||
> Tags run on the v1.20.x line (milestone v1.21 → tags v1.20.0, v1.20.1, …).
|
||||
|
||||
### REQ-245 — Deck rename + restructure
|
||||
|
||||
The deck files are renamed from `nova-no-humans-platform*` to
|
||||
`nova-autonomous-cloud-delivery*` across all five artifacts
|
||||
(source `.md`, `-marp.md`, `.html`, `.pptx`, `-talking-points.md`).
|
||||
The in-deck title becomes "Nova — The Autonomous Cloud Delivery Platform"
|
||||
(professional, conveys autonomy without the provocative "no-humans"
|
||||
wording). The narrative restructures to 18 main + 1 appendix slides:
|
||||
|
||||
1. The Problem (merged old 1+2; broader problem framing; no "arc"; no
|
||||
"18 capabilities verified"; not "humans are the problem"; add tribal
|
||||
knowledge / rockstar-operator framing)
|
||||
2. Nova's Vision
|
||||
3. Strategic Objectives + Anti-Goals
|
||||
4. Scope: Downstream of PDLC (moved up)
|
||||
5. RACI: Who Owns What (moved up)
|
||||
6. The Platform Pipeline
|
||||
7. The Decision Ledger
|
||||
8. The Attestation Matrix
|
||||
9. Telemetry & Live Ops
|
||||
10. Decision Ledger + Attestation Coverage
|
||||
11. Cost & ROI
|
||||
12. What's Deferred — and Why
|
||||
13. Roadmap to the North Star
|
||||
14. 12-Month Product Roadmap
|
||||
15. Quarter-by-Quarter Outcomes
|
||||
16. Production-Grade Guidance via Atelier (1/2)
|
||||
17. Production-Grade Guidance via Atelier (2/2)
|
||||
18. Recap + Ask
|
||||
A1. Metrics Glossary
|
||||
|
||||
Removed: old Slide 10 (Capability Health), old Slide 12 (Zero-Touch
|
||||
Efficiency), old Appendix A2 (Operating Model & Cost). Slide 5's first
|
||||
table removed.
|
||||
|
||||
### REQ-246 — Thesis rename + reframe
|
||||
|
||||
`.ciagent/NO_HUMANS_THESIS.md` is renamed (git mv) to
|
||||
`.ciagent/AUTONOMY_THESIS.md`. Content reframes from "removing humans" to
|
||||
"autonomy in operations, human at stage gates" — professional, not
|
||||
provocative. The operator-bottleneck framing is softened; the attestation
|
||||
model + provable trust are emphasized. Anti-claims are retained and
|
||||
reworded for a tech-leadership audience. All references across the repo
|
||||
are updated to the new filename + framing.
|
||||
|
||||
### REQ-247 — Strategic-docs sync (NORTH_STAR + PROJECT)
|
||||
|
||||
`NORTH_STAR.md` is updated:
|
||||
- Vision polished for a technical audience concerned about security,
|
||||
security remediation velocity, and reliability; "infrastructure
|
||||
operations become visible" is preserved as a recurring theme.
|
||||
- Strategic Objective #2 (provable trust) is reworded: trust is
|
||||
established by deterministic scripts that calculate a score, not by
|
||||
AI. The platform functions without AI. "AI decisions" are really
|
||||
automated decisions.
|
||||
- Strategic Objective #3 (ROI) is reworded with four CTO-grade metrics:
|
||||
Lead Time (PR → Production), Infrastructure Vulnerability Count
|
||||
(downward trend), MTTR, Cloud Spend Reduction. All flow into PowerBI
|
||||
views and are captured by the telemetry pipeline.
|
||||
- Strategic Objective #4 is replaced: integrate with externally owned
|
||||
PDLC, SDLC, Agentic, and Citizen Developer platforms regardless of
|
||||
source; Nova provides skills + MCP endpoints to make applications
|
||||
production-grade; all intents to deploy to production go through the
|
||||
same rigorous controls and quality gates.
|
||||
- Anti-goals #1 (hyperscaler competitor), #4 (legacy untagged), and #5
|
||||
(sold to operators) are removed. Two new anti-goals added: not an
|
||||
upstream development platform; not a replacement for the Product
|
||||
Lifecycle (PDLC).
|
||||
- Anti-goal #3 reworded to remove the "removes humans" framing.
|
||||
|
||||
`PROJECT.md` mission statement + scope are synchronized with the
|
||||
integration objective and the reworded strategic objectives.
|
||||
|
||||
### REQ-248 — RACI restructure (Quality Engineering + SRE)
|
||||
|
||||
The RACI matrix (slide + `docs/raci.md`) is restructured:
|
||||
- A **Quality Engineering** column is added.
|
||||
- The Platform column no longer holds the **A** for release attestation;
|
||||
accountability is reassigned to QA or SRE as appropriate.
|
||||
- "Release Management" is renamed to **SRE**.
|
||||
- "Release attestation" is split into two rows: the SRE part is
|
||||
**Production Readiness** (operational readiness sign-off).
|
||||
- The slide is sized to fit (text shrunk / low-impact rows dropped).
|
||||
|
||||
### REQ-249 — Atelier split (2 slides)
|
||||
|
||||
Slide 19 (Production-Grade Guidance via Atelier) is split into two slides:
|
||||
- **16 (1/2):** Skills + MCP server overview (the 9 skills, the 4 MCP
|
||||
tools, the plugin-registry + stdio surface).
|
||||
- **17 (2/2):** Agentic validation beyond deterministic scanners +
|
||||
vendored Atelier for audit reproducibility.
|
||||
The benefit wording is improved; the same spirit is retained.
|
||||
|
||||
### REQ-250 — Pipeline hardening (Checkov before plan; Wiz-or-Checkov on plan)
|
||||
|
||||
`scripts/run_platform.sh` (and `scripts/run_postapply.sh` where
|
||||
relevant) implement the two-stage policy scan:
|
||||
1. **Checkov runs on static code** (the generated `main.tf` / TF
|
||||
directory) **before** `terraform plan` — fail-fast, quick developer
|
||||
feedback on policy violations in the authored code.
|
||||
2. **After `terraform plan`:** if `WIZ_API_TOKEN` + `WIZ_API_URL` are
|
||||
set, run **Wiz against the plan**; otherwise run **Checkov against
|
||||
the plan** as a drop-in replacement. **Wiz and Checkov are never
|
||||
both run on the plan.**
|
||||
`adapters/wiz/wiz_adapter.py` is updated if needed for plan-mode
|
||||
input. Slide 6 + `docs/scope.md` reflect the new flow. Tests
|
||||
(`tests/test_pipeline.py`, `tests/test_pipeline_contract.py`, and
|
||||
any checkov/wiz tests) are updated and pass.
|
||||
|
||||
### REQ-251 — Theme CSS fix (Appendix A1) + footer cleanup
|
||||
|
||||
`docs/presentations/assets/nova-sp-theme.css` is fixed so the Appendix
|
||||
A1 Metrics Glossary table is readable (the table background color is
|
||||
corrected). The Marp footer no longer shows the version (`v1.20`) or
|
||||
the `Act %{page}/5` artifact. The title-slide subtitle no longer shows
|
||||
`v1.18 — Citizen Developer & Production-Grade Guidance`; it becomes
|
||||
"Product Development & Citizen Developer Overview" (or similar) to
|
||||
convey the audience for the platform.
|
||||
|
||||
### REQ-252 — Global citation + badge + version removal
|
||||
|
||||
Across all audience-facing slides (the Marp deck, the source-of-truth
|
||||
markdown, and the talking points):
|
||||
- All internal citations are removed: `D-###` decision IDs,
|
||||
`REQ-###` requirement IDs, and internal file paths
|
||||
(e.g. `outbox_writer.py`, `confidence_signal.py`).
|
||||
- All `<span class="badge planned">Planned</span>` badges are removed.
|
||||
- The version is removed from the footer and the title slide.
|
||||
Every benefit callout is rewritten for a tech-leadership audience
|
||||
(security, remediation velocity, reliability, lead time). A "less is
|
||||
more / no fluff" final prose pass is applied; the story stays clear.
|
||||
|
||||
### REQ-253 — Render + verify + ship
|
||||
|
||||
Changed/new mermaid diagrams are re-rendered (slide 1 new diagram, slide
|
||||
9 expand, Atelier split). HTML + PPTX are re-rendered via
|
||||
`scripts/render_slides.sh`. `tests/test_slides_pipeline.py` passes:
|
||||
asserts 18 main + 1 appendix slides, no badge spans, no version in the
|
||||
footer, no `D-###`/`REQ-###`/`.py` paths in audience-facing slides, and
|
||||
filename refs updated in render scripts + CI workflow + README.
|
||||
`tests/test_no_forge_mentions.py` passes. Full `pytest` passes
|
||||
(pipeline-hardening tests green). `run_platform.sh --check-only` passes.
|
||||
Milestone ship: tag the final phase on the v1.20.x line; create a
|
||||
release; attach the PPTX.
|
||||
|
||||
### Out of Scope (v1.21)
|
||||
|
||||
- **Live pilot estate activation** — still deferred (D-096).
|
||||
- **ML anomaly-forecasting service** — still deferred.
|
||||
- **Multi-cloud (Azure/GCP) implementation** — still deferred.
|
||||
- **Tamper-evident ledger (S3 Object Lock + JWS)** — still deferred
|
||||
(D-083); the deck describes it as a roadmap item without citing the
|
||||
decision ID in the audience-facing slides.
|
||||
|
||||
### v1.21 Traceability
|
||||
|
||||
| REQ | Phase | Status |
|
||||
|-----|-------|--------|
|
||||
| REQ-245 | P2 | pending |
|
||||
| REQ-246 | P1 | pending |
|
||||
| REQ-247 | P1 | pending |
|
||||
| REQ-248 | P2 | pending |
|
||||
| REQ-249 | P2 | pending |
|
||||
| REQ-250 | P4 | pending |
|
||||
| REQ-251 | P3 | pending |
|
||||
| REQ-252 | P2 | pending |
|
||||
| REQ-253 | P5 | pending |
|
||||
|
||||
@@ -1493,3 +1493,856 @@ Total: ~12–16 slides. Opening = arc preview; closing = recap + ask.
|
||||
cost estimate. No live AWS access required. If Infracost is not
|
||||
available, the `cost.estimated` event is omitted (degraded mode, not
|
||||
a failure).
|
||||
|
||||
---
|
||||
|
||||
## v1.18 Research — Citizen Developer & Production-Grade Guidance
|
||||
|
||||
> Phase 0 RESEARCH. Autonomy = full. Findings are evidence-grounded
|
||||
> (fetched from live sources, not assumed). The Atelier repo, the MCP
|
||||
> Python SDK v2 docs, the existing Nova schemas/ingestor, and the Marp
|
||||
> CLI README were all fetched directly. Decisions are logged with
|
||||
> confidence scores; low-confidence items are flagged.
|
||||
|
||||
### 1. Atelier Integration Reference
|
||||
|
||||
#### 1.1 The 8 core principles (C1–C8)
|
||||
|
||||
Source: `core/first-principles.md` (fetched 2026-08-06 from
|
||||
`https://git.cloudinit.dev/coreci/atelier/raw/branch/main/core/first-principles.md`).
|
||||
Precedence is a **total order** — a lower-numbered principle is never
|
||||
sacrificed for a higher-numbered one (C1 never sacrificed; C2 only for
|
||||
C1; C3 only for C1/C2; C4–C8 tradeable among themselves but always below
|
||||
C1–C3).
|
||||
|
||||
| ID | Principle | One-line description |
|
||||
|----|-----------|----------------------|
|
||||
| **C1** | Correctness | The system does what it is supposed to do, and nothing else. Highest principle; never overridden. Security is a subset (exploitable code is incorrect). Includes temporal correctness (a late answer is wrong when the deadline mattered). |
|
||||
| **C2** | Clarity | The intent of the code is obvious to its reader. Optimize for the reader; names reveal intent; comments explain *why* not *what*. Unclear code is where bugs hide. |
|
||||
| **C3** | Simplicity | The solution is as simple as possible, and no simpler. Complexity is the enemy of correctness; every line is a liability. Not laziness — the result of removing everything unnecessary. |
|
||||
| **C4** | Locality | Decisions and their consequences live near each other. State, logic, side effects that depend on each other live near each other. A change needing many distant files is a locality violation. |
|
||||
| **C5** | Reversibility | Every decision can be undone, and the cost of undoing is known. Migrations/deploys/schema/API changes reversible by default. Versioning, feature flags, rollback paths are the mechanisms. |
|
||||
| **C6** | Composability | Parts combine into wholes, and the parts are reusable in new wholes. A part that does one thing well composes; the boundary is its contract. Composable parts are understandable in isolation. |
|
||||
| **C7** | Observability | The system's behavior is visible to the people who must understand it. Logs/metrics/traces are first-class, designed in. An observable system answers "what/why/what next" without reading source. |
|
||||
| **C8** | Economy | The system uses no more resources than the task requires (time, memory, attention, money, complexity). Most tradeable principle; unbounded growth in any resource is a defect. |
|
||||
|
||||
The precedence string (from `core/first-principles.md` §3):
|
||||
`C1 Correctness > C2 Clarity > C3 Simplicity > C4 Locality > C5 Reversibility > C6 Composability > C7 Observability > C8 Economy`.
|
||||
|
||||
Conflict resolution (`core/conflict-resolution.md`, fetched): a
|
||||
deterministic 6-step procedure. The **hierarchy** is
|
||||
`core/first-principles.md` > `domains/<x>/first-principles.md` >
|
||||
`domains/<x>/<topic>.md` > `languages/<lang>.md` > `examples/<x>.md`.
|
||||
Same-level conflicts resolve by core derivation (via the matrix), then
|
||||
by specificity, then by filing an issue (a tie is a defect). A domain's
|
||||
"non-tradeable" declaration (e.g. Security: 8 of 10) promotes those
|
||||
rules to **C1-equivalent** — a binding escalation recorded in the
|
||||
matrix's derivation.
|
||||
|
||||
#### 1.2 The 19 domains and Nova-citizen-dev relevance
|
||||
|
||||
Source: `matrix/principles-matrix.md` (fetched) + the releases page
|
||||
(v0.4 milestone = 19 domains, 190 P-rules, confirmed in the v0.3.6
|
||||
release notes and the matrix Coverage Summary).
|
||||
|
||||
| # | Domain | Atelier path | P-rules | Nova-citizen-dev relevant? | Reason |
|
||||
|---|--------|--------------|---------|------------------------------|--------|
|
||||
| 1 | UI/UX | `domains/uiux/` | 10 | **NO** — excluded | v1.18 has no frontend (Out of Scope: "A Nova-built frontend / dashboard"). Decks are markdown, not a UI. |
|
||||
| 2 | API Design | `domains/api/` | 10 | **YES** | A citizen developer building a web API / worker / scheduled job touches API contracts. Maps to `skills/api.md`. |
|
||||
| 3 | Security | `domains/security/` | 10 | **YES** | Zero-trust, input validation, secret hygiene, fail-securely — universal for any production-grade service. Maps to `skills/security.md`. |
|
||||
| 4 | Data | `domains/data/` | 10 | **YES** | Schema-as-truth, migration safety, referential integrity — applies to any stateful service. Maps to `skills/data.md`. |
|
||||
| 5 | Testing | `domains/testing/` | 10 | **YES** | Tests-as-specification, determinism, edge-case coverage — required for a citizen developer's UAT. Maps to `skills/testing.md`. |
|
||||
| 6 | Performance | `domains/performance/` | 10 | **YES (reference, not a skill)** | Measure-first, bounded operations, no N+1, timeouts. NOT one of the 9 REQ-221 skills; Performance principles are cited inside the 9 skills + the index. |
|
||||
| 7 | Observability | `domains/observability/` | 10 | **YES** | Structured logs, correlation IDs, no secrets in logs — the "basic observability bootstrap" BA.A skill. Maps to `skills/observability.md`. |
|
||||
| 8 | Errors | `domains/errors/` | 10 | **YES** | Errors are data, fail loudly + specifically, preserve context — production-grade error handling. Maps to `skills/errors.md`. |
|
||||
| 9 | Documentation | `domains/documentation/` | 10 | **YES (reference, not a skill)** | Docs-as-code, audience awareness, examples mandatory. REQ-221 does NOT list `skills/documentation.md`; a self-referential "documentation skill" is redundant. Principles cited inside `docs/skills.md` index. |
|
||||
| 10 | Concurrency | `domains/concurrency/` | 10 | **YES (reference, not a skill)** | Immutability, bounded queues, timeouts — advanced for a citizen developer's first 5 skills. REQ-221 does NOT list `skills/concurrency.md`. Top rules cross-referenced inside `skills/api.md` + `skills/errors.md`. |
|
||||
| 11 | DevOps | `domains/devops/` | 10 | **YES** | Reproducibility, rollback-first, config-as-code — the citizen developer co-owns Release Management (RACI). Maps to `skills/devops.md`. |
|
||||
| 12 | Infrastructure as Code | `domains/infrastructure-as-code/` | 10 | **YES** | Declarative intent, idempotence, plan-before-apply, no secrets in HCL — directly relevant to the Nova contract→Terraform path. Maps to `skills/infrastructure-as-code.md`. |
|
||||
| 13 | Kubernetes | `domains/kubernetes/` | 10 | **NO** — excluded | Nova emits Terraform (ECS/Fargate per the architecture), not K8s manifests. Kyverno adapter is "ready but inactive" (D-053). Not citizen-dev-relevant. |
|
||||
| 14 | GitOps + Operators | `domains/gitops-operators/` | 10 | **NO** — excluded | Nova uses a push pipeline (contract → resolve → plan → apply), not a pull-based reconciler. Not citizen-dev-relevant. |
|
||||
| 15 | AI/ML | `domains/ai-ml/` | 10 | **YES (reference, not a skill)** | Reproducibility, data versioning, drift detection — relevant *to Nova itself* (Nova is an agentic platform), but a citizen developer on Nova is NOT building ML models; they consume Nova's agentic capability. REQ-221 does NOT list `skills/ai-ml.md`; the Atelier AI/ML domain is platform-team guidance, not citizen-dev guidance. |
|
||||
| 16 | i18n | `domains/i18n/` | 10 | **NO** — excluded | Not relevant to a citizen developer's first production-grade service on Nova. |
|
||||
| 17 | Compliance | `domains/compliance/` | 10 | **YES** | Audit logs append-only, policy-as-code, evidence-by-operation — directly relevant (Nova's compliance posture is a selling point). Maps to `skills/compliance.md`. |
|
||||
| 18 | Edge | `domains/edge/` | 10 | **NO** — excluded | Nova does not deploy edge/CDN for the citizen developer's first 5 skills; Route53/ACM/CloudFront are consumer-supplied extension points (D-049). |
|
||||
| 19 | Messaging | `domains/messaging/` | 10 | **NO** — excluded | The citizen developer's first 5 skills (web API / worker / scheduled job / static asset / observability bootstrap) do not require a broker; messaging is a future capability. |
|
||||
|
||||
**Relevant count:** 13 of 19 are relevant to *some* Nova audience
|
||||
(YES or YES-reference). Of those, **9 become skills** (per REQ-221, the
|
||||
planned count). The other 4 relevant domains (Performance, Documentation,
|
||||
Concurrency, AI/ML) are **reference-only** — their principles are cited
|
||||
inside skills or the `docs/skills.md` index, but they do NOT get their
|
||||
own skill file. This matches REQ-221's exact 9-skill list.
|
||||
|
||||
**Excluded count:** 6 of 19 (UI/UX, Kubernetes, GitOps, i18n, Edge,
|
||||
Messaging) are not relevant to a Nova citizen developer building a
|
||||
production-grade application — confirmed.
|
||||
|
||||
#### 1.3 Atelier domain → Nova skill mapping (final 9-skill list)
|
||||
|
||||
REQ-221 names exactly 9 skills. The research **confirms the planned 9**
|
||||
— no adjustment needed. The mapping (each skill cites its Atelier source
|
||||
path + distills the citizen-developer-relevant subset + links to
|
||||
agent-checklist triggers + maps to the BA.A 5-skill catalog):
|
||||
|
||||
| Nova skill file | Atelier domain path | P-rules distilled | BA.A catalog skill it extends |
|
||||
|-----------------|----------------------|-------------------|--------------------------------|
|
||||
| `skills/api.md` | `domains/api/` | P1 Contract Fidelity, P2 Clarity, P5 Versioning, P6 Idempotency, P8 Security, P9 Error Transparency | web API |
|
||||
| `skills/security.md` | `domains/security/` | P1 Zero Trust, P2 Least Privilege, P4 Input Validation, P6 Crypto Correctness, P8 Fail Securely, P9 Secret Hygiene | all 5 (cross-cutting) |
|
||||
| `skills/data.md` | `domains/data/` | P1 Truth, P3 Invariants in Schema, P4 Migration Safety, P7 Type Fidelity, P9 Referential Integrity | web API, worker, scheduled job |
|
||||
| `skills/testing.md` | `domains/testing/` | P1 Tests as Specification, P3 Determinism, P5 Coverage of Behavior, P9 Edge Case Coverage, P10 No Test Theater | all 5 (UAT is a citizen-developer RACI responsibility) |
|
||||
| `skills/observability.md` | `domains/observability/` | P1 Structured by Default, P2 Correlation, P6 No Secrets in Obs, P7 Actionable Alerts | basic observability bootstrap |
|
||||
| `skills/errors.md` | `domains/errors/` | P1 Errors are Data, P2 Fail Loudly, P3 Fail Specifically, P4 Preserve Context, P5 Recoverable When Possible | web API, worker, scheduled job |
|
||||
| `skills/devops.md` | `domains/devops/` | P1 Reproducibility, P4 Rollback First, P5 Progressive Delivery, P6 Config as Code, P8 Security at Every Layer | scheduled job, worker (deploy/release is co-owned Release Mgmt) |
|
||||
| `skills/infrastructure-as-code.md` | `domains/infrastructure-as-code/` | P1 Declarative Intent, P2 Idempotence, P4 Plan Before Apply, P5 Version Everything, P10 Secrets Never in Code | static asset (the contract→Terraform path) |
|
||||
| `skills/compliance.md` | `domains/compliance/` | P1 Audit Logs Append-Only, P2 Every Significant Action Logged, P4 Policy is Code, P5 Policy is Evaluated as a Gate, P9 Secrets Redacted in Audit | all 5 (cross-cutting; Nova's compliance posture) |
|
||||
|
||||
**Final recommendation: 9 skills, exactly as REQ-221 planned.**
|
||||
Confidence 0.95 — the planned list maps cleanly to the relevant Atelier
|
||||
domains and to the BA.A 5-skill catalog; the 4 "reference-only" domains
|
||||
(Performance, Documentation, Concurrency, AI/ML) are correctly *not*
|
||||
elevated to skills (a citizen developer's first production-grade service
|
||||
does not need a standalone Concurrency or AI/ML skill; Performance and
|
||||
Documentation principles are cited inside the 9 skills + the index).
|
||||
|
||||
#### 1.4 Agent-checklist → MCP `atelier.validate_against_principles` checks
|
||||
|
||||
Source: `review/agent-checklist.md` (fetched). The checklist has a
|
||||
**Core (C1–C8)** section (8 subsections, ~30 boolean items) plus
|
||||
**domain-specific trigger sections** (one per domain; Nova-relevant
|
||||
ones: API, Security, Data, Testing, Performance, Observability, Errors,
|
||||
Concurrency, DevOps, IaC, Compliance).
|
||||
|
||||
The MCP `atelier.validate_against_principles` tool (REQ-223, in
|
||||
`plugins/validation.py`) runs the relevant checklist items against a
|
||||
code/diff snippet. The tool input model:
|
||||
|
||||
```python
|
||||
class ValidateInput(BaseModel):
|
||||
snippet: str # the code/diff to validate
|
||||
language: str # e.g. "python", "terraform", "yaml"
|
||||
domains: list[str] # e.g. ["security", "api"] — which domain triggers to run
|
||||
run_core: bool = True # always run C1–C8 unless explicitly skipped
|
||||
```
|
||||
|
||||
The structured output model (Pydantic, returned as `structured_content`):
|
||||
|
||||
```python
|
||||
class Violation(BaseModel):
|
||||
principle: str # e.g. "C1", "security/P9"
|
||||
checklist_item: str # the verbatim checklist question
|
||||
severity: str # "C1" (blocking) | "non-tradeable" | "tradeable"
|
||||
evidence: str # the snippet substring + why it fails
|
||||
fix_hint: str # the principle's remediation guidance
|
||||
|
||||
class ValidateResult(BaseModel):
|
||||
snippet_id: str # hash of the snippet for replay
|
||||
passed: bool
|
||||
violations: list[Violation]
|
||||
domains_checked: list[str]
|
||||
core_checked: bool
|
||||
```
|
||||
|
||||
**Checklist → check mapping** (the validation plugin encodes each
|
||||
checklist item as a boolean predicate over the snippet + language):
|
||||
|
||||
| Checklist section | MCP check behavior | Nova-relevant? |
|
||||
|-------------------|--------------------|-----------------|
|
||||
| **C1 Correctness** (4 items) | Run all 4; any fail → `severity: "C1"` (blocking). | YES — always run (core) |
|
||||
| **C2 Clarity** (4 items) | Heuristic checks: name smell (`data/temp/x/doStuff`), comment-why ratio. | YES — always run |
|
||||
| **C3 Simplicity** (4 items) | Dead-code heuristic, function-length, premature-abstraction. | YES — always run |
|
||||
| **C4 Locality** (3 items) | Cross-file-change heuristic (for diffs); within-file coupling. | YES — always run |
|
||||
| **C5 Reversibility** (3 items) | Migration-has-down, deploy-has-rollback presence checks. | YES — always run |
|
||||
| **C6 Composability** (3 items) | Single-responsibility heuristic, boundary-typed check. | YES — always run |
|
||||
| **C7 Observability** (4 items) | Log-presence, error-context, metric, **no-secrets-in-logs** (hard check). | YES — always run |
|
||||
| **C8 Economy** (3 items) | Unbounded-growth, no-timeout, resource-leak heuristics. | YES — always run |
|
||||
| If API | 5 items: nouns-plural-lowercase, status codes, structured errors, schema validation, auth-required. | YES — when `domains` includes "api" |
|
||||
| If Security | 5 items: no-secrets-in-code/logs/URLs, input-validation, output-encoding, vetted-crypto, authz-checked. **All 5 are non-tradeable** (Security domain §3). | YES — when "security" |
|
||||
| If Data | 5 items: schema-reflects-domain, constraints-in-schema, migration-up-down, domain-types, no-SELECT-star. | YES — when "data" |
|
||||
| If Testing | 4 items: independence, determinism, edge-cases, failure-specificity. | YES — when "testing" |
|
||||
| If Performance | 4 items: no-unbounded, no-N+1, timeouts, cache-invalidation. | YES — when "performance" |
|
||||
| If Observability | 4 items: structured-logs, correlation-id, no-high-cardinality, alerts-have-runbooks. | YES — when "observability" |
|
||||
| If Errors | 4 items: not-swallowed, specific, context-preserved, recovery-attempted. | YES — when "errors" |
|
||||
| If Concurrency | 5 items: shared-state-minimized, minimal-locks, bounded-queues, timeouts, cancellation. | YES — when "concurrency" |
|
||||
| If DevOps | 4 items: pipeline-is-process, rollback-known, config-in-code, env-parity. | YES — when "devops" |
|
||||
| If IaC | 8 items: declarative, pinned-providers, remote-locked-state, plan-before-apply, no-secrets-in-HCL, versioned-modules, drift-is-incident, least-priv-providers. | YES — when "infrastructure-as-code" |
|
||||
| If Compliance | 10 items: append-only-audit, a-priori-action-set, retention-as-policy, policy-as-code, policy-as-gate, continuous-evidence, attributable-identity, subject-access, redacted-secrets, observable-posture. | YES — when "compliance" |
|
||||
|
||||
The validation plugin reads the vendored `review/agent-checklist.md`
|
||||
(frozen at the pinned tag — §1.6) so the checks are replayable against
|
||||
the exact checklist version that produced a result. The plugin maps each
|
||||
checklist line to a predicate function keyed by `(language, principle)`
|
||||
so a "no secrets in code" check runs differently for Python (ast scan for
|
||||
string-constant assignment) vs Terraform (HCL scan for hardcoded
|
||||
provider keys) vs YAML (scan for `api_key:` literals).
|
||||
|
||||
#### 1.5 Principle-lookup query model
|
||||
|
||||
`atelier.lookup_principle(domain: str, principle_id: str)` (REQ-223, in
|
||||
`plugins/principles.py`) resolves a principle reference to its full
|
||||
text + core derivation + checklist items. Resolution model:
|
||||
|
||||
**Input:**
|
||||
```python
|
||||
class LookupInput(BaseModel):
|
||||
domain: str # "security" | "api" | "data" | ... | "core"
|
||||
principle_id: str # "P4" | "C1" (core) | "P9"
|
||||
```
|
||||
|
||||
**Resolution path (the lookup algorithm):**
|
||||
1. If `domain == "core"`: load `vendor/core/first-principles.md`, parse
|
||||
the `### C<n>. <Name>` section for `principle_id` (e.g. `C1` →
|
||||
the "C1. Correctness" section). Return the full principle text.
|
||||
2. Else: load `vendor/domains/<domain>/first-principles.md`, parse the
|
||||
`### P<n>. <Name>` section for `principle_id` (e.g. `security/P4` →
|
||||
the "P4. Input Validation" section).
|
||||
3. **Cross-reference the matrix:** load
|
||||
`vendor/matrix/principles-matrix.md`, find the row for
|
||||
`<Domain> P<n>`, extract the `Core` column (e.g. Security P4 → `C1`).
|
||||
This is the core derivation.
|
||||
4. **Cross-reference the checklist:** load
|
||||
`vendor/review/agent-checklist.md`, find the `If <Domain>` section,
|
||||
extract the checklist items tagged with `P<n>` (the IaC section
|
||||
tags items with `(P1)`, `(P10)` etc.; the Security section items map
|
||||
to P9, P4, P5, P6, P1/P10 by content).
|
||||
5. **Check non-tradeable status:** load
|
||||
`vendor/domains/<domain>/first-principles.md` §3 (Conflict
|
||||
Resolution); if the principle is listed as "never sacrificed", mark
|
||||
`non_tradeable: true` (escalates it to C1-equivalent per
|
||||
`core/conflict-resolution.md` §6).
|
||||
|
||||
**Return (structured output):**
|
||||
```python
|
||||
class PrincipleLookup(BaseModel):
|
||||
domain: str # "security"
|
||||
principle_id: str # "P4"
|
||||
name: str # "Input Validation"
|
||||
text: str # full principle body
|
||||
core_derivation: list[str] # ["C1"] (from the matrix)
|
||||
non_tradeable: bool # True for security P1-P8, P9; False for P10
|
||||
checklist_items: list[str] # the verbatim checklist questions for this P-rule
|
||||
source_path: str # "domains/security/first-principles.md" (relative to vendor/)
|
||||
```
|
||||
|
||||
**Example resolution — `atelier.lookup_principle("security", "P4")`:**
|
||||
- `name`: "Input Validation"
|
||||
- `text`: "All input is untrusted until proven otherwise. Validation
|
||||
happens at the boundary, against a schema, with explicit failure
|
||||
modes."
|
||||
- `core_derivation`: `["C1"]` (matrix row: Security P4 → C1)
|
||||
- `non_tradeable`: `true` (Security §3 lists P4 as "never sacrificed")
|
||||
- `checklist_items`: `["Input is validated at the boundary", "Output is
|
||||
encoded for its context"]` (from `review/agent-checklist.md` If Security)
|
||||
- `source_path`: `"domains/security/first-principles.md"`
|
||||
|
||||
The two companion tools:
|
||||
- `atelier.list_domains()` → returns the 19 domain names + their
|
||||
P-rule counts + relevance flag (the plugin hardcodes the
|
||||
Nova-relevance table from §1.2 so the citizen developer's agent can
|
||||
filter to the 13 relevant / 9 skill-bearing domains).
|
||||
- `atelier.matrix_lookup(domain: str)` → returns the full domain→core
|
||||
mapping for one domain (all 10 P-rules → their core C-rule(s)), used
|
||||
by `validate_against_principles` to set `severity` and by conflict
|
||||
resolution when two findings collide.
|
||||
|
||||
#### 1.6 Recommended Atelier pinned tag to vendor
|
||||
|
||||
**Recommendation: vendor tag `v0.3.6`** (the v0.4 milestone release).
|
||||
|
||||
Evidence (from `https://git.cloudinit.dev/coreci/atelier/releases`,
|
||||
fetched 2026-08-06):
|
||||
- The latest release is **v0.3.6**, dated 2026-08-05 16:22:58 +00:00,
|
||||
tagged `v0.3.6` (commit `66b4767d25`), marked **Stable**, with the
|
||||
title "v0.3.6 — v0.4 milestone: Edge + Messaging + Language-Derived
|
||||
Docs".
|
||||
- It is the **v0.4 milestone release** (the release notes state:
|
||||
"v0.4 — Edge + Messaging + Language-Derived Docs (Milestone
|
||||
Release). Tag: v0.3.6 (NFR milestone — final patch IS the deliverable;
|
||||
no separate minor tag per branch-strategy.md)").
|
||||
- The matrix is at its complete state: **19 domains, 190 P-rules**
|
||||
(the Coverage Summary in `matrix/principles-matrix.md` confirms this
|
||||
exactly; the v0.3.6 release notes confirm "170 → 190 P-rules across
|
||||
19 domains"). All 190 P-rules trace to ≥1 core C-rule (no orphans —
|
||||
verified in the release audit).
|
||||
- `-11 commits to main since this release` — there is post-release
|
||||
activity on `main`, which is exactly why pinning matters: vendoring
|
||||
`main` HEAD would be a moving target. `v0.3.6` is the frozen,
|
||||
audited, reproducible snapshot. This satisfies D-136 (vendor for audit
|
||||
reproducibility) — an agentic validation result must be replayable
|
||||
against the exact principles that produced it.
|
||||
|
||||
**Vendoring mechanics (for REQ-224):**
|
||||
- `mcp/atelier/vendor/` = a clean copy of the Atelier repo at tag
|
||||
`v0.3.6` (the `core/`, `domains/`, `matrix/`, `review/` directories —
|
||||
the docs the MCP tools read; `examples/` and `languages/` are optional
|
||||
but cheap to include for completeness).
|
||||
- `mcp/atelier/vendor/VERSION.md` records: tag `v0.3.6`, commit
|
||||
`66b4767d25`, date 2026-08-05, milestone "v0.4 Edge + Messaging +
|
||||
Language-Derived Docs", P-rule count 190, domain count 19.
|
||||
- `scripts/update_atelier_vendor.sh` = a helper that takes a tag arg,
|
||||
fetches the tarball from
|
||||
`https://git.cloudinit.dev/coreci/atelier/archive/<tag>.tar.gz`,
|
||||
extracts the doc directories into `mcp/atelier/vendor/`, and updates
|
||||
`VERSION.md`. Intentional upgrades only (re-run + re-audit).
|
||||
|
||||
Confidence: 0.95. The only risk is that a v0.5 milestone lands before
|
||||
P5 ships — but the pinning model (VERSION.md + update script) makes a
|
||||
future upgrade a deliberate, audited action, not a silent drift.
|
||||
|
||||
---
|
||||
|
||||
### 2. MCP Python SDK v2 Reference
|
||||
|
||||
Source: `https://py.sdk.modelcontextprotocol.io/` (the official Python
|
||||
SDK docs, fetched 2026-08-06) + the Tools page
|
||||
(`.../servers/tools/`) + the Structured Output page
|
||||
(`.../servers/structured-output/`). The docs document **v2, the current
|
||||
stable release line** (Python 3.10+).
|
||||
|
||||
#### 2.1 Confirmed API patterns
|
||||
|
||||
1. **Server creation + import path.** The v2 high-level server class is
|
||||
`MCPServer` (NOT `FastMCP` — that was v1; v2 renamed/restructured):
|
||||
```python
|
||||
from mcp.server import MCPServer
|
||||
mcp = MCPServer("atelier") # one arg = server name
|
||||
```
|
||||
This is the exact pattern shown in the docs' landing-page example and
|
||||
the Tools-page example. There is no `FastMCP` import in v2.
|
||||
|
||||
2. **`@mcp.tool()` decorator — inputSchema from type hints.** Confirmed
|
||||
verbatim from the docs: "No JSON Schema. `a: int, b: int` *is* the
|
||||
schema." The SDK reads three things from the function:
|
||||
- **name** = the function name (`search_books`)
|
||||
- **description** = the docstring (the model sees this)
|
||||
- **arguments** = the type hints (`query: str`, `limit: int`)
|
||||
The SDK generates the JSON Schema and sends it during `tools/list`.
|
||||
Type hints are **the contract** — if a client sends `"limit": "ten"`,
|
||||
the SDK rejects it *before the function runs*. Optional args =
|
||||
default values (`limit: int = 10` → leaves `required`, gains
|
||||
`default: 10`). Richer constraints via
|
||||
`Annotated[int, Field(ge=1, le=50, description="...")]`. Enums via
|
||||
`Literal["a", "b"]`. Pydantic `BaseModel` parameter = structured
|
||||
"body" (nested as `$defs`).
|
||||
|
||||
3. **Multiple tools / dynamic registration (plugin-registry).** The
|
||||
`@mcp.tool()` decorator is called on the `mcp` object. A plugin
|
||||
receives `mcp` and calls `@mcp.tool()` on it — this is plain Python
|
||||
decorator application, no registration magic. The plugin-registry
|
||||
pattern (D-140):
|
||||
```python
|
||||
# plugins/principles.py
|
||||
from mcp.server import MCPServer
|
||||
def register(mcp: MCPServer) -> None:
|
||||
@mcp.tool()
|
||||
def atelier_lookup_principle(domain: str, principle_id: str) -> PrincipleLookup:
|
||||
"""Look up an Atelier principle by domain + ID."""
|
||||
...
|
||||
```
|
||||
`server.py` scans `plugins/`, imports each module, calls
|
||||
`register(mcp)`. Each plugin's `@mcp.tool()` calls register the tool
|
||||
on the shared `mcp` object. **This is the confirmed dynamic-
|
||||
registration pattern** — no `add_tool()` API is needed; the decorator
|
||||
does it.
|
||||
|
||||
4. **stdio transport.** The landing-page example shows `uv run mcp dev
|
||||
server.py` (Inspector). For stdio transport (D-135: stdio now), the
|
||||
server runs over stdio via the SDK's run entry point. The v2 server
|
||||
object supports stdio as the default transport. The exact run call is
|
||||
`mcp.run()` (the SDK handles the transport based on how the process
|
||||
is launched — stdio when invoked by an MCP host over stdio). The
|
||||
README's "no protocol handling" promise means `mcp.run()` is the only
|
||||
call needed. (HTTP transport is on the same server object — Out of
|
||||
Scope for v1.18, future milestone; the server object is
|
||||
transport-agnostic so adding HTTP later is a transport-only change,
|
||||
confirming D-135.)
|
||||
|
||||
5. **outputSchema / structured output.** Confirmed: **the return type
|
||||
annotation IS the output schema.** From the Structured Output page:
|
||||
"the return type annotation is the output schema. It's published in
|
||||
`tools/list` as `output_schema`." A Pydantic `BaseModel` return type
|
||||
produces an unwrapped object schema (no `result` wrapper); a
|
||||
`TypedDict` or `dataclass` works identically. The result carries
|
||||
both `content` (text, for the model) and `structured_content` (data,
|
||||
for the application). **Validation is enforced**: whatever the
|
||||
function returns is validated against the schema before it leaves the
|
||||
server — a mismatch is a tool error (not a corrupt result). This is
|
||||
exactly what `atelier.validate_against_principles` needs: a
|
||||
`ValidateResult(BaseModel)` return type gives the host a structured
|
||||
`violations` list while giving the model a JSON-text rendering of the
|
||||
same object. `structured_output=False` opts out (text-only); we do
|
||||
NOT opt out for the validation tool.
|
||||
|
||||
6. **`listChanged` capability / dynamic tool registration.** The v2
|
||||
docs (Tools page + landing page) describe tool registration as
|
||||
declarative (`@mcp.tool()` at import time). The docs do NOT document
|
||||
a runtime `listChanged` notification API on the high-level
|
||||
`MCPServer`. For Nova's use case (plugins loaded once at server
|
||||
startup, not added/removed at runtime), this is fine — all 4 tools
|
||||
are registered before `mcp.run()`. A future milestone that adds
|
||||
tools at runtime would need the low-level Server
|
||||
(`advanced/low-level-server/`) for explicit notification control.
|
||||
**Conclusion: no `listChanged` needed for v1.18; the plugin-registry
|
||||
loads at startup, before the stdio loop.** Confidence 0.85 (the docs
|
||||
are silent on a high-level `listChanged`; the low-level server has
|
||||
it, but we use the high-level server).
|
||||
|
||||
#### 2.2 Skeleton for `mcp/atelier/server.py` (P5 basis)
|
||||
|
||||
This is the 15-line pattern Nova's server should follow (the basis for
|
||||
P5 implementation):
|
||||
|
||||
```python
|
||||
import importlib, pathlib
|
||||
from mcp.server import MCPServer
|
||||
|
||||
mcp = MCPServer("atelier") # server name; stdio transport is the default
|
||||
|
||||
# Plugin-registry: scan plugins/, import each, call register(mcp).
|
||||
for p in sorted(pathlib.Path(__file__).parent.glob("plugins/*.py")):
|
||||
if p.stem != "__init__": importlib.import_module(f".plugins.{p.stem}", __package__).register(mcp)
|
||||
|
||||
@mcp.tool()
|
||||
def atelier_list_domains() -> list[dict]:
|
||||
"""List the 19 Atelier domains with P-rule counts + Nova-relevance."""
|
||||
return [{"domain": "security", "p_rules": 10, "nova_relevant": True}, ...]
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run() # stdio transport (D-135); HTTP-ready on the same object (future)
|
||||
```
|
||||
|
||||
**Notes on the skeleton:**
|
||||
- `MCPServer("atelier")` — one import, one constructor arg (the name).
|
||||
- The plugin loop uses `importlib` + a `register(mcp)` convention (D-140).
|
||||
Each plugin's `register` body contains `@mcp.tool()` calls that
|
||||
register that plugin's tools on the shared `mcp` object. `sorted()`
|
||||
makes plugin load order deterministic (audit reproducibility — a
|
||||
plugin load order that changes between runs would break replay).
|
||||
- The sample tool shows the pattern: `@mcp.tool()`, type hints ARE the
|
||||
input schema, docstring IS the description, return type IS the
|
||||
output schema. The real `atelier_list_domains` returns a
|
||||
`list[DomainInfo]` (a `list[BaseModel]` → wrapped in `{"result": [...]}`,
|
||||
per the Structured Output docs).
|
||||
- `mcp.run()` — the single entry point; stdio is the default. No
|
||||
transport boilerplate. Adding HTTP later = a transport argument or a
|
||||
different run call on the same object (D-135, Out of Scope for v1.18).
|
||||
- The vendored Atelier snapshot (`mcp/atelier/vendor/`) is read by the
|
||||
plugin tool functions (not shown in the skeleton); the plugins load
|
||||
the markdown files lazily on first tool call and cache the parsed
|
||||
structure in module-level dicts (C8 Economy — don't re-parse the
|
||||
matrix on every lookup).
|
||||
|
||||
---
|
||||
|
||||
### 3. Submission-Readiness Gap Analysis
|
||||
|
||||
#### 3.1 `contract.schema.json` defines SHAPE, not the readiness gate
|
||||
|
||||
Confirmed by reading `/root/acdl/schemas/contract.schema.json` (51
|
||||
lines). The schema defines the **contract shape** only:
|
||||
- `required`: `["id", "name", "environment", "infrastructure"]`
|
||||
- `id`: pattern `^[a-z][a-z0-9-]{2,5}$` (3–6 char acronym)
|
||||
- `name`: minLength 3
|
||||
- `environment`: enum `["dev", "qa", "prod", "dr"]`
|
||||
- `infrastructure`: map keyed by module name, each entry has `version`
|
||||
(optional semver) + `inputs` (required, additionalProperties allowed)
|
||||
- `additionalProperties: false` (top-level + per-module)
|
||||
|
||||
**What it does NOT define (the gap):**
|
||||
- ❌ No `tags` field (the 5 required Nova tags per D-054)
|
||||
- ❌ No per-env mandatory metadata (the W3.E table: dev=stack+environment;
|
||||
qa+=e2eSuite+loadTest; prod+=runbook+dashboard+oncall; dr+=drDrillRef)
|
||||
- ❌ No `policyPreconditions` field (declared policy expectations)
|
||||
- ❌ No `profile` field (`developer` | `agentic`; agentic requires
|
||||
`naturalLanguageIntent`, `confidenceAtSubmission`, `agentTrace`)
|
||||
- ❌ No `appSource` field (repo + ref pointer for runtime fetch)
|
||||
- ❌ No `contractId` field at the top level (the ingestor payload has
|
||||
`contractId` in the Lambda envelope, but the contract *blob* itself
|
||||
does not — the readiness schema promotes it to a required field per
|
||||
REQ-217)
|
||||
|
||||
The schema's own description confirms this is the shape: "A consumer
|
||||
contract declares intent: which infrastructure to deploy, in which
|
||||
environment, with which inputs." It is the *intent shape*, not the
|
||||
*ready-to-start gate*.
|
||||
|
||||
#### 3.2 The readiness schema is a SUPERSET gate ABOVE contract-schema validity
|
||||
|
||||
Confirmed by PROJECT.md (lines 635–643, the v1.18 scope statement) and
|
||||
REQ-217. The relationship:
|
||||
|
||||
```
|
||||
contract.schema.json (SHAPE — id/name/environment/infrastructure)
|
||||
▲
|
||||
│ references but does NOT redefine contract fields
|
||||
│
|
||||
submission-readiness.schema.json (GATE — superset above shape validity)
|
||||
= contract-shape-valid (delegate to contract.schema.json)
|
||||
+ tags (5 required Nova tags, D-054)
|
||||
+ per-env mandatory (W3.E table)
|
||||
+ policyPreconditions (declared policy expectations)
|
||||
+ profile (developer | agentic + agentic markers)
|
||||
+ appSource (repo + ref pointer)
|
||||
+ contractId (non-empty, promoted to required)
|
||||
```
|
||||
|
||||
PROJECT.md hard constraint (line 674–676): "The submission-readiness
|
||||
schema is a superset gate above `contract.schema.json`, NOT a
|
||||
duplicate — it references but does not redefine contract fields."
|
||||
|
||||
This means `submission-readiness.schema.json` uses
|
||||
`$ref` to `contract.schema.json` for the contract shape (or validates
|
||||
the contract blob against it as a first step), then adds the gate
|
||||
fields *alongside* it. The validator (REQ-218) calls
|
||||
`contract.schema.json` validation **first** (the existing
|
||||
`_validate_contract_schema` in the ingestor), then the readiness
|
||||
checks. This is a two-layer gate, not a merged schema.
|
||||
|
||||
#### 3.3 Fields the new `schemas/submission-readiness.schema.json` must add
|
||||
|
||||
Per REQ-217 + W3.E (PROJECT.md line 888) + D-054 (tagging standard):
|
||||
|
||||
| Field | Type | Required | Source / rule |
|
||||
|-------|------|----------|---------------|
|
||||
| `contractId` | string (non-empty) | **YES** | REQ-217. Promoted from the Lambda envelope to a contract-level required field. |
|
||||
| `environment` | enum `dev/qa/prod/dr` | **YES** | Already in `contract.schema.json`; the readiness schema references it (does not redefine) and uses it to select the per-env mandatory set. |
|
||||
| `tags` | object | **YES** | D-054 / `schemas/tagging-standard.json`. Required keys: `nova:owner`, `nova:contract`, `nova:environment`, `nova:cost-center` (`nova:ref` optional). The readiness schema references `tagging-standard.json`'s `required_tags` shape. |
|
||||
| `policyPreconditions` | object (map of string→boolean/string) | **YES** | REQ-217. Declared policy expectations the platform will enforce (e.g. `{"public-ingress": false}`). |
|
||||
| `profile` | enum `developer` \| `agentic` | **YES** | REQ-217 / W3.E. |
|
||||
| `profile` == `agentic` → requires: `naturalLanguageIntent` (string), `confidenceAtSubmission` (number 0–1), `agentTrace` (object/string) | per W3.E | **conditional** | REQ-22 / W3.E. These are "optional everywhere" per W3.E (a `developer` profile omits them) but **required when profile is `agentic`**. |
|
||||
| `appSource` | object `{repo: string, ref: string}` | **YES** | REQ-217. Repo + ref pointer for runtime fetch. |
|
||||
| **Per-env mandatory (W3.E):** | | | |
|
||||
| `dev` | `stack` + `environment` | **YES** | W3.E. (These are the base contract fields; the readiness schema enforces their presence for dev.) |
|
||||
| `qa` adds | `validation.e2eSuite` + `validation.loadTest` | **YES for qa** | W3.E. |
|
||||
| `prod` adds | `runbook` + `dashboard` + `oncall` | **YES for prod** | W3.E. |
|
||||
| `dr` adds | `drDrillRef` | **YES for dr** | W3.E. |
|
||||
| `inputs` map | object | optional everywhere | W3.E ("inputs map is always optional"). |
|
||||
|
||||
The per-env mandatory table is a **conditional `allOf`** in JSON Schema
|
||||
draft 2020-12: an `if`/`then` keyed on `environment` that requires the
|
||||
env-specific fields. The reason code
|
||||
`ENV_MISSING_MANDATORY:<env>:<field>` (REQ-218) maps directly to this
|
||||
conditional check.
|
||||
|
||||
#### 3.4 How `contract_ingestor.py` currently works (P3 wiring point)
|
||||
|
||||
Read `/root/acdl/core/lambda/contract_ingestor.py` (502 lines). The
|
||||
current entry point + dispatch:
|
||||
|
||||
- **Entry point:** `lambda_handler(event, context)` (line 460). Parses
|
||||
`event["body"]` (JSON string) → `payload`. Reads `action` (default
|
||||
`"submit_contract"`).
|
||||
- **Identity validation:** `_validate_caller_identity(event, payload)`
|
||||
(line 293) — checks IAM caller ARN, `consumerRepo` format,
|
||||
`contractId` format (regex `^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$`),
|
||||
`environment` enum (from `core/environments/*.json`, P10/REQ-174),
|
||||
error-length cap. Fails closed if no IAM identity (P10).
|
||||
- **Action dispatch (line 475):**
|
||||
- `submit_contract` → `_submit_contract(payload)` (line 135):
|
||||
validates required fields (`consumerRepo`, `contractId`, `contract`,
|
||||
`environment`), size-caps the contract blob (256 KB, P11/REQ-175),
|
||||
calls `_validate_contract_schema(contract)` (line 57 — validates
|
||||
against `schemas/contract.schema.json` via `jsonschema`; no-op if
|
||||
schema/jsonschema unavailable; bypassed by `NOVA_LAMBDA_LOCAL_BYPASS`),
|
||||
writes to DynamoDB `nova-contracts` (PK `consumerRepo`, SK
|
||||
`contractId#submittedAt`).
|
||||
- `report_error` → `_report_error` (D-055, GitHub/Gitea issue).
|
||||
- `validate_change_request` → `_validate_change_request` (REQ-93).
|
||||
- `onboard_consumer` → `_onboard_consumer` (P18/REQ-182, validates
|
||||
against `schemas/onboarding.schema.json`).
|
||||
- **Error mapping:** ValueError → 400 (or 401 for identity failures);
|
||||
other Exception → 500 (defensive top-level guard, `pragma: no cover`).
|
||||
|
||||
**Where P3 adds `--check-readiness` (D-133):**
|
||||
|
||||
The ingestor is a **Lambda handler**, not a CLI. D-133 says the
|
||||
validator is "invoked as `contract_ingestor.py --check-readiness`
|
||||
subcommand" — this is a **local CLI mode** for citizen-developer
|
||||
pre-flight validation, NOT a new Lambda action. The implementation
|
||||
pattern (confirmed by the existing code structure):
|
||||
|
||||
1. Add a `if __name__ == "__main__":` block at the bottom of
|
||||
`contract_ingestor.py` that parses `sys.argv` (argparse or manual).
|
||||
The existing file has NO `__main__` block (it's Lambda-only); P3
|
||||
adds one.
|
||||
2. The `--check-readiness` subcommand loads a contract file (or reads
|
||||
stdin), validates it against
|
||||
`schemas/submission-readiness.schema.json` (REQ-217) via the new
|
||||
`core/submission_readiness.py` validator (REQ-218), and prints a
|
||||
structured `ReadinessResult` (pass/fail per check + reason codes).
|
||||
3. The validator (`core/submission_readiness.py`) calls
|
||||
`_validate_contract_schema(contract)` first (reusing the existing
|
||||
function — the shape gate), then runs the readiness checks (tags,
|
||||
per-env mandatory, policyPreconditions, profile:agentic markers,
|
||||
appSource).
|
||||
4. On fail → the CLI exits non-zero with a **citizen-developer-facing
|
||||
error** (not a stack trace) — REQ-218. On pass → proceeds to
|
||||
existing ingestion (in the Lambda path, the readiness check would
|
||||
be a pre-write gate; in the CLI path, it's a pre-flight check that
|
||||
returns 0).
|
||||
|
||||
**Reason codes (REQ-218, the validator's return vocabulary):**
|
||||
`MISSING_TAGS`, `ENV_MISSING_MANDATORY:<env>:<field>`,
|
||||
`AGENTIC_MISSING_INTENT`, `MISSING_APP_SOURCE`,
|
||||
`POLICY_PRECONDITION_MISSING`. Each maps to a failed check in the
|
||||
schema's conditional `allOf`. The validator returns a list of these
|
||||
(not a single error) so a citizen developer sees *all* gaps at once,
|
||||
not one-at-a-time (C2 Clarity — the reader understands the full scope
|
||||
of fixes needed).
|
||||
|
||||
#### 3.5 Gitea release-asset API endpoint (for `scripts/attach_release_asset.py`)
|
||||
|
||||
Confirmed from the existing `scripts/ship_phase.sh` (line 38) which
|
||||
already uses the Gitea releases API, and from the Gitea API swagger
|
||||
(`https://gitea.com/api/swagger`, fetched — the OpenAPI/Swagger JSON is
|
||||
published there; the endpoint is standard Gitea).
|
||||
|
||||
**Release creation (existing pattern, `ship_phase.sh` line 38):**
|
||||
```
|
||||
POST https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases
|
||||
Authorization: token <NOVA_GITEA_TOKEN>
|
||||
Content-Type: application/json
|
||||
Body: {"tag_name": "...", "name": "...", "body": "..."}
|
||||
Response: {"id": <release_id>, ...}
|
||||
```
|
||||
|
||||
**Release asset attachment (the new endpoint, for
|
||||
`attach_release_asset.py`):**
|
||||
```
|
||||
POST https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases/{release_id}/assets
|
||||
Authorization: token <NOVA_GITEA_TOKEN>
|
||||
Content-Type: multipart/form-data
|
||||
Form fields:
|
||||
name = <filename, e.g. "nova-no-humans-platform.pptx">
|
||||
attachment = <the file, multipart>
|
||||
Response: {"id": <asset_id>, "name": "...", "size": ..., "download_count": 0, ...}
|
||||
```
|
||||
|
||||
The Gitea API endpoint is `POST
|
||||
/api/v1/repos/{owner}/{repo}/releases/{id}/assets` with a **multipart
|
||||
form** containing `name` (the display filename) and `attachment` (the
|
||||
file binary). The `{id}` is the numeric release ID returned by the
|
||||
release-creation call (the `d.get('id')` in `ship_phase.sh` line 40).
|
||||
`attach_release_asset.py` (REQ-228) takes a release tag (or ID) + a
|
||||
file path, resolves the tag → release ID (GET
|
||||
`/api/v1/repos/.../releases/tags/{tag}` if only the tag is known), then
|
||||
POSTs the multipart form. The token comes from `.env.secrets`
|
||||
(`NOVA_GITEA_TOKEN`, same as `ship_phase.sh` line 35).
|
||||
|
||||
**Implementation note:** `urllib` (used throughout `contract_ingestor.py`
|
||||
and `ship_phase.sh`) does not natively produce multipart form bodies —
|
||||
`attach_release_asset.py` must either (a) construct the multipart
|
||||
boundary + body manually (the standard `urllib` pattern), or (b) use
|
||||
`requests` if available. The repo's convention is stdlib-only
|
||||
(`urllib`, no `requests` dependency in the ingestor), so the script
|
||||
should construct the multipart body manually (C3 Simplicity — no new
|
||||
dependency for one script; C8 Economy — stdlib is sufficient). A
|
||||
~30-line `multipart_encode(fields, files)` helper is the standard
|
||||
stdlib pattern.
|
||||
|
||||
---
|
||||
|
||||
### 4. Marp PPTX Theme Fidelity
|
||||
|
||||
#### 4.1 The PPTX export path and inline-CSS survival
|
||||
|
||||
Source: the Marp CLI README (`https://github.com/marp-team/marp-cli`,
|
||||
fetched) + the existing `docs/presentations/README.md` (lines 93–105)
|
||||
+ the v1.9.2 theme commit `ae0cb58` (verified via `git show`).
|
||||
|
||||
**Confirmed export command (from `docs/presentations/README.md` line
|
||||
96–99):**
|
||||
```bash
|
||||
CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
|
||||
npx --yes @marp-team/marp-cli@latest --allow-local-files \
|
||||
docs/presentations/nova-no-humans-platform-marp.md \
|
||||
-o <output-path>.pptx
|
||||
```
|
||||
|
||||
**How PPTX export works (from the Marp CLI README, `--pptx` section):**
|
||||
The default (non-editable) PPTX "consists of **pre-rendered background
|
||||
images**." Marp renders each slide in a headless browser (Chrome/Chromium
|
||||
via the `--browser-path` / `CHROME_PATH` env), captures the rendered
|
||||
slide as a high-resolution image (default scale factor 2x — the README
|
||||
states: "By default, Marp CLI will use 2 as the default scale factor in
|
||||
PPTX"), and embeds those images as full-slide background pictures in the
|
||||
PPTX. Presenter notes are supported; the PPTX opens in PowerPoint,
|
||||
Keynote, Google Slides, LibreOffice Impress.
|
||||
|
||||
**Inline `style:` CSS survival — CONFIRMED YES.** Because the slides
|
||||
are **rasterized in a headless browser**, the browser's rendering engine
|
||||
applies the inline `style:` CSS block (H1/H2 `#D6002A`, title-slide bg
|
||||
`#1B1B1B` with 8px `#D6002A` accent, body text `#1B1B1B`, blockquote
|
||||
border `#D6002A`, table headers `#F0F0F0`, font `'Akkurat Pro'` +
|
||||
fallbacks) exactly as it does for HTML export. The CSS is *baked into
|
||||
the pixels* of each slide image. The PPTX is a sequence of images, not
|
||||
editable PPTX shapes — so there is no "CSS stripping" step. The S&P
|
||||
Global Energy theme **survives PPTX export** in the standard
|
||||
(non-editable) path.
|
||||
|
||||
The current unified deck (`docs/presentations/nova-no-humans-platform-marp.md`)
|
||||
already has the `style: |` block in its frontmatter (verified: line 8
|
||||
`style: |`, line 2 `marp: true`, line 3 `theme: default`). So the S&P
|
||||
theme is already inline; PPTX export will honor it.
|
||||
|
||||
**Caveat — `--pptx-editable` (NOT used):** The experimental
|
||||
`--pptx-editable` flag generates editable PPTX (texts/shapes, not
|
||||
images), and the README warns: "If the theme and inline styles are
|
||||
providing complex styles into the slide, `--pptx-editable` may throw an
|
||||
error or output the incomplete result." Nova does NOT use
|
||||
`--pptx-editable` (the S&P theme is complex inline CSS); the standard
|
||||
image-based PPTX is the path. REQ-228 specifies `--pptx
|
||||
--allow-local-files`, not `--pptx-editable`.
|
||||
|
||||
#### 4.2 Fallback (NOT needed, documented for completeness)
|
||||
|
||||
If PPTX export ever strips inline CSS (it does NOT in the standard
|
||||
path, per §4.1), the fallback is a **Marp custom theme CSS file**
|
||||
referenced via `--theme <path>`:
|
||||
|
||||
```bash
|
||||
CHROME_PATH=... npx @marp-team/marp-cli@latest --allow-local-files \
|
||||
--theme docs/presentations/assets/sp-theme.css \
|
||||
docs/presentations/nova-no-humans-platform-marp.md \
|
||||
-o output.pptx
|
||||
```
|
||||
|
||||
Marp CLI supports custom theme CSS files via `--theme <path>` (the
|
||||
README's "Use custom theme" section: "A custom theme created by user
|
||||
also can use easily by passing the path of CSS file"). The CSS file
|
||||
would be `docs/presentations/assets/sp-theme.css` containing the same
|
||||
rules currently in the inline `style:` block, prefixed with the
|
||||
`@theme` meta comment (Marpit convention: `/* @theme sp-energy */`).
|
||||
The deck's frontmatter `theme:` directive would then be set to the
|
||||
custom theme name instead of `default`.
|
||||
|
||||
**Recommendation: do NOT use the fallback.** The inline `style:` block
|
||||
survives the standard PPTX path (rasterized images). The fallback adds
|
||||
a file to maintain in sync with the inline block (a DRY violation —
|
||||
two sources of truth for the S&P colors). REQ-214 restores the S&P
|
||||
theme *in the unified deck's inline `style:` block* (the v1.9.2
|
||||
pattern); the PPTX export uses the same deck file. **The S&P colors
|
||||
survive PPTX export via the inline `style:` block. No `--theme` flag,
|
||||
no separate CSS file needed.** Confidence 0.90 (the only residual risk
|
||||
is a Marp CLI version regression that changes the rasterization path —
|
||||
mitigated by `@marp-team/marp-cli@latest` pinning in the render script
|
||||
and the PPTX slide-count/media verification step already in
|
||||
`docs/presentations/README.md` lines 332–340).
|
||||
|
||||
#### 4.3 `ship_phase.sh` release pattern + `attach_release_asset.py` extension
|
||||
|
||||
Confirmed from `scripts/ship_phase.sh` (read in full, 46 lines):
|
||||
|
||||
- **Line 38:** `POST
|
||||
https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases`
|
||||
with `Authorization: token <NOVA_GITEA_TOKEN>` (read from
|
||||
`.env.secrets`, line 35) + JSON body `{"tag_name", "name", "body"}`
|
||||
(line 37). The response's `id` is the release ID (line 40:
|
||||
`d.get('id')`).
|
||||
- The script creates the tag, pushes, creates the release, prints
|
||||
`release_id: <id> tag: <tag>`.
|
||||
|
||||
**How `attach_release_asset.py` extends it (REQ-228):**
|
||||
`attach_release_asset.py` is a **separate script** (not a modification
|
||||
to `ship_phase.sh`) that runs *after* the release exists. It takes a
|
||||
release tag (or ID) + a file path, then:
|
||||
1. **Resolve tag → release ID** (if only the tag is known): `GET
|
||||
/api/v1/repos/continuous-intelligence/acdl/releases/tags/{tag}` →
|
||||
the release object's `id`.
|
||||
2. **Upload the asset:** `POST
|
||||
/api/v1/repos/continuous-intelligence/acdl/releases/{id}/assets`
|
||||
with multipart form (`name` = filename, `attachment` = file binary)
|
||||
+ `Authorization: token <NOVA_GITEA_TOKEN>` (same `.env.secrets`
|
||||
source).
|
||||
3. **Print** `asset_id: <id> release: <tag> file: <name>` for the
|
||||
ship log.
|
||||
|
||||
The render+attach flow (REQ-228, triggered by any
|
||||
`docs/presentations/*-marp.md` or `docs/presentations/assets/` change,
|
||||
D-142):
|
||||
```
|
||||
render_deck.sh → HTML (committed) + PPTX (committed, D-141)
|
||||
↓
|
||||
attach_release_asset.py → PPTX uploaded to the phase's Gitea release
|
||||
```
|
||||
|
||||
PPTX is a **first-class artifact** (PROJECT.md line 682–683): committed
|
||||
to git (history) + attached to the release (download) — both always,
|
||||
not optional. This is the D-141 decision (no LFS — the binary is
|
||||
committed directly).
|
||||
|
||||
---
|
||||
|
||||
### 5. Assumptions logged (v1.18)
|
||||
|
||||
- **A1 (0.92):** The Atelier `v0.3.6` tag is the correct pin. It is the
|
||||
latest release (2026-08-05), the v0.4 milestone release, and the
|
||||
complete matrix state (19 domains, 190 P-rules). `-11 commits to main
|
||||
since this release` confirms `main` is a moving target — pinning is
|
||||
required for audit reproducibility (D-136). Risk: a v0.5 lands before
|
||||
P5 ships — mitigated by VERSION.md + update script (deliberate
|
||||
upgrade, not silent drift).
|
||||
- **A2 (0.88):** The MCP Python SDK v2 high-level server class is
|
||||
`MCPServer` (import `from mcp.server import MCPServer`), NOT
|
||||
`FastMCP`. The docs (landing page + Tools page) use `MCPServer`
|
||||
consistently; `FastMCP` was the v1 name. D-137 (MCP Python SDK v2)
|
||||
resolves to this import. Risk: the v1→v2 rename — if a future SDK
|
||||
patch restores a `FastMCP` alias, both imports would work, but the v2
|
||||
canonical name is `MCPServer`.
|
||||
- **A3 (0.85):** `mcp.run()` starts the stdio transport by default (no
|
||||
explicit transport argument needed for the stdio path). The docs
|
||||
show `uv run mcp dev server.py` (Inspector) and the "no protocol
|
||||
handling" promise implies `mcp.run()` is the single entry point. The
|
||||
exact `run()` signature for stdio vs HTTP is not spelled out on the
|
||||
landing page (it's in the "Running your server" section, not fetched
|
||||
in full); the D-135 decision (stdio now, HTTP-ready on the same
|
||||
object) is consistent with a single `run()` entry point. P5
|
||||
implementation should verify the exact run call from the
|
||||
"Running your server" docs page.
|
||||
- **A4 (0.90):** The standard (non-editable) PPTX export bakes inline
|
||||
`style:` CSS into the rasterized slide images. The Marp README
|
||||
states PPTX "consists of pre-rendered background images" — the
|
||||
browser rendering applies the CSS before rasterization. The S&P
|
||||
theme survives PPTX export. The `--pptx-editable` path (NOT used) is
|
||||
the only path that could strip CSS, and Nova does not use it.
|
||||
- **A5 (0.88):** The Gitea release-asset endpoint is `POST
|
||||
/api/v1/repos/{owner}/{repo}/releases/{id}/assets` with multipart
|
||||
`name` + `attachment`. This is the standard Gitea API (the swagger at
|
||||
`gitea.com/api/swagger` publishes the OpenAPI spec); the existing
|
||||
`ship_phase.sh` uses the sibling `.../releases` endpoint, confirming
|
||||
the API root + auth pattern. The `{id}` is the numeric release ID
|
||||
(resolvable from the tag via `GET .../releases/tags/{tag}`).
|
||||
- **A6 (0.85):** The submission-readiness schema uses JSON Schema draft
|
||||
2020-12 conditional `allOf` / `if-then` for the per-env mandatory
|
||||
table (W3.E). This is the standard pattern for "if environment=qa
|
||||
then require validation.e2eSuite + validation.loadTest." The
|
||||
`jsonschema` library (already a dependency, used in
|
||||
`contract_ingestor.py`) supports draft 2020-12 conditionals. The
|
||||
validator (`core/submission_readiness.py`) may implement the per-env
|
||||
check in Python (clearer reason codes) rather than relying solely on
|
||||
schema conditionals — the schema is the *shape*, the validator is
|
||||
the *gate* with the citizen-developer-facing reason codes (REQ-218).
|
||||
- **A7 (0.80):** The `--check-readiness` CLI mode is added as a
|
||||
`if __name__ == "__main__":` block in `contract_ingestor.py` (which
|
||||
currently has none — it's Lambda-only). D-133 says "invoked as
|
||||
`contract_ingestor.py --check-readiness`" — this is a local
|
||||
pre-flight CLI, not a new Lambda action. The validator lives in
|
||||
`core/submission_readiness.py` (REQ-218); the ingestor dispatches to
|
||||
it. This keeps the Lambda path unchanged (the readiness gate is a
|
||||
pre-write step in `_submit_contract` only if desired; the CLI path
|
||||
is the citizen-developer pre-flight). Risk: the exact wiring (does
|
||||
the Lambda also gate on readiness, or only the CLI?) is a P3
|
||||
implementation decision — REQ-218 says "On pass → proceeds to
|
||||
existing contract ingestion," implying the gate is in the
|
||||
submission path, but the CLI mode is the pre-flight surface.
|
||||
- **A8 (0.90):** The 9-skill list in REQ-221 is final (no adjustment).
|
||||
The research confirms the 9 Atelier domains map cleanly to the BA.A
|
||||
5-skill catalog; the 4 "reference-only" domains (Performance,
|
||||
Documentation, Concurrency, AI/ML) are correctly NOT elevated to
|
||||
skills. Adding a 10th skill would break REQ-221's exact list and the
|
||||
BA.A mapping.
|
||||
- **A9 (0.88):** The `mcp-engineer` persona is NOT needed — it folds
|
||||
into backend-engineer. The MCP plugin-registry (D-140) is a Python
|
||||
backend pattern (decorators, type hints, stdio, urllib). The SDK v2
|
||||
API surface is small and FastAPI/Pydantic-style (already in
|
||||
backend-engineer's range). D-143 logged in PERSONAS.md records this.
|
||||
|
||||
@@ -1683,3 +1683,212 @@ deferred (D-113/D-114).
|
||||
|
||||
Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS
|
||||
the release). **DONE.**
|
||||
|
||||
## v1.18 (complete — Citizen Developer & Production-Grade Guidance, tag line `v1.17.x`)
|
||||
|
||||
Nova advances from a platform that governs infrastructure delivery to one
|
||||
that **instructs the citizen developer on production-grade engineering**
|
||||
and defines a **clear, machine-checkable contract for what is acceptable
|
||||
to start**. Five user-directed inputs drive the milestone:
|
||||
|
||||
1. **S&P Global theme restoration** (P1) — the v1.17 P5 deck rebuild lost
|
||||
the S&P Global Energy brand visual identity (introduced v1.9.2 / P45).
|
||||
The Marp `style:` block (`#D6002A` red, `#1B1B1B` grey-90, Akkurat Pro,
|
||||
8px accent bar) is restored to the unified deck.
|
||||
2. **PDLC-upstream scope** (P2) — promotes Core Tenet #2 + Anti-Goal #1
|
||||
from buried tenets to a dedicated, unmissable scope statement: the PDLC
|
||||
is upstream of Nova; Nova governs infra + delivery only.
|
||||
3. **RACI matrix** (P2) — three-role responsibility matrix (Citizen
|
||||
Developer / Platform / Release Management co-owned) clarifies who owns
|
||||
what, with the compliance-standard-equivalence note.
|
||||
4. **Nova input contract** (P3) — `schemas/submission-readiness.schema.json`
|
||||
+ `core/submission_readiness.py` validator define "what is acceptable to
|
||||
start" as a superset gate above contract-schema validity.
|
||||
5. **Atelier integration** (P4+P5) — skills (markdown, extending BA.A) + an
|
||||
MCP server (plugin-registry, vendored Atelier, agentic validation
|
||||
beyond Wiz/Checkmarx/Mend).
|
||||
|
||||
**Milestone type:** Feature (P1 theme restoration + P3 schema/validator +
|
||||
P5 MCP server are new code). Tags run on the v1.17.x patch line:
|
||||
`v1.17.0` (P0) → `v1.17.1..v1.17.6` (P1–P6) → `v1.17.7` (P7 final =
|
||||
milestone release).
|
||||
|
||||
**Deck automation (cross-cutting, REQ-228):** any phase modifying
|
||||
`docs/presentations/*-marp.md` or `docs/presentations/assets/` re-renders
|
||||
HTML + PPTX, commits the PPTX binary to git, and attaches it to the
|
||||
phase's Gitea release.
|
||||
|
||||
**Phase count:** 8 (P0 pre-execution + 6 execution + 1 final).
|
||||
|
||||
**Phases:**
|
||||
- **P1 — sp-theme-restoration** (feat): restore S&P Global Marp theme to
|
||||
unified deck + HTML re-render + PPTX commit + release attach. REQ-214,228.
|
||||
- **P2 — pdlc-scope-raci** (docs): PDLC-upstream scope + RACI matrix +
|
||||
2 deck slides + HTML/PPTX re-render. REQ-215,216,228.
|
||||
- **P3 — submission-readiness** (feat): JSON Schema + validator + docs +
|
||||
tests. REQ-217,218,219,220.
|
||||
- **P4 — atelier-skills** (docs): 9 Atelier-derived skill files + index +
|
||||
BA.A extension. REQ-221,222.
|
||||
- **P5 — atelier-mcp** (feat): plugin-registry MCP server + vendored
|
||||
Atelier + 4 tools + tests. REQ-223,224,225.
|
||||
- **P6 — deck-slides-atelier** (docs): 3 new deck slides (scope/RACI/atelier)
|
||||
→ 21 slides + talking points + HTML/PPTX re-render + README. REQ-226,227,228.
|
||||
- **P7 — final-review-ship** (final): review + audit + milestone ship.
|
||||
|
||||
**Requirements:** REQ-214..228 (15 requirements). See
|
||||
`.ciagent/REQUIREMENTS.md` §v1.18.
|
||||
|
||||
**Open decisions to lock (CLARIFY/GRILL):** D-133 (validator location),
|
||||
D-134 (deck slide budget), D-135 (MCP transport), D-136 (Atelier vendoring),
|
||||
D-137 (MCP server language), D-138 (skill format), D-139 (RACI roles),
|
||||
D-140 (MCP plugin-registry), D-141 (PPTX storage), D-142 (deck render trigger).
|
||||
|
||||
**Outcome:** 15 requirements (REQ-214..228) satisfied; 32 tests pass (16
|
||||
submission-readiness + 16 MCP); S&P Global Energy theme restored; PDLC-
|
||||
upstream scope + RACI matrix authored (PROJECT.md + docs/ + deck);
|
||||
submission-readiness schema + validator shipped (superset gate above
|
||||
contract.schema.json); 9 Atelier-derived skills + docs/skills.md; MCP
|
||||
server (plugin-registry, stdio, vendored Atelier v0.3.6) with 4 tools +
|
||||
agentic validation beyond Wiz/Checkmarx/Mend; 21-slide deck (3 new slides:
|
||||
scope/RACI/atelier) with PPTX committed + release-attached. 10 decisions
|
||||
locked (D-133..D-142).
|
||||
|
||||
Ship tag at milestone COMPLETE: `v1.17.7` (feature milestone; final patch
|
||||
IS the release). **DONE.**
|
||||
|
||||
## v1.19 (complete — Nova 2nd-Release Sync, tag line `v1.18.x`)
|
||||
|
||||
> **NFR-only chore milestone.** Single execution phase. Establishes the
|
||||
> manual-only "2nd release" pipeline `~/acdl → ~/nova` (GitLab
|
||||
> `jonathanchery/nova`, separate repo + history, consumer/platform-team
|
||||
> audience). Replaces the old `~/gl/acdl` mirror sync.
|
||||
|
||||
### Phase P1 — nova-sync-script (Wave 1)
|
||||
- **Description:** Replace `scripts/sync_to_gl.sh` (kitchen-sink mirror sync
|
||||
into `~/gl/acdl`) with `scripts/sync_to_nova.sh` — a manual-only,
|
||||
consumer-subset, domain-committed 2nd-release pipeline into `~/nova`.
|
||||
Excludes `.ciagent/`, `terraform/`, `demo/`, runtime metrics, and
|
||||
internal-only scripts. Protects `~/nova/.git`. Commits per domain in a fixed
|
||||
order using positional `-m` conventional-commit messages. Validates
|
||||
conventional format. Never triggerable by CI (`--release` gate).
|
||||
- **Status:** complete
|
||||
- **Depends on:** —
|
||||
- **Requirements:** REQ-229
|
||||
- **Success Criteria:**
|
||||
- `scripts/sync_to_nova.sh` exists with `set -euo pipefail`.
|
||||
- Refuses without `--release` (exit 2); `--list-domains` prints 13 domains.
|
||||
- rsync excludes `.ciagent`, `terraform`, `demo`, internal scripts, runtime
|
||||
metrics; protects destination `.git`.
|
||||
- Domain commits in fixed order; positional `-m` mapping; conventional
|
||||
format validated.
|
||||
- `scripts/sync_to_gl.sh` removed.
|
||||
- `pytest` passes; `run_ci.sh` exits 0.
|
||||
|
||||
### Phase P2 — final-review-ship (Final Phase)
|
||||
- **Description:** Final review + audit + milestone ship. Merge to main, tag
|
||||
`v1.18.0` (first patch on the v1.18.x line), create Gitea release.
|
||||
- **Status:** complete
|
||||
- **Depends on:** [P1]
|
||||
- **Requirements:** REQ-229
|
||||
- **Success Criteria:**
|
||||
- Review + audit clean (no P0).
|
||||
- `phase/02-final-review-ship` merged to `milestone/v1.19-nova-sync` then to
|
||||
`main`.
|
||||
- Tag `v1.18.0` created; release notes summarize REQ-229.
|
||||
- Milestone branches deleted; CHECKPOINT cleared.
|
||||
|
||||
Ship tag at milestone COMPLETE: `v1.18.1` (NFR milestone; final patch IS the
|
||||
release). **DONE.**
|
||||
|
||||
---
|
||||
|
||||
## v1.20 — Consumer Cleanup + Transparent Terraform + Slide Pipeline
|
||||
|
||||
> **Multi-concern milestone.** Four user-directed inputs: (1) remove all
|
||||
> gitea/gitlab from synced files — the platform team must never know about
|
||||
> the dev forge; (2) radically simplify documentation for the Platform Team
|
||||
> audience; (3) make terraform runs transparent in workflows with feature-flag
|
||||
> client differentiation; (4) dedicated S&P-themed slide render pipeline +
|
||||
> 12-month product roadmap slides.
|
||||
>
|
||||
> Tags run on the v1.19.x line (milestone v1.20 → tags v1.19.x).
|
||||
|
||||
### Phase P0 — pre-execution
|
||||
- **Description:** Specify → clarify → research → plan. Validate v1.20
|
||||
requirements (REQ-230..244). Establish milestone version in config.json.
|
||||
- **Status:** complete
|
||||
- **Requirements:** REQ-230..244
|
||||
- **Success Criteria:**
|
||||
- `.ciagent/REQUIREMENTS.md` has v1.20 section with all 15 requirements.
|
||||
- `.ciagent/config.json` has `active_milestone: "v1.20"`.
|
||||
- Checkpoint written.
|
||||
|
||||
### Phase P1 — consumer-cleanup (gitea removal + doc simplification)
|
||||
- **Description:** Remove all gitea/gitlab mentions from synced files.
|
||||
Genericize forge-detection code. Drop `.gitea/` byte-identity test
|
||||
assertions. Add `test_no_forge_mentions.py` guard test. Simplify
|
||||
documentation: delete completed migration docs, move thesis to `.ciagent/`,
|
||||
strip ciagent-internal provenance from synced docs.
|
||||
- **Status:** complete
|
||||
- **Requirements:** REQ-230, REQ-231, REQ-232
|
||||
- **Success Criteria:**
|
||||
- `tests/test_no_forge_mentions.py` passes — zero gitea/gitlab mentions in
|
||||
synced subset.
|
||||
- `pytest` passes — all existing tests green after genericization.
|
||||
- Synced docs stripped of REQ-/D-/P- IDs, milestone headers, `.ciagent/`
|
||||
citations.
|
||||
- `docs/NOVA_MIGRATION.md` + `docs/NOVA_AWS_MIGRATION.md` deleted.
|
||||
- `docs/NO_HUMANS_THESIS.md` moved to `.ciagent/`.
|
||||
|
||||
### Phase P2 — slide-pipeline (S&P theme + render automation)
|
||||
- **Description:** Create dedicated S&P theme CSS, render_slides.sh pipeline,
|
||||
CI workflow, tests. Update Marp frontmatter to use dedicated theme. Fix
|
||||
README directory layout.
|
||||
- **Status:** complete
|
||||
- **Requirements:** REQ-239, REQ-240, REQ-241, REQ-242, REQ-243
|
||||
- **Success Criteria:**
|
||||
- `docs/presentations/assets/nova-sp-theme.css` exists with S&P colors.
|
||||
- Marp deck frontmatter references the theme CSS.
|
||||
- `scripts/render_slides.sh` renders mermaid PNGs + HTML + PPTX.
|
||||
- `workflows-src/slides.yml` + `.github/workflows/slides.yml` exist.
|
||||
- `tests/test_slides_pipeline.py` passes.
|
||||
- `docs/presentations/README.md` updated (no retired decks).
|
||||
|
||||
### Phase P3 — product-roadmap (12-month slides)
|
||||
- **Description:** Add 12-month product roadmap as Slide 20 + Slide 21 to the
|
||||
deck. Add matching talking-points sections. Render via new pipeline.
|
||||
- **Status:** complete
|
||||
- **Requirements:** REQ-244
|
||||
- **Success Criteria:**
|
||||
- Slide 20 + 21 in `nova-no-humans-platform-marp.md` + source-of-truth +
|
||||
talking-points.
|
||||
- HTML + PPTX re-rendered via `render_slides.sh`.
|
||||
- 4-quarter product arc grounded in NORTH_STAR + deferred metrics.
|
||||
|
||||
### Phase P4 — transparent-terraform (workflow refactor + feature flags)
|
||||
- **Description:** Split run_platform.sh → run_codegen.sh + run_postapply.sh.
|
||||
Rewrite deploy.yml with native terraform steps. Add var.enabled to all L1
|
||||
modules + L2 composition toggles. Wire forge repo variables as feature
|
||||
flags. Fix stale artifact path.
|
||||
- **Status:** complete
|
||||
- **Requirements:** REQ-233, REQ-234, REQ-235, REQ-236, REQ-237, REQ-238
|
||||
- **Success Criteria:**
|
||||
- `scripts/run_codegen.sh` + `scripts/run_postapply.sh` exist.
|
||||
- `deploy.yml` has native terraform init/validate/plan/apply steps.
|
||||
- Every L1 module has `variable "enabled"` + `count = var.enabled ? 1 : 0`.
|
||||
- L2 `composition.json` supports per-child `enabled`.
|
||||
- `deploy.yml` reads `vars.ENABLE_*` as `-var` flags.
|
||||
- Stale `/tmp/acdl_platform_run_v18` path fixed to `NOVA_WORK_DIR`.
|
||||
- `pytest` passes; `run_platform.sh` shim backward-compat verified.
|
||||
|
||||
### Phase P5 — final-review-ship (Final Phase)
|
||||
- **Description:** Final review + audit + milestone ship. Merge to main,
|
||||
tag `v1.19.4` (final patch = milestone release), create release.
|
||||
- **Status:** complete
|
||||
- **Depends on:** [P1, P2, P3, P4]
|
||||
- **Requirements:** REQ-230..244
|
||||
- **Success Criteria:**
|
||||
- Review + audit clean (no P0).
|
||||
- Milestone branches merged to main.
|
||||
- Tag `v1.19.4` created; release notes summarize all 15 requirements.
|
||||
- CHECKPOINT cleared; milestone branches deleted.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
],
|
||||
"active_project": "acdl",
|
||||
"active_projects": ["acdl"],
|
||||
"active_milestone": "v1.17",
|
||||
"active_milestone": "v1.21",
|
||||
"autonomy": {
|
||||
"level": "full",
|
||||
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ACDL CI Pipeline — Gitea Actions (dev environment)
|
||||
# Nova CI Pipeline (dev environment)
|
||||
#
|
||||
# This workflow implements the central pipeline contract:
|
||||
# pipelines/ci.yml (validated against schemas/pipeline.schema.json)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ACDL Reusable Deploy Workflow — Gitea Actions (dev environment)
|
||||
# Nova Reusable Deploy Workflow (dev environment)
|
||||
#
|
||||
# This reusable workflow implements the central deployment pipeline contract:
|
||||
# pipelines/contract.yml (validated against schemas/deploy-pipeline.schema.json)
|
||||
@@ -8,7 +8,7 @@
|
||||
# declared difference is the forge/runtime, not the stages or commands.
|
||||
#
|
||||
# Consumer repos invoke this workflow via a versioned tag (floating MAJOR + MINOR):
|
||||
# uses: acdl/.gitea/workflows/deploy.yml@v1.9 (Gitea)
|
||||
# uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
# uses: acdl/.github/workflows/deploy.yml@v1.9 (GitHub)
|
||||
#
|
||||
# Unversioned references (@main, bare) are discouraged — the consumer's setup
|
||||
@@ -38,8 +38,8 @@
|
||||
# that matches repo:org/consumer-repo:ref:refs/heads/main, and the session
|
||||
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
|
||||
#
|
||||
# Override (where OIDC is unavailable, e.g. Gitea pending
|
||||
# go-gitea/gitea#36988): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
|
||||
# Override (where OIDC is unavailable, e.g. pending
|
||||
# upstream forge OIDC support): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
|
||||
# as repository secrets. The platform-managed scheduled pipeline rotates
|
||||
# the key on a daily cadence. When .env.secrets is used locally instead,
|
||||
# rotating the key out of band is the consumer's responsibility.
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nova-terraform
|
||||
path: /tmp/acdl_platform_run_v18/tf/*.tf
|
||||
path: /tmp/nova_platform_run/tf/*.tf
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload platform log
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ACDL Modules Lifecycle Pipeline — Gitea Actions (dev environment)
|
||||
# Nova Modules Lifecycle Pipeline (dev environment)
|
||||
#
|
||||
# Matrix-runs each L1 module's examples/{simple,complex}.yml contracts through
|
||||
# apply→modify→destroy against live AWS. No per-module Python. The "test" =
|
||||
@@ -9,7 +9,7 @@
|
||||
# terraform files); the composition must be deterministic.
|
||||
#
|
||||
# This workflow implements pipelines/modules-lifecycle.yml (byte-identical
|
||||
# in .gitea/workflows/ and .github/workflows/).
|
||||
# in .github/workflows/).
|
||||
#
|
||||
# Lifecycle mode (REQ-134, v1.12): the `lifecycle_mode` input defaults to
|
||||
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Nova Slides Render — re-renders presentation deck when source files change.
|
||||
name: Nova Slides Render
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/presentations/**'
|
||||
- 'scripts/render_slides.sh'
|
||||
- 'assets/nova-sp-theme.css'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
render:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '20' }
|
||||
- name: Install Chrome
|
||||
run: |
|
||||
npx --yes @marp-team/marp-cli@latest --version
|
||||
npx --yes @mermaid-js/mermaid-cli --version
|
||||
- name: Render slides
|
||||
run: bash scripts/render_slides.sh
|
||||
- name: Commit rendered artifacts
|
||||
run: |
|
||||
git config user.name "nova-slides-bot"
|
||||
git config user.email "bot@nova.local"
|
||||
git add docs/presentations/*.html docs/presentations/*.pptx docs/presentations/assets/png/*.png
|
||||
git diff --cached --quiet || git commit -m "chore(slides): re-render deck [skip ci]"
|
||||
git push
|
||||
+10
-15
@@ -1,35 +1,30 @@
|
||||
# GitHub Workflows — Nova Platform CI/CD Catalog
|
||||
|
||||
This directory contains the 7 GitHub Actions workflows for the Nova
|
||||
platform. 3 are byte-identical Gitea mirrors (generated from
|
||||
`workflows-src/` by `scripts/sync_workflows.py`, P8/REQ-172); 4 are
|
||||
GitHub-only (Gitea act_runner feature gaps).
|
||||
This directory contains the GitHub Actions workflows for the Nova
|
||||
platform. 3 are generated from `workflows-src/<name>`; 4 are GitHub-only.
|
||||
|
||||
## Shared workflows (byte-identical Gitea + GitHub)
|
||||
## Shared workflows (generated from source)
|
||||
|
||||
These 3 are generated from `workflows-src/<name>` by
|
||||
`scripts/sync_workflows.py`; the `.gitea/workflows/<name>` mirror is kept
|
||||
byte-identical. Run `python3 scripts/sync_workflows.py --check` to verify
|
||||
These 3 are generated from `workflows-src/<name>`. Run `python3 scripts/sync_workflows.py --check` to verify
|
||||
no drift.
|
||||
|
||||
| Workflow | Trigger | Inputs | Required Secrets | Purpose |
|
||||
|----------|---------|--------|------------------|---------|
|
||||
| `ci.yml` | `pull_request: [main]` | — | — | Lint + test + check-only (runs on every PR) |
|
||||
| `deploy.yml` | `workflow_call` (reusable) + `push: [main]` | `contract` (string, required), `mode` (string, default `deploy`), `changeRequestId` (string), `environment` (string) | `NOVA_AWS_ACCESS_KEY_ID`, `NOVA_AWS_SECRET_ACCESS_KEY`, `NOVA_AWS_DEFAULT_REGION`, `NOVA_KMS_KEY_ID`, `NOVA_LAMBDA_URL` | Reusable deploy workflow (invoked by consumer repos via `uses: acdl/.github/workflows/deploy.yml@v1.15`) |
|
||||
| `deploy.yml` | `workflow_call` (reusable) + `push: [main]` | `contract` (string, required), `mode` (string, default `deploy`), `changeRequestId` (string), `environment` (string) | `NOVA_AWS_ACCESS_KEY_ID`, `NOVA_AWS_SECRET_ACCESS_KEY`, `NOVA_AWS_DEFAULT_REGION`, `NOVA_KMS_KEY_ID`, `NOVA_LAMBDA_URL` | Reusable deploy workflow (invoked by consumer repos via `uses: nova/.github/workflows/deploy.yml@v1.19`) |
|
||||
| `modules-lifecycle.yml` | `pull_request: [main]` + `workflow_dispatch` | `lifecycle_mode` (string, default `plan` — `plan` or `full`) | `NOVA_AWS_ACCESS_KEY_ID`, `NOVA_AWS_SECRET_ACCESS_KEY`, `NOVA_AWS_DEFAULT_REGION`, `NOVA_AWS_ACCOUNT_ID` | L1 + L2 module lifecycle pipeline (plan-only default; full apply/modify/destroy on override) |
|
||||
|
||||
## GitHub-only workflows (no Gitea mirror)
|
||||
## GitHub-only workflows
|
||||
|
||||
These 4 have no Gitea counterpart (Gitea act_runner lacks the features
|
||||
they require — reusable workflows, matrix `needs`, release API). See
|
||||
`.gitea/workflows/README.md` for the limitation rationale.
|
||||
These 4 have no counterpart (the dev forge lacks the features
|
||||
they require — reusable workflows, matrix `needs`, release API).
|
||||
|
||||
| Workflow | Trigger | Inputs | Required Secrets | Purpose |
|
||||
|----------|---------|--------|------------------|---------|
|
||||
| `platform-test.yml` | `pull_request: [main]` | — | — | Lint + unit + integration + schema-validation (replaces `ci.yml` for PRs) |
|
||||
| `primitives-plan.yml` | `pull_request: [main]` | — | `NOVA_AWS_*` | Plan-only for all L1 primitives (matrix) |
|
||||
| `patterns-plan.yml` | `pull_request: [main]` | — | `NOVA_AWS_*` | Plan-only for all L2 modules (matrix) |
|
||||
| `release.yml` | `push: [main]` | — | `NOVA_GITEA_TOKEN` (for Gitea release API) | Semver tag + MAJOR.MINOR/MAJOR floating-tag maintenance + release creation on merge to main |
|
||||
| `release.yml` | `push: [main]` | — | `NOVA_RELEASE_TOKEN` | Semver tag + MAJOR.MINOR/MAJOR floating-tag maintenance + release creation on merge to main |
|
||||
|
||||
## Reusable deploy workflow (`deploy.yml`)
|
||||
|
||||
@@ -38,7 +33,7 @@ Consumer repos invoke the deploy workflow via a versioned tag:
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.15
|
||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
with:
|
||||
contract: .nova/contract.yml
|
||||
environment: dev
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ACDL CI Pipeline — Gitea Actions (dev environment)
|
||||
# Nova CI Pipeline (dev environment)
|
||||
#
|
||||
# This workflow implements the central pipeline contract:
|
||||
# pipelines/ci.yml (validated against schemas/pipeline.schema.json)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ACDL Reusable Deploy Workflow — Gitea Actions (dev environment)
|
||||
# Nova Reusable Deploy Workflow (dev environment)
|
||||
#
|
||||
# This reusable workflow implements the central deployment pipeline contract:
|
||||
# pipelines/contract.yml (validated against schemas/deploy-pipeline.schema.json)
|
||||
@@ -8,7 +8,7 @@
|
||||
# declared difference is the forge/runtime, not the stages or commands.
|
||||
#
|
||||
# Consumer repos invoke this workflow via a versioned tag (floating MAJOR + MINOR):
|
||||
# uses: acdl/.gitea/workflows/deploy.yml@v1.9 (Gitea)
|
||||
# uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
# uses: acdl/.github/workflows/deploy.yml@v1.9 (GitHub)
|
||||
#
|
||||
# Unversioned references (@main, bare) are discouraged — the consumer's setup
|
||||
@@ -38,8 +38,8 @@
|
||||
# that matches repo:org/consumer-repo:ref:refs/heads/main, and the session
|
||||
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
|
||||
#
|
||||
# Override (where OIDC is unavailable, e.g. Gitea pending
|
||||
# go-gitea/gitea#36988): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
|
||||
# Override (where OIDC is unavailable, e.g. pending
|
||||
# upstream forge OIDC support): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
|
||||
# as repository secrets. The platform-managed scheduled pipeline rotates
|
||||
# the key on a daily cadence. When .env.secrets is used locally instead,
|
||||
# rotating the key out of band is the consumer's responsibility.
|
||||
@@ -155,7 +155,7 @@ jobs:
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: nova-terraform
|
||||
path: /tmp/acdl_platform_run_v18/tf/*.tf
|
||||
path: /tmp/nova_platform_run/tf/*.tf
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload platform log
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ACDL Modules Lifecycle Pipeline — Gitea Actions (dev environment)
|
||||
# Nova Modules Lifecycle Pipeline (dev environment)
|
||||
#
|
||||
# Matrix-runs each L1 module's examples/{simple,complex}.yml contracts through
|
||||
# apply→modify→destroy against live AWS. No per-module Python. The "test" =
|
||||
@@ -9,7 +9,7 @@
|
||||
# terraform files); the composition must be deterministic.
|
||||
#
|
||||
# This workflow implements pipelines/modules-lifecycle.yml (byte-identical
|
||||
# in .gitea/workflows/ and .github/workflows/).
|
||||
# in .github/workflows/).
|
||||
#
|
||||
# Lifecycle mode (REQ-134, v1.12): the `lifecycle_mode` input defaults to
|
||||
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
# Nova Slides Render — re-renders presentation deck when source files change.
|
||||
name: Nova Slides Render
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'docs/presentations/**'
|
||||
- 'scripts/render_slides.sh'
|
||||
- 'assets/nova-sp-theme.css'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
render:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with: { fetch-depth: 0 }
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '20' }
|
||||
- name: Install Chrome
|
||||
run: |
|
||||
npx --yes @marp-team/marp-cli@latest --version
|
||||
npx --yes @mermaid-js/mermaid-cli --version
|
||||
- name: Render slides
|
||||
run: bash scripts/render_slides.sh
|
||||
- name: Commit rendered artifacts
|
||||
run: |
|
||||
git config user.name "nova-slides-bot"
|
||||
git config user.email "bot@nova.local"
|
||||
git add docs/presentations/*.html docs/presentations/*.pptx docs/presentations/assets/png/*.png
|
||||
git diff --cached --quiet || git commit -m "chore(slides): re-render deck [skip ci]"
|
||||
git push
|
||||
+2
-1
@@ -40,4 +40,5 @@ metrics/lifecycle/
|
||||
*.cer
|
||||
*.crt
|
||||
*.jks
|
||||
*.keystore
|
||||
*.keystore.coverage
|
||||
.coverage
|
||||
|
||||
@@ -219,23 +219,9 @@ bash scripts/run_ci.sh --quiet # suppress per-stage banners
|
||||
|
||||
### Reusable deploy workflow
|
||||
|
||||
The deployment pipeline is defined by a **central deployment pipeline
|
||||
contract** (`pipelines/contract.yml`, validated against
|
||||
`schemas/deploy-pipeline.schema.json`) and exposed to consumer repos as a
|
||||
**reusable workflow**:
|
||||
|
||||
- `.github/workflows/deploy.yml` — GitHub Actions (production)
|
||||
|
||||
The workflow implements the same stages as `pipelines/contract.yml`
|
||||
(validate-contract → resolve-stack → security checks → infrastructure plan
|
||||
→ policy checks → confidence → evidence event → apply). A consumer repo
|
||||
invokes the reusable workflow via a **versioned tag** (floating MAJOR +
|
||||
MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.13`). The workflow checks
|
||||
out the consumer repo, then checks out the Nova platform repo into the
|
||||
runner workspace, and runs `scripts/run_platform.sh` against the consumer's
|
||||
contract — the consumer never clones the platform repo or invokes its
|
||||
scripts locally. See the [Consumer guide](docs/consumer-guide.md) for the
|
||||
end-to-end happy path.
|
||||
Consumer repos invoke the deploy pipeline via `.github/workflows/deploy.yml`
|
||||
(a reusable GitHub Actions workflow, versioned tag `nova/.github/workflows/deploy.yml@v1.19`).
|
||||
See the [Consumer guide](docs/consumer-guide.md) for the end-to-end happy path.
|
||||
|
||||
### Output streaming (run_platform.sh)
|
||||
|
||||
@@ -310,12 +296,6 @@ documented alternative:
|
||||
runs, or in **`.env.secrets`** (gitignored, chmod 600) for local testing.
|
||||
- The platform rotates platform-runner keys on a **daily cadence** —
|
||||
rotation is not the consumer's burden in the platform-runner path.
|
||||
- **When `.env.secrets` is used locally**, rotating the key **out of band is
|
||||
the consumer's responsibility**. The platform guarantees daily rotation
|
||||
for platform-runner runs; it does not guarantee rotation for
|
||||
locally-held copies. The consumer must rotate a local key via
|
||||
`scripts/rotate_spike_key.sh` (or equivalent) on their own cadence.
|
||||
|
||||
No long-lived credential is permitted persistently — the platform-runner
|
||||
key's useful lifetime is one workflow run, and the local alternative is
|
||||
rotated at least daily (platform-runner) or out of band (local).
|
||||
@@ -62,7 +62,7 @@ path above remains the v1.9 production audit record.
|
||||
**platform-level KMS key** (not per-contract — a per-contract key would
|
||||
explode the key-management surface), rotated **quarterly**. The `jws`
|
||||
field is added to the event shape when this ships.
|
||||
- **Async worker + DLQ:** a Lambda (or a Gitea Actions scheduled workflow)
|
||||
- **Async worker + DLQ:** a Lambda (or a forge Actions scheduled workflow)
|
||||
reads the outbox, writes to S3 Object Lock, signs with KMS. DLQ = an
|
||||
SQS dead-letter queue for failed writes. RTO = DLQ replay.
|
||||
- **Daily checkpoints (§9):** a daily job reads the last event hash and
|
||||
@@ -86,7 +86,7 @@ log" anti-goal requires.
|
||||
D-083 ships).
|
||||
- `prev_event_hash` (chain link; `GENESIS` for the first event).
|
||||
- `hash` (this event's SHA-256 over canonical JSON).
|
||||
- `approver_qa` (Gitea/GitHub username of the QA approver; populated on
|
||||
- `approver_qa` (CI username of the QA approver; populated on
|
||||
qa-promotion by v1.9's `hitl_gates.attest` — D-042).
|
||||
- `approver_prod` (SRE username; populated on prod-promotion by v1.9's
|
||||
`hitl_gates.attest`).
|
||||
@@ -112,7 +112,7 @@ log" anti-goal requires.
|
||||
- **D-042** — approver identities (`approver_qa`, `approver_prod`,
|
||||
`approver_dr`) live in the outbox; the separation-of-duties check
|
||||
(`core/separation_of_duties.py`) reads `approver_qa` and compares
|
||||
to the prod-dispatch `gitea.actor` / `github.actor`. v1.9's
|
||||
to the prod-dispatch CI actor. v1.9's
|
||||
`hitl_gates.attest` populates these attributes.
|
||||
- **D-083** (v1.9) — S3 Object Lock + JWS + async worker + DLQ + daily
|
||||
checkpoints deferred to a future milestone. Requires non-offline-
|
||||
|
||||
+4
-4
@@ -1,6 +1,6 @@
|
||||
"""HITL pre-execution attestation gates (REQ-108, D-084).
|
||||
|
||||
Records the approver identity (`gitea.actor` / `github.actor`) to the
|
||||
Records the approver identity (the CI actor (GITHUB_ACTOR or FORGE_ACTOR)) to the
|
||||
DynamoDB outbox for the contractId (attribute `approver_qa` /
|
||||
`approver_prod` / `approver_dr`), runs the separation-of-duties check on
|
||||
prod, invokes the 8-concern attestation matrix for the target env, and
|
||||
@@ -29,7 +29,7 @@ def attest(contract_id: str, env: str, approver: str,
|
||||
Args:
|
||||
contract_id: the contract UUID.
|
||||
env: dev/qa/prod/dr.
|
||||
approver: the approver's username (`gitea.actor` / `github.actor`).
|
||||
approver: the approver's username (the CI actor (GITHUB_ACTOR or FORGE_ACTOR)).
|
||||
evidence: optional operator-supplied evidence artifacts (for the
|
||||
attestation matrix operator-supplied concerns).
|
||||
outbox_client: optional moto-mocked DynamoDB outbox client for tests.
|
||||
@@ -41,7 +41,7 @@ def attest(contract_id: str, env: str, approver: str,
|
||||
return (True, "dev autonomous (no HITL gate)")
|
||||
|
||||
if not approver:
|
||||
return (False, f"no approver identity for {env} (GITHUB_ACTOR/GITEA_ACTOR unset)")
|
||||
return (False, f"no approver identity for {env} (GITHUB_ACTOR/FORGE_ACTOR unset)")
|
||||
|
||||
attr = _approver_attr(env)
|
||||
if not attr:
|
||||
@@ -88,7 +88,7 @@ def attest(contract_id: str, env: str, approver: str,
|
||||
|
||||
def approver_from_env() -> Optional[str]:
|
||||
"""Read the approver identity from the environment."""
|
||||
return os.environ.get("GITHUB_ACTOR") or os.environ.get("GITEA_ACTOR")
|
||||
return os.environ.get("GITHUB_ACTOR") or os.environ.get("FORGE_ACTOR")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+15
-15
@@ -18,32 +18,32 @@ gates. No partial deployment to roll back on rejection (qa, prod); dr is
|
||||
a separate deployment against a separate cluster/region. The
|
||||
canary/deployment-rollback model is explicitly not in scope for v1.
|
||||
|
||||
## Gitea-specific gate mechanics (D-042)
|
||||
## Forge-specific gate mechanics (D-042)
|
||||
|
||||
Gitea has **no Environments API** and ignores `environment:` blocks
|
||||
The dev forge has **no Environments API** and ignores `environment:` blocks
|
||||
(v1.0 D-013; re-confirmed in RESEARCH TARGET 1). The pre-execution gate
|
||||
is modeled as a `workflow_dispatch` with approval inputs:
|
||||
|
||||
- **qa gate:** `workflow_dispatch` with `approve_qa: true`; the dispatch
|
||||
run's `gitea.actor` is the QA approver.
|
||||
run's `CI actor` is the QA approver.
|
||||
- **prod gate:** `workflow_dispatch` with `approve_prod: true`;
|
||||
`gitea.actor` is the SRE approver.
|
||||
`CI actor` is the SRE approver.
|
||||
- **dr gate:** `workflow_dispatch` with `approve_dr: true`; same.
|
||||
|
||||
The approver identity of record = `gitea.actor` of the dispatch run
|
||||
(D-042). There is no other approval-identity signal in Gitea. The real
|
||||
OIDC path (blocked on go-gitea/gitea#36988) does not change this —
|
||||
The approver identity of record = `CI actor` of the dispatch run
|
||||
(D-042). There is no other approval-identity signal in the dev forge. The real
|
||||
OIDC path (blocked on upstream forge OIDC support) does not change this —
|
||||
OIDC authorizes the *runner* to AWS, it does not change how the platform
|
||||
records the *human* approver.
|
||||
|
||||
On GitHub, the equivalent is `github.actor` of the `workflow_dispatch`
|
||||
On GitHub, the equivalent is `CI actor` of the `workflow_dispatch`
|
||||
run; GitHub Environments with required reviewers are the native gate,
|
||||
but the `workflow_dispatch` approval-input fallback is used for
|
||||
byte-identical Gitea + GitHub workflows.
|
||||
byte-identical across forges.
|
||||
|
||||
## Reviewer routing (ARCHITECTURE.md §10.2)
|
||||
|
||||
Gitea CODEOWNERS routes the right reviewer to the right gate:
|
||||
CODEOWNERS routes the right reviewer to the right gate:
|
||||
|
||||
- qa → QA team
|
||||
- prod → SRE team
|
||||
@@ -105,7 +105,7 @@ concern is missing or expired for prod/dr.
|
||||
| 1 business day | PENDING_ATTESTATION_WARNING | Notify team + platform on-call (elevated path); emit `PENDING_ATTESTATION_TIMEOUT_WARNING` event |
|
||||
| 2 business days | PENDING_ATTESTATION_AUTO_FREEZE | Auto-freeze; require re-submission; emit `PENDING_ATTESTATION_AUTO_FREEZE` event; new submission linked via `supersedes` |
|
||||
|
||||
**Implementation:** a Gitea `on: schedule` workflow (runs hourly) that
|
||||
**Implementation:** an `on: schedule` workflow (runs hourly) that
|
||||
scans the DynamoDB outbox for `PENDING_ATTESTATION` events with `ts`
|
||||
older than 1/2 business days and emits the warn/freeze events. Not
|
||||
implemented in v1.9 (roadmap item; the attestation gates themselves are
|
||||
@@ -126,11 +126,11 @@ The identity-distinctness check is platform-internal, not GitHub-native,
|
||||
not Kyverno (in v1). Sequence:
|
||||
|
||||
1. On promotion dev → qa, the platform reads the QA approver's identity
|
||||
from the `workflow_dispatch` run's `gitea.actor` (or `github.actor`)
|
||||
from the `workflow_dispatch` run's `CI actor`
|
||||
and writes it to the DynamoDB outbox keyed by `contractId` (attribute
|
||||
`approver_qa`).
|
||||
2. On promotion qa → prod, the platform reads the stored `approver_qa`
|
||||
from the outbox and the new SRE approver's `gitea.actor` from the
|
||||
from the outbox and the new SRE approver identity from the
|
||||
prod-dispatch run.
|
||||
3. If `approver_qa == approver_prod`, the platform blocks the prod
|
||||
promotion, writes a `SEPARATION_OF_DUTIES_VIOLATION` event to the
|
||||
@@ -163,8 +163,8 @@ v1.9 (Phase 41 + Phase 42) wires the gates end-to-end:
|
||||
|
||||
## Decision trail
|
||||
|
||||
- **D-042** — approver identity = `gitea.actor` of the `workflow_dispatch`
|
||||
run; no Environments API in Gitea. On GitHub, `github.actor`.
|
||||
- **D-042** — approver identity = `CI actor` of the `workflow_dispatch`
|
||||
run; no Environments API in the dev forge.
|
||||
- **D-013** (v1.0) — the `workflow_dispatch` approval-input fallback,
|
||||
re-used for the real platform's pre-execution gate model.
|
||||
- **D-084** (v1.9) — 8-concern attestation matrix: offline-testable
|
||||
|
||||
@@ -27,7 +27,7 @@ CHANGE_REQUESTS_TABLE = os.environ.get("CHANGE_REQUESTS_TABLE", "nova-change-req
|
||||
GITHUB_TOKEN_SECRET_ID = os.environ.get("GITHUB_TOKEN_SECRET_ID", "nova/github-token")
|
||||
PLATFORM_REPO = os.environ.get("PLATFORM_REPO", "nova/acdl")
|
||||
# P1-9: Forge-agnostic API base URL. Defaults to GitHub; set GITHUB_API_BASE
|
||||
# to a Gitea API root (e.g. https://git.cloudinit.dev/api/v1) for Gitea.
|
||||
# to a compatible forge API root (e.g. https://forge.example.com/api/v1).
|
||||
GITHUB_API_BASE = os.environ.get("GITHUB_API_BASE", "https://api.github.com")
|
||||
|
||||
# P11 (REQ-175): consistent cap for error/stackTrace fields (was 10k vs 2k).
|
||||
@@ -96,22 +96,22 @@ def _iso8601_now():
|
||||
|
||||
|
||||
def _forge_type():
|
||||
"""P1-9: Detect whether the API base is GitHub or Gitea.
|
||||
"""Detect whether the API base is GitHub or a compatible forge.
|
||||
|
||||
Gitea API roots contain '/api/v1'; GitHub's is 'api.github.com'.
|
||||
Compatible forge API roots contain '/api/v1'; GitHub's is 'api.github.com'.
|
||||
"""
|
||||
if "/api/v1" in GITHUB_API_BASE:
|
||||
return "gitea"
|
||||
return "generic_forge"
|
||||
return "github"
|
||||
|
||||
|
||||
def _issues_search_url(owner, repo, encoded_query):
|
||||
"""P1-9: Build the issue search URL based on forge type.
|
||||
"""Build the issue search URL based on forge type.
|
||||
|
||||
GitHub uses /search/issues?q=...; Gitea uses /repos/{owner}/{repo}/issues?...
|
||||
GitHub uses /search/issues?q=...; compatible forges use /repos/{owner}/{repo}/issues?...
|
||||
with query params (no /search/issues endpoint).
|
||||
"""
|
||||
if _forge_type() == "gitea":
|
||||
if _forge_type() == "generic_forge":
|
||||
return (
|
||||
f"{GITHUB_API_BASE}/repos/{owner}/{repo}/issues"
|
||||
f"?state=open&type=issues&q={encoded_query}"
|
||||
@@ -123,7 +123,7 @@ def _issues_search_url(owner, repo, encoded_query):
|
||||
|
||||
|
||||
def _issues_create_url(owner, repo):
|
||||
"""URL for creating an issue (same pattern for both GitHub + Gitea)."""
|
||||
"""URL for creating an issue (same pattern across forges)."""
|
||||
return f"{GITHUB_API_BASE}/repos/{owner}/{repo}/issues"
|
||||
|
||||
|
||||
@@ -499,4 +499,23 @@ def lambda_handler(event, context):
|
||||
return {"statusCode": 401, "body": json.dumps({"error": str(e)})}
|
||||
return {"statusCode": 400, "body": json.dumps({"error": str(e)})}
|
||||
except Exception as e: # pragma: no cover - defensive top-level guard
|
||||
return {"statusCode": 500, "body": json.dumps({"error": str(e)})}
|
||||
return {"statusCode": 500, "body": json.dumps({"error": str(e)})}
|
||||
|
||||
|
||||
# --- CLI: --check-readiness (D-133, REQ-218) ---------------------------
|
||||
# Invoked as: python3 -m core.lambda.contract_ingestor --check-readiness <submission.json>
|
||||
# Delegates to core.submission_readiness.check_readiness() and prints the
|
||||
# structured ReadinessResult. Exits 0 if ready, 1 if not.
|
||||
if __name__ == "__main__": # pragma: no cover - CLI entry
|
||||
import sys
|
||||
if "--check-readiness" in sys.argv:
|
||||
sys.path.insert(
|
||||
0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
)
|
||||
from core.submission_readiness import cli_main
|
||||
|
||||
# Strip the --check-readiness flag; pass the file path.
|
||||
rest = [a for a in sys.argv[1:] if a != "--check-readiness"]
|
||||
sys.exit(cli_main(["check-readiness"] + rest))
|
||||
else:
|
||||
print("Usage: python3 -m core.lambda.contract_ingestor --check-readiness <submission.json>")
|
||||
@@ -1,6 +1,6 @@
|
||||
"""Check that qaApprover != prodApprover for a contract (ARCHITECTURE.md
|
||||
§10.3, D-042). Reads `approver_qa` from the DynamoDB outbox for the
|
||||
contractId, compares to the prod-dispatch `gitea.actor` / `github.actor`.
|
||||
contractId, compares to the prod-dispatch the CI actor.
|
||||
Blocks on equality, emits `SEPARATION_OF_DUTIES_VIOLATION`, routes a halt
|
||||
artifact to SRE on-call.
|
||||
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
"""core/submission_readiness.py — Nova submission-readiness validator (REQ-218).
|
||||
|
||||
Defines what is acceptable to start — a superset gate ABOVE
|
||||
contract.schema.json validity. Invoked as
|
||||
``contract_ingestor.py --check-readiness`` (D-133). Returns a structured
|
||||
ReadinessResult (pass/fail per check, with reason codes). On fail → the
|
||||
ingestor rejects with a citizen-developer-facing error (not a stack
|
||||
trace). On pass → proceeds to existing contract ingestion.
|
||||
|
||||
The validator calls contract.schema.json validation first (the shape),
|
||||
then the readiness checks (the gate): tags, env mandatory, policy
|
||||
preconditions, profile:agentic markers, appSource.
|
||||
|
||||
Reason codes:
|
||||
MISSING_TAGS — one or more required Nova tags are absent
|
||||
ENV_MISSING_MANDATORY:<env>:<field> — a per-env mandatory field is missing
|
||||
AGENTIC_MISSING_INTENT — profile=agentic but naturalLanguageIntent absent
|
||||
MISSING_APP_SOURCE — appSource (repo + ref) is missing
|
||||
POLICY_PRECONDITION_MISSING — a declared policy precondition is absent
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any
|
||||
|
||||
_SCHEMA_DIR = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "schemas"
|
||||
)
|
||||
|
||||
REQUIRED_TAGS = [
|
||||
"nova:owner",
|
||||
"nova:contract",
|
||||
"nova:environment",
|
||||
"nova:cost-center",
|
||||
"nova:ref",
|
||||
]
|
||||
|
||||
ENV_MANDATORY: dict[str, list[str]] = {
|
||||
"dev": [], # dev requires only the base contract shape (id+environment+infrastructure)
|
||||
"qa": ["validation.e2eSuite", "validation.loadTest"],
|
||||
"prod": ["runbook", "dashboard", "oncall"],
|
||||
"dr": ["drDrillRef"],
|
||||
}
|
||||
|
||||
AGENTIC_REQUIRED = ["naturalLanguageIntent", "confidenceAtSubmission", "agentTrace"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReadinessResult:
|
||||
"""Structured result of the submission-readiness gate."""
|
||||
|
||||
ready: bool
|
||||
reason_codes: list[str] = field(default_factory=list)
|
||||
contract_id: str | None = None
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
"ready": self.ready,
|
||||
"reason_codes": self.reason_codes,
|
||||
"contractId": self.contract_id,
|
||||
}
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.ready:
|
||||
return f"READY — contract {self.contract_id} passes submission-readiness gate"
|
||||
codes = "; ".join(self.reason_codes) if self.reason_codes else "unknown"
|
||||
return f"NOT READY — contract {self.contract_id}: {codes}"
|
||||
|
||||
|
||||
def _validate_contract_schema(contract: dict[str, Any]) -> list[str]:
|
||||
"""Validate the contract against contract.schema.json (the shape).
|
||||
Returns a list of reason codes (empty if valid). Falls back to no-op
|
||||
if jsonschema or the schema file is unavailable (the contract is
|
||||
validated upstream by run_platform.sh in the normal path).
|
||||
"""
|
||||
codes: list[str] = []
|
||||
try:
|
||||
import jsonschema
|
||||
|
||||
schema_path = os.path.join(_SCHEMA_DIR, "contract.schema.json")
|
||||
with open(schema_path) as f:
|
||||
schema = json.load(f)
|
||||
jsonschema.validate(instance=contract, schema=schema)
|
||||
except (OSError, ImportError):
|
||||
pass
|
||||
except jsonschema.ValidationError as e:
|
||||
codes.append(f"CONTRACT_SCHEMA_INVALID:{e.message}")
|
||||
return codes
|
||||
|
||||
|
||||
def _get_nested(data: dict[str, Any], dotted_key: str) -> Any:
|
||||
parts = dotted_key.split(".")
|
||||
val: Any = data
|
||||
for p in parts:
|
||||
if not isinstance(val, dict) or p not in val:
|
||||
return None
|
||||
val = val[p]
|
||||
return val
|
||||
|
||||
|
||||
def check_readiness(submission: dict[str, Any]) -> ReadinessResult:
|
||||
"""Run the full submission-readiness gate.
|
||||
|
||||
1. Validate the contract shape (contract.schema.json).
|
||||
2. Validate the readiness schema (submission-readiness.schema.json).
|
||||
3. Run the semantic readiness checks (tags, env mandatory, agentic, appSource, policy).
|
||||
|
||||
Returns a ReadinessResult. Never raises — all failures are reason codes.
|
||||
"""
|
||||
contract_id = submission.get("contractId") or submission.get("id", "unknown")
|
||||
codes: list[str] = []
|
||||
|
||||
# Step 1: contract shape validation
|
||||
contract_shape = {k: v for k, v in submission.items() if k in ("id", "name", "environment", "infrastructure")}
|
||||
if contract_shape:
|
||||
codes.extend(_validate_contract_schema(contract_shape))
|
||||
|
||||
# Step 2: readiness schema validation
|
||||
try:
|
||||
import jsonschema
|
||||
|
||||
schema_path = os.path.join(_SCHEMA_DIR, "submission-readiness.schema.json")
|
||||
with open(schema_path) as f:
|
||||
readiness_schema = json.load(f)
|
||||
jsonschema.validate(instance=submission, schema=readiness_schema)
|
||||
except (OSError, ImportError):
|
||||
pass
|
||||
except jsonschema.ValidationError as e:
|
||||
codes.append(f"READINESS_SCHEMA_INVALID:{e.message}")
|
||||
|
||||
# Step 3: semantic checks (reason codes for citizen-developer-facing errors)
|
||||
|
||||
# 3a: tags
|
||||
tags = submission.get("tags", {})
|
||||
missing_tags = [t for t in REQUIRED_TAGS if t not in tags or not tags[t]]
|
||||
if missing_tags:
|
||||
codes.append(f"MISSING_TAGS:{','.join(missing_tags)}")
|
||||
|
||||
# 3b: env mandatory (W3.E per-env table)
|
||||
env = submission.get("environment")
|
||||
if env and env in ENV_MANDATORY:
|
||||
for field_key in ENV_MANDATORY[env]:
|
||||
val = _get_nested(submission, field_key)
|
||||
if val is None:
|
||||
codes.append(f"ENV_MISSING_MANDATORY:{env}:{field_key}")
|
||||
|
||||
# 3c: agentic profile markers
|
||||
if submission.get("profile") == "agentic":
|
||||
for marker in AGENTIC_REQUIRED:
|
||||
if not submission.get(marker):
|
||||
codes.append(f"AGENTIC_MISSING_INTENT:{marker}")
|
||||
|
||||
# 3d: appSource
|
||||
app_source = submission.get("appSource")
|
||||
if not app_source or not app_source.get("repo") or not app_source.get("ref"):
|
||||
codes.append("MISSING_APP_SOURCE")
|
||||
|
||||
# 3e: policy preconditions (warn if declared but not enforced this milestone)
|
||||
policy = submission.get("policyPreconditions", {})
|
||||
if not policy:
|
||||
codes.append("POLICY_PRECONDITION_MISSING")
|
||||
|
||||
ready = len(codes) == 0
|
||||
return ReadinessResult(ready=ready, reason_codes=codes, contract_id=contract_id)
|
||||
|
||||
|
||||
def cli_main(argv: list[str]) -> int:
|
||||
"""CLI entry: python3 -m core.submission_readiness <contract.json>
|
||||
|
||||
Also invoked via contract_ingestor.py --check-readiness (D-133).
|
||||
Prints the ReadinessResult to stdout; exits 0 if ready, 1 if not.
|
||||
"""
|
||||
if len(argv) < 2:
|
||||
print("Usage: submission_readiness <contract.json>", file=sys.stderr)
|
||||
return 2
|
||||
path = argv[1]
|
||||
try:
|
||||
with open(path) as f:
|
||||
submission = json.load(f)
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
print(f"ERROR: cannot read {path}: {e}", file=sys.stderr)
|
||||
return 2
|
||||
result = check_readiness(submission)
|
||||
print(result)
|
||||
print(json.dumps(result.to_dict(), indent=2))
|
||||
return 0 if result.ready else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(cli_main(sys.argv))
|
||||
@@ -1,7 +1,5 @@
|
||||
# Nova Metrics Catalog
|
||||
|
||||
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (REQ-195)
|
||||
> Generated: 2026-08-04
|
||||
|
||||
This is the canonical catalog of every executive KPI in Nova's
|
||||
leadership metrics layer. Each metric carries a **status**:
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Nova Deferred Metrics Activation Roadmap
|
||||
|
||||
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (REQ-210)
|
||||
> Generated: 2026-08-04
|
||||
|
||||
This document lists all 8 deferred metrics + the onboarding-funnel
|
||||
"granted" half, with their blocking decisions, unblock requirements,
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Nova Metrics Views — PowerBI Data Dictionary
|
||||
|
||||
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (REQ-190, REQ-209)
|
||||
> Generated: 2026-08-04
|
||||
|
||||
This document is the column-level data dictionary for the PowerBI export
|
||||
views in `metrics/powerbi/`. Each fact/dimension table and placeholder
|
||||
|
||||
@@ -1,270 +0,0 @@
|
||||
# Nova AWS Resource Migration Runbook (REQ-163, P4)
|
||||
|
||||
> **Milestone:** v1.15-Nova (Wave 4, P4). Renames every `acdl-*` AWS
|
||||
> resource name → `nova-*` via Terraform. This is the heaviest Terraform
|
||||
> phase of the rebrand and requires a **maintenance window**.
|
||||
>
|
||||
> **Plan-validated only.** Per A1, `NOVA_LIFECYCLE_MODE` defaults to
|
||||
> `plan` (no live AWS mutation from CI). `terraform validate` passes; the
|
||||
> live apply steps below are executed by a platform operator during the
|
||||
> scheduled maintenance window. Each step has a verification + rollback.
|
||||
|
||||
## Scope (renamed resources)
|
||||
|
||||
| AWS resource | Before | After | Strategy |
|
||||
|---|---|---|---|
|
||||
| KMS alias | `alias/acdl-platform` | `alias/nova-platform` | cheap rename |
|
||||
| SNS topic | `acdl-sod-halt` | `nova-sod-halt` | recreate |
|
||||
| Security group | `acdl-ecs-sg` | `nova-ecs-sg` | recreate |
|
||||
| Lambda (role/policy/function) | `acdl-contract-ingestor` | `nova-contract-ingestor` | recreate |
|
||||
| DynamoDB contracts | `acdl-contracts` | `nova-contracts` | scan + copy |
|
||||
| DynamoDB change-requests | `acdl-change-requests` | `nova-change-requests` | scan + copy |
|
||||
| Secrets Manager secret | `acdl/github-token` | `nova/github-token` | recreate + re-store |
|
||||
| ECR repo | `acdl-microservice` | `nova-microservice` | re-push |
|
||||
| ECS cluster/service/task/role | `acdl-microservice` | `nova-microservice` | recreate |
|
||||
| IAM user + policy | `acdl-spike-runner` (+ `-policy`) | `nova-spike-runner` (+ `-policy`) | re-bootstrap |
|
||||
| IAM act-runner role | `acdl-act-runner-role` | `nova-act-runner-role` | re-bootstrap |
|
||||
| IAM deploy role | `acdl-deploy-<repo>` | `nova-deploy-<repo>` | re-bootstrap |
|
||||
| S3 state bucket | `acdl-tfstate-581513795199-us-east-1` | `nova-tfstate-581513795199-us-east-1` | `-migrate-state` |
|
||||
| DynamoDB outbox | `acdl-outbox` | `nova-outbox` | scan + copy |
|
||||
| Platform VPC/subnet/IGW/RT | `acdl-shared*` | `nova-shared*` | recreate (brief downtime) |
|
||||
| CI VPC/subnet/SG/cluster | `acdl-ci-*` | `nova-ci-*` | recreate (CI-only) |
|
||||
| ALB name prefix | `acdl-alb` | `nova-alb` | recreate (brief downtime, LAST) |
|
||||
|
||||
## Migration ordering (binding)
|
||||
|
||||
Order: **KMS alias → SNS/SG → Lambda → DynamoDB → ECR → IAM → state bucket → ALB**.
|
||||
Each step is independently rollback-able. The ALB is last because it
|
||||
requires the briefest downtime window.
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight
|
||||
|
||||
1. **Announce the maintenance window** (consumers are notified via the
|
||||
P1 migration guide `docs/NOVA_MIGRATION.md`).
|
||||
2. **Back up state** for every stack (see §State bucket — back up the
|
||||
state JSON *before* `-migrate-state`).
|
||||
3. Confirm `NOVA_LIFECYCLE_MODE=plan` (default) so CI does not mutate
|
||||
AWS during the window.
|
||||
4. Confirm the new `nova-*` destination tables/repos will be created by
|
||||
the same Terraform apply (no manual pre-creation needed).
|
||||
|
||||
## Step 1 — KMS alias (`alias/acdl-platform` → `alias/nova-platform`)
|
||||
|
||||
- **Command (in `terraform/platform/`):**
|
||||
```bash
|
||||
terraform init -upgrade
|
||||
terraform apply -replace=aws_kms_alias.nova_platform
|
||||
```
|
||||
(Terraform destroys the old alias + creates the new one — aliases are
|
||||
cheap; the underlying key ID is unchanged.)
|
||||
- **Verify:** `aws kms list-aliases --query 'Aliases[?AliasName==`alias/nova-platform`]'` returns the new alias; `alias/acdl-platform` is gone.
|
||||
- **Rollback:** `terraform apply -replace=aws_kms_alias.nova_platform` against the prior revision (re-creates `alias/acdl-platform`). Resources encrypted by the key are unaffected (key ID unchanged).
|
||||
|
||||
## Step 2 — SNS topic + Security group (recreate)
|
||||
|
||||
- **Command:** `terraform apply` in `terraform/platform/`.
|
||||
- SNS `acdl-sod-halt` → `nova-sod-halt` (the topic ARN changes; update `NOVA_SOD_HALT_TOPIC_ARN` wherever it is set).
|
||||
- SG `acdl-ecs-sg` → `nova-ecs-sg` (the security group is re-attached to running ECS tasks; brief task restart).
|
||||
- **Verify:** `aws sns list-topics` shows `nova-sod-halt`; `aws ec2 describe-security-groups` shows `nova-ecs-sg`.
|
||||
- **Rollback:** `terraform apply` the prior revision re-creates the `acdl-*` names. The SNS topic has no message backlog (halt artifacts are fire-and-forget); the SG drift resolves on next task deploy.
|
||||
|
||||
## Step 3 — Lambda (recreate)
|
||||
|
||||
- **Command:** `terraform apply` in `terraform/platform/`.
|
||||
- Lambda function `acdl-contract-ingestor` → `nova-contract-ingestor`.
|
||||
- Execution role `acdl-contract-ingestor-role` → `nova-contract-ingestor-role`.
|
||||
- Inline policy `acdl-contract-ingestor-policy` → `nova-contract-ingestor-policy`.
|
||||
- The Lambda env vars (`CONTRACTS_TABLE`, `GITHUB_TOKEN_SECRET_ID`) now resolve to `nova-*` defaults.
|
||||
- **Verify:** `aws lambda list-functions` shows `nova-contract-ingestor`; the Function URL returns 200 on a SigV4-signed invoke. The `consumer_invoke_policy.json` rendered output (Terraform `consumer_invoke_policy_rendered`) now references `function:nova-contract-ingestor` — re-distribute to consumer deploy roles.
|
||||
- **Rollback:** `terraform apply` the prior revision re-creates `acdl-contract-ingestor`. Consumer deploy roles must point back at the old Function ARN (re-distribute the prior `consumer_invoke_policy.json`).
|
||||
|
||||
## Step 4 — DynamoDB (scan + copy)
|
||||
|
||||
DynamoDB table names are immutable post-creation, so the migration is a
|
||||
**scan + copy** (not a rename). The new `nova-*` tables are created by
|
||||
the same Terraform apply (Step 3). The data-migration script copies
|
||||
every item and verifies row counts.
|
||||
|
||||
- **Command (from repo root):**
|
||||
```bash
|
||||
# Dry-run first (no writes):
|
||||
python3 scripts/migrate_dynamodb_data.py
|
||||
# Execute the copy:
|
||||
python3 scripts/migrate_dynamodb_data.py --apply
|
||||
# A single table:
|
||||
python3 scripts/migrate_dynamodb_data.py --table contracts --apply
|
||||
```
|
||||
The script scans `acdl-contracts` → copies to `nova-contracts`, and
|
||||
`acdl-change-requests` → `nova-change-requests`, then verifies the
|
||||
destination row count == source row count (re-scan, not
|
||||
`DescribeTable.ItemCount` which lags ~6h).
|
||||
- **Verify:**
|
||||
```bash
|
||||
# Row counts must match (printed by the script). Manual cross-check:
|
||||
aws dynamodb scan --table-name nova-contracts --select COUNT
|
||||
aws dynamodb scan --table-name acdl-contracts --select COUNT
|
||||
```
|
||||
Then **point consumers at the new tables** (the Lambda already reads
|
||||
`nova-*` defaults; any direct DynamoDB consumers update their env).
|
||||
- **Keep the old tables** (`acdl-contracts`, `acdl-change-requests`)
|
||||
until consumers are verified reading from `nova-*`. **Deletion is a
|
||||
manual post-verification step:**
|
||||
```bash
|
||||
aws dynamodb delete-table --table-name acdl-contracts
|
||||
aws dynamodb delete-table --table-name acdl-change-requests
|
||||
```
|
||||
Only delete after a full soak period confirms `nova-*` reads succeed.
|
||||
- **Rollback:** Re-point consumers at `acdl-*` (the old tables are
|
||||
retained). The copy is additive (no data loss). To roll back a partial
|
||||
copy, re-run `--apply` (idempotent — `PutItem` overwrites).
|
||||
|
||||
### Outbox table (`acdl-outbox` → `nova-outbox`)
|
||||
|
||||
The evidence outbox table follows the same scan+copy pattern (it is
|
||||
created by `terraform/bootstrap/create_state_backend.py`).
|
||||
- **Command:** `python3 scripts/migrate_dynamodb_data.py --source acdl-outbox --dest nova-outbox --apply`
|
||||
- The `core/outbox_writer.py` default + `core/regression_verify.py`
|
||||
CAP-015 probe now reference `nova-outbox` (P4 updated both). The
|
||||
regression gate's live-AWS CAP-015 will return `Verified` once the
|
||||
`nova-outbox` table exists live; until then it is `Decayed` (the gate
|
||||
is re-run at milestone complete after the live migration).
|
||||
|
||||
## Step 5 — ECR (re-push)
|
||||
|
||||
- **Command:** `terraform apply` in `terraform/microservice/` creates
|
||||
the new `nova-microservice` ECR repo. Re-push the image:
|
||||
```bash
|
||||
python3 scripts/push_consumer_image.py # creates nova-microservice + prints docker tag/push
|
||||
```
|
||||
(The script's `ECR_REPO_NAME` is now `nova-microservice`.)
|
||||
- **Verify:** `aws ecr describe-repositories` shows `nova-microservice`; `docker pull <acct>.dkr.ecr.us-east-1.amazonaws.com/nova-microservice:latest` succeeds.
|
||||
- **Rollback:** The old `acdl-microservice` repo is retained until the
|
||||
soak passes. Re-push to it if a rollback is needed. Delete it manually:
|
||||
`aws ecr delete-repository --repository-name acdl-microservice --force`.
|
||||
|
||||
## Step 6 — IAM (re-bootstrap)
|
||||
|
||||
- **Command:**
|
||||
```bash
|
||||
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
||||
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
||||
python3 terraform/bootstrap/create_state_backend.py # creates nova-outbox (idempotent)
|
||||
python3 terraform/bootstrap/create_iam_user.py # creates nova-spike-runner
|
||||
python3 terraform/bootstrap/apply_iam_baseline.py # creates nova-spike-runner-policy + nova-act-runner-role
|
||||
bash scripts/rotate_spike_key.sh # rotates the nova-spike-runner key
|
||||
```
|
||||
The deploy role `acdl-deploy-<repo>` → `nova-deploy-<repo>` is
|
||||
created by the bootstrap (the deploy workflow
|
||||
`.gitea/.github/workflows/deploy.yml` now references
|
||||
`role/nova-deploy-{1}`).
|
||||
- **Verify:** `aws iam get-user --user-name nova-spike-runner`;
|
||||
`aws iam list-attached-user-policies --user-name nova-spike-runner`
|
||||
shows `nova-spike-runner-policy`;
|
||||
`aws iam get-role --role-name nova-act-runner-role`.
|
||||
- **Rollback:** Re-run the prior bootstrap scripts (they create
|
||||
`acdl-spike-runner` + `acdl-act-runner-role`). The deploy workflow's
|
||||
`role-to-assume` must be reverted to `acdl-deploy-` (prior revision).
|
||||
|
||||
## Step 7 — State bucket (`acdl-tfstate-*` → `nova-tfstate-*`, `-migrate-state`)
|
||||
|
||||
The S3 state backend is renamed. Terraform's `-migrate-state` copies the
|
||||
state objects to the new bucket. **Back up the state JSON first.**
|
||||
|
||||
- **Back up state (per stack):**
|
||||
```bash
|
||||
for stack in platform microservice ci-vpc; do
|
||||
aws s3 cp s3://acdl-tfstate-581513795199-us-east-1/$stack/terraform.tfstate \
|
||||
./backup-$stack.tfstate
|
||||
done
|
||||
```
|
||||
- **Command (per stack):** the backend config in each
|
||||
`terraform/*/terraform.tf` now points at `nova-tfstate-...`.
|
||||
```bash
|
||||
cd terraform/platform
|
||||
terraform init -migrate-state # copies state acdl-tfstate → nova-tfstate
|
||||
cd ../microservice
|
||||
terraform init -migrate-state
|
||||
cd ../ci-vpc
|
||||
terraform init -migrate-state
|
||||
```
|
||||
- **Verify:** `aws s3 ls s3://nova-tfstate-581513795199-us-east-1/`
|
||||
shows the state keys; `terraform state list` in each dir lists the
|
||||
expected resources.
|
||||
- **Rollback:** Point the backend back at `acdl-tfstate-*` and re-run
|
||||
`terraform init -migrate-state` (restores from the backup bucket). The
|
||||
old `acdl-tfstate-*` bucket is retained until the soak passes. Delete
|
||||
it manually:
|
||||
`aws s3 rb s3://acdl-tfstate-581513795199-us-east-1 --force`.
|
||||
|
||||
## Step 8 — ALB (recreate, brief downtime, LAST)
|
||||
|
||||
The ALB is last because its recreation requires the briefest downtime
|
||||
window (the ECS service is re-attached to the new target group).
|
||||
|
||||
- **Command:** `terraform apply` in `terraform/microservice/`. The ALB
|
||||
`acdl-microservice` / `acdl-alb` → `nova-microservice` / `nova-alb`.
|
||||
- **Verify:** `aws elbv2 describe-load-balancers` shows the new ALB;
|
||||
`curl http://<new-alb-dns>/` returns 200.
|
||||
- **Rollback:** `terraform apply` the prior revision re-creates the
|
||||
`acdl-*` ALB (brief downtime again). The old ALB DNS is retained until
|
||||
consumers are re-pointed.
|
||||
|
||||
---
|
||||
|
||||
## Post-migration
|
||||
|
||||
1. **Soak:** run consumers against `nova-*` for a full verification
|
||||
window (deploy a test contract end-to-end).
|
||||
2. **Delete old resources** (manual, only after soak):
|
||||
- DynamoDB: `acdl-contracts`, `acdl-change-requests`, `acdl-outbox`
|
||||
- ECR: `acdl-microservice`
|
||||
- IAM: `acdl-spike-runner` (+ policy), `acdl-act-runner-role`,
|
||||
`acdl-deploy-<repo>`
|
||||
- S3: `acdl-tfstate-581513795199-us-east-1`
|
||||
- SNS: `acdl-sod-halt`
|
||||
- SG: `acdl-ecs-sg`
|
||||
- Secrets Manager: `acdl/github-token`
|
||||
- KMS alias: `alias/acdl-platform`
|
||||
- ALB: `acdl-alb` / `acdl-microservice`
|
||||
3. **Regression gate:** re-run `bash scripts/run_regression.sh`. The
|
||||
live-AWS CAP-013..016 probes should return `Verified` (the `nova-*`
|
||||
tables + state bucket exist). CAP-015 (outbox) flips from `Decayed`
|
||||
→ `Verified` once `nova-outbox` is live.
|
||||
|
||||
## What P5 owns (not P4)
|
||||
|
||||
- **Remove dual-read fallback:** `core/env.py` `get_env()` drops the
|
||||
`ACDL_*` fallback; shell scripts drop `:-$ACDL_X`. P4 keeps the
|
||||
dual-read (deployments don't break mid-window).
|
||||
- **`nova_tagging.py` hard-fail on `acdl:*`:** P3 set hard mode (no
|
||||
`acdl:*`-only tags); P5 tightens to fail on any `acdl:*` presence. P4
|
||||
leaves P3's behavior.
|
||||
- **Delete `ACDL_*` Gitea secrets:** the `NOVA_*` aliases created in P2
|
||||
are now the only source.
|
||||
- **Finalize `docs/NOVA_MIGRATION.md`:** mark the migration complete
|
||||
(cutoff passed).
|
||||
- **Milestone ship:** tag `v1.15.4`, merge to `main`, Gitea release.
|
||||
|
||||
## Files touched in P4
|
||||
|
||||
- `terraform/platform/main.tf`, `terraform/microservice/main.tf`,
|
||||
`terraform/ci-vpc/main.tf` — resource renames + backend bucket.
|
||||
- `terraform/{platform,microservice,ci-vpc}/terraform.tf` — state bucket.
|
||||
- `terraform/platform/consumer_invoke_policy.json` — Lambda ARN.
|
||||
- `terraform/bootstrap/{create_state_backend,create_iam_user,apply_iam_baseline}.py`,
|
||||
`spike_runner_policy.json`, `.bootstrap_state.json`, `README.md` —
|
||||
IAM/outbox/state-bucket renames.
|
||||
- `modules/l1/*/terraform/**` + `modules/l1/alb/instance.json` — L1
|
||||
resource-name defaults.
|
||||
- `modules/l2/microservice/composition.json` — `nova-app-role` default.
|
||||
- `core/lambda/contract_ingestor.py` — default table names (D-111).
|
||||
- `core/outbox_writer.py`, `core/regression_verify.py`,
|
||||
`core/local_emulators.py` — outbox table consistency (cross-territory,
|
||||
minimal).
|
||||
- `.gitea/workflows/deploy.yml` + `.github/workflows/deploy.yml` —
|
||||
`nova-deploy-` role ARN + artifact names.
|
||||
- `scripts/migrate_dynamodb_data.py` (NEW), `scripts/rotate_spike_key.sh`,
|
||||
`scripts/push_consumer_image.py`.
|
||||
- `tests/**` — fixtures updated to assert `nova-*`.
|
||||
@@ -1,177 +0,0 @@
|
||||
# Nova Migration Guide — What Consumers Must Know
|
||||
|
||||
> **STATUS: COMPLETE (milestone v1.15.4, 2026-07-30).** The Nova rebrand
|
||||
> is fully rolled out. The dual-read / parallel-write grace period has
|
||||
> ended (P5 cutoff passed). All `ACDL_*` env var fallbacks, `.acdl/`
|
||||
> consumer-path fallbacks, `/acdl/` SSM-path fallbacks, `acdl:*` tag-key
|
||||
> fallbacks, and `acdl-*` AWS resource names are removed. Consumers must
|
||||
> use the `NOVA_*` / `.nova/` / `/nova/` / `nova:*` / `nova-*` names
|
||||
> exclusively. If you have not yet migrated, follow the steps below.
|
||||
|
||||
> **Nova** is the new product brand for the platform formerly known as
|
||||
> **ACDL** (Agentic Cloud Delivery Platform). This guide documents the
|
||||
> breaking changes from the rebrand rollout (Phases P2–P4, cutoff P5)
|
||||
> and tells you exactly what to do.
|
||||
|
||||
## What is NOT changing
|
||||
|
||||
- **The Gitea repository name** (`continuous-intelligence/acdl`) is **not**
|
||||
changing. Only the product brand is changing. The `uses:` reference
|
||||
(`acdl/.github/workflows/deploy.yml@vX.Y`) and the GitHub `acdl/acdl` repo
|
||||
path are unchanged for the duration of the rebrand; the workflow
|
||||
`uses:` reference will be migrated in a later, separately-announced step.
|
||||
- **The platform behavior** is unchanged. Same pipeline stages, same
|
||||
contract schema, same confidence model, same evidence stream, same
|
||||
modules. Only the brand, the on-disk path, the env var names, the SSM
|
||||
path, the AWS tag keys, and the AWS resource names are changing.
|
||||
|
||||
## The 5 breaking changes
|
||||
|
||||
Five things that consumers may reference are being renamed. Each is
|
||||
scheduled into a phase, ships with a grace period, and has a cutoff.
|
||||
|
||||
### 1. Consumer contract path — Phase P2
|
||||
|
||||
- **Old:** `.acdl/contract.yml`
|
||||
- **New:** `.nova/contract.yml`
|
||||
- **Phase:** P2 (env vars + consumer path)
|
||||
- **Grace period:** during P2–P4 the deploy workflow reads **both** paths
|
||||
(`.nova/contract.yml` first, falling back to `.acdl/contract.yml` if the
|
||||
new path is absent). Your existing contracts keep working until P5.
|
||||
- **Cutoff:** P5 removes the `.acdl/` fallback. Move your contract file
|
||||
before P5.
|
||||
- **What you must do:** rename the directory in your consumer repo from
|
||||
`.acdl/` to `.nova/` and update any `contract:` workflow input that
|
||||
points at the old path. Nothing else changes in the contract content.
|
||||
|
||||
### 2. Environment variables — Phase P2
|
||||
|
||||
- **Old:** `ACDL_*` (e.g. `ACDL_LIFECYCLE_MODE`, `ACDL_AWS_ACCOUNT_ID`,
|
||||
`ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID`, …)
|
||||
- **New:** `NOVA_*` (e.g. `NOVA_LIFECYCLE_MODE`, `NOVA_AWS_ACCOUNT_ID`,
|
||||
`NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID`, …)
|
||||
- **Phase:** P2 (env vars + consumer path)
|
||||
- **Grace period — dual-read fallback:** during P2–P4 the platform reads
|
||||
**`NOVA_*` first, then falls back to `ACDL_*`** if the Nova variable is
|
||||
unset. This means your CI secrets, workflow env blocks, and local
|
||||
`.env.secrets` keep working unchanged through P4. You do not need to
|
||||
rename everything in one shot — rename a variable and the dual-read picks
|
||||
it up; leave one old and it still resolves.
|
||||
- **Cutoff:** P5 removes the `ACDL_*` fallback. After P5, only `NOVA_*`
|
||||
is read.
|
||||
- **What you must do:** rename your `ACDL_*` CI secrets, workflow `env:`
|
||||
blocks, and any local `.env.secrets` entries to `NOVA_*`. Because of the
|
||||
dual-read, you can do this incrementally across P2–P4 — but it must be
|
||||
complete before P5.
|
||||
|
||||
### 3. SSM parameter path — Phase P3 (DONE)
|
||||
|
||||
- **Old:** `/acdl/{env}/{contractId}/{output}`
|
||||
- **New:** `/nova/{env}/{contractId}/{output}`
|
||||
- **Phase:** P3 (SSM paths + tag keys) — **shipped in P3**
|
||||
- **Grace period — parallel-write:** during P3–P4 the platform **writes
|
||||
every output to both** the `/acdl/…` and `/nova/…` SSM paths, and reads
|
||||
from `/nova/…` first (falling back to `/acdl/…`). Any hardcoded SSM path
|
||||
reads in your application code keep resolving through P4. The P3
|
||||
migration script (`scripts/migrate_ssm_paths.py`) copies existing
|
||||
`/acdl/…` parameters to `/nova/…`, verifies the copy, and deletes the
|
||||
old ones.
|
||||
- **Cutoff:** P5 stops writing to `/acdl/…` and removes the read fallback.
|
||||
After P5 only `/nova/…` exists.
|
||||
- **What you must do:** if your application code or runbooks read deploy
|
||||
outputs from SSM by hardcoded path, update the path prefix from `/acdl/`
|
||||
to `/nova/`. If you consume outputs only via the PR-comment / GitHub
|
||||
issue surface, you do nothing — the platform republishes under the new
|
||||
path automatically.
|
||||
|
||||
### 4. AWS tag keys — Phase P3 (DONE)
|
||||
|
||||
- **Old:** `acdl:owner`, `acdl:environment`, `acdl:contract`,
|
||||
`acdl:cost-center`, `acdl:ref`
|
||||
- **New:** `nova:owner`, `nova:environment`, `nova:contract`,
|
||||
`nova:cost-center`, `nova:ref`
|
||||
- **Phase:** P3 (SSM paths + tag keys) — **shipped in P3**
|
||||
- **Grace period — parallel-tag period:** during P3–P4 the platform
|
||||
**tags every resource with both** the `acdl:*` and `nova:*` keys (same
|
||||
values). The ABAC session policy matches on **either** key set, so your
|
||||
existing scoped permissions keep working. The default cost-center value
|
||||
moves from `acdl-default` to `nova-default` (both written during the
|
||||
parallel-tag period). Terraform now emits `nova:*` keys; old `acdl:*`
|
||||
tags on pre-P3 live resources are removed by the P4 runbook's
|
||||
`scripts/untag_acdl_keys.py` step after the `nova:*` tags are applied
|
||||
live.
|
||||
- **Cutoff:** P5 stops writing the `acdl:*` keys and the ABAC policy matches
|
||||
only on `nova:*`. After P5, resources created before P5 still carry the
|
||||
old `acdl:*` tags (tags are not retroactively rewritten) but **new**
|
||||
resources are tagged `nova:*` only, and the policy no longer grants
|
||||
access via `acdl:*`.
|
||||
- **What you must do:** if you have IAM policies, Cost Explorer filters,
|
||||
or billing groupings that key off `acdl:*` tag keys, add a parallel
|
||||
`nova:*` condition (or migrate to `nova:*`) before P5. The platform
|
||||
handles the dual-tagging; you only need to update your own tag-key
|
||||
references.
|
||||
|
||||
### 5. AWS resource names — Phase P4
|
||||
|
||||
- **Old:** `acdl-*` (DynamoDB tables `acdl-contracts`,
|
||||
`acdl-change-requests`; Lambda `acdl-contract-ingestor`; SNS
|
||||
`acdl-sod-halt`; security group `acdl-ecs-sg`; KMS alias
|
||||
`alias/acdl-platform`; ECS services, ECR repos, IAM user
|
||||
`acdl-spike-runner`, state bucket `acdl-tfstate-*`, ALB `acdl-alb`,
|
||||
`acdl-deploy-*`)
|
||||
- **New:** `nova-*` (the same resources, prefixed `nova-`)
|
||||
- **Phase:** P4 (resource names) — **maintenance window**
|
||||
- **Grace period:** P4 is a **planned maintenance window**. AWS resources
|
||||
cannot be renamed in place, so P4 provisions the `nova-*` resources,
|
||||
migrates data (DynamoDB tables, S3 state), repoints the platform, and
|
||||
tears down the `acdl-*` resources. The platform team schedules and
|
||||
announces the window; consumers do not provision or rename anything
|
||||
themselves.
|
||||
- **Cutoff:** the `acdl-*` resources are decommissioned at the end of the
|
||||
P4 maintenance window. After P4, only `nova-*` resources exist.
|
||||
- **What you must do:** nothing for the resource names themselves — the
|
||||
platform owns the rename. If your application code or runbooks reference
|
||||
a specific `acdl-*` resource by name (e.g. a hardcoded DynamoDB table
|
||||
name or ECR URI), update it to the `nova-*` name during P4. The platform
|
||||
publishes the exact old → new name mapping with the P4 announcement.
|
||||
|
||||
## Timeline at a glance
|
||||
|
||||
| Phase | What ships | Grace period | Cutoff |
|
||||
|-------|------------|--------------|--------|
|
||||
| **P1** (this phase) | Brand prose, docs, decks, schema `$id`, release titles | n/a (prose only) | n/a |
|
||||
| **P2** | `.nova/` contract path + `NOVA_*` env vars | dual-read: `.nova/`→`.acdl/`, `NOVA_*`→`ACDL_*` | **P5** removes fallback |
|
||||
| **P3** | `/nova/` SSM path + `nova:*` tag keys | parallel-write (SSM) + parallel-tag (ABAC matches either) | **P5** removes old path/tags |
|
||||
| **P4** | `nova-*` AWS resource names | maintenance window (platform-owned migration) | end of P4 window |
|
||||
| **P5** | Fallback removal | — | `ACDL_*` env vars, `.acdl/` path, `/acdl/` SSM, `acdl:*` tags stop working |
|
||||
|
||||
## What consumers must do (checklist)
|
||||
|
||||
1. **Before P5 — contract path:** move `.acdl/contract.yml` →
|
||||
`.nova/contract.yml` in your consumer repo; update the `contract:`
|
||||
workflow input. *(Can be done any time in P2–P4.)*
|
||||
2. **Before P5 — env vars:** rename `ACDL_*` CI secrets / workflow `env:`
|
||||
blocks / local `.env.secrets` to `NOVA_*`. *(Incremental during P2–P4;
|
||||
dual-read keeps you green.)*
|
||||
3. **Before P5 — SSM reads:** if you read deploy outputs from SSM by
|
||||
hardcoded `/acdl/…` path, update to `/nova/…`. *(Skip if you consume
|
||||
outputs via PR comments only.)*
|
||||
4. **Before P5 — tag-key references:** if you have IAM policies, Cost
|
||||
Explorer filters, or billing groupings keyed off `acdl:*`, add or
|
||||
migrate to `nova:*`. *(Platform handles dual-tagging.)*
|
||||
5. **During P4 — resource-name references:** if your code or runbooks
|
||||
reference a specific `acdl-*` AWS resource by name, update to the
|
||||
`nova-*` name per the P4 mapping announcement. *(Platform owns the
|
||||
rename itself.)*
|
||||
|
||||
## Questions
|
||||
|
||||
If anything in this guide is unclear, or you are unsure whether your
|
||||
consumer repo references a renamed value, open an issue on the platform
|
||||
repo. The platform team will confirm what you need to change and when.
|
||||
|
||||
> **Note:** the real Gitea repository name (`continuous-intelligence/acdl`)
|
||||
> is **not** changing — only the product brand. The `uses:` workflow
|
||||
> reference and repo path are migrated in a separately-announced later step;
|
||||
> until then, keep your `uses: acdl/.github/workflows/deploy.yml@vX.Y`
|
||||
> reference as-is.
|
||||
+1
-1
@@ -77,7 +77,7 @@ milestone (D-113).
|
||||
only (D-114); live apply is deferred.
|
||||
- **OIDC trust policy** — the onboarding Terraform uses a placeholder
|
||||
OIDC provider; real OIDC federation is blocked on
|
||||
go-gitea/gitea#36988 (carries forward from v1.1).
|
||||
upstream forge OIDC support (carries forward from v1.1).
|
||||
|
||||
## See also
|
||||
|
||||
|
||||
@@ -230,7 +230,7 @@ change to the modules/stack/confidence/audit.
|
||||
- A MAJOR bump requires a new registry entry (immutable publication); the
|
||||
old entry enters a 12-month deprecation window.
|
||||
- The central deploy pipeline is referenced by a floating MAJOR + MINOR tag
|
||||
(e.g. `@v1.13`); patch fixes flow within the tag, breaking changes land
|
||||
(e.g. `@v1.19`); patch fixes flow within the tag, breaking changes land
|
||||
under the next MINOR tag.
|
||||
|
||||
See [Versioning](pipeline/versioning) for the consumer-facing details.
|
||||
|
||||
+13
-13
@@ -19,7 +19,7 @@ definitions.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["your repo<br/>(app code + contracts + CI definitions)"] -->|uses: acdl/.github/workflows/deploy.yml@v1.13| B
|
||||
A["your repo<br/>(app code + contracts + CI definitions)"] -->|uses: nova/.github/workflows/deploy.yml@v1.19| B
|
||||
B["platform runners<br/>(modules + pipelines + adapters + schemas)"] -->|contract -> resolver -> stack -> adapter<br/>-> security checks -> infrastructure plan -> policy checks<br/>-> confidence -> apply -> evidence event| C
|
||||
C["your resources in AWS"]
|
||||
```
|
||||
@@ -27,13 +27,13 @@ flowchart LR
|
||||
## Versioning the `uses:` reference
|
||||
|
||||
The central deployment pipeline is **always versioned with floating MAJOR
|
||||
and MINOR tags** (e.g. `acdl/pipelines/contract.yml@v1.13`). Version
|
||||
and MINOR tags** (e.g. `nova/pipelines/contract.yml@v1.19`). Version
|
||||
constraints cannot be expressed inside the contract, so the tag in
|
||||
`uses:` is the only immutability lever a consumer has. See
|
||||
[Versioning](pipeline/versioning) for the full rationale.
|
||||
|
||||
**Unversioned references are discouraged.** Do not use `@main` or a bare
|
||||
`acdl/pipelines/contract.yml`.
|
||||
`nova/pipelines/contract.yml`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -47,7 +47,7 @@ platform-managed. See [Environments](environments/).
|
||||
environment is bound, your first pipeline run emits a friendly onboarding
|
||||
prompt. See [Environments](environments/).
|
||||
- **Authorization to reference the central pipeline.** Onboarding grants
|
||||
your repo the right to `uses: acdl/.github/workflows/deploy.yml@v1.13`.
|
||||
your repo the right to `uses: nova/.github/workflows/deploy.yml@v1.19`.
|
||||
Contact the platform team if you have not been onboarded.
|
||||
|
||||
## Step 1 — Create a consumer repo
|
||||
@@ -94,7 +94,7 @@ Nova deployment workflow with a **versioned tag** (floating MAJOR + MINOR):
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
with:
|
||||
contract: .nova/contract.yml
|
||||
environment: dev
|
||||
@@ -140,7 +140,7 @@ name: microservice
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/contract.yml@v1.13`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
|
||||
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `nova/pipelines/contract.yml@v1.19`). Bare or `@main` references are discouraged. See [Versioning](pipeline/versioning). |
|
||||
| `module` | string | yes | Module name from the registry — any primitive or module (e.g. `static-assets`, `microservice`, `s3`). See the [module catalog](modules/). |
|
||||
| `environment` | string | yes | The platform-managed environment to deploy to (e.g. `dev`). See [Environments](environments/). |
|
||||
| `inputs` | object | yes | Module-specific inputs (see the module's README). |
|
||||
@@ -177,14 +177,14 @@ on:
|
||||
branches: [main]
|
||||
jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
with:
|
||||
contract: .nova/contract.yml
|
||||
```
|
||||
|
||||
That is the entire consumer-side workflow. When you push to `main`:
|
||||
|
||||
1. The platform runner resolves `uses: acdl/.github/workflows/deploy.yml@v1.13`
|
||||
1. The platform runner resolves `uses: nova/.github/workflows/deploy.yml@v1.19`
|
||||
to the reusable workflow **at the pinned tag**.
|
||||
2. A **platform-provided runner** checks out **your** repo.
|
||||
3. The runner checks out the **Nova platform repo** into the workspace —
|
||||
@@ -326,8 +326,8 @@ per-module extension points. Common examples:
|
||||
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
|
||||
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
||||
| Module catalog | [modules/](modules/) | All primitives and modules. |
|
||||
| Sample contract | `contracts/static-assets.yaml` | The reference example contract (uses `@v1.13`). |
|
||||
| Sample contract | `contracts/microservice.yaml` | The microservice example contract (uses `@v1.13`). |
|
||||
| Sample contract | `contracts/static-assets.yaml` | The reference example contract (uses `@v1.19`). |
|
||||
| Sample contract | `contracts/microservice.yaml` | The microservice example contract (uses `@v1.19`). |
|
||||
| Module examples | `modules/<name>/examples/` | Validated per-module example contracts (`simple.yaml` + `complex.yaml`). |
|
||||
| Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
|
||||
| Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. |
|
||||
@@ -353,7 +353,7 @@ destruction:
|
||||
use `mode: decommission` with the `changeRequestId` input:
|
||||
|
||||
```yaml
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
with:
|
||||
contract: .nova/contract.yml
|
||||
mode: decommission
|
||||
@@ -421,7 +421,7 @@ name: static-assets
|
||||
```
|
||||
|
||||
**Shape 2 — single contract + `environment` workflow input:** the
|
||||
reusable deploy workflow (`acdl/.github/workflows/deploy.yml@v1.13`)
|
||||
reusable deploy workflow (`nova/.github/workflows/deploy.yml@v1.19`)
|
||||
declares an `environment` input. When non-empty, it overrides the
|
||||
contract's `environment` field at load time (before interpolation), so
|
||||
the same contract can be promoted by passing a different environment:
|
||||
@@ -436,7 +436,7 @@ on: workflow_dispatch:
|
||||
required: true
|
||||
jobs:
|
||||
deploy-qa:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
with:
|
||||
environment: qa
|
||||
contract: .nova/contract.yml
|
||||
|
||||
@@ -78,10 +78,3 @@ Planned future features (no dates; tracked in the internal roadmap):
|
||||
- [Consumer Guide](consumer-guide) — start here if you are a consumer.
|
||||
- [Architecture](architecture) — start here if you are a platform engineer.
|
||||
- The [README](https://github.com/nova/nova) describes the platform repo.
|
||||
|
||||
> **Note:** The product brand is **Nova** (formerly ACDL — Agentic Cloud
|
||||
> Delivery Platform). The Gitea repository name (`continuous-intelligence/acdl`)
|
||||
> and the GitHub `uses:` reference (`acdl/.github/workflows/deploy.yml@…`)
|
||||
> are unchanged during the rebrand transition; only the product name is
|
||||
> changing. See the [Nova migration guide](NOVA_MIGRATION) for the
|
||||
> scheduled breaking changes.
|
||||
@@ -39,7 +39,7 @@ It is exposed to consumer repos as a **reusable workflow**:
|
||||
- `.github/workflows/deploy.yml` — GitHub Actions (production)
|
||||
|
||||
A consumer repo invokes the reusable workflow via a **versioned tag**
|
||||
(floating MAJOR + MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.13`).
|
||||
(floating MAJOR + MINOR, e.g. `nova/.github/workflows/deploy.yml@v1.19`).
|
||||
The workflow checks out the consumer repo, then checks out the Nova platform
|
||||
repo into the runner workspace, and runs `scripts/run_platform.sh` against
|
||||
the consumer's contract. The consumer never clones the platform repo or
|
||||
|
||||
@@ -26,7 +26,7 @@ tag** in a consumer's CI workflow definition:
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
uses: acdl/.github/workflows/deploy.yml@v1.13
|
||||
uses: nova/.github/workflows/deploy.yml@v1.19
|
||||
with:
|
||||
contract: .nova/contract.yml
|
||||
```
|
||||
@@ -36,7 +36,7 @@ itself — the contract no longer carries a `uses:` field). The CI workflow
|
||||
`uses:` tag is the only immutability lever a consumer has.
|
||||
|
||||
**Unversioned references are discouraged.** Do not use `@main` or a bare
|
||||
`acdl/.github/workflows/deploy.yml` — `main` is constantly updated and can
|
||||
`nova/.github/workflows/deploy.yml` — `main` is constantly updated and can
|
||||
cause unexpected failures. Pinning to a MAJOR+MINOR tag means:
|
||||
|
||||
- **Immutability** — the pipeline behavior you tested is the behavior you
|
||||
|
||||
@@ -23,7 +23,7 @@ Step 1: full markdown Step 2: Marp deck Step 3: HTML + PPTX
|
||||
|
||||
### Step 1 — Full markdown (source of truth)
|
||||
|
||||
**File convention:** `<deck-name>.md` (e.g. `how-the-platform-works.md`).
|
||||
**File convention:** `<deck-name>.md` (e.g. `nova-no-humans-platform.md`).
|
||||
|
||||
Write the complete deck as a standard markdown file. This is the **source of
|
||||
truth** — it contains:
|
||||
@@ -45,7 +45,7 @@ fact is wrong, fix it here and re-run Steps 2 and 3.
|
||||
|
||||
### Step 2 — Marp deck synthesis
|
||||
|
||||
**File convention:** `<deck-name>-marp.md` (e.g. `how-the-platform-works-marp.md`).
|
||||
**File convention:** `<deck-name>-marp.md` (e.g. `nova-no-humans-platform-marp.md`).
|
||||
|
||||
Synthesize the full markdown into a lean Marp deck:
|
||||
|
||||
@@ -69,7 +69,7 @@ Synthesize the full markdown into a lean Marp deck:
|
||||
|
||||
Both formats are derived from the Marp deck. **HTML is committed to the repo**
|
||||
(viewable in any browser, self-contained with base64-embedded images). **PPTX
|
||||
is uploaded to the Gitea release** as a downloadable attachment (binary, not
|
||||
is uploaded to the release** as a downloadable attachment (binary, not
|
||||
committed to git).
|
||||
|
||||
#### HTML export (committed to repo)
|
||||
@@ -90,7 +90,7 @@ file that renders the full deck with the S&P Global Energy theme.
|
||||
committed artifacts, not generated on-the-fly — they must be re-rendered and
|
||||
re-committed when the Marp deck is updated.
|
||||
|
||||
#### PPTX export (uploaded to Gitea release)
|
||||
#### PPTX export (uploaded to release)
|
||||
|
||||
```bash
|
||||
CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
|
||||
@@ -100,14 +100,16 @@ CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
|
||||
```
|
||||
|
||||
The `--allow-local-files` flag is **required** for PPTX export so the local
|
||||
PNG diagrams are embedded in the file. PPTX files are not committed to the
|
||||
repo (binary, no meaningful diffs) — they are uploaded to the Gitea release
|
||||
as downloadable attachments.
|
||||
PNG diagrams are embedded in the file. As of v1.18 (REQ-228, D-141), PPTX
|
||||
files **are committed to the repo** as first-class binary artifacts (no LFS)
|
||||
and are also attached to the phase's release via
|
||||
`scripts/attach_release_asset.py`. The render + commit + attach pipeline is
|
||||
automated by `scripts/render_deck.sh`.
|
||||
|
||||
### Step 4 — Talking points (presenter cues)
|
||||
|
||||
**File convention:** `<deck-name>-talking-points.md` (e.g.
|
||||
`how-the-platform-works-talking-points.md`).
|
||||
`nova-no-humans-platform-talking-points.md`).
|
||||
|
||||
Distill the source of truth (Step 1) into presenter-ready cues, indexed by
|
||||
the Marp deck (Step 2) slide structure:
|
||||
@@ -142,35 +144,20 @@ and re-distill.
|
||||
```
|
||||
docs/presentations/
|
||||
├── README.md ← this file
|
||||
├── how-the-platform-works.md ← Step 1: full source of truth
|
||||
├── how-the-platform-works-marp.md ← Step 2: Marp deck (11 main + TOC + 8 appendix = 20)
|
||||
├── how-the-platform-works.html ← Step 3: rendered HTML (committed)
|
||||
├── how-the-platform-works-talking-points.md ← Step 4: presenter cues (20 sections)
|
||||
├── the-developer-experience.md ← Step 1: full source of truth
|
||||
├── the-developer-experience-marp.md ← Step 2: Marp deck (11 main + TOC + 7 appendix = 19)
|
||||
├── the-developer-experience.html ← Step 3: rendered HTML (committed)
|
||||
├── the-developer-experience-talking-points.md ← Step 4: presenter cues (19 sections)
|
||||
├── nova-no-humans-platform.md ← Step 1: full source of truth (19 main slides + speaker notes)
|
||||
├── nova-no-humans-platform-marp.md ← Step 2: Marp deck (19 main + 2 appendix = 21 slides)
|
||||
├── nova-no-humans-platform.html ← Step 3: rendered HTML (committed, S&P-themed)
|
||||
├── nova-no-humans-platform.pptx ← Step 3: rendered PPTX (committed, S&P-themed)
|
||||
├── nova-no-humans-platform-talking-points.md ← Step 4: presenter cues (21 sections)
|
||||
└── assets/
|
||||
├── nova-sp-theme.css ← S&P Global Energy Marp theme (all slide chrome)
|
||||
├── puppeteer-config.json ← no-sandbox config for mmdc
|
||||
├── mmd/ ← mermaid source files (Step 2 input)
|
||||
│ ├── sp-theme.json ← S&P Red/Black/White theme (mermaid-cli --configFile)
|
||||
│ ├── platform-works-01-contract-driven.mmd
|
||||
│ ├── platform-works-02-frictions.mmd
|
||||
│ ├── platform-works-02-end-to-end-flow.mmd
|
||||
│ ├── platform-works-03-north-star.mmd
|
||||
│ ├── platform-works-03-scope-boundary.mmd
|
||||
│ ├── platform-works-04-confidence-signal.mmd
|
||||
│ ├── platform-works-05-attestation-flow.mmd
|
||||
│ ├── platform-works-07-zero-trust.mmd
|
||||
│ ├── developer-experience-01b-scope-boundary.mmd
|
||||
│ ├── developer-experience-02-what-dev-does.mmd
|
||||
│ ├── developer-experience-03-no-cloning.mmd
|
||||
│ ├── developer-experience-04-promotion-journey.mmd
|
||||
│ ├── developer-experience-05-catalog.mmd
|
||||
│ ├── developer-experience-07-decommission.mmd
|
||||
│ ├── developer-experience-08-semver.mmd
|
||||
│ ├── platform-architecture.mmd ← shared high-level logical architecture (both decks)
|
||||
│ └── road-to-north-star.mmd
|
||||
│ ├── platform-architecture.mmd
|
||||
│ ├── road-to-north-star.mmd
|
||||
│ └── ... (per-slide .mmd files)
|
||||
└── png/ ← rendered mermaid PNGs (committed, S&P-themed)
|
||||
└── png/ ← rendered PNGs (embedded in Marp)
|
||||
├── platform-works-01-contract-driven.png
|
||||
├── platform-works-02-frictions.png
|
||||
@@ -298,7 +285,7 @@ diagram images in `assets/png/`). The resulting HTML is self-contained.
|
||||
**The HTML files are committed artifacts** — re-render and re-commit whenever
|
||||
the Marp source changes.
|
||||
|
||||
### Export a Marp deck to PPTX (uploaded to Gitea release)
|
||||
### Export a Marp deck to PPTX (uploaded to release)
|
||||
|
||||
```bash
|
||||
CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
|
||||
@@ -309,7 +296,7 @@ CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
|
||||
|
||||
`--allow-local-files` is **required** for PPTX so local PNG diagrams are
|
||||
embedded in the file. PPTX files are not committed to git — upload them as
|
||||
attachments to the Gitea release.
|
||||
attachments to the release.
|
||||
|
||||
## Adding a new presentation
|
||||
|
||||
@@ -322,7 +309,7 @@ attachments to the Gitea release.
|
||||
no speaker notes, embedded PNGs, and maturity badges.
|
||||
4. **Render to HTML** with `--allow-local-files` and commit the HTML to
|
||||
`docs/presentations/<deck-name>.html`.
|
||||
5. **Render to PPTX** with `--allow-local-files` and upload to the Gitea
|
||||
5. **Render to PPTX** with `--allow-local-files` and upload to the release
|
||||
release (do not commit PPTX to git).
|
||||
6. **Distill the talking points** as `<deck-name>-talking-points.md` — one
|
||||
section per Marp slide, 3-6 talking point bullets + key takeaway, content
|
||||
@@ -341,7 +328,13 @@ attachments to the Gitea release.
|
||||
|
||||
## Current decks
|
||||
|
||||
| Deck | Source of truth (Step 1) | Marp deck (Step 2) | Rendered HTML (Step 3) | Talking points (Step 4) | Slides | Audience |
|
||||
| Deck | Source of truth (Step 1) | Marp deck (Step 2) | Rendered HTML + PPTX (Step 3) | Talking points (Step 4) | Slides | Audience |
|
||||
|---|---|---|---|---|---|---|
|
||||
| How the Platform Works | `how-the-platform-works.md` | `how-the-platform-works-marp.md` | `how-the-platform-works.html` | `how-the-platform-works-talking-points.md` | 11 main + TOC + 8 appendix (20) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
||||
| The Developer Experience | `the-developer-experience.md` | `the-developer-experience-marp.md` | `the-developer-experience.html` | `the-developer-experience-talking-points.md` | 11 main + TOC + 7 appendix (19) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
||||
| Nova — The No-Humans Infrastructure Platform | `nova-no-humans-platform.md` | `nova-no-humans-platform-marp.md` | `nova-no-humans-platform.html` + `.pptx` (committed + release-attached) | `nova-no-humans-platform-talking-points.md` | 19 main + 2 appendix (21) | CTO, Head of Cloud, Head of Infra, Head of DevOps |
|
||||
|
||||
> **v1.18 (D-130):** the two legacy decks (How the Platform Works + The
|
||||
> Developer Experience) were consolidated into a single unified narrative
|
||||
> deck with a 5-act arc (Problem → Vision → How → Proof → Roadmap). v1.18
|
||||
> (REQ-226) adds 3 slides (17 Scope, 18 RACI, 19 Atelier) → 21 total. The
|
||||
> S&P Global Energy theme is restored (REQ-214, P1). PPTX is committed to
|
||||
> git + attached to the release (REQ-228, D-141).
|
||||
@@ -0,0 +1,77 @@
|
||||
/* @theme nova-sp */
|
||||
/* Nova — S&P Global Energy theme for Marp decks.
|
||||
*
|
||||
* Palette: S&P Red (#D6002A), Black (#1B1B1B), White (#FFFFFF), Grey (#F0F0F0).
|
||||
* Font: Akkurat Pro (fallback Helvetica Neue / Arial).
|
||||
*
|
||||
* This theme extends Marp's default and applies the S&P palette to ALL slide
|
||||
* chrome — backgrounds, headers/footers, pagination, tables, blockquotes,
|
||||
* code blocks — not just headings.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--sp-red: #D6002A;
|
||||
--sp-black: #1B1B1B;
|
||||
--sp-white: #FFFFFF;
|
||||
--sp-grey: #F0F0F0;
|
||||
--sp-dark-grey: #2E2E2E;
|
||||
}
|
||||
|
||||
/* Base section */
|
||||
section {
|
||||
font-family: "Akkurat Pro", "Helvetica Neue", "Arial", sans-serif;
|
||||
font-size: 22px;
|
||||
color: var(--sp-black);
|
||||
background: var(--sp-white);
|
||||
}
|
||||
|
||||
/* Headings — S&P Red */
|
||||
h1 { color: var(--sp-red); font-size: 34px; margin-bottom: 0.3em; }
|
||||
h2 { color: var(--sp-red); font-size: 26px; margin-bottom: 0.2em; }
|
||||
h3 { color: var(--sp-red); font-size: 22px; margin-bottom: 0.2em; }
|
||||
h4 { color: var(--sp-dark-grey); font-size: 20px; margin-bottom: 0.15em; }
|
||||
|
||||
/* Title slides — black background, red top border */
|
||||
section.title {
|
||||
background: var(--sp-black);
|
||||
color: var(--sp-white);
|
||||
border-top: 8px solid var(--sp-red);
|
||||
}
|
||||
section.title h1 { color: var(--sp-white); }
|
||||
section.title h2 { color: var(--sp-white); }
|
||||
|
||||
/* Tables — grey header with red underline */
|
||||
table { font-size: 18px; width: 100%; border-collapse: collapse; }
|
||||
th { background: var(--sp-grey); border-bottom: 2px solid var(--sp-red); padding: 6px 10px; text-align: left; }
|
||||
td { border-bottom: 1px solid var(--sp-grey); padding: 6px 10px; }
|
||||
|
||||
/* Blockquotes — red left border */
|
||||
blockquote { border-left: 4px solid var(--sp-red); color: var(--sp-dark-grey); font-size: 20px; padding-left: 12px; }
|
||||
|
||||
/* Code — dark background */
|
||||
pre { background: var(--sp-black); color: var(--sp-white); border-radius: 4px; padding: 12px; font-size: 16px; }
|
||||
code { background: var(--sp-grey); color: var(--sp-black); border-radius: 2px; padding: 1px 4px; font-size: 18px; }
|
||||
pre code { background: transparent; color: inherit; }
|
||||
|
||||
/* Images — centered, max height */
|
||||
img { display: block; margin: 0 auto; max-height: 320px; }
|
||||
|
||||
/* Header/footer — subtle grey */
|
||||
header { color: var(--sp-dark-grey); border-bottom: 1px solid var(--sp-grey); }
|
||||
footer { color: var(--sp-dark-grey); border-top: 1px solid var(--sp-grey); }
|
||||
|
||||
/* Maturity badges */
|
||||
.badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 14px; font-weight: 600; }
|
||||
.badge.today { background: #c6f6d5; color: #22543d; }
|
||||
.badge.planned { background: #fef3c7; color: #78350f; }
|
||||
|
||||
/* Pagination — S&P Red progress bar */
|
||||
.bespoke-progress-parent { background: var(--sp-grey); }
|
||||
.bespoke-progress-bar { background: var(--sp-red) !important; }
|
||||
|
||||
/* Lists — tighter */
|
||||
ul { margin-top: 0.3em; }
|
||||
li { margin-bottom: 0.2em; }
|
||||
|
||||
/* Strong — S&P Red for emphasis in lead lines */
|
||||
strong { color: var(--sp-red); }
|
||||
@@ -1,18 +1,10 @@
|
||||
---
|
||||
marp: true
|
||||
theme: default
|
||||
theme: nova-sp
|
||||
paginate: true
|
||||
size: 16x9
|
||||
header: 'Nova — The No-Humans Infrastructure Platform'
|
||||
footer: 'Act %{page}/5 — v1.17'
|
||||
style: |
|
||||
section { font-size: 0.85em; }
|
||||
h1 { color: #1a1a2e; }
|
||||
h2 { color: #16213e; }
|
||||
table { font-size: 0.75em; }
|
||||
.badge { padding: 2px 8px; border-radius: 3px; font-size: 0.8em; }
|
||||
.badge.planned { background: #fff3cd; color: #856404; }
|
||||
section.title { background: #1a1a2e; color: white; }
|
||||
footer: 'Act %{page}/5 — v1.20'
|
||||
---
|
||||
|
||||
<!-- _class: title -->
|
||||
@@ -22,7 +14,7 @@ style: |
|
||||
|
||||
**Shifting from Operational Overhead to Strategic Value**
|
||||
|
||||
v1.17 — Strategic Direction, Leadership Metrics & Unified Story
|
||||
v1.18 — Citizen Developer & Production-Grade Guidance
|
||||
|
||||
---
|
||||
|
||||
@@ -37,7 +29,7 @@ v1.17 — Strategic Direction, Leadership Metrics & Unified Story
|
||||
2. **Vision** — Nova's strategic direction (NORTH_STAR)
|
||||
3. **How** — the pipeline, Decision Ledger, attestation gates
|
||||
4. **Proof** — grounded metrics that make the claim defensible
|
||||
5. **Roadmap** — deferred metrics with unblock paths + the ask
|
||||
5. **Roadmap** — deferred metrics with unblock paths + the ask + scope + RACI
|
||||
|
||||
**Benefit:** you leave knowing which claims are proven today, which are pipeline-ready, and which are deferred with a documented unblock path — no marketing, just grounded evidence.
|
||||
|
||||
@@ -289,6 +281,56 @@ From `docs/METRICS_DEFERRED_ROADMAP.md`.
|
||||
|
||||
---
|
||||
|
||||
## Slide 17 — Scope: Downstream of PDLC
|
||||
|
||||
**Nova governs infrastructure + delivery. The PDLC (product backlog, code authorship, IDE) is upstream — Nova never penetrates it.**
|
||||
|
||||
- **The PDLC is upstream:** product backlog, code authorship (AI agent / IDE / agentic SDLC), sprint planning, application business logic
|
||||
- **Nova is downstream:** contract ingestion → submission-readiness gate → policy → cloud lifecycle → environment progression → audit + attestation
|
||||
- **Integration is only through the contract boundary:** the citizen developer's AI coding agent, an upstream agentic SDLC, or any dev platform may all produce submissions — the source does not matter as all are subject to the same compliance standards
|
||||
- Nova validates the submission, not the author
|
||||
- Cites `docs/scope.md` + `PROJECT.md` § Scope
|
||||
|
||||
**Benefit:** you now know the scope boundary — Nova is purpose-built for infrastructure operations, not product development; integration is through one validated contract.
|
||||
|
||||
---
|
||||
|
||||
## Slide 18 — RACI: Who Owns What
|
||||
|
||||
**Three roles, one matrix — the citizen developer owns FRs + UAT, the platform owns NFRs + infra + QA + prod deploy, release management is co-owned.**
|
||||
|
||||
| Work Category | Citizen Dev | Platform | Release Mgmt |
|
||||
|---|---|---|---|
|
||||
| Functional Requirements (FRs) | **R/A** | C | I |
|
||||
| User Acceptance Testing (UAT) | **R/A** | C | I |
|
||||
| Non-Functional Requirements (NFRs) | I | **R/A** | C |
|
||||
| Infrastructure (cloud, state, IAM) | I | **R/A** | C |
|
||||
| QA (policy, confidence, schema) | C | **R/A** | I |
|
||||
| Production deployment to cloud | I | **R/A** | C |
|
||||
| Release attestation (QA + SRE) | **A** | R | **R** |
|
||||
|
||||
- **Compliance-standard equivalence:** FRs + UAT may come from any upstream source (AI agent, agentic SDLC, dev platform) — all pass the same submission-readiness gate
|
||||
- **Release co-ownership:** the platform runs the attestations agentically; the citizen developer oversees and triggers the actual release (human at the stage gate)
|
||||
- Cites `docs/raci.md` + `PROJECT.md` § RACI Matrix
|
||||
|
||||
**Benefit:** you now know exactly what you bring (FRs + UAT), what Nova provides (NFRs + infra + QA + prod deploy), and what you co-own (the release attestation).
|
||||
|
||||
---
|
||||
|
||||
## Slide 19 — Production-Grade Guidance via Atelier
|
||||
|
||||
**Nova instructs the citizen developer's AI agent on production-grade engineering — skills + an MCP server with agentic validation beyond deterministic scanners.**
|
||||
|
||||
- **Skills (9):** markdown files under `skills/` keyed to Atelier domain paths (api, security, data, testing, observability, errors, devops, infrastructure-as-code, compliance) — extending the BA.A 5-skill catalog
|
||||
- **MCP server:** `mcp/atelier/server.py` (plugin-registry, stdio) — 4 tools: `lookup_principle`, `list_domains`, `matrix_lookup`, `validate_against_principles`
|
||||
- **Agentic validation:** catches C1 correctness + C2 clarity + C7 observability gaps that Wiz/Checkmarx/Mend cannot — deterministic tools check policy/secrets; the MCP server checks engineering discipline
|
||||
- **Vendored Atelier** (pinned tag v0.3.6): audit reproducibility — a validation result is replayable against the exact principles that produced it
|
||||
- Cites `docs/skills.md` + `mcp/atelier/README.md`
|
||||
|
||||
**Benefit:** you now know the citizen developer is not unguided — Nova provides production-grade engineering principles via skills + an MCP server, so the AI agent's submissions meet the same standards regardless of upstream source.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: title -->
|
||||
<!-- _paginate: false -->
|
||||
|
||||
@@ -321,4 +363,41 @@ From `docs/METRICS_DEFERRED_ROADMAP.md`.
|
||||
- **Zero-cost steady state:** all resources torn down post-v1.11 (D-096); the platform runs offline
|
||||
- References the pre-mortem (`PRE_MORTEM.md`: v1.10 decay root cause + structural mitigations)
|
||||
|
||||
**Benefit:** you now know the operating cost is negligible — and the structural mitigation that prevents decay.
|
||||
**Benefit:** you now know the operating cost is negligible — and the structural mitigation that prevents decay.
|
||||
---
|
||||
|
||||
<!-- _class: title -->
|
||||
<!-- _paginate: false -->
|
||||
|
||||
## Slide 20 — 12-Month Product Roadmap
|
||||
|
||||
**The product arc from pilot activation to agentic substrate — four quarters, four outcomes.**
|
||||
|
||||
| Quarter | Theme | Board-level outcome |
|
||||
|---------|-------|---------------------|
|
||||
| **Q1** | <span class="badge planned">Pilot Activation</span> | Nova runs a real customer estate end-to-end, autonomously, with a measurable zero-touch rate |
|
||||
| **Q2** | <span class="badge planned">Provable Trust</span> | Every AI decision lands in a tamper-evident ledger; CFO sees real cloud-spend reconciliation |
|
||||
| **Q3** | <span class="badge planned">Compounding ROI</span> | Quarter-over-quarter cloud spend drops; drift is detected and reversed without a human |
|
||||
| **Q4** | <span class="badge planned">Agentic Substrate</span> | AI agents deploy through Nova by default; Nova is the substrate, not a vendor arriving late |
|
||||
|
||||
**Grounded in:** the 4 strategic objectives (autonomy, provable trust, ROI, agentic substrate) + the deferred-metric unblock paths.
|
||||
|
||||
**Benefit:** you now know the 12-month product arc — each quarter activates a strategic objective and its corresponding board-level metric, from pilot activation through agentic substrate leadership.
|
||||
|
||||
---
|
||||
|
||||
<!-- _class: title -->
|
||||
<!-- _paginate: false -->
|
||||
|
||||
## Slide 21 — Quarter-by-Quarter Outcomes
|
||||
|
||||
| Quarter | Product theme | Key deliverable | Target metric | Grounding |
|
||||
|---------|--------------|-----------------|---------------|-----------|
|
||||
| **Q1** | Pilot Activation | Re-provision live AWS; activate first pilot estate; onboarding auto-grant | Touchless Resolution ≥ 99% · Escalation < 0.1% · AI Accuracy ≥ 99.5% | Strategic Objective #1 — autonomy as the default |
|
||||
| **Q2** | Provable Trust | Tamper-evident ledger (Object Lock + JWS); daily checkpoints; live cost reconciliation (CUR) | Decision Ledger Coverage 100% · Cost Savings ≥ 25% | Strategic Objective #2 — trust is the moat |
|
||||
| **Q3** | Compounding ROI + Drift | Drift detection scheduler; auto-reversal; Infracost→CUR reconciliation on pilot estate | Drift Auto-Reversal ≥ 95% · Spend Reduction ≥ 25% | Strategic Objective #3 — CFO-pointable numbers |
|
||||
| **Q4** | Agentic Substrate + Predictive | ML anomaly-forecasting; AI-agent intent surface; multi-cloud (Azure/GCP) preview | Predictive:Reactive ≥ 3:1 · AI-Agent Intent Share ≥ 40% (first measurement) | Strategic Objective #4 — default substrate for agents |
|
||||
|
||||
**Month-18 destination:** *"Nova is the layer enterprise leadership points to when they say 'we don't have an infrastructure ops team anymore, and the audit trail is stronger than it ever was.'"*
|
||||
|
||||
**Benefit:** you now know the quarter-by-quarter detail — each quarter has a concrete deliverable, a target metric grounded in a strategic objective, and a path from "honestly deferred" to "shipped and measured."
|
||||
|
||||
@@ -103,6 +103,27 @@
|
||||
- "Pipeline-ready" → "production-proven" is the value proposition
|
||||
- **Key takeaway:** approve a pilot + the ledger build-out to move from pipeline-ready to production-proven
|
||||
|
||||
### Slide 17 — Scope: Downstream of PDLC
|
||||
- Nova governs infra + delivery only; the PDLC (product backlog, code authorship, IDE) is upstream
|
||||
- Integration is only through the validated contract boundary
|
||||
- Any upstream source (AI agent, agentic SDLC, dev platform) may produce submissions — all subject to the same compliance standards
|
||||
- Nova validates the submission, not the author
|
||||
- **Key takeaway:** Nova is purpose-built for infrastructure operations, not product development; the scope boundary is clean
|
||||
|
||||
### Slide 18 — RACI: Who Owns What
|
||||
- Citizen Developer owns FRs + UAT (via any upstream source — AI agent, SDLC, dev platform — all pass the same gate)
|
||||
- Platform owns NFRs + infra + QA + prod deploy
|
||||
- Release Management is co-owned: platform runs attestations agentically, citizen developer oversees + triggers the release (human at stage gate)
|
||||
- The compliance-standard equivalence is the key: the source does not matter; the submission does
|
||||
- **Key takeaway:** you bring FRs + UAT; Nova provides NFRs + infra + QA + prod deploy; the release is co-owned with you at the stage gate
|
||||
|
||||
### Slide 19 — Production-Grade Guidance via Atelier
|
||||
- Nova instructs the citizen developer's AI agent via skills (9 markdown files) + an MCP server (4 tools, plugin-registry, stdio)
|
||||
- The MCP server provides agentic validation beyond deterministic scanners — catches correctness, clarity, observability gaps that Wiz/Checkmarx/Mend cannot
|
||||
- Atelier is vendored (pinned tag) for audit reproducibility — a validation result is replayable
|
||||
- This is how Nova ensures the citizen developer's submissions meet production-grade standards regardless of upstream source
|
||||
- **Key takeaway:** the citizen developer is not unguided — Nova provides engineering principles via skills + MCP, so every submission meets the same standards
|
||||
|
||||
### Appendix A1 — Metrics Glossary
|
||||
- Reference for every metric mentioned in the deck
|
||||
- Use if the audience asks "what does X mean?"
|
||||
@@ -110,4 +131,34 @@
|
||||
### Appendix A2 — Operating Model & Cost
|
||||
- The operating cost is negligible (~$0.007/month)
|
||||
- The zero-cost steady state (D-096 teardown) is the structural mitigation
|
||||
- References the pre-mortem for the decay-prevention story
|
||||
- References the pre-mortem for the decay-prevention story
|
||||
---
|
||||
|
||||
## Slide 20 — 12-Month Product Roadmap
|
||||
|
||||
**Key takeaway:** The next 12 months have a clear product arc — pilot activation → provable trust → compounding ROI → agentic substrate. Each quarter activates one strategic objective.
|
||||
|
||||
- This is the *product* roadmap, not the technical roadmap. The technical milestones (v1.0–v1.19) are behind us; this is forward-looking.
|
||||
- Q1 (Pilot Activation): re-provision live AWS, activate the first pilot estate, light up the three post-pilot metrics. Onboarding auto-grant ships.
|
||||
- Q2 (Provable Trust): tamper-evident ledger (Object Lock + JWS), daily checkpoints, live cost reconciliation. Trust is the moat — features can be copied; an immutable decision history cannot.
|
||||
- Q3 (Compounding ROI + Drift): drift detection + auto-reversal, Infracost→CUR reconciliation on the pilot estate. This is the quarter the CFO points to a number that improves quarter-over-quarter.
|
||||
- Q4 (Agentic Substrate + Predictive): ML anomaly-forecasting, AI-agent intent surface, multi-cloud preview. The Future Horizon target moves from aspiration to first measurement.
|
||||
- The roadmap is grounded in the four strategic objectives from the North Star — autonomy, provable trust, ROI, agentic substrate — and the deferred-metric unblock paths from Slide 15.
|
||||
|
||||
**If asked "what about multi-cloud?"**: Q4 preview. AWS-only through Q3; Azure/GCP enters preview in Q4. We optimize for depth first, breadth second.
|
||||
|
||||
**If asked "what about the ML service?"**: Q4. The predictive-vs-reactive ≥3:1 target requires an ML anomaly-forecasting emitter — the most technically ambitious deliverable on the roadmap.
|
||||
|
||||
---
|
||||
|
||||
## Slide 21 — Quarter-by-Quarter Outcomes
|
||||
|
||||
**Key takeaway:** Each quarter has a concrete deliverable, a target metric, and a strategic-objective grounding. Nothing is hand-waved.
|
||||
|
||||
- Q1: three post-pilot metrics go live (Touchless ≥99%, Escalation <0.1%, Accuracy ≥99.5%). The measurement pipeline is already grounded; the denominator activates when the pilot estate runs.
|
||||
- Q2: Decision Ledger Coverage was already grounded — the *tamper-evidence* is the Q2 upgrade (SQLite hash-chain → S3 Object Lock + JWS). Cost Savings ≥25% becomes CFO-grade with live CUR reconciliation.
|
||||
- Q3: Drift Auto-Reversal ≥95% unblocks when the drift scheduler ships. Spend Reduction ≥25% is the same target, now measured against the pilot baseline.
|
||||
- Q4: Predictive:Reactive ≥3:1 requires the ML forecasting service. AI-Agent Intent Share ≥40% moves from aspiration to first measurement.
|
||||
- The month-18 destination: "Nova is the layer enterprise leadership points to when they say 'we don't have an infrastructure ops team anymore, and the audit trail is stronger than it ever was.'"
|
||||
|
||||
**If asked "are these committed or aspirational?"**: Q1–Q3 are committed (grounded pipeline + known unblock paths). Q4 targets are committed-deliverable, aspirational-metric — the ML service ships, the ≥40% intent share is first measurement (we don't control adoption rate).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -414,4 +414,40 @@ This appendix shows the real cost figures + the zero-cost steady state.
|
||||
---
|
||||
|
||||
> **End of deck.** 16 main slides + 2 appendix slides = 18 total.
|
||||
> Both old decks (`how-the-platform-works` + `the-developer-experience`) are retired (D-130).
|
||||
> Both old decks (`how-the-platform-works` + `the-developer-experience`) are retired (D-130).
|
||||
---
|
||||
|
||||
## Slide 20 — 12-Month Product Roadmap
|
||||
|
||||
**The product arc from pilot activation to agentic substrate — four quarters, four outcomes.**
|
||||
|
||||
This slide shows the 12-month product roadmap — the forward-looking product-outcome arc. It is distinct from Slide 15 (the deferred-metric unblock paths), which explains *why* metrics are deferred and *how* they unblock. This slide shows *what's coming, when* — organized by quarter and board-level outcome.
|
||||
|
||||
**Speaker notes:**
|
||||
|
||||
- The roadmap is organized by product outcome, not by technical milestone. Each quarter activates one strategic objective from the North Star.
|
||||
- Q1 is pilot activation — re-provision live AWS, light up the first pilot estate, and activate the three post-pilot denominator metrics (Touchless Resolution Rate, Human Escalation Frequency, AI Decision Accuracy). The onboarding auto-grant ships, so consumers can self-provision without platform-team intervention.
|
||||
- Q2 is provable trust — the audit substrate goes from a SQLite hash-chain to a tamper-evident ledger (S3 Object Lock + JWS daily checkpoints). Live cost reconciliation (CUR) goes live so the CFO sees real cloud-spend data, not just pre-apply Infracost estimates.
|
||||
- Q3 is compounding ROI + drift — drift detection ships (the scheduler that was deferred), drift auto-reversal activates (≥95% within one detection cycle), and the ≥25% spend-reduction target is measured against the pilot estate's 12-month pre-Nova baseline. This is the quarter the CFO points to a number that improves quarter-over-quarter.
|
||||
- Q4 is agentic substrate + predictive — the ML anomaly-forecasting service ships (predictive-vs-reactive ≥3:1), the AI-agent intent consumption surface goes live, and multi-cloud (Azure/GCP) enters preview. The Future Horizon target (AI-Agent Intent Share ≥40%) moves from aspiration to first measurement.
|
||||
|
||||
**The month-18 destination:** *"Nova is the layer enterprise leadership points to when they say 'we don't have an infrastructure ops team anymore, and the audit trail is stronger than it ever was' — and it is the default substrate their AI engineering teams reach for first when an agent needs to deploy."*
|
||||
|
||||
> **Benefit:** you now know the 12-month product arc — each quarter activates a strategic objective and its corresponding board-level metric, from pilot activation through agentic substrate leadership.
|
||||
|
||||
---
|
||||
|
||||
## Slide 21 — Quarter-by-Quarter Outcomes
|
||||
|
||||
This slide is the detail table behind Slide 20 — the concrete deliverable, target metric, and strategic-objective grounding for each quarter.
|
||||
|
||||
**Speaker notes:**
|
||||
|
||||
- Each row has a product theme (the narrative), a key deliverable (what ships), a target metric (the board-level number), and a grounding (which strategic objective it activates).
|
||||
- Q1's three target metrics (Touchless ≥99%, Escalation <0.1%, Accuracy ≥99.5%) are the post-pilot metrics from the North Star — their measurement pipeline is grounded this milestone, but the denominator is zero until a pilot estate activates. Q1 is when the denominator goes live.
|
||||
- Q2's Decision Ledger Coverage (100%) is already grounded; the *tamper-evidence* is the Q2 upgrade. Cost Savings ≥25% is the Infracost-grounded target — Q2 is when actual-spend reconciliation (CUR) makes it CFO-grade.
|
||||
- Q3's Drift Auto-Reversal ≥95% is the deferred metric that unblocks when the drift detection scheduler ships. Spend Reduction ≥25% is the same target, now measured against the pilot baseline (not just estimated).
|
||||
- Q4's Predictive:Reactive ≥3:1 requires the ML forecasting service — the most technically ambitious deliverable. AI-Agent Intent Share ≥40% is the Future Horizon target — it moves from aspiration to first measurement when the agentic consumption surface goes live.
|
||||
- The roadmap does NOT duplicate the 19 completed technical milestones (v1.0–v1.19) — it is forward-looking only. It does NOT duplicate Slide 15's deferred-metric unblock paths — it uses them as the mechanism, but the audience sees the product arc, not the decision IDs.
|
||||
|
||||
> **Benefit:** you now know the quarter-by-quarter detail — each quarter has a concrete deliverable, a target metric grounded in a strategic objective, and a path from "honestly deferred" to "shipped and measured."
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,84 @@
|
||||
# RACI — Who Owns What
|
||||
|
||||
> D-139). This page is the citizen-developer-facing copy.
|
||||
|
||||
Nova's delivery lifecycle has three roles. This page clarifies who owns
|
||||
what — so the citizen developer knows what they bring, what the platform
|
||||
provides, and what is co-owned.
|
||||
|
||||
## The Three Roles
|
||||
|
||||
### Citizen Developer (CD)
|
||||
|
||||
That's you — the consumer (technical developer L3A or non-technical L3B).
|
||||
You are **Responsible** for all **Functional Requirements (FRs)** and
|
||||
**User Acceptance Testing (UAT)**. You produce the FRs + UAT via your AI
|
||||
coding agent, an upstream agentic SDLC platform, or any upstream
|
||||
development platform. **The source does not matter** — all are subject
|
||||
to the same compliance standards (the submission-readiness gate, the
|
||||
contract schema, the policy envelope, the immutable audit stream). Nova
|
||||
validates the submission, not the author.
|
||||
|
||||
### Platform (Nova)
|
||||
|
||||
Nova is **Responsible** for all **Non-Functional Requirements (NFRs)**,
|
||||
**Infrastructure** (cloud resource lifecycle, state, IAM), **QA** (the
|
||||
platform-side quality checks: policy enforcement, confidence scoring,
|
||||
schema validation), and **Production deployments to cloud** (the apply
|
||||
path, the pipeline, the release mechanics).
|
||||
|
||||
### Release Management (RM) — co-owned
|
||||
|
||||
The release is **co-owned**. The platform performs the QA + SRE
|
||||
attestations agentically (it runs the confidence signal, the policy
|
||||
checks, the separation-of-duties). The citizen developer **oversees and
|
||||
triggers** the actual release — the human attestation at the stage gate
|
||||
is your authorization. The platform runs the checks; you authorize the
|
||||
promotion. This is the "autonomy in operations, human at stage gates"
|
||||
model.
|
||||
|
||||
## The Matrix
|
||||
|
||||
| Work Category | Citizen Developer | Platform | Release Management |
|
||||
|---|---|---|---|
|
||||
| **Functional Requirements (FRs)** | **R/A** | C | I |
|
||||
| **User Acceptance Testing (UAT)** | **R/A** | C | I |
|
||||
| **Non-Functional Requirements (NFRs)** | I | **R/A** | C |
|
||||
| **Infrastructure (cloud, state, IAM)** | I | **R/A** | C |
|
||||
| **QA (policy, confidence, schema checks)** | C | **R/A** | I |
|
||||
| **Production deployment to cloud** | I | **R/A** | C |
|
||||
| **Release attestation (QA + SRE sign-off)** | **A** | R | **R** |
|
||||
|
||||
**Key:** **R** = Responsible (does the work) · **A** = Accountable (owns
|
||||
the outcome, sign-off) · **C** = Consulted · **I** = Informed.
|
||||
|
||||
## What This Means in Practice
|
||||
|
||||
**You (Citizen Developer) bring:**
|
||||
- Your application code + a contract that declares intent.
|
||||
- Your FRs (what the application does).
|
||||
- Your UAT (you accept the deployment when it meets your FRs).
|
||||
|
||||
**Nova (Platform) provides:**
|
||||
- The NFRs (security, observability, compliance — baked into the
|
||||
pipeline, not your concern).
|
||||
- The infrastructure (cloud resources, state management, IAM scoping).
|
||||
- The QA (policy enforcement, confidence scoring, schema validation).
|
||||
- The production deployment (the apply path, the pipeline, the release).
|
||||
|
||||
**You co-own the release:**
|
||||
- Nova runs the attestations (QA confidence, SRE operational readiness).
|
||||
- You authorize the promotion at the stage gate. No promotion happens
|
||||
without your recorded attestation.
|
||||
|
||||
## Compliance Standards Apply Equally
|
||||
|
||||
Your FRs + UAT may come from any source — an AI coding agent, an
|
||||
agentic SDLC platform, or a traditional IDE. Nova does not
|
||||
differentiate. All submissions pass through the same gate
|
||||
(`schemas/submission-readiness.schema.json`): tags, environment
|
||||
metadata, policy preconditions, profile markers. The compliance
|
||||
standards are the same regardless of how the code was authored. This
|
||||
is by design: the audit trail is the same, the policy envelope is the
|
||||
same, the evidence stream is the same. The source does not matter; the
|
||||
submission does.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Scope — Nova is Downstream of PDLC
|
||||
|
||||
> This page is the citizen-developer-facing copy.
|
||||
|
||||
## The Boundary
|
||||
|
||||
The **Product Development Lifecycle (PDLC)** is **upstream** of Nova. The
|
||||
PDLC includes:
|
||||
|
||||
- Product backlog / roadmap planning
|
||||
- Code authorship (via AI coding agent, IDE, or agentic SDLC platform)
|
||||
- Sprint planning / issue tracking
|
||||
- Application business logic
|
||||
- IDE workflows / developer experience
|
||||
|
||||
Nova never penetrates the PDLC. Nova's domain is **infrastructure +
|
||||
delivery only**.
|
||||
|
||||
## What Nova Does
|
||||
|
||||
Nova governs the downstream half:
|
||||
|
||||
- **Contract ingestion** — the validated entry point
|
||||
- **Submission-readiness gate** — what is acceptable to start
|
||||
(`schemas/submission-readiness.schema.json`)
|
||||
- **Policy enforcement** — the confidence signal, Checkov, tagging
|
||||
- **Cloud resource lifecycle** — Terraform plan/apply, state, IAM
|
||||
- **Environment progression** — dev (autonomous) → qa (QA attestation) →
|
||||
prod (SRE attestation) → dr (SRE attestation)
|
||||
- **Immutable audit + attestation** — the Decision Ledger, the evidence
|
||||
stream, the HITL gates
|
||||
|
||||
## The Integration Point
|
||||
|
||||
Integration between the PDLC and Nova is **only** through the validated,
|
||||
published contract boundary:
|
||||
|
||||
```
|
||||
PDLC (upstream) Nova (downstream)
|
||||
───────────────── ─────────────────
|
||||
product backlog contract ingestion
|
||||
code authorship (AI agent / IDE / SDLC) → submission-readiness gate
|
||||
sprint planning → policy enforcement
|
||||
application business logic → cloud resource lifecycle
|
||||
→ environment progression (dev→qa→prod→dr)
|
||||
→ immutable audit + attestation
|
||||
```
|
||||
|
||||
The citizen developer's AI coding agent, an upstream agentic SDLC
|
||||
platform, or any upstream development platform may all produce
|
||||
submissions. **The source does not matter** — all are subject to the
|
||||
same compliance standards. Nova validates the submission, not the
|
||||
author.
|
||||
|
||||
## What Nova is Not
|
||||
|
||||
- Not an upstream development platform (no product backlogs, IDE, code
|
||||
authorship).
|
||||
- Not a general-purpose AI agent platform (autonomy is narrow, bounded
|
||||
by policy envelopes).
|
||||
- Not a legacy infrastructure bridge (no VMs/bare metal/OS).
|
||||
- Not a permissive delivery highway (no escape hatches past confidence
|
||||
or HITL).
|
||||
- Not a mutable audit log (VCS history ≠ regulatory evidence).
|
||||
|
||||
These anti-goals (from `docs/vision.md` §7 and Core Tenet #2) are
|
||||
promoted here from buried tenets to an unmissable scope statement.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Skills — Production-Grade Guidance for the Citizen Developer
|
||||
|
||||
> **Source of truth (v1.18, REQ-221, REQ-222).** The Nova skill catalog
|
||||
> extends the BA.A 5-skill catalog (web API, worker, scheduled job, static
|
||||
> asset, basic observability bootstrap) with Atelier-derived production-
|
||||
> grade engineering principles. Each skill is a markdown file under
|
||||
> `skills/` keyed to an Atelier domain path.
|
||||
|
||||
## How the Citizen Developer's AI Agent Consumes Skills
|
||||
|
||||
1. **Before completing a task**, read the relevant skill file(s) that
|
||||
match the task's domain.
|
||||
2. **Run `review/agent-checklist.md`** (from Atelier) before finishing —
|
||||
the checklist items are the gate between "the code is written" and
|
||||
"the task is done."
|
||||
3. **Use the Atelier MCP server** (`mcp/atelier/server.py`, P5) for
|
||||
agentic validation — the `atelier.validate_against_principles` tool
|
||||
catches correctness/clarity/simplicity/observability gaps that
|
||||
deterministic scanners (Wiz, Checkmarx, Mend) cannot.
|
||||
|
||||
## The 9 Skills
|
||||
|
||||
| Skill | Atelier Source | Core Principles | BA.A Mapping |
|
||||
|---|---|---|---|
|
||||
| [`api.md`](../skills/api.md) | `domains/api/` | C1, C2, C6 | web API |
|
||||
| [`security.md`](../skills/security.md) | `domains/security/` | C1 | cross-cutting (all 5) |
|
||||
| [`data.md`](../skills/data.md) | `domains/data/` | C1, C4, C6 | web API, worker, scheduled job |
|
||||
| [`testing.md`](../skills/testing.md) | `domains/testing/` | C1, C5 | UAT (citizen-dev RACI) |
|
||||
| [`observability.md`](../skills/observability.md) | `domains/observability/` | C7 | basic observability bootstrap |
|
||||
| [`errors.md`](../skills/errors.md) | `domains/errors/` | C1, C7 | web API, worker, scheduled job |
|
||||
| [`devops.md`](../skills/devops.md) | `domains/devops/` | C5, C7, C8 | scheduled job, worker |
|
||||
| [`infrastructure-as-code.md`](../skills/infrastructure-as-code.md) | `domains/infrastructure-as-code/` | C1, C5, C8 | static asset |
|
||||
| [`compliance.md`](../skills/compliance.md) | `domains/compliance/` | C1, C5 | cross-cutting (all 5) |
|
||||
|
||||
## Atelier Provenance
|
||||
|
||||
The skills are derived from [Atelier](https://example.com/atelier)
|
||||
— a first-principles docs-as-code engineering framework with 8 core
|
||||
principles (C1–C8) and 19 domains, each with 10 derived P-rules. The
|
||||
skills distill the citizen-developer-relevant subset of each domain's
|
||||
first-principles, link to the agent-checklist triggers, and map to the
|
||||
existing BA.A catalog.
|
||||
|
||||
Atelier is vendored under `mcp/atelier/vendor/` (pinned tag) for
|
||||
audit reproducibility — an agentic validation result is replayable
|
||||
against the exact principles that produced it.
|
||||
|
||||
## The 8 Core Principles (from Atelier)
|
||||
|
||||
| # | Principle | One-line |
|
||||
|---|---|---|
|
||||
| C1 | Correctness | The system does what it is supposed to do, and nothing else. |
|
||||
| C2 | Clarity | The intent of the code is obvious to its reader. |
|
||||
| C3 | Simplicity | The solution is as simple as possible, and no simpler. |
|
||||
| C4 | Locality | Decisions and their consequences live near each other. |
|
||||
| C5 | Reversibility | Every decision can be undone, and the cost of undoing is known. |
|
||||
| C6 | Composability | Parts combine into wholes, and the parts are reusable. |
|
||||
| C7 | Observability | The system's behavior is visible to those who must understand it. |
|
||||
| C8 | Economy | The system uses no more resources than the task requires. |
|
||||
|
||||
Precedence: C1 > C2 > C3 > C4 > C5 > C6 > C7 > C8. Correctness is never
|
||||
sacrificed.
|
||||
|
||||
## Reference-Only Domains (cited inside skills, not elevated to skill files)
|
||||
|
||||
These 4 Atelier domains are relevant to a citizen developer but are cited
|
||||
inside the 9 skills above rather than getting their own skill file:
|
||||
|
||||
- **Performance** (`domains/performance/`) — cited in `observability.md` +
|
||||
`devops.md` (bounded operations, timeouts, N+1)
|
||||
- **Documentation** (`domains/documentation/`) — the runbook requirement
|
||||
(W3.E prod mandatory) is the documentation skill in practice
|
||||
- **Concurrency** (`domains/concurrency/`) — cited in `errors.md` +
|
||||
`devops.md` (bounded queues, cancellation, timeout)
|
||||
- **AI/ML** (`domains/ai-ml/`) — scope: engineering discipline (data
|
||||
versioning, evaluation, serving, drift), not algorithm design
|
||||
|
||||
## Excluded Domains (not relevant to Nova citizen developer)
|
||||
|
||||
6 Atelier domains are excluded from the Nova skill catalog (not relevant
|
||||
to a citizen developer building on Nova's infrastructure platform):
|
||||
|
||||
- UI/UX — Nova has no frontend (frontend-engineer deactivated, PERSONAS.md)
|
||||
- Kubernetes — Nova is AWS-only this milestone (NORTH_STAR Non-Goal #7)
|
||||
- GitOps + Operators — future roadmap (no GitOps reconciler today)
|
||||
- Edge — not in scope (Nova is cloud, not edge)
|
||||
- Messaging — not in scope (Nova deploys infra, not message brokers)
|
||||
- i18n — application-level concern, not infrastructure
|
||||
@@ -0,0 +1,150 @@
|
||||
# Submission Readiness — What is Acceptable to Start
|
||||
|
||||
> **Source of truth:** `schemas/submission-readiness.schema.json` (v1.18,
|
||||
> REQ-217). The validator is `core/submission_readiness.py` (REQ-218),
|
||||
> invoked as `python3 -m core.lambda.contract_ingestor --check-readiness
|
||||
> <submission.json>` (D-133).
|
||||
|
||||
Nova's submission-readiness gate defines what is **acceptable to start**.
|
||||
It is a superset gate *above* contract-schema validity: the contract schema
|
||||
(`schemas/contract.schema.json`) defines the **shape** (id / name /
|
||||
environment / infrastructure); the readiness schema defines the **gate**
|
||||
(tags, per-env mandatory metadata, policy preconditions, profile markers,
|
||||
appSource). Both must pass before ingestion proceeds.
|
||||
|
||||
## How It Works
|
||||
|
||||
```
|
||||
citizen developer submits
|
||||
↓
|
||||
contract.schema.json validation (shape) ← the existing check
|
||||
↓
|
||||
submission-readiness.schema.json (gate) ← the new check
|
||||
├── contractId present (non-empty)
|
||||
├── environment valid (dev/qa/prod/dr)
|
||||
├── tags: all 5 Nova tags present (D-054)
|
||||
├── policyPreconditions declared
|
||||
├── profile: developer or agentic
|
||||
│ └── if agentic: naturalLanguageIntent + confidenceAtSubmission + agentTrace
|
||||
├── appSource: repo + ref (for runtime fetch)
|
||||
└── per-env mandatory (W3.E):
|
||||
dev → stack + environment
|
||||
qa → + validation.e2eSuite + validation.loadTest
|
||||
prod → + runbook + dashboard + oncall
|
||||
dr → + drDrillRef
|
||||
↓
|
||||
ready → proceed to contract ingestion
|
||||
not ready → reject with citizen-developer-facing error (reason code)
|
||||
```
|
||||
|
||||
## Reason Codes
|
||||
|
||||
When a submission is not ready, the validator returns one or more reason
|
||||
codes. These are citizen-developer-facing — no stack traces.
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `MISSING_TAGS:<tag1>,<tag2>` | One or more required Nova tags are absent |
|
||||
| `ENV_MISSING_MANDATORY:<env>:<field>` | A per-env mandatory field (W3.E) is missing |
|
||||
| `AGENTIC_MISSING_INTENT:<marker>` | profile=agentic but a required marker is absent |
|
||||
| `MISSING_APP_SOURCE` | appSource (repo + ref) is missing |
|
||||
| `POLICY_PRECONDITION_MISSING` | No policy preconditions declared |
|
||||
| `CONTRACT_SCHEMA_INVALID:<detail>` | The contract shape failed contract.schema.json |
|
||||
| `READINESS_SCHEMA_INVALID:<detail>` | The submission failed the readiness schema |
|
||||
|
||||
## Good Example
|
||||
|
||||
```json
|
||||
{
|
||||
"contractId": "uuid-1234",
|
||||
"id": "webapi",
|
||||
"name": "Customer Web API",
|
||||
"environment": "dev",
|
||||
"tags": {
|
||||
"nova:owner": "consumer-repo",
|
||||
"nova:contract": "uuid-1234",
|
||||
"nova:environment": "dev",
|
||||
"nova:cost-center": "nova-default",
|
||||
"nova:ref": "CHG0678912"
|
||||
},
|
||||
"policyPreconditions": {
|
||||
"public-ingress": false,
|
||||
"encryption_enabled": true,
|
||||
"deletion_protection": true
|
||||
},
|
||||
"profile": "developer",
|
||||
"appSource": {
|
||||
"repo": "consumer/web-api",
|
||||
"ref": "main"
|
||||
},
|
||||
"infrastructure": {
|
||||
"static-assets": {
|
||||
"inputs": {
|
||||
"bucket_name": "webapi-assets"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Result: **READY** — passes the shape + the gate.
|
||||
|
||||
## Rejected Examples
|
||||
|
||||
### Missing Tags
|
||||
|
||||
```json
|
||||
{
|
||||
"contractId": "uuid-1234",
|
||||
"environment": "dev",
|
||||
"tags": {
|
||||
"nova:owner": "consumer-repo"
|
||||
},
|
||||
"policyPreconditions": {"public-ingress": false},
|
||||
"profile": "developer",
|
||||
"appSource": {"repo": "consumer/repo", "ref": "main"}
|
||||
}
|
||||
```
|
||||
|
||||
Result: `NOT READY — MISSING_TAGS:nova:contract,nova:environment,nova:cost-center,nova:ref`
|
||||
|
||||
### Agentic Missing Intent
|
||||
|
||||
```json
|
||||
{
|
||||
"contractId": "uuid-1234",
|
||||
"environment": "qa",
|
||||
"tags": { "nova:owner": "x", "nova:contract": "x", "nova:environment": "qa", "nova:cost-center": "x", "nova:ref": "x" },
|
||||
"policyPreconditions": {"public-ingress": false},
|
||||
"profile": "agentic",
|
||||
"appSource": {"repo": "x", "ref": "x"},
|
||||
"validation": {"e2eSuite": true, "loadTest": true}
|
||||
}
|
||||
```
|
||||
|
||||
Result: `NOT READY — AGENTIC_MISSING_INTENT:naturalLanguageIntent; AGENTIC_MISSING_INTENT:confidenceAtSubmission; AGENTIC_MISSING_INTENT:agentTrace`
|
||||
|
||||
### Env Missing Mandatory (prod without runbook)
|
||||
|
||||
```json
|
||||
{
|
||||
"contractId": "uuid-1234",
|
||||
"environment": "prod",
|
||||
"tags": { "nova:owner": "x", "nova:contract": "x", "nova:environment": "prod", "nova:cost-center": "x", "nova:ref": "x" },
|
||||
"policyPreconditions": {"public-ingress": false},
|
||||
"profile": "developer",
|
||||
"appSource": {"repo": "x", "ref": "x"}
|
||||
}
|
||||
```
|
||||
|
||||
Result: `NOT READY — ENV_MISSING_MANDATORY:prod:runbook; ENV_MISSING_MANDATORY:prod:dashboard; ENV_MISSING_MANDATORY:prod:oncall`
|
||||
|
||||
## Compliance-Standard Equivalence
|
||||
|
||||
The submission-readiness gate applies **equally** to all upstream sources.
|
||||
Whether the citizen developer's submission originated from an AI coding
|
||||
agent, an agentic SDLC platform, or a traditional development platform —
|
||||
the same tags, the same env mandatory, the same policy preconditions, the
|
||||
same profile markers are required. The source does not matter; the
|
||||
submission does. This is the RACI compliance-standard equivalence note
|
||||
(`docs/raci.md`) made machine-checkable.
|
||||
@@ -0,0 +1 @@
|
||||
# mcp/atelier — Nova Atelier MCP server package (v1.18)
|
||||
@@ -0,0 +1,95 @@
|
||||
# Nova Atelier MCP Server
|
||||
|
||||
> exposes Atelier engineering principles to the citizen developer's AI
|
||||
> agent. Plugin-registry architecture; stdio transport;
|
||||
> vendored Atelier for audit reproducibility.
|
||||
|
||||
## What This Is
|
||||
|
||||
The server exposes 4 tools that let a citizen developer's AI coding agent
|
||||
look up production-grade engineering principles and validate code against
|
||||
them — agentic validation that goes **beyond deterministic scanners**
|
||||
(Wiz, Checkmarx, Mend) by catching correctness, clarity, simplicity, and
|
||||
observability gaps.
|
||||
|
||||
## Tools
|
||||
|
||||
| Tool | Description |
|
||||
|---|---|
|
||||
| `atelier.lookup_principle(domain, principle_id)` | Look up a principle by domain + P-rule ID (e.g., `security`, `P4`). Returns the principle text + the core C-rule it derives from. |
|
||||
| `atelier.list_domains()` | List the 19 Atelier domains with P-rule counts + Nova-relevance. |
|
||||
| `atelier.matrix_lookup(domain)` | Look up the domain→core principle mapping for a given domain. |
|
||||
| `atelier.validate_against_principles(snippet, domains?)` | Validate a code/diff snippet against the Atelier agent-checklist. Returns pass/fail per check item with the principle citation. |
|
||||
|
||||
## Architecture — Plugin Registry
|
||||
|
||||
```
|
||||
mcp/atelier/
|
||||
├── server.py # entrypoint: loads plugins, starts server
|
||||
├── plugins/
|
||||
│ ├── __init__.py
|
||||
│ ├── principles.py # lookup_principle, list_domains, matrix_lookup
|
||||
│ └── validation.py # validate_against_principles
|
||||
├── vendor/ # pinned Atelier snapshot
|
||||
│ ├── VERSION.md # pinned tag + upgrade instructions
|
||||
│ ├── core/first-principles.md
|
||||
│ ├── domains/security/first-principles.md
|
||||
│ ├── review/agent-checklist.md
|
||||
│ └── matrix/principles-matrix.md
|
||||
└── README.md # this file
|
||||
```
|
||||
|
||||
Each plugin module exposes `register(mcp) -> None` and calls `@mcp.tool()`
|
||||
for its tools. `server.py` scans `plugins/` and calls `register` on each.
|
||||
**Future capabilities drop in as a new plugin file — no `server.py` edits.**
|
||||
|
||||
## Running
|
||||
|
||||
### With the MCP Python SDK installed
|
||||
|
||||
```bash
|
||||
pip install "mcp[cli]"
|
||||
python3 -m mcp.atelier.server
|
||||
```
|
||||
|
||||
The server runs over stdio. An MCP client (e.g., the citizen developer's
|
||||
AI coding agent) spawns it as a subprocess and calls tools via JSON-RPC.
|
||||
|
||||
### Without the SDK (fallback / test mode)
|
||||
|
||||
The server degrades to a plain-Python tool registry. Tools are callable
|
||||
directly — this is how tests run without the SDK installed:
|
||||
|
||||
```python
|
||||
from mcp.atelier.server import NovaAtelierServer
|
||||
s = NovaAtelierServer()
|
||||
s.load_plugins()
|
||||
result = s.call_tool("atelier_lookup_principle", {"domain": "security", "principle_id": "P4"})
|
||||
```
|
||||
|
||||
## Vendoring
|
||||
|
||||
Atelier is vendored under `vendor/` at a pinned tag (`v0.3.6`, see
|
||||
`vendor/VERSION.md`). An agentic validation result is only reproducible if
|
||||
the principles that produced it are pinned. Live-fetch breaks replayability
|
||||
(Atelier `main` drifts). To upgrade:
|
||||
|
||||
```bash
|
||||
bash scripts/update_atelier_vendor.sh <new-tag>
|
||||
```
|
||||
|
||||
## Extensibility
|
||||
|
||||
To add a new tool (e.g., a cost-estimation tool, a policy-as-code
|
||||
evaluator): create `plugins/<name>.py`, expose `register(mcp)`, and call
|
||||
`@mcp.tool()` on your function. The server picks it up automatically. No
|
||||
`server.py` edit. This is the extensibility insurance for future
|
||||
capabilities.
|
||||
|
||||
## Transport
|
||||
|
||||
- **Now:** stdio (local agent consumption — the citizen developer's AI
|
||||
agent spawns the server as a subprocess).
|
||||
- **Future:** Streamable HTTP (the MCP SDK supports it on the same
|
||||
`MCPServer` object; adding it is a transport-only change in `server.py`,
|
||||
not a rewrite).
|
||||
@@ -0,0 +1 @@
|
||||
# mcp/atelier package
|
||||
@@ -0,0 +1 @@
|
||||
# mcp/atelier/plugins package
|
||||
@@ -0,0 +1,99 @@
|
||||
"""mcp/atelier/plugins/principles.py — principle lookup, domain listing, matrix lookup.
|
||||
|
||||
Implements 3 MCP tools (REQ-223):
|
||||
- atelier.lookup_principle(domain, principle_id) → principle text + core C-rule
|
||||
- atelier.list_domains() → 19 domains with P-rule counts + Nova-relevance
|
||||
- atelier.matrix_lookup(domain) → domain→core principle mapping
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
_VENDOR = Path(__file__).resolve().parent.parent / "vendor"
|
||||
|
||||
DOMAINS = [
|
||||
{"domain": "api", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "security", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "data", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "testing", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "performance", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "observability", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "errors", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "documentation", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "concurrency", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "devops", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "infrastructure-as-code", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "kubernetes", "p_rules": 10, "nova_relevant": False},
|
||||
{"domain": "gitops-operators", "p_rules": 10, "nova_relevant": False},
|
||||
{"domain": "ai-ml", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "i18n", "p_rules": 10, "nova_relevant": False},
|
||||
{"domain": "compliance", "p_rules": 10, "nova_relevant": True},
|
||||
{"domain": "edge", "p_rules": 10, "nova_relevant": False},
|
||||
{"domain": "messaging", "p_rules": 10, "nova_relevant": False},
|
||||
{"domain": "ui-ux", "p_rules": 10, "nova_relevant": False},
|
||||
]
|
||||
|
||||
_MATRIX = {
|
||||
"security": [
|
||||
{"p": "P1", "core": "C1", "title": "Boundary Validation"},
|
||||
{"p": "P2", "core": "C1, C8", "title": "Least Privilege"},
|
||||
{"p": "P3", "core": "C1", "title": "Defense in Depth"},
|
||||
{"p": "P4", "core": "C1, C7", "title": "Secrets Never Exposed"},
|
||||
{"p": "P5", "core": "C1", "title": "Authenticated by Default"},
|
||||
{"p": "P6", "core": "C1", "title": "Encrypted in Transit and at Rest"},
|
||||
{"p": "P7", "core": "C1, C7", "title": "Auditable Actions"},
|
||||
{"p": "P8", "core": "C1, C8", "title": "Patched Dependencies"},
|
||||
{"p": "P9", "core": "C1, C6", "title": "Isolated Blast Radius"},
|
||||
{"p": "P10", "core": "C1", "title": "Secure by Default"},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def register(mcp: Any) -> None:
|
||||
"""Register the principles tools with the MCP server (or fallback registry)."""
|
||||
|
||||
@mcp.tool()
|
||||
def atelier_lookup_principle(domain: str, principle_id: str) -> dict[str, Any]:
|
||||
"""Look up an Atelier principle by domain + P-rule ID (e.g., 'security', 'P4').
|
||||
|
||||
Returns the principle title, text, and the core C-rule(s) it derives from.
|
||||
"""
|
||||
fp = _VENDOR / "domains" / domain / "first-principles.md"
|
||||
if not fp.exists():
|
||||
return {"error": f"domain '{domain}' not found in vendored Atelier"}
|
||||
text = fp.read_text()
|
||||
# Parse the P-rule section
|
||||
pattern = rf"## ({principle_id}\s*—\s*.+?)\n(.+?)(?=\n## |\Z)"
|
||||
match = re.search(pattern, text, re.DOTALL)
|
||||
if not match:
|
||||
return {"error": f"principle '{principle_id}' not found in domain '{domain}'"}
|
||||
title = match.group(1).strip()
|
||||
body = match.group(2).strip()
|
||||
# Find core C-rule from matrix
|
||||
matrix_entry = next(
|
||||
(e for e in _MATRIX.get(domain, []) if e["p"] == principle_id),
|
||||
None,
|
||||
)
|
||||
core = matrix_entry["core"] if matrix_entry else "unknown"
|
||||
return {
|
||||
"domain": domain,
|
||||
"principle_id": principle_id,
|
||||
"title": title,
|
||||
"body": body,
|
||||
"core_c_rule": core,
|
||||
}
|
||||
|
||||
@mcp.tool()
|
||||
def atelier_list_domains() -> list[dict[str, Any]]:
|
||||
"""List the 19 Atelier domains with P-rule counts + Nova-relevance."""
|
||||
return DOMAINS
|
||||
|
||||
@mcp.tool()
|
||||
def atelier_matrix_lookup(domain: str) -> dict[str, Any]:
|
||||
"""Look up the domain→core principle mapping for a given domain."""
|
||||
if domain not in _MATRIX:
|
||||
return {"domain": domain, "mapping": [], "note": "full matrix not vendored for this domain; see Atelier live repo"}
|
||||
return {"domain": domain, "mapping": _MATRIX[domain]}
|
||||
@@ -0,0 +1,78 @@
|
||||
"""mcp/atelier/plugins/validation.py — agentic validation against Atelier principles.
|
||||
|
||||
Implements 1 MCP tool (REQ-223):
|
||||
- atelier.validate_against_principles(snippet, domains) → pass/fail per
|
||||
checklist item with the principle citation. This is the agentic
|
||||
validation BEYOND deterministic scanners (Wiz/Checkmarx/Mend) — it
|
||||
catches correctness/clarity/simplicity/observability gaps that
|
||||
deterministic tools cannot.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
# Condensed checklist: core C1-C8 + security domain. Each item is a
|
||||
# (check_id, description, heuristic_pattern, principle_citation).
|
||||
_CHECKLIST = [
|
||||
# C1 Correctness
|
||||
{"id": "C1.1", "desc": "Does the code do what the task asked, completely?", "heuristic": r"TODO|FIXME|pass\s*$", "principle": "C1 Correctness", "neg": True},
|
||||
{"id": "C1.2", "desc": "Does it handle failure cases? (errors, timeouts)", "heuristic": r"except\s*:?\s*pass", "principle": "C1 Correctness", "neg": True},
|
||||
{"id": "C1.3", "desc": "Is there a test that would fail if the code were wrong?", "heuristic": r"def test_|describe\(", "principle": "C1 Correctness", "neg": False, "optional": True},
|
||||
# C2 Clarity
|
||||
{"id": "C2.1", "desc": "Are names intent-revealing? (no 'data', 'temp', 'x')", "heuristic": r"\b(data|temp|x|foo|bar|doStuff)\b", "principle": "C2 Clarity", "neg": True},
|
||||
# C3 Simplicity
|
||||
{"id": "C3.1", "desc": "Is there dead code? (unreachable branches)", "heuristic": r"return\s+\w+\s*$.*return", "principle": "C3 Simplicity", "neg": True, "multiline": True},
|
||||
# C7 Observability
|
||||
{"id": "C7.1", "desc": "Are there logs for significant events?", "heuristic": r"log(ger|ging)?|print\(|console\.", "principle": "C7 Observability", "neg": False, "optional": True},
|
||||
{"id": "C7.2", "desc": "Are there secrets in logs?", "heuristic": r"password|secret|token|api_key", "principle": "C7 Observability + Security P4", "neg": True},
|
||||
# Security
|
||||
{"id": "SEC.1", "desc": "No secrets in code/logs/URLs", "heuristic": r"(password|secret|token|api_key)\s*=\s*['\"]", "principle": "Security P4 Secrets Never Exposed", "neg": True},
|
||||
{"id": "SEC.2", "desc": "Input validated at the boundary", "heuristic": r"validate|schema|assert", "principle": "Security P1 Boundary Validation", "neg": False, "optional": True},
|
||||
{"id": "SEC.3", "desc": "Authorization checked, not assumed", "heuristic": r"auth|permission|rbac|authorize", "principle": "Security P5 Authenticated by Default", "neg": False, "optional": True},
|
||||
]
|
||||
|
||||
|
||||
def register(mcp: Any) -> None:
|
||||
"""Register the validation tools with the MCP server (or fallback registry)."""
|
||||
|
||||
@mcp.tool()
|
||||
def atelier_validate_against_principles(snippet: str, domains: list[str] | None = None) -> dict[str, Any]:
|
||||
"""Validate a code/diff snippet against Atelier principles.
|
||||
|
||||
Runs the agent-checklist items against the snippet and returns
|
||||
pass/fail per item with the principle citation. This is the
|
||||
agentic validation BEYOND deterministic scanners (Wiz/Checkmarx/
|
||||
Mend) — it catches correctness/clarity/simplicity/observability
|
||||
gaps that deterministic tools cannot.
|
||||
|
||||
Args:
|
||||
snippet: The code or diff text to validate.
|
||||
domains: Optional list of domains to include (default: core + security).
|
||||
"""
|
||||
results: list[dict[str, Any]] = []
|
||||
for check in _CHECKLIST:
|
||||
pattern = check["heuristic"]
|
||||
flags = re.DOTALL if check.get("multiline") else 0
|
||||
found = bool(re.search(pattern, snippet, flags))
|
||||
# neg=True means finding the pattern is a FAIL; neg=False means finding is a PASS
|
||||
if check.get("neg"):
|
||||
status = "FAIL" if found else "PASS"
|
||||
else:
|
||||
if check.get("optional"):
|
||||
status = "PASS" if found else "WARN"
|
||||
else:
|
||||
status = "PASS" if found else "WARN"
|
||||
results.append({
|
||||
"check_id": check["id"],
|
||||
"description": check["desc"],
|
||||
"status": status,
|
||||
"principle": check["principle"],
|
||||
})
|
||||
all_pass = all(r["status"] == "PASS" for r in results)
|
||||
return {
|
||||
"overall": "PASS" if all_pass else "FAIL",
|
||||
"results": results,
|
||||
"domains_checked": domains or ["core", "security"],
|
||||
"note": "Agentic validation beyond Wiz/Checkmarx/Mend — catches correctness, clarity, simplicity, observability gaps.",
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
"""mcp/atelier/server.py — Nova Atelier MCP server (REQ-223, D-135, D-137, D-140).
|
||||
|
||||
Plugin-registry architecture (D-140): plugins/<name>.py modules each expose
|
||||
``register(mcp) -> None`` and call ``@mcp.tool()`` for their tools. This file
|
||||
scans ``plugins/`` and calls ``register`` on each. Future capabilities drop
|
||||
in as new plugin files — no server.py edits.
|
||||
|
||||
Transport: stdio (D-135). The MCP Python SDK v2 (``modelcontextprotocol/
|
||||
python-sdk``, D-137) is the target. If the SDK is not installed, the server
|
||||
degrades to a plain-Python tool registry that can be tested directly — the
|
||||
tools are callable without MCP. This makes the server testable in CI
|
||||
without the SDK installed.
|
||||
|
||||
Usage (with SDK):
|
||||
python3 -m mcp.atelier.server
|
||||
|
||||
Usage (without SDK, for testing):
|
||||
from mcp.atelier.server import NovaAtelierServer
|
||||
s = NovaAtelierServer()
|
||||
s.load_plugins()
|
||||
result = s.call_tool("atelier.lookup_principle", {"domain": "security", "principle_id": "P4"})
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import types
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Callable
|
||||
|
||||
_PLUGIN_DIR = pathlib.Path(__file__).parent / "plugins"
|
||||
_VENDOR_DIR = pathlib.Path(__file__).parent / "vendor"
|
||||
|
||||
|
||||
class _ToolRegistry:
|
||||
"""A minimal tool registry that mimics the MCP ``@mcp.tool()`` decorator.
|
||||
|
||||
When the MCP SDK is available, ``NovaAtelierServer`` wraps a real
|
||||
``MCPServer`` and the decorator registers tools with the SDK. When the
|
||||
SDK is absent, this registry is the fallback — tools are callable via
|
||||
``call_tool()`` for testing.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._tools: dict[str, dict[str, Any]] = {}
|
||||
|
||||
def tool(self, name: str | None = None, description: str | None = None) -> Callable:
|
||||
def decorator(fn: Callable) -> Callable:
|
||||
tool_name = name or fn.__name__
|
||||
self._tools[tool_name] = {
|
||||
"fn": fn,
|
||||
"description": description or fn.__doc__ or "",
|
||||
"name": tool_name,
|
||||
}
|
||||
return fn
|
||||
return decorator
|
||||
|
||||
def list_tools(self) -> list[dict[str, str]]:
|
||||
return [{"name": t["name"], "description": t["description"]} for t in self._tools.values()]
|
||||
|
||||
def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
||||
if name not in self._tools:
|
||||
raise KeyError(f"Unknown tool: {name}")
|
||||
return self._tools[name]["fn"](**arguments)
|
||||
|
||||
|
||||
class NovaAtelierServer:
|
||||
"""The Nova Atelier MCP server.
|
||||
|
||||
Wraps an MCP SDK ``MCPServer`` if available; otherwise uses the
|
||||
``_ToolRegistry`` fallback. Plugins are loaded from ``plugins/``.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.registry = _ToolRegistry()
|
||||
self._mcp = None
|
||||
try:
|
||||
from mcp.server import MCPServer # type: ignore[import-not-found]
|
||||
self._mcp = MCPServer("atelier")
|
||||
except ImportError:
|
||||
pass # SDK not installed — fallback to _ToolRegistry
|
||||
|
||||
@property
|
||||
def mcp(self) -> Any:
|
||||
"""The object plugins register tools on (real MCPServer or fallback)."""
|
||||
return self._mcp if self._mcp is not None else self.registry
|
||||
|
||||
def load_plugins(self) -> list[str]:
|
||||
"""Scan plugins/ and call ``register(mcp)`` on each. Returns loaded names."""
|
||||
loaded: list[str] = []
|
||||
for p in sorted(_PLUGIN_DIR.glob("*.py")):
|
||||
if p.stem == "__init__":
|
||||
continue
|
||||
mod_name = f"mcp.atelier.plugins.{p.stem}"
|
||||
mod = importlib.import_module(mod_name)
|
||||
if hasattr(mod, "register"):
|
||||
mod.register(self.mcp if self._mcp else self.registry)
|
||||
loaded.append(p.stem)
|
||||
return loaded
|
||||
|
||||
def list_tools(self) -> list[dict[str, str]]:
|
||||
if self._mcp is not None:
|
||||
return [{"name": t.name, "description": t.description} for t in self._mcp._tools.values()] # type: ignore[attr-defined]
|
||||
return self.registry.list_tools()
|
||||
|
||||
def call_tool(self, name: str, arguments: dict[str, Any]) -> Any:
|
||||
if self._mcp is not None:
|
||||
raise RuntimeError("MCP SDK call_tool not supported in fallback mode — use the MCP client")
|
||||
return self.registry.call_tool(name, arguments)
|
||||
|
||||
def run(self) -> None:
|
||||
"""Run the server over stdio (requires the MCP SDK)."""
|
||||
if self._mcp is None:
|
||||
raise RuntimeError("MCP SDK not installed — cannot run server. Install: pip install mcp")
|
||||
self._mcp.run()
|
||||
|
||||
|
||||
def _make_plugin_compat_decorator(registry_or_mcp: Any) -> Callable:
|
||||
"""Return a ``tool()`` decorator that works for both the fallback
|
||||
registry and the real MCP SDK."""
|
||||
if hasattr(registry_or_mcp, "tool"):
|
||||
return registry_or_mcp.tool
|
||||
# Fallback: wrap registry.tool() as a decorator factory
|
||||
return registry_or_mcp.tool
|
||||
|
||||
|
||||
def main() -> None:
|
||||
server = NovaAtelierServer()
|
||||
loaded = server.load_plugins()
|
||||
print(f"Atelier MCP server — {len(loaded)} plugins loaded: {', '.join(loaded)}", file=sys.stderr)
|
||||
if server._mcp is None:
|
||||
print("MCP SDK not installed — server is in fallback (test) mode.", file=sys.stderr)
|
||||
print("Tools: " + ", ".join(t["name"] for t in server.list_tools()), file=sys.stderr)
|
||||
else:
|
||||
server.run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# Vendored Atelier — Version Pin
|
||||
|
||||
> **Pinned tag:** `v0.3.6` (the v0.4 milestone release, 2026-08-05)
|
||||
> **Commit:** `666b137dbb3c00e81f8740d18b639bc67587d29f`
|
||||
> **P-rule count:** 190 (19 domains × 10 P-rules)
|
||||
> **Vendor date:** 2026-08-06
|
||||
> **Vendor reason:** audit reproducibility (D-136) — an agentic validation
|
||||
> result is only replayable if the principles that produced it are pinned.
|
||||
|
||||
## Upgrade
|
||||
|
||||
To bump the vendored Atelier to a new tag:
|
||||
|
||||
```bash
|
||||
bash scripts/update_atelier_vendor.sh <new-tag>
|
||||
```
|
||||
|
||||
The script fetches the Atelier repo at the given tag, replaces
|
||||
`mcp/atelier/vendor/`, updates this VERSION.md, and commits the change.
|
||||
Upgrades are **intentional** — never automatic. Atelier `main` is a
|
||||
moving target; pinning is required for audit reproducibility.
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Core First Principles
|
||||
|
||||
The 8 universal axioms. Every domain principle derives from one or more
|
||||
of these. Precedence: C1 > C2 > C3 > C4 > C5 > C6 > C7 > C8.
|
||||
|
||||
## C1 — Correctness
|
||||
The system does what it is supposed to do, and nothing else.
|
||||
|
||||
## C2 — Clarity
|
||||
The intent of the code is obvious to its reader.
|
||||
|
||||
## C3 — Simplicity
|
||||
The solution is as simple as possible, and no simpler.
|
||||
|
||||
## C4 — Locality
|
||||
Decisions and their consequences live near each other.
|
||||
|
||||
## C5 — Reversibility
|
||||
Every decision can be undone, and the cost of undoing is known.
|
||||
|
||||
## C6 — Composability
|
||||
Parts combine into wholes, and the parts are reusable.
|
||||
|
||||
## C7 — Observability
|
||||
The system's behavior is visible to those who must understand it.
|
||||
|
||||
## C8 — Economy
|
||||
The system uses no more resources than the task requires.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Security — First Principles
|
||||
|
||||
## P1 — Boundary Validation
|
||||
All input is validated at the trust boundary. (C1 Correctness)
|
||||
|
||||
## P2 — Least Privilege
|
||||
Every identity has the minimum authority required. (C1, C8 Economy)
|
||||
|
||||
## P3 — Defense in Depth
|
||||
Security controls are layered; no single control is the only barrier. (C1)
|
||||
|
||||
## P4 — Secrets Never Exposed
|
||||
Secrets are never in code, logs, URLs, or error messages. (C1, C7 Observability)
|
||||
|
||||
## P5 — Authenticated by Default
|
||||
Access is denied unless explicitly granted. (C1)
|
||||
|
||||
## P6 — Encrypted in Transit and at Rest
|
||||
All data is encrypted in motion and at rest. (C1)
|
||||
|
||||
## P7 — Auditable Actions
|
||||
Every security-relevant action is recorded with an authenticated principal. (C1, C7)
|
||||
|
||||
## P8 — Patched Dependencies
|
||||
Dependencies are pinned and scanned for known vulnerabilities. (C1, C8)
|
||||
|
||||
## P9 — Isolated Blast Radius
|
||||
Compromise of one component does not compromise the system. (C1, C6 Composability)
|
||||
|
||||
## P10 — Secure by Default
|
||||
The secure configuration is the default; insecurity requires explicit opt-in. (C1)
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
# Principles Matrix (Vendored Stub)
|
||||
|
||||
Maps every domain P-rule back to the core C-rule(s) it derives from.
|
||||
Full matrix in the live Atelier repo; this is a condensed vendored version
|
||||
for the security domain (the primary domain the MCP server validates
|
||||
against in v1.18).
|
||||
|
||||
| Domain | P-rule | Core C-rule(s) |
|
||||
|---|---|---|
|
||||
| security | P1 Boundary Validation | C1 Correctness |
|
||||
| security | P2 Least Privilege | C1, C8 Economy |
|
||||
| security | P3 Defense in Depth | C1 |
|
||||
| security | P4 Secrets Never Exposed | C1, C7 Observability |
|
||||
| security | P5 Authenticated by Default | C1 |
|
||||
| security | P6 Encrypted in Transit and at Rest | C1 |
|
||||
| security | P7 Auditable Actions | C1, C7 |
|
||||
| security | P8 Patched Dependencies | C1, C8 |
|
||||
| security | P9 Isolated Blast Radius | C1, C6 Composability |
|
||||
| security | P10 Secure by Default | C1 |
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
# Agent Pre-Completion Checklist (Vendored)
|
||||
|
||||
Every AI agent runs this checklist before completing a task.
|
||||
|
||||
## Core Principles Checklist (C1–C8)
|
||||
|
||||
### C1 Correctness
|
||||
- Does the code do what the task asked, completely?
|
||||
- Does it handle the specified edge cases? (nulls, empties, max, min)
|
||||
- Does it handle the failure cases? (errors, timeouts, invalid input)
|
||||
- Is there a test that would fail if the code were wrong?
|
||||
|
||||
### C2 Clarity
|
||||
- Can a stranger read this and understand it without asking you?
|
||||
- Are names intent-revealing? (No `data`, `temp`, `x`, `doStuff`)
|
||||
- Do comments explain *why*, not *what*?
|
||||
|
||||
### C3 Simplicity
|
||||
- Is this the simplest solution that is complete?
|
||||
- Is there dead code? (Unreachable branches, unused variables)
|
||||
- Is there premature abstraction? (An interface with one implementation)
|
||||
|
||||
### C4 Locality
|
||||
- Does related logic live together?
|
||||
- Are side effects near their causes?
|
||||
|
||||
### C5 Reversibility
|
||||
- Is this change undoable? (migration has a `down`, deploy has a rollback)
|
||||
- Did I avoid irreversible actions without explicit confirmation?
|
||||
|
||||
### C6 Composability
|
||||
- Does this component/function do one thing?
|
||||
- Is the boundary (props/args/return) explicit and typed?
|
||||
|
||||
### C7 Observability
|
||||
- Are there logs for significant events?
|
||||
- Do errors carry enough context to debug? (request ID, user, action)
|
||||
- Are there no secrets in logs?
|
||||
|
||||
### C8 Economy
|
||||
- Is memory bounded? (No unbounded growth, no loading everything)
|
||||
- Is time bounded? (No N+1, no blocking without timeout)
|
||||
|
||||
## Domain-Specific (Security)
|
||||
|
||||
- No secrets in code, logs, URLs, or error messages
|
||||
- Input is validated at the boundary
|
||||
- Output is encoded for its context
|
||||
- Crypto uses vetted libraries (no MD5/SHA1 for security)
|
||||
- Authorization is checked, not assumed
|
||||
@@ -1,6 +1,5 @@
|
||||
# Nova Metrics Directory
|
||||
|
||||
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (D-128)
|
||||
|
||||
This directory holds Nova's telemetry/observability artifacts. The
|
||||
metrics layer is **Nova-native** (D-120): JSONL event log + SQLite cold
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Nova PowerBI Dashboard — Import Guide
|
||||
|
||||
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (REQ-208)
|
||||
> Generated: 2026-08-04
|
||||
|
||||
This guide documents how to import Nova's metrics views into PowerBI
|
||||
via the folder connector, and suggests a starter visual model.
|
||||
|
||||
@@ -48,6 +48,11 @@
|
||||
"description": "Target group target type (ip or instance).",
|
||||
"required": false,
|
||||
"default": "ip"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -85,20 +90,42 @@
|
||||
{
|
||||
"type": "aws:elbv2:loadbalancer",
|
||||
"description": "Application load balancer in the VPC subnets.",
|
||||
"inputs": ["name", "subnets", "security_group", "load_balancer_type"],
|
||||
"outputs": ["lb_arn"]
|
||||
"inputs": [
|
||||
"name",
|
||||
"subnets",
|
||||
"security_group",
|
||||
"load_balancer_type"
|
||||
],
|
||||
"outputs": [
|
||||
"lb_arn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "aws:elbv2:targetgroup",
|
||||
"description": "Target group for the ECS service tasks.",
|
||||
"inputs": ["name", "port", "protocol", "vpc_id", "target_type"],
|
||||
"outputs": ["target_group_arn"]
|
||||
"inputs": [
|
||||
"name",
|
||||
"port",
|
||||
"protocol",
|
||||
"vpc_id",
|
||||
"target_type"
|
||||
],
|
||||
"outputs": [
|
||||
"target_group_arn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "aws:elbv2:listener",
|
||||
"description": "Listener forwarding the LB port to the target group.",
|
||||
"inputs": ["lb_arn", "port", "protocol", "target_group_arn"],
|
||||
"outputs": ["listener_arn"]
|
||||
"inputs": [
|
||||
"lb_arn",
|
||||
"port",
|
||||
"protocol",
|
||||
"target_group_arn"
|
||||
],
|
||||
"outputs": [
|
||||
"listener_arn"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
resource "aws_lb" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = var.name
|
||||
load_balancer_type = var.load_balancer_type
|
||||
subnets = local.subnet_list
|
||||
@@ -6,6 +7,7 @@ resource "aws_lb" "this" {
|
||||
}
|
||||
|
||||
resource "aws_lb_target_group" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name_prefix = "${var.name}-"
|
||||
port = var.port
|
||||
protocol = var.protocol
|
||||
@@ -18,13 +20,14 @@ resource "aws_lb_target_group" "this" {
|
||||
}
|
||||
|
||||
resource "aws_lb_listener" "this" {
|
||||
load_balancer_arn = aws_lb.this.id
|
||||
count = var.enabled ? 1 : 0
|
||||
load_balancer_arn = aws_lb.this[0].id
|
||||
port = var.port
|
||||
protocol = var.protocol
|
||||
|
||||
default_action {
|
||||
type = "forward"
|
||||
target_group_arn = aws_lb_target_group.this.arn
|
||||
target_group_arn = aws_lb_target_group.this[0].arn
|
||||
}
|
||||
|
||||
depends_on = [aws_lb_target_group.this]
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
output "lb_arn" {
|
||||
value = aws_lb.this.id
|
||||
value = aws_lb.this[0].id
|
||||
description = "The load balancer ARN."
|
||||
}
|
||||
|
||||
output "listener_arn" {
|
||||
value = aws_lb_listener.this.arn
|
||||
value = aws_lb_listener.this[0].arn
|
||||
description = "The listener ARN."
|
||||
}
|
||||
|
||||
output "target_group_arn" {
|
||||
value = aws_lb_target_group.this.arn
|
||||
value = aws_lb_target_group.this[0].arn
|
||||
description = "The target group ARN."
|
||||
}
|
||||
|
||||
@@ -50,3 +50,9 @@ variable "vpc_id" {
|
||||
description = "VPC ID for the target group (ref to vpc or platform VPC)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -43,6 +43,11 @@
|
||||
"type": "string",
|
||||
"description": "AWS region (CloudFront is global but the provider region is used for the OAC).",
|
||||
"required": true
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -75,17 +80,37 @@
|
||||
{
|
||||
"type": "aws:cloudfront:distribution",
|
||||
"description": "CloudFront distribution with S3 origin via OAC.",
|
||||
"inputs": ["bucket_regional_domain_name", "price_class", "viewer_protocol_policy", "default_ttl", "max_ttl", "waf_web_acl_arn", "oac_id"],
|
||||
"outputs": ["distribution_arn", "distribution_domain_name"]
|
||||
"inputs": [
|
||||
"bucket_regional_domain_name",
|
||||
"price_class",
|
||||
"viewer_protocol_policy",
|
||||
"default_ttl",
|
||||
"max_ttl",
|
||||
"waf_web_acl_arn",
|
||||
"oac_id"
|
||||
],
|
||||
"outputs": [
|
||||
"distribution_arn",
|
||||
"distribution_domain_name"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "aws:cloudfront:originaccesscontrol",
|
||||
"description": "Origin Access Control for the S3 origin.",
|
||||
"inputs": ["name", "origin_type", "signing_behavior"],
|
||||
"outputs": ["oac_id"]
|
||||
"inputs": [
|
||||
"name",
|
||||
"origin_type",
|
||||
"signing_behavior"
|
||||
],
|
||||
"outputs": [
|
||||
"oac_id"
|
||||
]
|
||||
}
|
||||
],
|
||||
"intra_refs": [
|
||||
{"from": "aws:cloudfront:distribution.oac_id", "to": "aws:cloudfront:originaccesscontrol.oac_id"}
|
||||
{
|
||||
"from": "aws:cloudfront:distribution.oac_id",
|
||||
"to": "aws:cloudfront:originaccesscontrol.oac_id"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
resource "aws_cloudfront_origin_access_control" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = local.oac_name
|
||||
origin_access_control_origin_type = local.oac_origin_type
|
||||
signing_behavior = local.oac_signing_behavior
|
||||
@@ -6,10 +7,11 @@ resource "aws_cloudfront_origin_access_control" "this" {
|
||||
}
|
||||
|
||||
resource "aws_cloudfront_distribution" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
origin {
|
||||
origin_id = "s3-origin"
|
||||
domain_name = var.bucket_regional_domain_name
|
||||
origin_access_control_id = aws_cloudfront_origin_access_control.this.id
|
||||
origin_access_control_id = aws_cloudfront_origin_access_control.this[0].id
|
||||
s3_origin_config {
|
||||
origin_access_identity = ""
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
output "distribution_arn" {
|
||||
value = aws_cloudfront_distribution.this.arn
|
||||
value = aws_cloudfront_distribution.this[0].arn
|
||||
description = "The CloudFront distribution ARN."
|
||||
}
|
||||
|
||||
output "distribution_domain_name" {
|
||||
value = aws_cloudfront_distribution.this.domain_name
|
||||
value = aws_cloudfront_distribution.this[0].domain_name
|
||||
description = "The CloudFront distribution domain name."
|
||||
}
|
||||
|
||||
output "oac_id" {
|
||||
value = aws_cloudfront_origin_access_control.this.id
|
||||
value = aws_cloudfront_origin_access_control.this[0].id
|
||||
description = "The Origin Access Control ID."
|
||||
}
|
||||
|
||||
@@ -38,3 +38,9 @@ variable "region" {
|
||||
description = "AWS region (CloudFront is global but the provider region is used for the OAC)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
"type": "string",
|
||||
"description": "ARN of the CMK for repository encryption; if absent, uses AWS-managed key.",
|
||||
"required": false
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -48,4 +53,4 @@
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ locals {
|
||||
}
|
||||
|
||||
resource "aws_ecr_repository" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = var.name
|
||||
image_tag_mutability = "MUTABLE"
|
||||
image_scanning_configuration {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
output "repository_url" {
|
||||
value = aws_ecr_repository.this.repository_url
|
||||
value = aws_ecr_repository.this[0].repository_url
|
||||
description = "The ECR repository URL."
|
||||
}
|
||||
|
||||
output "repository_arn" {
|
||||
value = aws_ecr_repository.this.arn
|
||||
value = aws_ecr_repository.this[0].arn
|
||||
description = "The ECR repository ARN."
|
||||
}
|
||||
@@ -13,4 +13,10 @@ variable "kms_key_arn" {
|
||||
type = string
|
||||
description = "ARN of the CMK for ECR encryption; if absent, uses managed key."
|
||||
default = null
|
||||
}
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
"type": "string",
|
||||
"description": "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key.",
|
||||
"required": false
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -43,4 +48,4 @@
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
resource "aws_ecs_cluster" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = var.name
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
output "cluster_arn" {
|
||||
value = aws_ecs_cluster.this.arn
|
||||
value = aws_ecs_cluster.this[0].arn
|
||||
description = "The ECS cluster ARN."
|
||||
}
|
||||
|
||||
output "cluster_id" {
|
||||
value = aws_ecs_cluster.this.id
|
||||
value = aws_ecs_cluster.this[0].id
|
||||
description = "The ECS cluster ID."
|
||||
}
|
||||
|
||||
@@ -15,3 +15,9 @@ variable "kms_key_arn" {
|
||||
description = "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -79,6 +79,11 @@
|
||||
"description": "ECS task definition family name.",
|
||||
"required": false,
|
||||
"default": "app"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -107,14 +112,32 @@
|
||||
{
|
||||
"type": "aws:ecs:task_definition",
|
||||
"description": "Fargate task definition; the adapter jsonencodes image/port/env into container_definitions.",
|
||||
"inputs": ["image", "port", "cpu", "memory", "env", "family"],
|
||||
"outputs": ["task_def_arn"]
|
||||
"inputs": [
|
||||
"image",
|
||||
"port",
|
||||
"cpu",
|
||||
"memory",
|
||||
"env",
|
||||
"family"
|
||||
],
|
||||
"outputs": [
|
||||
"task_def_arn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "aws:ecs:service",
|
||||
"description": "Fargate service running the task definition in the cluster + subnets.",
|
||||
"inputs": ["cluster_arn", "subnets", "security_group", "lb_target_group_arn", "desired_count", "launch_type"],
|
||||
"outputs": ["service_arn"]
|
||||
"inputs": [
|
||||
"cluster_arn",
|
||||
"subnets",
|
||||
"security_group",
|
||||
"lb_target_group_arn",
|
||||
"desired_count",
|
||||
"launch_type"
|
||||
],
|
||||
"outputs": [
|
||||
"service_arn"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
resource "aws_ecs_task_definition" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
family = var.family
|
||||
cpu = tostring(var.cpu)
|
||||
memory = tostring(var.memory)
|
||||
@@ -8,9 +9,10 @@ resource "aws_ecs_task_definition" "this" {
|
||||
}
|
||||
|
||||
resource "aws_ecs_service" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = "nova-microservice"
|
||||
cluster = var.cluster_arn
|
||||
task_definition = aws_ecs_task_definition.this.arn
|
||||
task_definition = aws_ecs_task_definition.this[0].arn
|
||||
desired_count = var.desired_count
|
||||
launch_type = var.launch_type
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
output "service_arn" {
|
||||
value = aws_ecs_service.this.id
|
||||
value = aws_ecs_service.this[0].id
|
||||
description = "The ECS service ARN."
|
||||
}
|
||||
|
||||
output "task_def_arn" {
|
||||
value = aws_ecs_task_definition.this.arn
|
||||
value = aws_ecs_task_definition.this[0].arn
|
||||
description = "The ECS task definition ARN."
|
||||
}
|
||||
|
||||
@@ -78,3 +78,9 @@ variable "family" {
|
||||
description = "ECS task definition family name."
|
||||
default = "app"
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
"type": "string",
|
||||
"description": "AWS region the role is created in.",
|
||||
"required": true
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -48,4 +53,4 @@
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
resource "aws_iam_role" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = var.role_name
|
||||
assume_role_policy = local.assume_role_policy
|
||||
}
|
||||
|
||||
resource "aws_iam_role_policy" "ecr_logs" {
|
||||
count = local.inline_policy != null ? 1 : 0
|
||||
count = (local.inline_policy != null && var.enabled) ? 1 : 0
|
||||
name = local.inline_policy.name
|
||||
role = aws_iam_role.this.id
|
||||
role = aws_iam_role.this[0].id
|
||||
policy = local.inline_policy.policy
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
output "role_arn" {
|
||||
value = aws_iam_role.this.arn
|
||||
value = aws_iam_role.this[0].arn
|
||||
description = "The IAM role ARN."
|
||||
}
|
||||
|
||||
output "role_id" {
|
||||
value = aws_iam_role.this.id
|
||||
value = aws_iam_role.this[0].id
|
||||
description = "The IAM role ID."
|
||||
}
|
||||
|
||||
@@ -21,3 +21,9 @@ variable "region" {
|
||||
description = "AWS region (provider-level; not a resource arg)."
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
"description": "Number of days before the key is deleted after deletion is requested (default 30).",
|
||||
"required": false,
|
||||
"default": 30
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -49,4 +54,4 @@
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
resource "aws_kms_key" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
description = var.description
|
||||
enable_key_rotation = true
|
||||
deletion_window_in_days = var.deletion_window_days
|
||||
}
|
||||
|
||||
resource "aws_kms_alias" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
name = local.alias_name
|
||||
target_key_id = aws_kms_key.this.key_id
|
||||
target_key_id = aws_kms_key.this[0].key_id
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
output "kms_key_arn" {
|
||||
value = aws_kms_key.this.arn
|
||||
value = aws_kms_key.this[0].arn
|
||||
description = "The KMS key ARN."
|
||||
}
|
||||
|
||||
output "kms_key_id" {
|
||||
value = aws_kms_key.this.key_id
|
||||
value = aws_kms_key.this[0].key_id
|
||||
description = "The KMS key ID."
|
||||
}
|
||||
@@ -14,4 +14,10 @@ variable "deletion_window_days" {
|
||||
type = number
|
||||
description = "Deletion window in days (7-30)."
|
||||
default = 30
|
||||
}
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -79,6 +79,11 @@
|
||||
"description": "Database admin password",
|
||||
"required": false,
|
||||
"default": "ACdlcI2026!"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
|
||||
@@ -5,6 +5,7 @@ resource "aws_db_subnet_group" "this" {
|
||||
}
|
||||
|
||||
resource "aws_db_instance" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
engine = var.engine
|
||||
engine_version = var.engine_version
|
||||
instance_class = var.instance_class
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
output "db_endpoint" {
|
||||
value = aws_db_instance.this.endpoint
|
||||
value = aws_db_instance.this[0].endpoint
|
||||
description = "The RDS instance endpoint."
|
||||
}
|
||||
|
||||
output "db_arn" {
|
||||
value = aws_db_instance.this.arn
|
||||
value = aws_db_instance.this[0].arn
|
||||
description = "The RDS instance ARN."
|
||||
}
|
||||
|
||||
@@ -64,3 +64,9 @@ variable "subnet_ids" {
|
||||
description = "Comma-separated subnet IDs for the DB subnet group (VPC-dependent)."
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
"type": "string",
|
||||
"description": "ARN of the CMK for SSE-KMS; if absent, uses managed key.",
|
||||
"required": false
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -57,4 +62,4 @@
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
resource "aws_s3_bucket" "this" {
|
||||
count = var.enabled ? 1 : 0
|
||||
bucket = var.bucket_name
|
||||
tags = local.tags
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_versioning" "this" {
|
||||
bucket = aws_s3_bucket.this.id
|
||||
count = var.enabled ? 1 : 0
|
||||
bucket = aws_s3_bucket.this[0].id
|
||||
|
||||
versioning_configuration {
|
||||
status = "Enabled"
|
||||
@@ -12,7 +14,8 @@ resource "aws_s3_bucket_versioning" "this" {
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
|
||||
bucket = aws_s3_bucket.this.id
|
||||
count = var.enabled ? 1 : 0
|
||||
bucket = aws_s3_bucket.this[0].id
|
||||
|
||||
rule {
|
||||
apply_server_side_encryption_by_default {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
output "bucket_arn" {
|
||||
value = aws_s3_bucket.this.arn
|
||||
value = aws_s3_bucket.this[0].arn
|
||||
description = "The S3 bucket ARN."
|
||||
}
|
||||
|
||||
output "bucket_name" {
|
||||
value = aws_s3_bucket.this.id
|
||||
value = aws_s3_bucket.this[0].id
|
||||
description = "The bucket name (echoes the input)."
|
||||
}
|
||||
|
||||
output "bucket_regional_domain_name" {
|
||||
value = aws_s3_bucket.this.bucket_regional_domain_name
|
||||
value = aws_s3_bucket.this[0].bucket_regional_domain_name
|
||||
description = "The bucket regional domain name (e.g. nova-spike-bucket.s3.us-east-1.amazonaws.com)."
|
||||
}
|
||||
@@ -19,4 +19,10 @@ variable "tags" {
|
||||
type = map(string)
|
||||
description = "Additional tags to merge with the module defaults."
|
||||
default = {}
|
||||
}
|
||||
}
|
||||
|
||||
variable "enabled" {
|
||||
type = bool
|
||||
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
default = true
|
||||
}
|
||||
|
||||
@@ -71,6 +71,11 @@
|
||||
"type": "string",
|
||||
"description": "ECS cluster ARN to deploy the service into",
|
||||
"required": false
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
@@ -99,4 +104,4 @@
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ resource "aws_ecs_service" "uptime" {
|
||||
name = "nova-uptime"
|
||||
cluster = local.cluster_ref
|
||||
task_definition = aws_ecs_task_definition.uptime.arn
|
||||
desired_count = var.feature_flag_enabled ? 1 : 0
|
||||
desired_count = var.enabled ? (var.feature_flag_enabled ? 1 : 0) : 0
|
||||
launch_type = "FARGATE"
|
||||
|
||||
dynamic "network_configuration" {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user