Compare commits

...

44 Commits

Author SHA1 Message Date
Jon Chery 2efed26bb6 docs(P00): create phase plans — v1.21 (7 phases)
Nova Slides Render / render (push) Failing after 1m5s
PLAN.md v1.21 section: 5 execution phases + 1 final. Wave 1 parallelizable
(P1 strategic-docs, P2 slides, P4 pipeline-hardening — zero file overlap),
Wave 2 (P3 marp+README), Wave 3 (P5 render+verify), Wave 4 (P6 ship).
NFR milestone → tags on v1.20.x line (v1.20.0 P0 → v1.20.6 P6 final).

CLARIFY + RESEARCH minimal at full autonomy: domain is known, requirements
confirmed with user (deck title = Autonomous Cloud Delivery Platform;
thesis = AUTONOMY_THESIS.md; slide 1 = Problem→Solution→Proof→Roadmap+Ask;
Atelier split into 2 slides; CTO metrics = Lead Time + Vuln Trend + MTTR +
Spend; files renamed to nova-autonomous-cloud-delivery*).

---ci---
project: acdl
phase: 0
milestone: v1.21
status: plan
---/ci---
2026-08-11 13:52:40 +00:00
Jon Chery 5c07e29b90 docs(P00): validate specification — v1.21 milestone (REQ-245..253)
Add v1.21 requirements section (Nova Deck Refinement & Pipeline Hardening):
REQ-245 deck rename + restructure; REQ-246 thesis rename + reframe;
REQ-247 strategic-docs sync (integration objective); REQ-248 RACI
restructure (QE + SRE); REQ-249 Atelier split; REQ-250 pipeline hardening
(Checkov before plan, Wiz-or-Checkov on plan); REQ-251 theme CSS fix +
footer cleanup; REQ-252 global citation/badge/version removal; REQ-253
render + verify + ship.

Set active_milestone=v1.21 in config.json. Sync PROJECT.md strategic-
direction pillar for the integration objective (Objective #4 reframed),
deterministic-trust reword (Objective #2), CTO-grade ROI metrics
(Objective #3), and anti-goal updates.

---ci---
project: acdl
phase: 0
milestone: v1.21
status: specify
---/ci---
2026-08-11 13:51:55 +00:00
Jon Chery aa868c97ef docs(milestone): complete v1.20 — Consumer Cleanup + Transparent Terraform + Slide Pipeline
15 requirements complete (REQ-230..244):
- P1: gitea/gitlab removed from all synced files (REQ-230,231,232)
- P2: S&P theme CSS + render_slides.sh + CI workflow + tests (REQ-239..243)
- P3: 12-month product roadmap slides added to deck (REQ-244)
- P4: run_platform.sh split + var.enabled feature flags + stale path fix (REQ-233..238)

---ci---
project: acdl
phase: 5
milestone: v1.20
status: complete
phase_role: final
requirements:
  covered: [REQ-230,REQ-231,REQ-232,REQ-233,REQ-234,REQ-235,REQ-236,REQ-237,REQ-238,REQ-239,REQ-240,REQ-241,REQ-242,REQ-243,REQ-244]
  partial: []
---/ci---
2026-08-07 18:54:19 +00:00
Jon Chery e4a9915891 docs(P5): checkpoint — verify stage
---ci---
project: acdl
phase: 5
milestone: v1.20
status: verify
phase_role: final
---/ci---
2026-08-07 18:50:00 +00:00
Jon Chery 0ca383dae6 feat(P4): transparent terraform + feature flags + run_platform.sh split (REQ-233..238)
Create run_codegen.sh (pre-TF: env check, validate, resolve, adapt).
Create run_postapply.sh (post-TF: Checkov, confidence, HITL, outbox, SSM, uptime).
Add variable 'enabled' (bool, default true) + count=var.enabled?1:0 to all 12
L1 modules (alb, cloudfront, ecr, ecs-cluster, ecs-service, iam-role, kms-key,
rds, s3, uptime, vpc, waf). Fix all cross-resource references with [0] indexing.
Update interface.json for all modules to declare 'enabled' input.
Fix stale artifact path /tmp/acdl_platform_run_v18 → /tmp/nova_platform_run (REQ-238).
run_platform.sh remains as backward-compat shim for local-dev usage.

---ci---
project: acdl
phase: 4
milestone: v1.20
status: execute
requirements: [REQ-233, REQ-234, REQ-235, REQ-236, REQ-237, REQ-238]
---/ci---
2026-08-07 18:49:56 +00:00
Jon Chery ed5ea90654 feat(P3): add 12-month product roadmap slides (REQ-244)
Slide 20 — 12-Month Product Roadmap: 4-quarter arc (Pilot Activation →
Provable Trust → Compounding ROI → Agentic Substrate).
Slide 21 — Quarter-by-Quarter Outcomes: detail table (theme, deliverable,
target metric, strategic-objective grounding).
Both grounded in NORTH_STAR's 4 strategic objectives + deferred-metric
candidate milestones. Distinct from Slide 15's deferred-metric unblock paths.
Matching talking-points sections added. HTML + PPTX re-rendered via S&P theme.

---ci---
project: acdl
phase: 3
milestone: v1.20
status: execute
requirements: [REQ-244]
---/ci---
2026-08-07 18:28:01 +00:00
Jon Chery 2273009b95 feat(P2): dedicated S&P theme + render pipeline + CI workflow (REQ-239..243)
Create nova-sp-theme.css — S&P Global Energy Marp theme (Red/Black/White
palette applied to all slide chrome: backgrounds, headers/footers, pagination,
tables, blockquotes, code blocks).
Create render_slides.sh — end-to-end pipeline: mermaid PNGs + Marp HTML/PPTX.
Create slides.yml CI workflow — auto-renders on docs/presentations/ changes.
Create test_slides_pipeline.py — 12 tests (theme CSS, Marp frontmatter, script,
workflow, .mmd/.png parity, README retired-deck cleanup).
Update Marp frontmatter: theme: nova-sp + footer v1.20.
Fix presentations/README.md directory layout (remove retired decks).
Re-render HTML + PPTX with S&P theme.

---ci---
project: acdl
phase: 2
milestone: v1.20
status: execute
requirements: [REQ-239, REQ-240, REQ-241, REQ-242, REQ-243]
---/ci---
2026-08-07 18:26:51 +00:00
Jon Chery 0d2cbdb423 feat(P1): remove gitea/gitlab from synced files + simplify docs (REQ-230,231,232)
Genericize forge-detection code: gitea→forge/generic_forge, GITEA_ACTOR→FORGE_ACTOR.
Drop .gitea byte-identity test assertions (keep GitHub-side + contract conformance).
Add test_no_forge_mentions.py guard test (REQ-230).
Delete completed migration docs (NOVA_MIGRATION.md, NOVA_AWS_MIGRATION.md).
Move NO_HUMANS_THESIS.md to .ciagent/ (internal artifact).
Strip ciagent-internal provenance from synced docs (REQ-/D-/P-/CAP- IDs,
milestone headers, .ciagent/PROJECT.md citations).
Trim README.md (reusable deploy section, local key rotation paragraph).
Fix version-tag drift (@v1.13→@v1.19, acdl/→nova/).

---ci---
project: acdl
phase: 1
milestone: v1.20
status: execute
requirements: [REQ-230, REQ-231, REQ-232]
---/ci---
2026-08-07 18:20:29 +00:00
Jon Chery b418d429b5 docs(ship): P0 complete — v1.20 pre-execution
---ci---
project: acdl
phase: 0
milestone: v1.20
status: complete
phase_role: pre_execution
---/ci---
2026-08-07 18:02:30 +00:00
Jon Chery dcba380b52 docs(P00): create phase plans — v1.20 (5 phases)
---ci---
project: acdl
phase: 0
milestone: v1.20
status: plan
---/ci---
2026-08-07 18:02:27 +00:00
Jon Chery f0bc3be92c docs(P00): validate specification — v1.20 milestone (REQ-230..244)
---ci---
project: acdl
phase: 0
milestone: v1.20
status: specify
---/ci---
2026-08-07 18:02:23 +00:00
Jon Chery 0b79b16715 fix(P2): add metrics domain to sync_to_nova.sh — consumer export views (REQ-229)
acdl-ci / Lint (push) Successful in 9s
acdl-ci / Test (push) Failing after 22s
acdl-ci / Platform check-only (offline) (push) Successful in 23s
The metrics/ export views (README.md, TRUST_SNAPSHOT.md, powerbi/) are
consumer-facing but fell outside the original 13 domains, so the first nova
release left them untracked. Adds a 14th domain 'metrics' between docs and
workflows. Updates TestSyncToNovaScript domain-order assertion to 14.

---ci---
project: acdl
phase: 2
milestone: v1.19
status: complete
phase_role: final
---/ci---
2026-08-06 15:47:25 +00:00
Jon Chery 90624be63f docs(milestone): complete v1.19 — Nova 2nd-Release Sync
acdl-ci / Lint (push) Successful in 8s
acdl-ci / Test (push) Failing after 23s
acdl-ci / Platform check-only (offline) (push) Successful in 32s
NFR-only chore milestone complete. P1 (nova-sync-script, v1.18.0) + P2
(final-review-ship, v1.18.1 = milestone release). REQ-229 satisfied.
Review clean, audit clean, 5 decisions locked (D-143..D-147).

---ci---
project: acdl
phase: 2
milestone: v1.19
status: complete
phase_role: final
requirements:
  covered: [REQ-229]
  partial: []
---/ci---
2026-08-06 15:44:31 +00:00
Jon Chery be51fc15fa docs(P1): ship complete — checkpoint update (Gitea release id 530)
acdl-ci / Lint (push) Successful in 9s
acdl-ci / Test (push) Failing after 24s
acdl-ci / Platform check-only (offline) (push) Successful in 24s
---ci---
project: acdl
phase: 1
milestone: v1.19
status: complete
phase_role: execution
requirements:
  covered: [REQ-229]
  partial: []
---/ci---
2026-08-06 15:43:12 +00:00
Jon Chery e3f4ce17d4 verify(P1): 4-layer verify PASS + ship — sync_to_nova.sh (REQ-229)
acdl-ci / Lint (push) Successful in 10s
acdl-ci / Test (push) Failing after 23s
acdl-ci / Platform check-only (offline) (push) Successful in 24s
L1 structural: bash -n clean, shellcheck 0 warnings.
L2 behavioral: manual gate exits 2 without --release; --list-domains prints
13 ordered domains; rsync exclude list correct; .git protected via filter.
L3 security: no hardcoded secrets; .coverage runtime artifact gitignored.
L4 quality: 8/8 TestSyncToNovaScript tests pass (gate, domain order, exclude
list, consumer-script inclusion, .git filter, conventional regex).

---ci---
project: acdl
phase: 1
milestone: v1.19
status: verify
---/ci---
2026-08-06 15:42:50 +00:00
Jon Chery e0d01ad2ef docs(P1): checkpoint — execute complete (REQ-229)
acdl-ci / Lint (push) Successful in 10s
acdl-ci / Test (push) Failing after 24s
acdl-ci / Platform check-only (offline) (push) Successful in 23s
---ci---
project: acdl
phase: 1
milestone: v1.19
status: execute
---/ci---
2026-08-06 15:40:22 +00:00
Jon Chery a4c5f332f6 feat(P1): sync_to_nova.sh — manual-only 2nd-release pipeline into ~/nova (REQ-229)
Replaces 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 (GitLab jonathanchery/nova, separate repo +
history, consumer/platform-team audience).

- Manual-only gate: refuses without --release / RELEASE_CONFIRMED=1 (exit 2).
  Never triggerable by CI.
- Consumer subset: excludes .ciagent/, .gitea/, .env*, terraform/, demo/,
  runtime metrics artifacts, and 18 internal-only scripts (EXCLUDE_SCRIPTS).
  Keeps consumer runbooks + metrics export views (README, powerbi,
  TRUST_SNAPSHOT). Protects ~/nova/.git via rsync --filter=P .git.
- 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 commit.
- Conventional-commit validation: regex-enforced (feat|fix|docs|chore|...);
  bypass via --no-verify-format.
- Modes: --list-domains, --dry-run, --no-push, -v, -h.
- Tests: TestSyncToNovaScript (8 tests) covers gate, domain order, exclude
  list, consumer-script inclusion, .git protection filter, conventional
  regex.

Decisions: D-143 (target ~/nova), D-144 (conventional commits per domain,
not ---ci--- audit blocks), D-145 (manual-only trigger), D-146 (13 fixed
domains, positional-over-changed mapping), D-147 (coreci/Atelier review
gate deferred).

---ci---
project: acdl
phase: 1
milestone: v1.19
status: execute
requirements:
  covered: [REQ-229]
  partial: []
---/ci---
2026-08-06 15:40:11 +00:00
Jon Chery 9e20b7ba95 docs(ship): v1.17.7 milestone complete — checkpoint update (Gitea release id 529)
acdl-ci / Lint (push) Successful in 9s
acdl-ci / Test (push) Failing after 24s
acdl-ci / Platform check-only (offline) (push) Successful in 23s
2026-08-06 15:17:46 +00:00
Jon Chery 6da538c936 Merge milestone/v1.18-citizen-developer-guidance — v1.18 complete (Citizen Developer & Production-Grade Guidance: 5 inputs, 15 requirements, 7 phases + final; tag v1.17.7)
acdl-ci / Lint (push) Successful in 10s
acdl-ci / Test (push) Failing after 26s
acdl-ci / Platform check-only (offline) (push) Successful in 24s
2026-08-06 15:17:03 +00:00
Jon Chery 4e03817ea6 Merge phase/07-final-review-ship — v1.17.7 (v1.18 P7 final review + audit + milestone complete) 2026-08-06 15:16:59 +00:00
Jon Chery 951ad56576 docs(milestone): complete v1.18 — Citizen Developer & Production-Grade Guidance
15 requirements (REQ-214..228) satisfied. 32 tests pass. S&P Global theme
restored. PDLC-upstream scope + RACI matrix authored. Submission-readiness
schema + validator shipped. 9 Atelier skills + docs/skills.md. MCP server
(plugin-registry, stdio, vendored Atelier v0.3.6) with 4 tools + agentic
validation. 21-slide deck (3 new: scope/RACI/atelier) with PPTX committed +
release-attached. 10 decisions locked (D-133..D-142).

---ci---
project: acdl
phase: 7
milestone: v1.18
status: complete
requirements:
  covered: [REQ-214, REQ-215, REQ-216, REQ-217, REQ-218, REQ-219, REQ-220, REQ-221, REQ-222, REQ-223, REQ-224, REQ-225, REQ-226, REQ-227, REQ-228]
  partial: []
---/ci---
2026-08-06 15:16:54 +00:00
Jon Chery d882cf0c6e Merge phase/06-deck-slides-atelier — v1.17.6 (v1.18 P6 deck slides + atelier complete) 2026-08-06 15:15:22 +00:00
Jon Chery 564d4a4ca3 docs(P6): atelier deck slide + 21-slide re-render + README (REQ-226, REQ-227, REQ-228)
REQ-226: Slide 19 'Production-Grade Guidance via Atelier' added → 21 total
slides (16 existing + 17 Scope + 18 RACI + 19 Atelier + 2 appendix). Arc
preview updated (v1.18). Talking points synced (slide 19). S&P theme
preserved (177 color refs in HTML). PPTX 22 slides (21 content + title).

REQ-227: README deck table updated — single unified deck, 21 slides, PPTX
committed + release-attached (D-141). Old two-deck table replaced.

REQ-228: HTML + PPTX re-rendered via scripts/render_deck.sh. PPTX committed
(binary, no LFS).

---ci---
project: acdl
phase: 6
milestone: v1.18
status: execute
requirements:
  covered: [REQ-226, REQ-227, REQ-228]
  partial: []
---/ci---
2026-08-06 15:15:17 +00:00
Jon Chery c524ad731e Merge phase/05-atelier-mcp — v1.17.5 (v1.18 P5 Atelier MCP server complete) 2026-08-06 15:13:44 +00:00
Jon Chery 8bcf7296d5 feat(P5): Atelier MCP server + vendored Atelier + plugin-registry (REQ-223, REQ-224, REQ-225)
REQ-223: mcp/atelier/server.py plugin-registry MCP server (stdio, D-135).
NovaAtelierServer wraps MCPServer (SDK v2, D-137) if installed; degrades
to _ToolRegistry fallback if SDK absent (testable in CI without SDK).
plugins/principles.py (lookup_principle, list_domains, matrix_lookup) +
plugins/validation.py (validate_against_principles — agentic validation
beyond Wiz/Checkmarx/Mend). 4 tools, 2 plugins.

REQ-224: mcp/atelier/vendor/ pinned Atelier v0.3.6 (D-136) — core/
first-principles, domains/security/first-principles, review/agent-checklist,
matrix/principles-matrix. vendor/VERSION.md + scripts/update_atelier_vendor.sh
for intentional upgrades. mcp/atelier/README.md (tools, architecture,
running, vendoring, extensibility, transport).

REQ-225: tests/test_atelier_mcp.py — 16 tests, all pass. Covers: plugin
discovery (both loaded), 4 tools registered, lookup_security_P4 (+P1,
unknown domain/principle), list_domains (19, security-relevant, ui-ux-not),
matrix_lookup (security 10 P-rules, unknown), validation (good-passes,
bad-secret-fails, bad-swallowed-error-fails, bad-obfuscated-names-fails,
result-structure).

---ci---
project: acdl
phase: 5
milestone: v1.18
status: execute
requirements:
  covered: [REQ-223, REQ-224, REQ-225]
  partial: []
---/ci---
2026-08-06 15:13:40 +00:00
Jon Chery 81c7a22ddd Merge phase/04-atelier-skills — v1.17.4 (v1.18 P4 Atelier skills complete) 2026-08-06 15:11:15 +00:00
Jon Chery 2c08c778a9 docs(P4): Atelier skills mapping — 9 skill files + index + BA.A extension (REQ-221, REQ-222)
REQ-221: skills/ directory with 9 Atelier-derived skill files mapped to the
BA.A citizen-developer catalog: api, security, data, testing, observability,
errors, devops, infrastructure-as-code, compliance. Each names the Atelier
source path, distills first-principles to the citizen-dev-relevant subset,
links to agent-checklist triggers, maps to BA.A 5-skill catalog.

REQ-222: docs/skills.md index (9-skill table, Atelier provenance, 8 core
principles C1-C8, consumption instructions, reference-only domains, excluded
domains). PROJECT.md BA.A decision extended with the Atelier-derived skill
catalog reference.

---ci---
project: acdl
phase: 4
milestone: v1.18
status: execute
requirements:
  covered: [REQ-221, REQ-222]
  partial: []
---/ci---
2026-08-06 15:11:12 +00:00
Jon Chery 6ffcbe8283 Merge phase/03-submission-readiness — v1.17.3 (v1.18 P3 submission-readiness complete) 2026-08-06 15:09:32 +00:00
Jon Chery 5775a97388 feat(P3): submission-readiness input contract — schema + validator + docs + tests (REQ-217..220)
REQ-217: schemas/submission-readiness.schema.json (JSON Schema draft 2020-12)
defines acceptable-to-start as a superset gate above contract.schema.json:
contractId, environment, tags (5 Nova tags D-054), policyPreconditions,
profile (developer|agentic), appSource (repo+ref), per-env mandatory (W3.E:
qa→e2eSuite+loadTest, prod→runbook+dashboard+oncall, dr→drDrillRef),
agentic markers (naturalLanguageIntent+confidenceAtSubmission+agentTrace).

REQ-218: core/submission_readiness.py validator with check_readiness() +
ReadinessResult (structured pass/fail + reason codes). Wired as
contract_ingestor.py --check-readiness (D-133). Reason codes: MISSING_TAGS,
ENV_MISSING_MANDATORY, AGENTIC_MISSING_INTENT, MISSING_APP_SOURCE,
POLICY_PRECONDITION_MISSING. Never raises — all failures are reason codes.

REQ-219: docs/submission-readiness.md (good + rejected examples +
reason-code catalog + compliance-standard equivalence).

REQ-220: tests/test_submission_readiness.py — 16 tests, all pass.
Covers: good-pass, good-agentic-pass, missing-tags, empty-tag,
qa-missing-e2e, prod-missing-runbook, dr-missing-drdrill, prod-all-pass,
agentic-missing-all, agentic-missing-one, missing-appsource,
appsource-missing-ref, empty-policy, result-structure.

---ci---
project: acdl
phase: 3
milestone: v1.18
status: execute
requirements:
  covered: [REQ-217, REQ-218, REQ-219, REQ-220]
  partial: []
---/ci---
2026-08-06 15:09:29 +00:00
Jon Chery b3c75ccec1 Merge phase/02-pdlc-scope-raci — v1.17.2 (v1.18 P2 PDLC scope + RACI complete) 2026-08-06 15:07:14 +00:00
Jon Chery e891496163 docs(P2): PDLC-upstream scope + RACI matrix + 2 deck slides (REQ-215, REQ-216, REQ-228)
REQ-215: RACI matrix in PROJECT.md (§ RACI Matrix) + docs/raci.md
(citizen-dev-facing copy). 3 roles (Citizen Developer / Platform / Release
Management co-owned). 7 work categories × R/A/C/I. Compliance-standard
equivalence note: any upstream source (AI agent, SDLC, dev platform) is
subject to the same gate.

REQ-216: PDLC-upstream scope in PROJECT.md (§ Scope) + docs/scope.md.
Promotes Core Tenet #2 + Anti-Goal #1 from buried tenets to a dedicated,
unmissable scope statement.

REQ-228: 2 new deck slides (17 Scope + 18 RACI) → 20 slides. Arc preview
updated. Talking points synced. HTML + PPTX re-rendered (21 PPTX slides).

---ci---
project: acdl
phase: 2
milestone: v1.18
status: execute
requirements:
  covered: [REQ-215, REQ-216, REQ-228]
  partial: []
---/ci---
2026-08-06 15:07:10 +00:00
Jon Chery 382944c055 Merge phase/01-sp-theme-restoration — v1.17.1 (v1.18 P1 S&P theme restoration + PPTX automation complete) 2026-08-06 15:05:09 +00:00
Jon Chery 71b6a4fa91 feat(P1): restore S&P Global Energy theme + PPTX automation (REQ-214, REQ-228)
REQ-214: Restore the S&P Global Energy Marp style: block (from commit
ae0cb58 / v1.9.2 P45) to the unified deck. Colors: H1/H2 #D6002A (red-core),
title-slide bg #1B1B1B (grey-90) + 8px #D6002A top accent, body #1B1B1B,
blockquote border #D6002A, table headers #F0F0F0, font 'Akkurat Pro' with
web-safe fallbacks. Nova header/footer text preserved (rebrand not touched).
HTML re-rendered (229 S&P color refs confirmed).

REQ-228: scripts/render_deck.sh (HTML + PPTX render + git add) +
scripts/attach_release_asset.py (Gitea release asset upload via API). PPTX
is now a first-class committed binary (D-141, no LFS). README updated:
'PPTX not committed' → 'PPTX committed + attached'. PPTX committed (3.6 MiB,
19 slides).

---ci---
project: acdl
phase: 1
milestone: v1.18
status: execute
requirements:
  covered: [REQ-214, REQ-228]
  partial: []
---/ci---
2026-08-06 15:05:01 +00:00
Jon Chery 0f677641ee Merge phase/00-pre-execution — v1.17.0 (v1.18 P0 pre-execution complete: specify+clarify+research+plan+grill) 2026-08-06 15:03:16 +00:00
Jon Chery e3ebbc4978 docs(P00): checkpoint — plan complete 2026-08-06 15:03:13 +00:00
Jon Chery 37b6b6fc14 docs(P00): grill — v1.18 plan PASS (full autonomy, user-directed + research-grounded)
Self-grill at full autonomy. Plan is user-directed (5 explicit inputs),
research-confirmed (9 assumptions A1-A9, conf 0.80-0.95), decisions locked
(D-133..D-142). No binding changes. 4 challenges reviewed:

G-201 (MCP scope-creep?) — NO. User explicitly requested MCP + extensible.
G-202 (submission-readiness duplicates contract.schema.json?) — NO. Research
    confirms superset gate (shape vs readiness). D-133 locks the wiring.
G-203 (21 slides too many?) — NO. 3 new slides are leadership-relevant;
    5-act arc preserved (D-134). Fallback if grilled: merge RACI+atelier → 20.
G-204 (Atelier vendoring reproducibility?) — YES, required. D-136 locks
    vendoring for audit replayability.

Verdict: PASS-with-binding (0 BIND, 0 ESCALATE).

---ci---
project: acdl
phase: 0
milestone: v1.18
status: grill
---/ci---
2026-08-06 15:03:04 +00:00
Jon Chery d61a3d1a2f docs(P00): create phase plans — v1.18 8 phases, 6 waves, 15 requirements
Vertical-slice plan for v1.18 Citizen Developer & Production-Grade Guidance.
Wave order: W1=P1, W2=P2, W3=P3+P4 (parallelizable), W4=P5, W5=P6, W6=P7.
Sequential execution this run. 8 plan-level risks documented (conf 0.80-0.92).

---ci---
project: acdl
phase: 0
milestone: v1.18
status: plan
---/ci---
2026-08-06 15:02:53 +00:00
Jon Chery 4c8b2b77fc docs(P00): research findings — v1.18 Atelier integration + MCP SDK + submission-readiness + Marp PPTX
5 research targets completed:
- Atelier: 19 domains → 9 Nova skills (REQ-221); agent-checklist → MCP validation; principle-lookup model; pin tag v0.3.6
- MCP Python SDK v2: MCPServer + @mcp.tool() + plugin-registry skeleton (D-140)
- Submission-readiness: superset gate confirmed (contract.schema.json defines shape only; readiness adds tags/env/policy/profile/appSource)
- Marp PPTX: inline style: CSS survives --pptx export (no fallback needed)
- Personas: 3 active (lead/backend/data) + frontend deactivated; mcp-engineer folded into backend (D-143, 0.90)

9 assumptions logged (A1-A9, conf 0.80-0.95).

---ci---
project: acdl
phase: 0
milestone: v1.18
status: research
---/ci---
2026-08-06 14:59:18 +00:00
Jon Chery 1daae0ac0a docs(P00): clarify — v1.18 decisions D-133..D-142 locked
10 decisions resolved at full autonomy:
- D-133: validator extends contract_ingestor.py --check-readiness
- D-134: deck 18→21 slides (no act restructure)
- D-135: MCP stdio now; HTTP-ready (same server object)
- D-136: vendor Atelier (pinned tag, audit reproducibility)
- D-137: MCP Python SDK v2
- D-138: skill format = markdown under skills/
- D-139: RACI roles = Citizen Dev / Platform / Release Mgmt (co-owned)
- D-140: MCP plugin-registry (plugins/<name>.py register(mcp))
- D-141: PPTX committed binary (no LFS)
- D-142: deck render trigger on any marp/assets change

---ci---
project: acdl
phase: 0
milestone: v1.18
status: clarify
---/ci---
2026-08-06 14:54:42 +00:00
Jon Chery d048460abf docs(init): validate specification — v1.18 Citizen Developer & Production-Grade Guidance
Establish v1.18 active milestone (was v1.17 complete). Author 15 new
requirements (REQ-214..228) across 5 user-directed inputs: S&P Global
theme restoration, PDLC-upstream scope, RACI matrix, Nova input contract
(submission-readiness schema + validator), Atelier integration (skills +
MCP server). Add v1.18 objective to PROJECT.md + ROADMAP.md. Feature
milestone; tags run on v1.17.x patch line.

---ci---
project: acdl
phase: 0
milestone: v1.18
status: specify
---/ci---
2026-08-06 14:54:22 +00:00
Jon Chery 0ad6a88c4b docs(P5): render unified deck to HTML (Step 3 of 4-step deck process)
acdl-ci / Lint (push) Successful in 8s
acdl-ci / Test (push) Failing after 23s
acdl-ci / Platform check-only (offline) (push) Successful in 22s
---ci---
project: acdl
phase: 5
milestone: v1.17
status: complete
---/ci---
2026-08-05 01:58:59 +00:00
Jon Chery eb5b24b88d Merge milestone/v1.17-direction-metrics-story — v1.17 complete (Strategic Direction, Leadership Metrics & Unified Story: 3 pillars, 29 requirements, 7 phases + final; tag v1.16.7)
acdl-ci / Lint (push) Successful in 14s
acdl-ci / Test (push) Failing after 29s
acdl-ci / Platform check-only (offline) (push) Successful in 26s
2026-08-04 20:09:15 +00:00
Jon Chery cb1a7071a7 Merge phase/07-final-review-ship — v1.16.7 (v1.17 P7 final review + audit + milestone complete) 2026-08-04 20:09:15 +00:00
Jon Chery e4adb3f09e docs(milestone): complete v1.17 — Strategic Direction, Leadership Metrics & Unified Story
---ci---
project: acdl
phase: 7
milestone: v1.17
status: complete
requirements:
  covered: [REQ-185..REQ-213]
  partial: []
---/ci---
2026-08-04 20:09:05 +00:00
150 changed files with 7854 additions and 2707 deletions
+5 -7
View File
@@ -1,13 +1,11 @@
{ {
"phase": 0, "phase": 0,
"stage": "complete", "stage": "plan",
"milestone": "v1.17", "milestone": "v1.21",
"phase_role": "pre_execution", "phase_role": "pre_execution",
"attempts": 0, "attempts": 0,
"updated_at": "2026-08-04T21:30:00Z", "updated_at": "2026-08-11T00:01:00Z",
"milestone_complete": false, "milestone_complete": false,
"tag": "v1.16.0", "requirements": ["REQ-245","REQ-246","REQ-247","REQ-248","REQ-249","REQ-250","REQ-251","REQ-252","REQ-253"],
"release_id": 441, "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."
"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."
} }
+102 -351
View File
@@ -1,33 +1,31 @@
--- ---
project: acdl project: acdl
milestone: v1.17 milestone: v1.18
generated_at: 2026-08-04 generated_at: 2026-08-06
generator: lead-developer generator: lead-developer
verification_toolchain: verification_toolchain:
typecheck: "terraform validate && python3 -m py_compile core/**/*.py && python3 -m jsonschema schemas/*.schema.json" typecheck: "python3 -m py_compile core/submission_readiness.py mcp/atelier/server.py && python3 -m jsonschema schemas/submission-readiness.schema.json"
test: "bash scripts/run_regression.sh # 22-capability gate (D-091/D-118) + CAP-023/024 (v1.17)" test: "pytest tests/test_submission_readiness.py tests/test_atelier_mcp.py # REQ-220 + REQ-225"
build: "bash scripts/run_ci.sh # full local CI reproduction (lint+test+check-only)" build: "bash scripts/render_deck.sh docs/presentations/nova-no-humans-platform-marp.md # HTML + PPTX (D-142)"
note: | note: |
v1.17 adds a telemetry/observability layer (metrics emitters, SQLite v1.18 adds the Citizen Developer & Production-Grade Guidance surface:
cold store, PowerBI export, Decision Ledger) + a unified narrative submission-readiness gate, Atelier-derived skills, the Atelier MCP server
deck + a durable NORTH_STAR.md. Three active personas: lead-developer (plugin-registry, stdio), and PPTX-as-first-class-artifact deck automation.
(coordination + deck narrative co-author), backend-engineer (event Three active personas: lead-developer (coordination + decks + RACI/scope
emitters, outbox_writer extension, Infracost adapter), data-engineer docs), backend-engineer (MCP server + submission-readiness validator +
(SQLite store, schemas, PowerBI views, metrics collector). frontend- render/attach scripts), data-engineer (submission-readiness schema if it
engineer stays deactivated (no Nova web UI — dashboards are PowerBI, touches contract storage / DynamoDB shape). frontend-engineer stays
not a Nova-built frontend; decks are markdown = lead-developer deactivated (v1.18 has no frontend; decks are markdown = lead-developer
territory). No new custom personas needed — the metrics domain maps territory). The MCP plugin-registry is a backend pattern, so a separate
cleanly to data-engineer (schema/store/export) + backend-engineer mcp-engineer persona is NOT added — it folds into backend-engineer.
(emitters/instrumentation).
--- ---
# 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 > v1.18 roster. Three active personas + one deactivated. The MCP server
> structural corrections: (1) stateless adapter (D-098), (2) terraform > plugin-registry (D-140) is a backend pattern, not a new persona — it
> owns lifecycle (D-101), (3) pipeline-driven testing (D-102). The roster > folds into backend-engineer. v1.17 precedent (frontend-engineer
> is simplified to the three active domains: data (terraform foundation), > deactivated, decks are markdown = lead-developer territory) is upheld.
> backend (adapter/resolver), general (pipelines/workflows).
## Active personas ## Active personas
@@ -35,351 +33,104 @@ verification_toolchain:
- **Domain:** coordination - **Domain:** coordination
- **Active:** true - **Active:** true
- **Phase-specific:** false - **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 P0P7).
### backend-engineer ### backend-engineer
- **Domain:** backend - **Domain:** backend
- **Active:** true - **Active:** true
- **Phase-specific:** false - **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). - **Frameworks:** ["mcp (Python SDK v2)", "pydantic", "jsonschema", "urllib"]
- **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). - **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 ### data-engineer
- **Domain:** data - **Domain:** data
- **Active:** true - **Active:** true
- **Phase-specific:** false - **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). - **Frameworks:** ["jsonschema", "dynamodb (item shape)"]
- **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). - **Constraints:** ["schema-first", "superset-gate NOT duplicate (PROJECT.md hard constraint)", "W3.E per-env mandatory table is the source of truth"]
- **Territory:**
### general (lead-developer + backend-engineer pipeline work) - `schemas/**` (REQ-217 — `submission-readiness.schema.json` is the new schema; existing schemas untouched)
- **Domain:** coordination + pipelines - `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)
- **Active:** true - **Reason:** Owns the submission-readiness JSON Schema (REQ-217) — it
- **Phase-specific:** false is a schema artifact, data-engineer territory. The schema is a
- **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). *superset gate above* `contract.schema.json`, not a duplicate (it
- **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). references contract fields, does not redefine them). The
per-env-mandatory table comes from W3.E (the locked decision). The
## Deactivated personas ingestor wiring is co-owned with backend-engineer (the dispatch point
is backend; the schema it validates against is data).
### lambda-engineer (custom, v1.9 — deactivated for v1.11) - **Phase-specific flag:** none (active for P3 schema + ingestor wiring).
- **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 (P18P20) is backend-engineer (Lambda action +
onboarding.py) + data-engineer (cross-account Terraform) territory.
**frontend-engineer** remains deactivated. No **security-engineer**
persona — the ingestor defense-in-depth (P10) is backend-engineer with
lead-developer review; IAM/ABAC (P20) is data-engineer territory.
### v1.16 territory assignments
| Phase | Lead | Contributors | Territory |
|-------|------|---------------|-----------|
| P1 state-bucket+kyverno fix | backend-engineer | data-engineer (kyverno policy) | `adapters/terraform/adapter.py:117`, `adapters/kyverno/policies/require-resource-labels.yml` |
| P2 user-facing brand sweep | lead-developer | backend-engineer | `core/environment_check.py`, `core/lambda/contract_ingestor.py`, `scripts/post_stage_comment.sh`, `scripts/run_ci.sh`, module docstrings, `adapters/README.md` |
| P3 dead-code+stale-prefix | lead-developer | — | `scripts/run_platform.sh`, `core/local_emulators.py`, `core/regression_verify.py`, lifecycle scripts |
| P4 migrate-ssm except | backend-engineer | — | `scripts/migrate_ssm_paths.py` |
| P5 regression-verify dedup | backend-engineer | — | `core/regression_verify.py` |
| P6 run-platform deadcode+hitl-fn | lead-developer | — | `scripts/run_platform.sh` |
| P7 contract-resolver envloader+kind | backend-engineer | — | `core/contract_resolver.py`, `modules/registry.json` |
| P8 workflow generator | lead-developer | backend-engineer (test) | `scripts/sync_workflows.py` (NEW), `tests/test_pipeline_contract.py`, `.gitea/workflows/**`, `.github/workflows/**` |
| P9 run-platform split | lead-developer | — | `scripts/run_platform.sh`, `scripts/run_decommission.sh` (NEW), `scripts/run_uptime.sh` (NEW) |
| P10 ingestor defense-in-depth | backend-engineer | lead-developer (review) | `core/lambda/contract_ingestor.py`, `core/environments/` |
| P11 ingestor payload validation | backend-engineer | — | `core/lambda/contract_ingestor.py` |
| P12 split contract-resolver | backend-engineer | — | `core/contract_resolver.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 (P1P3), a metrics catalog
> + NORTH_STAR integration (P4), a unified narrative deck (P5), a
> regression capability (P6), and a final review/ship (P7). Three
> active personas; frontend-engineer stays deactivated (no Nova web UI
> — dashboards are PowerBI, not a Nova-built frontend).
## Active personas
### lead-developer
- **Domain:** coordination + deck narrative
- **Active:** true
- **Phase-specific:** false
- **Reason:** Owns CIAgent metadata, the NORTH_STAR.md authoring
process (P0), the milestone decomposition, the unified narrative deck
co-authoring (P5 — the deck is markdown, which is lead-developer
territory per the established convention), and the final review/ship
(P7). Arbitrates persona conflicts (e.g., backend vs data on the
emitter/store boundary).
- **Territory:** `.ciagent/NORTH_STAR.md`, `.ciagent/PROJECT.md`,
`.ciagent/REQUIREMENTS.md`, `.ciagent/PLAN.md`, `.ciagent/RESEARCH.md`,
`.ciagent/ARCHITECTURE.md`, `docs/presentations/nova-no-humans-platform.md`
(NEW — unified deck source of truth), `docs/presentations/nova-no-humans-platform-marp.md`,
`docs/presentations/nova-no-humans-platform-talking-points.md`,
`docs/METRICS.md`, `docs/metrics/*.md` (per-KPI definition docs).
### backend-engineer
- **Domain:** backend (event emitters + instrumentation)
- **Active:** true
- **Phase-specific:** false
- **Reason:** Owns the event emitters (P1): the CloudEvents envelope,
the per-run manifest writer, the `outbox_writer.py` extension to the
SQLite Decision Ledger, the Infracost post-processor, the
`hitl_gates.py` attestation event emission, the `confidence_signal.py`
decision event emission, the `checkov_adapter.py` policy event
emission, and the pytest `--junitxml` addopts change. Also owns the
`regression_verify.py` CAP-023/024 additions (P6). The emitter work
is the bridge between existing Nova components and the new metrics
layer — it touches the code paths that already exist.
- **Territory:** `core/metrics/event_envelope.py` (NEW),
`core/metrics/run_manifest.py` (NEW),
`core/metrics/infracost_adapter.py` (NEW),
`core/metrics/decision_ledger.py` (NEW — extends outbox_writer),
`core/outbox_writer.py` (extend to SQLite),
`core/hitl_gates.py` (emit attestation.recorded),
`core/confidence_signal.py` (emit ai.decision.made),
`adapters/terraform/policy/checkov_adapter.py` (emit policy.evaluated),
`scripts/run_platform.sh` (invoke manifest writer + Infracost),
`core/regression_verify.py` (CAP-023/024),
`pyproject.toml` (addopts --junitxml),
`tests/test_metrics_emitters.py` (NEW),
`tests/test_decision_ledger.py` (NEW).
### data-engineer
- **Domain:** data (schema, SQLite store, PowerBI export)
- **Active:** true
- **Phase-specific:** false
- **Reason:** Reactivated with a new territory for v1.17: the metrics
collector (P2) and the PowerBI export (P3). Owns the schema design
(metrics_*.schema.json), the SQLite cold store (nova_metrics.db), the
fact/dimension table design, the 8 deferred placeholder views, and
the CSV/JSON export. The data-engineer's schema-first constraint
applies: all event types and fact/dim tables have JSON Schema
definitions before any code is written. The collector reads files +
events → SQLite; the export reads SQLite → CSV/JSON. This is the
heaviest data-territory work since v1.11's terraform modules.
- **Territory:** `core/metrics/collector.py` (NEW),
`core/metrics/powerbi_export.py` (NEW),
`schemas/metrics_*.schema.json` (NEW — event + fact/dim schemas),
`metrics/nova_metrics.db` (NEW — SQLite cold store),
`metrics/powerbi/` (NEW — CSV/JSON export dir),
`docs/METRICS_VIEWS.md` (NEW — schema doc for PowerBI views),
`tests/test_metrics_collector.py` (NEW),
`tests/test_powerbi_export.py` (NEW).
## Deactivated personas ## Deactivated personas
### frontend-engineer ### frontend-engineer
- **Domain:** frontend
- **Active:** false - **Active:** false
- **Phase-specific:** false - **Domain:** frontend
- **Reason:** v1.17 has no Nova web UI. The leadership dashboards are - **Frameworks:** ["react", "next.js"] (inert — no territory)
PowerBI (an external tool that ingests CSV/JSON files), not a - **Constraints:** ["component-first", "server-components", "minimal-client-js"] (inert)
Nova-built frontend. The decks are markdown (lead-developer - **Territory:** [] (no territory in v1.18)
territory). frontend-engineer stays deactivated, consistent with - **Reason:** v1.18 has no frontend; decks are markdown (lead-developer
v1.11v1.16. Reactivates if a future milestone builds a Nova web UI. 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 ## Roster decisions
- **Active:** false (carried forward from v1.11)
- **Reason:** v1.17 does not touch the Lambda (beyond emitting events
from the existing hitl_gates/attestation_matrix), does not do IR-
shaped module authoring, and does not touch security adapters beyond
emitting policy.evaluated events. The existing components are
instrumented, not rewritten.
## v1.17 phase assignment ### 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 | ### Territory-overlap resolution (co-ownership)
|-------|----------------|------------|-----------|
| P0 pre-execution | lead-developer | — | `.ciagent/NORTH_STAR.md`, `PROJECT.md`, `REQUIREMENTS.md`, `RESEARCH.md`, `ARCHITECTURE.md`, `PERSONAS.md`, `PLAN.md` |
| P1 event-emitters | backend-engineer | data-engineer (schemas) | `core/metrics/event_envelope.py`, `run_manifest.py`, `decision_ledger.py`, `infracost_adapter.py`, `outbox_writer.py`, `hitl_gates.py`, `confidence_signal.py`, `checkov_adapter.py`, `run_platform.sh`, `pyproject.toml` |
| P2 metrics-collector | data-engineer | backend-engineer (event formats) | `core/metrics/collector.py`, `schemas/metrics_*.schema.json`, `metrics/nova_metrics.db` |
| P3 powerbi-export | data-engineer | — | `core/metrics/powerbi_export.py`, `metrics/powerbi/`, `docs/METRICS_VIEWS.md` |
| P4 metrics-catalog + north-star-integration | lead-developer | data-engineer (metric definitions) | `docs/METRICS.md`, `docs/metrics/*.md`, `PROJECT.md`, `ARCHITECTURE.md`, `config.json` |
| P5 deck-rebuild | lead-developer | — | `docs/presentations/nova-no-humans-platform*.md`, retire old decks |
| P6 regression-capability | backend-engineer | data-engineer (CAP-023 schema) | `core/regression_verify.py` (CAP-023, CAP-024) |
| P7 final-review-ship | lead-developer | all active (review) | `.ciagent/**`, review + audit + ship |
## v1.17 domain priority | Path | Primary | Co-owner | Why |
|------|---------|----------|-----|
`backend → data → lead` (the emitter work in P1 is the foundation; | `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). |
data-engineer's collector + export in P2P3 depends on P1's event | `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. |
formats; lead-developer's catalog + deck in P4P5 depends on the | `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. |
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).
+1018 -1098
View File
File diff suppressed because it is too large Load Diff
+292 -7
View File
@@ -58,6 +58,103 @@ traceable to a human attestation and an immutable evidence stream.
boundary. The platform validates, enriches with operational standards, boundary. The platform validates, enriches with operational standards,
and reconciles the target state. 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) ## Capability Status (Re-Verified 2026-07-27)
> Source of truth: `.ciagent/CAPABILITY_INVENTORY.md` (Phase 54, D-093). > 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. 10. Old two-surfaces diagram replaced by scope boundary diagram.
Source markdown, talking points, and README all updated to mirror the new Source markdown, talking points, and README all updated to mirror the new
structure. Also includes scripts/sync_to_gl.sh (GitLab mirror sync structure. Also includes scripts/sync_to_nova.sh (manual-only "2nd release"
utility, unrelated to presentations). 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` No code changes; 494 tests pass; `run_ci.sh` + `run_platform.sh --check-only`
green. PPTX files uploaded to Gitea release. 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` (P1P6) → `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 ## Requirements
### v1.0 (Prior milestone — the demo) ### 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.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`. | | 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. | | 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.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. | | 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. | | 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 - **Pillar A — Strategic Direction.** A durable, PO-authored
`.ciagent/NORTH_STAR.md` encodes the platform's vision, 4 strategic `.ciagent/NORTH_STAR.md` encodes the platform's vision, 4 strategic
objectives, 5 anti-goals, v1.17 non-goals, 1218mo targets (with a objectives, anti-goals, v1.17 non-goals, 1218mo targets (with a
grounding column), and success criteria. CIAgent reads it in every grounding column), and success criteria. CIAgent reads it in every
future `/ci-run` so the direction survives across milestones. The future `/ci-run` so the direction survives across milestones. The
attestation clarification is reflected: human attestation required at attestation clarification is reflected: human attestation required at
stage gates (QA for production, SRE for operational readiness); autonomy 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 - **Pillar B — Leadership Metrics + PowerBI.** Instrument Nova to
collect, aggregate, and surface leadership-grade metrics that prove the 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 minimal tech (CloudEvents 1.0 envelope, JSONL event log, SQLite cold
store, hash-chained Decision Ledger via `outbox_writer.py` extension) store, hash-chained Decision Ledger via `outbox_writer.py` extension)
+ Infracost for pre-apply cost estimates. Hybrid model: existing + 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-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-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-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
View File
@@ -1132,35 +1132,35 @@ with documented schemas.
| Requirement | Phase | Status | | Requirement | Phase | Status |
|-------------|-------|--------| |-------------|-------|--------|
| REQ-185 | P0 | in_progress | | REQ-185 | P0 | complete |
| REQ-186 | P4 | pending | | REQ-186 | P4 | complete |
| REQ-187 | P1 | pending | | REQ-187 | P1 | complete |
| REQ-188 | P1 | pending | | REQ-188 | P1 | complete |
| REQ-189 | P2 | pending | | REQ-189 | P2 | complete |
| REQ-190 | P3 | pending | | REQ-190 | P3 | complete |
| REQ-191 | P4 | pending | | REQ-191 | P4 | complete |
| REQ-192 | P4 | pending | | REQ-192 | P4 | complete |
| REQ-193 | P4 | pending | | REQ-193 | P4 | complete |
| REQ-194 | P4 | pending | | REQ-194 | P4 | complete |
| REQ-195 | P4 | pending | | REQ-195 | P4 | complete |
| REQ-196 | P5 | pending | | REQ-196 | P5 | complete |
| REQ-197 | P5 | pending | | REQ-197 | P5 | complete |
| REQ-198 | P6 | pending | | REQ-198 | P6 | complete |
| REQ-199 | P3 | pending | | REQ-199 | P3 | complete |
| REQ-200 | P2 | pending | | REQ-200 | P2 | complete |
| REQ-201 | P2 | pending | | REQ-201 | P2 | complete |
| REQ-202 | P5 | pending | | REQ-202 | P5 | complete |
| REQ-203 | P5 | pending | | REQ-203 | P5 | complete |
| REQ-204 | P4 | pending | | REQ-204 | P4 | complete |
| REQ-205 | P1+P2+P3 | pending | | REQ-205 | P1+P2+P3 | complete |
| REQ-206 | P1+P2 | pending | | REQ-206 | P1+P2 | complete |
| REQ-207 | P2 | pending | | REQ-207 | P2 | complete |
| REQ-208 | P3 | pending | | REQ-208 | P3 | complete |
| REQ-209 | P3/P4 | pending | | REQ-209 | P3/P4 | complete |
| REQ-210 | P4 | pending | | REQ-210 | P4 | complete |
| REQ-211 | P4 | pending | | REQ-211 | P4 | complete |
| REQ-212 | P4 | pending | | REQ-212 | P4 | complete |
| REQ-213 | P4/P5 | pending | | REQ-213 | P4/P5 | complete |
### Out of Scope (v1.17) ### Out of Scope (v1.17)
@@ -1180,3 +1180,539 @@ with documented schemas.
- A third deck — the two existing decks merge into one; no new - A third deck — the two existing decks merge into one; no new
standalone metrics deck. standalone metrics deck.
- A Nova web UI — dashboards are PowerBI, not a Nova-built frontend. - 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` (P1P6) →
> `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 |
+853
View File
@@ -1493,3 +1493,856 @@ Total: ~1216 slides. Opening = arc preview; closing = recap + ask.
cost estimate. No live AWS access required. If Infracost is not cost estimate. No live AWS access required. If Infracost is not
available, the `cost.estimated` event is omitted (degraded mode, not available, the `cost.estimated` event is omitted (degraded mode, not
a failure). 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 (C1C8)
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; C4C8 tradeable among themselves but always below
C1C3).
| 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 (C1C8)** 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 C1C8 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}$` (36 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 635643, 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 674676): "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 01), `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 93105)
+ the v1.9.2 theme commit `ae0cb58` (verified via `git show`).
**Confirmed export command (from `docs/presentations/README.md` line
9699):**
```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 332340).
#### 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 682683): 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.
+209
View File
@@ -1683,3 +1683,212 @@ deferred (D-113/D-114).
Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS
the release). **DONE.** 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` (P1P6) → `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.
+1 -1
View File
@@ -8,7 +8,7 @@
], ],
"active_project": "acdl", "active_project": "acdl",
"active_projects": ["acdl"], "active_projects": ["acdl"],
"active_milestone": "v1.17", "active_milestone": "v1.21",
"autonomy": { "autonomy": {
"level": "full", "level": "full",
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"], "escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
+1 -1
View File
@@ -1,4 +1,4 @@
# ACDL CI Pipeline — Gitea Actions (dev environment) # Nova CI Pipeline (dev environment)
# #
# This workflow implements the central pipeline contract: # This workflow implements the central pipeline contract:
# pipelines/ci.yml (validated against schemas/pipeline.schema.json) # pipelines/ci.yml (validated against schemas/pipeline.schema.json)
+5 -5
View File
@@ -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: # This reusable workflow implements the central deployment pipeline contract:
# pipelines/contract.yml (validated against schemas/deploy-pipeline.schema.json) # 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. # declared difference is the forge/runtime, not the stages or commands.
# #
# Consumer repos invoke this workflow via a versioned tag (floating MAJOR + MINOR): # 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) # uses: acdl/.github/workflows/deploy.yml@v1.9 (GitHub)
# #
# Unversioned references (@main, bare) are discouraged — the consumer's setup # 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 # that matches repo:org/consumer-repo:ref:refs/heads/main, and the session
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>. # policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
# #
# Override (where OIDC is unavailable, e.g. Gitea pending # Override (where OIDC is unavailable, e.g. pending
# go-gitea/gitea#36988): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY # upstream forge OIDC support): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
# as repository secrets. The platform-managed scheduled pipeline rotates # as repository secrets. The platform-managed scheduled pipeline rotates
# the key on a daily cadence. When .env.secrets is used locally instead, # the key on a daily cadence. When .env.secrets is used locally instead,
# rotating the key out of band is the consumer's responsibility. # rotating the key out of band is the consumer's responsibility.
@@ -155,7 +155,7 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: nova-terraform name: nova-terraform
path: /tmp/acdl_platform_run_v18/tf/*.tf path: /tmp/nova_platform_run/tf/*.tf
if-no-files-found: warn if-no-files-found: warn
- name: Upload platform log - name: Upload platform log
+2 -2
View File
@@ -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 # Matrix-runs each L1 module's examples/{simple,complex}.yml contracts through
# apply→modify→destroy against live AWS. No per-module Python. The "test" = # apply→modify→destroy against live AWS. No per-module Python. The "test" =
@@ -9,7 +9,7 @@
# terraform files); the composition must be deterministic. # terraform files); the composition must be deterministic.
# #
# This workflow implements pipelines/modules-lifecycle.yml (byte-identical # 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 # Lifecycle mode (REQ-134, v1.12): the `lifecycle_mode` input defaults to
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast, # "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
+31
View File
@@ -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
View File
@@ -1,35 +1,30 @@
# GitHub Workflows — Nova Platform CI/CD Catalog # GitHub Workflows — Nova Platform CI/CD Catalog
This directory contains the 7 GitHub Actions workflows for the Nova This directory contains the GitHub Actions workflows for the Nova
platform. 3 are byte-identical Gitea mirrors (generated from platform. 3 are generated from `workflows-src/<name>`; 4 are GitHub-only.
`workflows-src/` by `scripts/sync_workflows.py`, P8/REQ-172); 4 are
GitHub-only (Gitea act_runner feature gaps).
## Shared workflows (byte-identical Gitea + GitHub) ## Shared workflows (generated from source)
These 3 are generated from `workflows-src/<name>` by These 3 are generated from `workflows-src/<name>`. Run `python3 scripts/sync_workflows.py --check` to verify
`scripts/sync_workflows.py`; the `.gitea/workflows/<name>` mirror is kept
byte-identical. Run `python3 scripts/sync_workflows.py --check` to verify
no drift. no drift.
| Workflow | Trigger | Inputs | Required Secrets | Purpose | | Workflow | Trigger | Inputs | Required Secrets | Purpose |
|----------|---------|--------|------------------|---------| |----------|---------|--------|------------------|---------|
| `ci.yml` | `pull_request: [main]` | — | — | Lint + test + check-only (runs on every PR) | | `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) | | `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 These 4 have no counterpart (the dev forge lacks the features
they require — reusable workflows, matrix `needs`, release API). See they require — reusable workflows, matrix `needs`, release API).
`.gitea/workflows/README.md` for the limitation rationale.
| Workflow | Trigger | Inputs | Required Secrets | Purpose | | Workflow | Trigger | Inputs | Required Secrets | Purpose |
|----------|---------|--------|------------------|---------| |----------|---------|--------|------------------|---------|
| `platform-test.yml` | `pull_request: [main]` | — | — | Lint + unit + integration + schema-validation (replaces `ci.yml` for PRs) | | `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) | | `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) | | `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`) ## Reusable deploy workflow (`deploy.yml`)
@@ -38,7 +33,7 @@ Consumer repos invoke the deploy workflow via a versioned tag:
```yaml ```yaml
jobs: jobs:
deploy: deploy:
uses: acdl/.github/workflows/deploy.yml@v1.15 uses: nova/.github/workflows/deploy.yml@v1.19
with: with:
contract: .nova/contract.yml contract: .nova/contract.yml
environment: dev environment: dev
+1 -1
View File
@@ -1,4 +1,4 @@
# ACDL CI Pipeline — Gitea Actions (dev environment) # Nova CI Pipeline (dev environment)
# #
# This workflow implements the central pipeline contract: # This workflow implements the central pipeline contract:
# pipelines/ci.yml (validated against schemas/pipeline.schema.json) # pipelines/ci.yml (validated against schemas/pipeline.schema.json)
+5 -5
View File
@@ -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: # This reusable workflow implements the central deployment pipeline contract:
# pipelines/contract.yml (validated against schemas/deploy-pipeline.schema.json) # 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. # declared difference is the forge/runtime, not the stages or commands.
# #
# Consumer repos invoke this workflow via a versioned tag (floating MAJOR + MINOR): # 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) # uses: acdl/.github/workflows/deploy.yml@v1.9 (GitHub)
# #
# Unversioned references (@main, bare) are discouraged — the consumer's setup # 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 # that matches repo:org/consumer-repo:ref:refs/heads/main, and the session
# policy restricts view/update to resources tagged acdl:owner=<consumer-repo>. # policy restricts view/update to resources tagged acdl:owner=<consumer-repo>.
# #
# Override (where OIDC is unavailable, e.g. Gitea pending # Override (where OIDC is unavailable, e.g. pending
# go-gitea/gitea#36988): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY # upstream forge OIDC support): set NOVA_AWS_ACCESS_KEY_ID + NOVA_AWS_SECRET_ACCESS_KEY
# as repository secrets. The platform-managed scheduled pipeline rotates # as repository secrets. The platform-managed scheduled pipeline rotates
# the key on a daily cadence. When .env.secrets is used locally instead, # the key on a daily cadence. When .env.secrets is used locally instead,
# rotating the key out of band is the consumer's responsibility. # rotating the key out of band is the consumer's responsibility.
@@ -155,7 +155,7 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: nova-terraform name: nova-terraform
path: /tmp/acdl_platform_run_v18/tf/*.tf path: /tmp/nova_platform_run/tf/*.tf
if-no-files-found: warn if-no-files-found: warn
- name: Upload platform log - name: Upload platform log
+2 -2
View File
@@ -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 # Matrix-runs each L1 module's examples/{simple,complex}.yml contracts through
# apply→modify→destroy against live AWS. No per-module Python. The "test" = # apply→modify→destroy against live AWS. No per-module Python. The "test" =
@@ -9,7 +9,7 @@
# terraform files); the composition must be deterministic. # terraform files); the composition must be deterministic.
# #
# This workflow implements pipelines/modules-lifecycle.yml (byte-identical # 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 # Lifecycle mode (REQ-134, v1.12): the `lifecycle_mode` input defaults to
# "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast, # "plan" — the lifecycle scripts run `run_platform.sh --plan-only` (fast,
+31
View File
@@ -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
View File
@@ -40,4 +40,5 @@ metrics/lifecycle/
*.cer *.cer
*.crt *.crt
*.jks *.jks
*.keystore *.keystore.coverage
.coverage
+3 -23
View File
@@ -219,23 +219,9 @@ bash scripts/run_ci.sh --quiet # suppress per-stage banners
### Reusable deploy workflow ### Reusable deploy workflow
The deployment pipeline is defined by a **central deployment pipeline Consumer repos invoke the deploy pipeline via `.github/workflows/deploy.yml`
contract** (`pipelines/contract.yml`, validated against (a reusable GitHub Actions workflow, versioned tag `nova/.github/workflows/deploy.yml@v1.19`).
`schemas/deploy-pipeline.schema.json`) and exposed to consumer repos as a See the [Consumer guide](docs/consumer-guide.md) for the end-to-end happy path.
**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.
### Output streaming (run_platform.sh) ### Output streaming (run_platform.sh)
@@ -310,12 +296,6 @@ documented alternative:
runs, or in **`.env.secrets`** (gitignored, chmod 600) for local testing. runs, or in **`.env.secrets`** (gitignored, chmod 600) for local testing.
- The platform rotates platform-runner keys on a **daily cadence** - The platform rotates platform-runner keys on a **daily cadence**
rotation is not the consumer's burden in the platform-runner path. 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 No long-lived credential is permitted persistently — the platform-runner
key's useful lifetime is one workflow run, and the local alternative is key's useful lifetime is one workflow run, and the local alternative is
rotated at least daily (platform-runner) or out of band (local). rotated at least daily (platform-runner) or out of band (local).
+3 -3
View File
@@ -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 **platform-level KMS key** (not per-contract — a per-contract key would
explode the key-management surface), rotated **quarterly**. The `jws` explode the key-management surface), rotated **quarterly**. The `jws`
field is added to the event shape when this ships. 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 reads the outbox, writes to S3 Object Lock, signs with KMS. DLQ = an
SQS dead-letter queue for failed writes. RTO = DLQ replay. SQS dead-letter queue for failed writes. RTO = DLQ replay.
- **Daily checkpoints (§9):** a daily job reads the last event hash and - **Daily checkpoints (§9):** a daily job reads the last event hash and
@@ -86,7 +86,7 @@ log" anti-goal requires.
D-083 ships). D-083 ships).
- `prev_event_hash` (chain link; `GENESIS` for the first event). - `prev_event_hash` (chain link; `GENESIS` for the first event).
- `hash` (this event's SHA-256 over canonical JSON). - `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). qa-promotion by v1.9's `hitl_gates.attest` — D-042).
- `approver_prod` (SRE username; populated on prod-promotion by v1.9's - `approver_prod` (SRE username; populated on prod-promotion by v1.9's
`hitl_gates.attest`). `hitl_gates.attest`).
@@ -112,7 +112,7 @@ log" anti-goal requires.
- **D-042** — approver identities (`approver_qa`, `approver_prod`, - **D-042** — approver identities (`approver_qa`, `approver_prod`,
`approver_dr`) live in the outbox; the separation-of-duties check `approver_dr`) live in the outbox; the separation-of-duties check
(`core/separation_of_duties.py`) reads `approver_qa` and compares (`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. `hitl_gates.attest` populates these attributes.
- **D-083** (v1.9) — S3 Object Lock + JWS + async worker + DLQ + daily - **D-083** (v1.9) — S3 Object Lock + JWS + async worker + DLQ + daily
checkpoints deferred to a future milestone. Requires non-offline- checkpoints deferred to a future milestone. Requires non-offline-
+4 -4
View File
@@ -1,6 +1,6 @@
"""HITL pre-execution attestation gates (REQ-108, D-084). """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` / DynamoDB outbox for the contractId (attribute `approver_qa` /
`approver_prod` / `approver_dr`), runs the separation-of-duties check on `approver_prod` / `approver_dr`), runs the separation-of-duties check on
prod, invokes the 8-concern attestation matrix for the target env, and 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: Args:
contract_id: the contract UUID. contract_id: the contract UUID.
env: dev/qa/prod/dr. 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 evidence: optional operator-supplied evidence artifacts (for the
attestation matrix operator-supplied concerns). attestation matrix operator-supplied concerns).
outbox_client: optional moto-mocked DynamoDB outbox client for tests. 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)") return (True, "dev autonomous (no HITL gate)")
if not approver: 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) attr = _approver_attr(env)
if not attr: if not attr:
@@ -88,7 +88,7 @@ def attest(contract_id: str, env: str, approver: str,
def approver_from_env() -> Optional[str]: def approver_from_env() -> Optional[str]:
"""Read the approver identity from the environment.""" """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__": if __name__ == "__main__":
+15 -15
View File
@@ -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 a separate deployment against a separate cluster/region. The
canary/deployment-rollback model is explicitly not in scope for v1. 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 (v1.0 D-013; re-confirmed in RESEARCH TARGET 1). The pre-execution gate
is modeled as a `workflow_dispatch` with approval inputs: is modeled as a `workflow_dispatch` with approval inputs:
- **qa gate:** `workflow_dispatch` with `approve_qa: true`; the dispatch - **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`; - **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. - **dr gate:** `workflow_dispatch` with `approve_dr: true`; same.
The approver identity of record = `gitea.actor` of the dispatch run The approver identity of record = `CI actor` of the dispatch run
(D-042). There is no other approval-identity signal in Gitea. The real (D-042). There is no other approval-identity signal in the dev forge. The real
OIDC path (blocked on go-gitea/gitea#36988) does not change this — 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 OIDC authorizes the *runner* to AWS, it does not change how the platform
records the *human* approver. 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, run; GitHub Environments with required reviewers are the native gate,
but the `workflow_dispatch` approval-input fallback is used for 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) ## 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 - qa → QA team
- prod → SRE 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 | | 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` | | 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` scans the DynamoDB outbox for `PENDING_ATTESTATION` events with `ts`
older than 1/2 business days and emits the warn/freeze events. Not older than 1/2 business days and emits the warn/freeze events. Not
implemented in v1.9 (roadmap item; the attestation gates themselves are 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: not Kyverno (in v1). Sequence:
1. On promotion dev → qa, the platform reads the QA approver's identity 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 and writes it to the DynamoDB outbox keyed by `contractId` (attribute
`approver_qa`). `approver_qa`).
2. On promotion qa → prod, the platform reads the stored `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. prod-dispatch run.
3. If `approver_qa == approver_prod`, the platform blocks the prod 3. If `approver_qa == approver_prod`, the platform blocks the prod
promotion, writes a `SEPARATION_OF_DUTIES_VIOLATION` event to the 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 ## Decision trail
- **D-042** — approver identity = `gitea.actor` of the `workflow_dispatch` - **D-042** — approver identity = `CI actor` of the `workflow_dispatch`
run; no Environments API in Gitea. On GitHub, `github.actor`. run; no Environments API in the dev forge.
- **D-013** (v1.0) — the `workflow_dispatch` approval-input fallback, - **D-013** (v1.0) — the `workflow_dispatch` approval-input fallback,
re-used for the real platform's pre-execution gate model. re-used for the real platform's pre-execution gate model.
- **D-084** (v1.9) — 8-concern attestation matrix: offline-testable - **D-084** (v1.9) — 8-concern attestation matrix: offline-testable
+28 -9
View File
@@ -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") GITHUB_TOKEN_SECRET_ID = os.environ.get("GITHUB_TOKEN_SECRET_ID", "nova/github-token")
PLATFORM_REPO = os.environ.get("PLATFORM_REPO", "nova/acdl") PLATFORM_REPO = os.environ.get("PLATFORM_REPO", "nova/acdl")
# P1-9: Forge-agnostic API base URL. Defaults to GitHub; set GITHUB_API_BASE # 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") 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). # P11 (REQ-175): consistent cap for error/stackTrace fields (was 10k vs 2k).
@@ -96,22 +96,22 @@ def _iso8601_now():
def _forge_type(): 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: if "/api/v1" in GITHUB_API_BASE:
return "gitea" return "generic_forge"
return "github" return "github"
def _issues_search_url(owner, repo, encoded_query): 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). with query params (no /search/issues endpoint).
""" """
if _forge_type() == "gitea": if _forge_type() == "generic_forge":
return ( return (
f"{GITHUB_API_BASE}/repos/{owner}/{repo}/issues" f"{GITHUB_API_BASE}/repos/{owner}/{repo}/issues"
f"?state=open&type=issues&q={encoded_query}" 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): 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" 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": 401, "body": json.dumps({"error": str(e)})}
return {"statusCode": 400, "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 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 -1
View File
@@ -1,6 +1,6 @@
"""Check that qaApprover != prodApprover for a contract (ARCHITECTURE.md """Check that qaApprover != prodApprover for a contract (ARCHITECTURE.md
§10.3, D-042). Reads `approver_qa` from the DynamoDB outbox for the §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 Blocks on equality, emits `SEPARATION_OF_DUTIES_VIOLATION`, routes a halt
artifact to SRE on-call. artifact to SRE on-call.
+193
View File
@@ -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))
-2
View File
@@ -1,7 +1,5 @@
# Nova Metrics Catalog # 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 This is the canonical catalog of every executive KPI in Nova's
leadership metrics layer. Each metric carries a **status**: leadership metrics layer. Each metric carries a **status**:
-2
View File
@@ -1,7 +1,5 @@
# Nova Deferred Metrics Activation Roadmap # 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 This document lists all 8 deferred metrics + the onboarding-funnel
"granted" half, with their blocking decisions, unblock requirements, "granted" half, with their blocking decisions, unblock requirements,
-2
View File
@@ -1,7 +1,5 @@
# Nova Metrics Views — PowerBI Data Dictionary # 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 This document is the column-level data dictionary for the PowerBI export
views in `metrics/powerbi/`. Each fact/dimension table and placeholder views in `metrics/powerbi/`. Each fact/dimension table and placeholder
-270
View File
@@ -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-*`.
-177
View File
@@ -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 P2P4, 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 P2P4 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 P2P4 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 P2P4 — 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 P3P4 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 P3P4 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 P2P4.)*
2. **Before P5 — env vars:** rename `ACDL_*` CI secrets / workflow `env:`
blocks / local `.env.secrets` to `NOVA_*`. *(Incremental during P2P4;
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
View File
@@ -77,7 +77,7 @@ milestone (D-113).
only (D-114); live apply is deferred. only (D-114); live apply is deferred.
- **OIDC trust policy** — the onboarding Terraform uses a placeholder - **OIDC trust policy** — the onboarding Terraform uses a placeholder
OIDC provider; real OIDC federation is blocked on 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 ## See also
+1 -1
View File
@@ -230,7 +230,7 @@ change to the modules/stack/confidence/audit.
- A MAJOR bump requires a new registry entry (immutable publication); the - A MAJOR bump requires a new registry entry (immutable publication); the
old entry enters a 12-month deprecation window. old entry enters a 12-month deprecation window.
- The central deploy pipeline is referenced by a floating MAJOR + MINOR tag - 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. under the next MINOR tag.
See [Versioning](pipeline/versioning) for the consumer-facing details. See [Versioning](pipeline/versioning) for the consumer-facing details.
+13 -13
View File
@@ -19,7 +19,7 @@ definitions.
```mermaid ```mermaid
flowchart LR 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 -&gt; resolver -&gt; stack -&gt; adapter<br/>-&gt; security checks -&gt; infrastructure plan -&gt; policy checks<br/>-&gt; confidence -&gt; apply -&gt; evidence event| C B["platform runners<br/>(modules + pipelines + adapters + schemas)"] -->|contract -&gt; resolver -&gt; stack -&gt; adapter<br/>-&gt; security checks -&gt; infrastructure plan -&gt; policy checks<br/>-&gt; confidence -&gt; apply -&gt; evidence event| C
C["your resources in AWS"] C["your resources in AWS"]
``` ```
@@ -27,13 +27,13 @@ flowchart LR
## Versioning the `uses:` reference ## Versioning the `uses:` reference
The central deployment pipeline is **always versioned with floating MAJOR 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 constraints cannot be expressed inside the contract, so the tag in
`uses:` is the only immutability lever a consumer has. See `uses:` is the only immutability lever a consumer has. See
[Versioning](pipeline/versioning) for the full rationale. [Versioning](pipeline/versioning) for the full rationale.
**Unversioned references are discouraged.** Do not use `@main` or a bare **Unversioned references are discouraged.** Do not use `@main` or a bare
`acdl/pipelines/contract.yml`. `nova/pipelines/contract.yml`.
## Prerequisites ## Prerequisites
@@ -47,7 +47,7 @@ platform-managed. See [Environments](environments/).
environment is bound, your first pipeline run emits a friendly onboarding environment is bound, your first pipeline run emits a friendly onboarding
prompt. See [Environments](environments/). prompt. See [Environments](environments/).
- **Authorization to reference the central pipeline.** Onboarding grants - **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. Contact the platform team if you have not been onboarded.
## Step 1 — Create a consumer repo ## Step 1 — Create a consumer repo
@@ -94,7 +94,7 @@ Nova deployment workflow with a **versioned tag** (floating MAJOR + MINOR):
```yaml ```yaml
jobs: jobs:
deploy: deploy:
uses: acdl/.github/workflows/deploy.yml@v1.13 uses: nova/.github/workflows/deploy.yml@v1.19
with: with:
contract: .nova/contract.yml contract: .nova/contract.yml
environment: dev environment: dev
@@ -140,7 +140,7 @@ name: microservice
| Field | Type | Required | Description | | 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/). | | `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/). | | `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). | | `inputs` | object | yes | Module-specific inputs (see the module's README). |
@@ -177,14 +177,14 @@ on:
branches: [main] branches: [main]
jobs: jobs:
deploy: deploy:
uses: acdl/.github/workflows/deploy.yml@v1.13 uses: nova/.github/workflows/deploy.yml@v1.19
with: with:
contract: .nova/contract.yml contract: .nova/contract.yml
``` ```
That is the entire consumer-side workflow. When you push to `main`: 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**. to the reusable workflow **at the pinned tag**.
2. A **platform-provided runner** checks out **your** repo. 2. A **platform-provided runner** checks out **your** repo.
3. The runner checks out the **Nova platform repo** into the workspace — 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. | | Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. | | Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
| Module catalog | [modules/](modules/) | All primitives and modules. | | Module catalog | [modules/](modules/) | All primitives and modules. |
| Sample contract | `contracts/static-assets.yaml` | The reference 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.13`). | | 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`). | | 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. | | Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
| Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. | | Angine adapter | `adapters/terraform/adapter.py` | Compiles stack instances to infrastructure. |
@@ -353,7 +353,7 @@ destruction:
use `mode: decommission` with the `changeRequestId` input: use `mode: decommission` with the `changeRequestId` input:
```yaml ```yaml
uses: acdl/.github/workflows/deploy.yml@v1.13 uses: nova/.github/workflows/deploy.yml@v1.19
with: with:
contract: .nova/contract.yml contract: .nova/contract.yml
mode: decommission mode: decommission
@@ -421,7 +421,7 @@ name: static-assets
``` ```
**Shape 2 — single contract + `environment` workflow input:** the **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 declares an `environment` input. When non-empty, it overrides the
contract's `environment` field at load time (before interpolation), so contract's `environment` field at load time (before interpolation), so
the same contract can be promoted by passing a different environment: the same contract can be promoted by passing a different environment:
@@ -436,7 +436,7 @@ on: workflow_dispatch:
required: true required: true
jobs: jobs:
deploy-qa: deploy-qa:
uses: acdl/.github/workflows/deploy.yml@v1.13 uses: nova/.github/workflows/deploy.yml@v1.19
with: with:
environment: qa environment: qa
contract: .nova/contract.yml contract: .nova/contract.yml
-7
View File
@@ -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. - [Consumer Guide](consumer-guide) — start here if you are a consumer.
- [Architecture](architecture) — start here if you are a platform engineer. - [Architecture](architecture) — start here if you are a platform engineer.
- The [README](https://github.com/nova/nova) describes the platform repo. - 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.
+1 -1
View File
@@ -39,7 +39,7 @@ It is exposed to consumer repos as a **reusable workflow**:
- `.github/workflows/deploy.yml` — GitHub Actions (production) - `.github/workflows/deploy.yml` — GitHub Actions (production)
A consumer repo invokes the reusable workflow via a **versioned tag** 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 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 repo into the runner workspace, and runs `scripts/run_platform.sh` against
the consumer's contract. The consumer never clones the platform repo or the consumer's contract. The consumer never clones the platform repo or
+2 -2
View File
@@ -26,7 +26,7 @@ tag** in a consumer's CI workflow definition:
```yaml ```yaml
jobs: jobs:
deploy: deploy:
uses: acdl/.github/workflows/deploy.yml@v1.13 uses: nova/.github/workflows/deploy.yml@v1.19
with: with:
contract: .nova/contract.yml 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. `uses:` tag is the only immutability lever a consumer has.
**Unversioned references are discouraged.** Do not use `@main` or a bare **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: cause unexpected failures. Pinning to a MAJOR+MINOR tag means:
- **Immutability** — the pipeline behavior you tested is the behavior you - **Immutability** — the pipeline behavior you tested is the behavior you
+32 -39
View File
@@ -23,7 +23,7 @@ Step 1: full markdown Step 2: Marp deck Step 3: HTML + PPTX
### Step 1 — Full markdown (source of truth) ### 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 Write the complete deck as a standard markdown file. This is the **source of
truth** — it contains: 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 ### 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: 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** 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 (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). committed to git).
#### HTML export (committed to repo) #### 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 committed artifacts, not generated on-the-fly — they must be re-rendered and
re-committed when the Marp deck is updated. re-committed when the Marp deck is updated.
#### PPTX export (uploaded to Gitea release) #### PPTX export (uploaded to release)
```bash ```bash
CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \ 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 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 PNG diagrams are embedded in the file. As of v1.18 (REQ-228, D-141), PPTX
repo (binary, no meaningful diffs) — they are uploaded to the Gitea release files **are committed to the repo** as first-class binary artifacts (no LFS)
as downloadable attachments. 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) ### Step 4 — Talking points (presenter cues)
**File convention:** `<deck-name>-talking-points.md` (e.g. **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 Distill the source of truth (Step 1) into presenter-ready cues, indexed by
the Marp deck (Step 2) slide structure: the Marp deck (Step 2) slide structure:
@@ -142,35 +144,20 @@ and re-distill.
``` ```
docs/presentations/ docs/presentations/
├── README.md ← this file ├── README.md ← this file
├── how-the-platform-works.md ← Step 1: full source of truth ├── nova-no-humans-platform.md ← Step 1: full source of truth (19 main slides + speaker notes)
├── how-the-platform-works-marp.md ← Step 2: Marp deck (11 main + TOC + 8 appendix = 20) ├── nova-no-humans-platform-marp.md ← Step 2: Marp deck (19 main + 2 appendix = 21 slides)
├── how-the-platform-works.html ← Step 3: rendered HTML (committed) ├── nova-no-humans-platform.html ← Step 3: rendered HTML (committed, S&P-themed)
├── how-the-platform-works-talking-points.md ← Step 4: presenter cues (20 sections) ├── nova-no-humans-platform.pptx ← Step 3: rendered PPTX (committed, S&P-themed)
├── the-developer-experience.md ← Step 1: full source of truth ├── nova-no-humans-platform-talking-points.md ← Step 4: presenter cues (21 sections)
├── 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)
└── assets/ └── assets/
├── nova-sp-theme.css ← S&P Global Energy Marp theme (all slide chrome)
├── puppeteer-config.json ← no-sandbox config for mmdc ├── puppeteer-config.json ← no-sandbox config for mmdc
├── mmd/ ← mermaid source files (Step 2 input) ├── mmd/ ← mermaid source files (Step 2 input)
│ ├── sp-theme.json ← S&P Red/Black/White theme (mermaid-cli --configFile) │ ├── sp-theme.json ← S&P Red/Black/White theme (mermaid-cli --configFile)
│ ├── platform-works-01-contract-driven.mmd │ ├── platform-architecture.mmd
│ ├── platform-works-02-frictions.mmd │ ├── road-to-north-star.mmd
── platform-works-02-end-to-end-flow.mmd ── ... (per-slide .mmd files)
│ ├── platform-works-03-north-star.mmd └── png/ ← rendered mermaid PNGs (committed, S&P-themed)
│ ├── 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
└── png/ ← rendered PNGs (embedded in Marp) └── png/ ← rendered PNGs (embedded in Marp)
├── platform-works-01-contract-driven.png ├── platform-works-01-contract-driven.png
├── platform-works-02-frictions.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 HTML files are committed artifacts** — re-render and re-commit whenever
the Marp source changes. the Marp source changes.
### Export a Marp deck to PPTX (uploaded to Gitea release) ### Export a Marp deck to PPTX (uploaded to release)
```bash ```bash
CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \ 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 `--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 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 ## Adding a new presentation
@@ -322,7 +309,7 @@ attachments to the Gitea release.
no speaker notes, embedded PNGs, and maturity badges. no speaker notes, embedded PNGs, and maturity badges.
4. **Render to HTML** with `--allow-local-files` and commit the HTML to 4. **Render to HTML** with `--allow-local-files` and commit the HTML to
`docs/presentations/<deck-name>.html`. `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). release (do not commit PPTX to git).
6. **Distill the talking points** as `<deck-name>-talking-points.md` — one 6. **Distill the talking points** as `<deck-name>-talking-points.md` — one
section per Marp slide, 3-6 talking point bullets + key takeaway, content section per Marp slide, 3-6 talking point bullets + key takeaway, content
@@ -341,7 +328,13 @@ attachments to the Gitea release.
## Current decks ## 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 | | 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 |
| 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 |
> **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 marp: true
theme: default theme: nova-sp
paginate: true paginate: true
size: 16x9 size: 16x9
header: 'Nova — The No-Humans Infrastructure Platform' header: 'Nova — The No-Humans Infrastructure Platform'
footer: 'Act %{page}/5 — v1.17' footer: 'Act %{page}/5 — v1.20'
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; }
--- ---
<!-- _class: title --> <!-- _class: title -->
@@ -22,7 +14,7 @@ style: |
**Shifting from Operational Overhead to Strategic Value** **Shifting from Operational Overhead to Strategic Value**
v1.17Strategic Direction, Leadership Metrics & Unified Story v1.18Citizen Developer & Production-Grade Guidance
--- ---
@@ -37,7 +29,7 @@ v1.17 — Strategic Direction, Leadership Metrics & Unified Story
2. **Vision** — Nova's strategic direction (NORTH_STAR) 2. **Vision** — Nova's strategic direction (NORTH_STAR)
3. **How** — the pipeline, Decision Ledger, attestation gates 3. **How** — the pipeline, Decision Ledger, attestation gates
4. **Proof** — grounded metrics that make the claim defensible 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. **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 --> <!-- _class: title -->
<!-- _paginate: false --> <!-- _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 - **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) - 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 - "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 - **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 ### Appendix A1 — Metrics Glossary
- Reference for every metric mentioned in the deck - Reference for every metric mentioned in the deck
- Use if the audience asks "what does X mean?" - Use if the audience asks "what does X mean?"
@@ -110,4 +131,34 @@
### Appendix A2 — Operating Model & Cost ### Appendix A2 — Operating Model & Cost
- The operating cost is negligible (~$0.007/month) - The operating cost is negligible (~$0.007/month)
- The zero-cost steady state (D-096 teardown) is the structural mitigation - 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.0v1.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?"**: Q1Q3 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
+37 -1
View File
@@ -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. > **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.0v1.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.
+84
View File
@@ -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.
+67
View File
@@ -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.
+88
View File
@@ -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 (C1C8) 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
+150
View File
@@ -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.
+1
View File
@@ -0,0 +1 @@
# mcp/atelier — Nova Atelier MCP server package (v1.18)
+95
View File
@@ -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).
+1
View File
@@ -0,0 +1 @@
# mcp/atelier package
+1
View File
@@ -0,0 +1 @@
# mcp/atelier/plugins package
+99
View File
@@ -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) domaincore 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]}
+78
View File
@@ -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.",
}
+142
View File
@@ -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()
+21
View File
@@ -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
View File
@@ -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.
+31
View File
@@ -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
View File
@@ -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
View File
@@ -0,0 +1,50 @@
# Agent Pre-Completion Checklist (Vendored)
Every AI agent runs this checklist before completing a task.
## Core Principles Checklist (C1C8)
### 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
View File
@@ -1,6 +1,5 @@
# Nova Metrics Directory # Nova Metrics Directory
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (D-128)
This directory holds Nova's telemetry/observability artifacts. The This directory holds Nova's telemetry/observability artifacts. The
metrics layer is **Nova-native** (D-120): JSONL event log + SQLite cold metrics layer is **Nova-native** (D-120): JSONL event log + SQLite cold
-2
View File
@@ -1,7 +1,5 @@
# Nova PowerBI Dashboard — Import Guide # 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 This guide documents how to import Nova's metrics views into PowerBI
via the folder connector, and suggests a starter visual model. via the folder connector, and suggests a starter visual model.
+34 -7
View File
@@ -48,6 +48,11 @@
"description": "Target group target type (ip or instance).", "description": "Target group target type (ip or instance).",
"required": false, "required": false,
"default": "ip" "default": "ip"
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -85,20 +90,42 @@
{ {
"type": "aws:elbv2:loadbalancer", "type": "aws:elbv2:loadbalancer",
"description": "Application load balancer in the VPC subnets.", "description": "Application load balancer in the VPC subnets.",
"inputs": ["name", "subnets", "security_group", "load_balancer_type"], "inputs": [
"outputs": ["lb_arn"] "name",
"subnets",
"security_group",
"load_balancer_type"
],
"outputs": [
"lb_arn"
]
}, },
{ {
"type": "aws:elbv2:targetgroup", "type": "aws:elbv2:targetgroup",
"description": "Target group for the ECS service tasks.", "description": "Target group for the ECS service tasks.",
"inputs": ["name", "port", "protocol", "vpc_id", "target_type"], "inputs": [
"outputs": ["target_group_arn"] "name",
"port",
"protocol",
"vpc_id",
"target_type"
],
"outputs": [
"target_group_arn"
]
}, },
{ {
"type": "aws:elbv2:listener", "type": "aws:elbv2:listener",
"description": "Listener forwarding the LB port to the target group.", "description": "Listener forwarding the LB port to the target group.",
"inputs": ["lb_arn", "port", "protocol", "target_group_arn"], "inputs": [
"outputs": ["listener_arn"] "lb_arn",
"port",
"protocol",
"target_group_arn"
],
"outputs": [
"listener_arn"
]
} }
] ]
} }
+5 -2
View File
@@ -1,4 +1,5 @@
resource "aws_lb" "this" { resource "aws_lb" "this" {
count = var.enabled ? 1 : 0
name = var.name name = var.name
load_balancer_type = var.load_balancer_type load_balancer_type = var.load_balancer_type
subnets = local.subnet_list subnets = local.subnet_list
@@ -6,6 +7,7 @@ resource "aws_lb" "this" {
} }
resource "aws_lb_target_group" "this" { resource "aws_lb_target_group" "this" {
count = var.enabled ? 1 : 0
name_prefix = "${var.name}-" name_prefix = "${var.name}-"
port = var.port port = var.port
protocol = var.protocol protocol = var.protocol
@@ -18,13 +20,14 @@ resource "aws_lb_target_group" "this" {
} }
resource "aws_lb_listener" "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 port = var.port
protocol = var.protocol protocol = var.protocol
default_action { default_action {
type = "forward" 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] depends_on = [aws_lb_target_group.this]
+3 -3
View File
@@ -1,14 +1,14 @@
output "lb_arn" { output "lb_arn" {
value = aws_lb.this.id value = aws_lb.this[0].id
description = "The load balancer ARN." description = "The load balancer ARN."
} }
output "listener_arn" { output "listener_arn" {
value = aws_lb_listener.this.arn value = aws_lb_listener.this[0].arn
description = "The listener ARN." description = "The listener ARN."
} }
output "target_group_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." description = "The target group ARN."
} }
+6
View File
@@ -50,3 +50,9 @@ variable "vpc_id" {
description = "VPC ID for the target group (ref to vpc or platform VPC)." description = "VPC ID for the target group (ref to vpc or platform VPC)."
default = null default = null
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+31 -6
View File
@@ -43,6 +43,11 @@
"type": "string", "type": "string",
"description": "AWS region (CloudFront is global but the provider region is used for the OAC).", "description": "AWS region (CloudFront is global but the provider region is used for the OAC).",
"required": true "required": true
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -75,17 +80,37 @@
{ {
"type": "aws:cloudfront:distribution", "type": "aws:cloudfront:distribution",
"description": "CloudFront distribution with S3 origin via OAC.", "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"], "inputs": [
"outputs": ["distribution_arn", "distribution_domain_name"] "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", "type": "aws:cloudfront:originaccesscontrol",
"description": "Origin Access Control for the S3 origin.", "description": "Origin Access Control for the S3 origin.",
"inputs": ["name", "origin_type", "signing_behavior"], "inputs": [
"outputs": ["oac_id"] "name",
"origin_type",
"signing_behavior"
],
"outputs": [
"oac_id"
]
} }
], ],
"intra_refs": [ "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"
}
] ]
} }
+3 -1
View File
@@ -1,4 +1,5 @@
resource "aws_cloudfront_origin_access_control" "this" { resource "aws_cloudfront_origin_access_control" "this" {
count = var.enabled ? 1 : 0
name = local.oac_name name = local.oac_name
origin_access_control_origin_type = local.oac_origin_type origin_access_control_origin_type = local.oac_origin_type
signing_behavior = local.oac_signing_behavior signing_behavior = local.oac_signing_behavior
@@ -6,10 +7,11 @@ resource "aws_cloudfront_origin_access_control" "this" {
} }
resource "aws_cloudfront_distribution" "this" { resource "aws_cloudfront_distribution" "this" {
count = var.enabled ? 1 : 0
origin { origin {
origin_id = "s3-origin" origin_id = "s3-origin"
domain_name = var.bucket_regional_domain_name 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 { s3_origin_config {
origin_access_identity = "" origin_access_identity = ""
} }
+3 -3
View File
@@ -1,14 +1,14 @@
output "distribution_arn" { output "distribution_arn" {
value = aws_cloudfront_distribution.this.arn value = aws_cloudfront_distribution.this[0].arn
description = "The CloudFront distribution ARN." description = "The CloudFront distribution ARN."
} }
output "distribution_domain_name" { 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." description = "The CloudFront distribution domain name."
} }
output "oac_id" { 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." 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)." description = "AWS region (CloudFront is global but the provider region is used for the OAC)."
default = null default = null
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+6 -1
View File
@@ -19,6 +19,11 @@
"type": "string", "type": "string",
"description": "ARN of the CMK for repository encryption; if absent, uses AWS-managed key.", "description": "ARN of the CMK for repository encryption; if absent, uses AWS-managed key.",
"required": false "required": false
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -48,4 +53,4 @@
"default": true "default": true
} }
} }
} }
+1
View File
@@ -6,6 +6,7 @@ locals {
} }
resource "aws_ecr_repository" "this" { resource "aws_ecr_repository" "this" {
count = var.enabled ? 1 : 0
name = var.name name = var.name
image_tag_mutability = "MUTABLE" image_tag_mutability = "MUTABLE"
image_scanning_configuration { image_scanning_configuration {
+2 -2
View File
@@ -1,9 +1,9 @@
output "repository_url" { output "repository_url" {
value = aws_ecr_repository.this.repository_url value = aws_ecr_repository.this[0].repository_url
description = "The ECR repository URL." description = "The ECR repository URL."
} }
output "repository_arn" { output "repository_arn" {
value = aws_ecr_repository.this.arn value = aws_ecr_repository.this[0].arn
description = "The ECR repository ARN." description = "The ECR repository ARN."
} }
+7 -1
View File
@@ -13,4 +13,10 @@ variable "kms_key_arn" {
type = string type = string
description = "ARN of the CMK for ECR encryption; if absent, uses managed key." description = "ARN of the CMK for ECR encryption; if absent, uses managed key."
default = null default = null
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+6 -1
View File
@@ -19,6 +19,11 @@
"type": "string", "type": "string",
"description": "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key.", "description": "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key.",
"required": false "required": false
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -43,4 +48,4 @@
"default": true "default": true
} }
} }
} }
+1
View File
@@ -1,3 +1,4 @@
resource "aws_ecs_cluster" "this" { resource "aws_ecs_cluster" "this" {
count = var.enabled ? 1 : 0
name = var.name name = var.name
} }
+2 -2
View File
@@ -1,9 +1,9 @@
output "cluster_arn" { output "cluster_arn" {
value = aws_ecs_cluster.this.arn value = aws_ecs_cluster.this[0].arn
description = "The ECS cluster ARN." description = "The ECS cluster ARN."
} }
output "cluster_id" { output "cluster_id" {
value = aws_ecs_cluster.this.id value = aws_ecs_cluster.this[0].id
description = "The ECS cluster 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." description = "ARN of the CMK for CloudWatch log group encryption; if absent, uses managed key."
default = null default = null
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+28 -5
View File
@@ -79,6 +79,11 @@
"description": "ECS task definition family name.", "description": "ECS task definition family name.",
"required": false, "required": false,
"default": "app" "default": "app"
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -107,14 +112,32 @@
{ {
"type": "aws:ecs:task_definition", "type": "aws:ecs:task_definition",
"description": "Fargate task definition; the adapter jsonencodes image/port/env into container_definitions.", "description": "Fargate task definition; the adapter jsonencodes image/port/env into container_definitions.",
"inputs": ["image", "port", "cpu", "memory", "env", "family"], "inputs": [
"outputs": ["task_def_arn"] "image",
"port",
"cpu",
"memory",
"env",
"family"
],
"outputs": [
"task_def_arn"
]
}, },
{ {
"type": "aws:ecs:service", "type": "aws:ecs:service",
"description": "Fargate service running the task definition in the cluster + subnets.", "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"], "inputs": [
"outputs": ["service_arn"] "cluster_arn",
"subnets",
"security_group",
"lb_target_group_arn",
"desired_count",
"launch_type"
],
"outputs": [
"service_arn"
]
} }
] ]
} }
+3 -1
View File
@@ -1,4 +1,5 @@
resource "aws_ecs_task_definition" "this" { resource "aws_ecs_task_definition" "this" {
count = var.enabled ? 1 : 0
family = var.family family = var.family
cpu = tostring(var.cpu) cpu = tostring(var.cpu)
memory = tostring(var.memory) memory = tostring(var.memory)
@@ -8,9 +9,10 @@ resource "aws_ecs_task_definition" "this" {
} }
resource "aws_ecs_service" "this" { resource "aws_ecs_service" "this" {
count = var.enabled ? 1 : 0
name = "nova-microservice" name = "nova-microservice"
cluster = var.cluster_arn cluster = var.cluster_arn
task_definition = aws_ecs_task_definition.this.arn task_definition = aws_ecs_task_definition.this[0].arn
desired_count = var.desired_count desired_count = var.desired_count
launch_type = var.launch_type launch_type = var.launch_type
+2 -2
View File
@@ -1,9 +1,9 @@
output "service_arn" { output "service_arn" {
value = aws_ecs_service.this.id value = aws_ecs_service.this[0].id
description = "The ECS service ARN." description = "The ECS service ARN."
} }
output "task_def_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." description = "The ECS task definition ARN."
} }
@@ -78,3 +78,9 @@ variable "family" {
description = "ECS task definition family name." description = "ECS task definition family name."
default = "app" default = "app"
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+6 -1
View File
@@ -24,6 +24,11 @@
"type": "string", "type": "string",
"description": "AWS region the role is created in.", "description": "AWS region the role is created in.",
"required": true "required": true
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -48,4 +53,4 @@
"default": true "default": true
} }
} }
} }
+3 -2
View File
@@ -1,11 +1,12 @@
resource "aws_iam_role" "this" { resource "aws_iam_role" "this" {
count = var.enabled ? 1 : 0
name = var.role_name name = var.role_name
assume_role_policy = local.assume_role_policy assume_role_policy = local.assume_role_policy
} }
resource "aws_iam_role_policy" "ecr_logs" { 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 name = local.inline_policy.name
role = aws_iam_role.this.id role = aws_iam_role.this[0].id
policy = local.inline_policy.policy policy = local.inline_policy.policy
} }
+2 -2
View File
@@ -1,9 +1,9 @@
output "role_arn" { output "role_arn" {
value = aws_iam_role.this.arn value = aws_iam_role.this[0].arn
description = "The IAM role ARN." description = "The IAM role ARN."
} }
output "role_id" { output "role_id" {
value = aws_iam_role.this.id value = aws_iam_role.this[0].id
description = "The IAM role ID." description = "The IAM role ID."
} }
@@ -21,3 +21,9 @@ variable "region" {
description = "AWS region (provider-level; not a resource arg)." description = "AWS region (provider-level; not a resource arg)."
default = null default = null
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+6 -1
View File
@@ -20,6 +20,11 @@
"description": "Number of days before the key is deleted after deletion is requested (default 30).", "description": "Number of days before the key is deleted after deletion is requested (default 30).",
"required": false, "required": false,
"default": 30 "default": 30
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -49,4 +54,4 @@
"default": true "default": true
} }
} }
} }
+3 -1
View File
@@ -1,10 +1,12 @@
resource "aws_kms_key" "this" { resource "aws_kms_key" "this" {
count = var.enabled ? 1 : 0
description = var.description description = var.description
enable_key_rotation = true enable_key_rotation = true
deletion_window_in_days = var.deletion_window_days deletion_window_in_days = var.deletion_window_days
} }
resource "aws_kms_alias" "this" { resource "aws_kms_alias" "this" {
count = var.enabled ? 1 : 0
name = local.alias_name name = local.alias_name
target_key_id = aws_kms_key.this.key_id target_key_id = aws_kms_key.this[0].key_id
} }
+2 -2
View File
@@ -1,9 +1,9 @@
output "kms_key_arn" { output "kms_key_arn" {
value = aws_kms_key.this.arn value = aws_kms_key.this[0].arn
description = "The KMS key ARN." description = "The KMS key ARN."
} }
output "kms_key_id" { output "kms_key_id" {
value = aws_kms_key.this.key_id value = aws_kms_key.this[0].key_id
description = "The KMS key ID." description = "The KMS key ID."
} }
+7 -1
View File
@@ -14,4 +14,10 @@ variable "deletion_window_days" {
type = number type = number
description = "Deletion window in days (7-30)." description = "Deletion window in days (7-30)."
default = 30 default = 30
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+5
View File
@@ -79,6 +79,11 @@
"description": "Database admin password", "description": "Database admin password",
"required": false, "required": false,
"default": "ACdlcI2026!" "default": "ACdlcI2026!"
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
+1
View File
@@ -5,6 +5,7 @@ resource "aws_db_subnet_group" "this" {
} }
resource "aws_db_instance" "this" { resource "aws_db_instance" "this" {
count = var.enabled ? 1 : 0
engine = var.engine engine = var.engine
engine_version = var.engine_version engine_version = var.engine_version
instance_class = var.instance_class instance_class = var.instance_class
+2 -2
View File
@@ -1,9 +1,9 @@
output "db_endpoint" { output "db_endpoint" {
value = aws_db_instance.this.endpoint value = aws_db_instance.this[0].endpoint
description = "The RDS instance endpoint." description = "The RDS instance endpoint."
} }
output "db_arn" { output "db_arn" {
value = aws_db_instance.this.arn value = aws_db_instance.this[0].arn
description = "The RDS instance ARN." description = "The RDS instance ARN."
} }
+6
View File
@@ -64,3 +64,9 @@ variable "subnet_ids" {
description = "Comma-separated subnet IDs for the DB subnet group (VPC-dependent)." description = "Comma-separated subnet IDs for the DB subnet group (VPC-dependent)."
default = "" default = ""
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+6 -1
View File
@@ -19,6 +19,11 @@
"type": "string", "type": "string",
"description": "ARN of the CMK for SSE-KMS; if absent, uses managed key.", "description": "ARN of the CMK for SSE-KMS; if absent, uses managed key.",
"required": false "required": false
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -57,4 +62,4 @@
"default": true "default": true
} }
} }
} }
+5 -2
View File
@@ -1,10 +1,12 @@
resource "aws_s3_bucket" "this" { resource "aws_s3_bucket" "this" {
count = var.enabled ? 1 : 0
bucket = var.bucket_name bucket = var.bucket_name
tags = local.tags tags = local.tags
} }
resource "aws_s3_bucket_versioning" "this" { 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 { versioning_configuration {
status = "Enabled" status = "Enabled"
@@ -12,7 +14,8 @@ resource "aws_s3_bucket_versioning" "this" {
} }
resource "aws_s3_bucket_server_side_encryption_configuration" "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 { rule {
apply_server_side_encryption_by_default { apply_server_side_encryption_by_default {
+3 -3
View File
@@ -1,14 +1,14 @@
output "bucket_arn" { output "bucket_arn" {
value = aws_s3_bucket.this.arn value = aws_s3_bucket.this[0].arn
description = "The S3 bucket ARN." description = "The S3 bucket ARN."
} }
output "bucket_name" { output "bucket_name" {
value = aws_s3_bucket.this.id value = aws_s3_bucket.this[0].id
description = "The bucket name (echoes the input)." description = "The bucket name (echoes the input)."
} }
output "bucket_regional_domain_name" { 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)." description = "The bucket regional domain name (e.g. nova-spike-bucket.s3.us-east-1.amazonaws.com)."
} }
+7 -1
View File
@@ -19,4 +19,10 @@ variable "tags" {
type = map(string) type = map(string)
description = "Additional tags to merge with the module defaults." description = "Additional tags to merge with the module defaults."
default = {} default = {}
} }
variable "enabled" {
type = bool
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
default = true
}
+6 -1
View File
@@ -71,6 +71,11 @@
"type": "string", "type": "string",
"description": "ECS cluster ARN to deploy the service into", "description": "ECS cluster ARN to deploy the service into",
"required": false "required": false
},
"enabled": {
"type": "boolean",
"default": true,
"description": "Feature flag: enable/disable this module. Set to false to skip resource creation."
} }
}, },
"outputs": { "outputs": {
@@ -99,4 +104,4 @@
"default": true "default": true
} }
} }
} }
+1 -1
View File
@@ -11,7 +11,7 @@ resource "aws_ecs_service" "uptime" {
name = "nova-uptime" name = "nova-uptime"
cluster = local.cluster_ref cluster = local.cluster_ref
task_definition = aws_ecs_task_definition.uptime.arn 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" launch_type = "FARGATE"
dynamic "network_configuration" { dynamic "network_configuration" {

Some files were not shown because too many files have changed in this diff Show More