Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7866fda84 | |||
| 2efed26bb6 | |||
| 5c07e29b90 | |||
| aa868c97ef | |||
| e4a9915891 | |||
| 0ca383dae6 | |||
| ed5ea90654 | |||
| 2273009b95 | |||
| 0d2cbdb423 | |||
| b418d429b5 | |||
| dcba380b52 | |||
| f0bc3be92c | |||
| 0b79b16715 | |||
| 90624be63f |
@@ -0,0 +1,66 @@
|
|||||||
|
# Nova — The Autonomous Cloud Delivery Platform: Autonomy Defensibility Brief
|
||||||
|
|
||||||
|
> Strategic direction, leadership metrics & unified story
|
||||||
|
> Last refined: v1.21 — reframe from "no-humans" to "autonomous operations"
|
||||||
|
|
||||||
|
## The thesis
|
||||||
|
|
||||||
|
Nova is the autonomous infrastructure layer that lets product teams
|
||||||
|
ship without engaging an operator, and lets executives trust the
|
||||||
|
platform not because it never fails but because every decision is
|
||||||
|
captured, scored, and accountable.
|
||||||
|
|
||||||
|
**Autonomy in operations; human at stage gates.** Normal operations —
|
||||||
|
provisioning, healing, remediation — run without an operator in the
|
||||||
|
loop. Human attestation remains required at stage gates: QA signs off
|
||||||
|
for production, SRE greenlights based on operational readiness. The
|
||||||
|
absence of an operator in the loop is never the absence of a record.
|
||||||
|
|
||||||
|
## Grounded proof (measurable today)
|
||||||
|
|
||||||
|
| Proof | Source | Status |
|
||||||
|
|-------|--------|--------|
|
||||||
|
| Capabilities verified, none broken (live-AWS caps honestly skipped, resources torn down to zero-cost steady state) | regression report | grounded |
|
||||||
|
| Decision Ledger captures 100% of automated decisions with outcome backfill | decision ledger store | grounded |
|
||||||
|
| Attestation coverage: 100% of prod/dr promotions attested by a human | attestation gates + outbox | grounded |
|
||||||
|
| Confidence-gated policy engine (deterministic, not an LLM) — weighted inputs, band outcome | confidence signal | grounded |
|
||||||
|
| Attestation matrix with separation-of-duties on prod | attestation matrix + separation-of-duties | grounded |
|
||||||
|
| Pre-apply cost estimates (offline) | cost adapter | grounded |
|
||||||
|
| Test suite passes | test results | grounded |
|
||||||
|
|
||||||
|
## Deferred proof (measurable when blocking work lifts)
|
||||||
|
|
||||||
|
| Proof | Blocking work | Unblock requirement |
|
||||||
|
|-------|----------------|---------------------|
|
||||||
|
| Touchless resolution rate across production estates | 0 consumers today | Pilot estate activation |
|
||||||
|
| Live infrastructure health (ECS, ALB, RPS) | Live AWS torn down | Live AWS re-provisioning |
|
||||||
|
| Onboarding funnel: requested → granted | Auto-grant not built | Auto-grant implementation |
|
||||||
|
| Drift auto-reversal rate | No drift scheduler | Drift detection scheduler |
|
||||||
|
| Predictive vs reactive ratio | No emitter | ML anomaly-forecasting service |
|
||||||
|
| Tamper-evident ledger checkpoints (S3 Object Lock + JWS) | Audit ledger build-out | Audit ledger build-out |
|
||||||
|
|
||||||
|
## Anti-claims (what Nova is NOT)
|
||||||
|
|
||||||
|
1. **Nova's decisions are NOT made by an LLM.** They are made by a
|
||||||
|
confidence-gated policy engine: deterministic scripts calculate a
|
||||||
|
score, and a band outcome gates the action. The platform functions
|
||||||
|
without AI. The Decision Ledger captures this real decision path —
|
||||||
|
not a fabricated "AI agent." When an LLM planner is added, it will
|
||||||
|
emit richer `alternatives_considered` without schema breakage.
|
||||||
|
2. **Nova does NOT remove humans from accountability.** Only from
|
||||||
|
normal operations. Every stage-gate promotion (qa/prod/dr) requires
|
||||||
|
a human attestation recorded with approver identity,
|
||||||
|
separation-of-duties check, and the evidence matrix.
|
||||||
|
3. **Nova is NOT for legacy, untagged, or freeform infrastructure.** It
|
||||||
|
requires Terraform-managed, policy-aligned, fully-tagged inputs.
|
||||||
|
4. **Nova does NOT fabricate metrics.** Every metric is grounded (cites
|
||||||
|
a source), derived (documented formula), or deferred (cites the
|
||||||
|
blocking work). No fabricated numbers in any deck slide or metrics
|
||||||
|
entry (the "no fabrication" hard constraint).
|
||||||
|
|
||||||
|
## What "won" looks like
|
||||||
|
|
||||||
|
By month 18, 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 layer their
|
||||||
|
AI engineering teams reach for first when an agent needs to deploy.
|
||||||
@@ -1,13 +1,11 @@
|
|||||||
{
|
{
|
||||||
"phase": 1,
|
"phase": 0,
|
||||||
"stage": "complete",
|
"stage": "plan",
|
||||||
"milestone": "v1.19",
|
"milestone": "v1.21",
|
||||||
"phase_role": "execution",
|
"phase_role": "pre_execution",
|
||||||
"attempts": 0,
|
"attempts": 0,
|
||||||
"updated_at": "2026-08-06T20:00:00Z",
|
"updated_at": "2026-08-11T00:01:00Z",
|
||||||
"milestone_complete": false,
|
"milestone_complete": false,
|
||||||
"tag": "v1.18.0",
|
"requirements": ["REQ-245","REQ-246","REQ-247","REQ-248","REQ-249","REQ-250","REQ-251","REQ-252","REQ-253"],
|
||||||
"release_id": 530,
|
"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-229"],
|
|
||||||
"notes": "v1.19 P1 SHIP complete: tag v1.18.0 (first patch on v1.18.x line), Gitea release id 530 created. 4-layer verify PASS. Next: P2 final-review-ship (review + audit + milestone complete)."
|
|
||||||
}
|
}
|
||||||
+57
-36
@@ -1,7 +1,7 @@
|
|||||||
# NORTH_STAR — Nova
|
# NORTH_STAR — Nova
|
||||||
|
|
||||||
> **Status:** Draft (pending interactive GRILL → final)
|
> **Status:** Draft (pending interactive GRILL → final)
|
||||||
> **Milestone:** v1.17 — Strategic Direction, Leadership Metrics & Unified Story
|
> **Milestone:** v1.21 — Nova Deck Refinement & Pipeline Hardening
|
||||||
> **Owner:** Product Owner
|
> **Owner:** Product Owner
|
||||||
> **Purpose:** Durable strategic intent. Read by CIAgent in every future
|
> **Purpose:** Durable strategic intent. Read by CIAgent in every future
|
||||||
> `/ci-run` so the platform's direction survives across milestones. This
|
> `/ci-run` so the platform's direction survives across milestones. This
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
## Vision
|
## Vision
|
||||||
|
|
||||||
> **Infrastructure operations become invisible. Every environment
|
> **Infrastructure operations become visible. Every environment
|
||||||
> provisioned, every incident healed, every risk remediated — by an
|
> provisioned, every incident healed, every risk remediated — by an
|
||||||
> autonomous system whose trustworthiness is provable, not promised.
|
> autonomous system whose trustworthiness is provable, not promised.
|
||||||
> Human attestation remains required at stage gates — QA signs off for
|
> Human attestation remains required at stage gates — QA signs off for
|
||||||
@@ -22,9 +22,12 @@
|
|||||||
> operator is never in the loop of normal operations.**
|
> operator is never in the loop of normal operations.**
|
||||||
|
|
||||||
Nova is the autonomous infrastructure layer that lets product teams ship
|
Nova is the autonomous infrastructure layer that lets product teams ship
|
||||||
without engaging an operator, and lets executives trust the AI not because
|
without engaging an operator, and lets executives trust the platform not
|
||||||
it never fails but because every decision is captured, scored, and
|
because it never fails but because every decision is captured, scored,
|
||||||
accountable.
|
and accountable. The recurring theme across the platform is that
|
||||||
|
**infrastructure operations become visible** — security posture,
|
||||||
|
remediation velocity, reliability, and lead time are surfaced as
|
||||||
|
queryable signals rather than hidden in tribal knowledge.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -38,46 +41,64 @@ human by design; operational escalations (AI confidence too low to
|
|||||||
proceed) are the failure mode we drive toward zero. Everything else
|
proceed) are the failure mode we drive toward zero. Everything else
|
||||||
collapses if autonomy isn't real.
|
collapses if autonomy isn't real.
|
||||||
|
|
||||||
**2. Establish provable trust in AI decisions.**
|
**2. Establish provable trust in automated decisions.**
|
||||||
Build the audit substrate — Decision Ledger, confidence scoring, circuit
|
Trust is established by deterministic scripts that calculate a score and
|
||||||
breakers, blast-radius controls — that turns "autonomous" from a
|
a band outcome that gates the action — the platform functions without AI.
|
||||||
marketing claim into a defensible one. Trust is the moat. Features can be
|
"AI decisions" are really automated decisions. The audit substrate —
|
||||||
copied; an immutable, queryable decision history cannot.
|
Decision Ledger, confidence scoring, circuit breakers, blast-radius
|
||||||
|
controls — turns "autonomous" from a marketing claim into a defensible
|
||||||
|
one. Trust is the moat. Features can be copied; an immutable, queryable
|
||||||
|
decision history cannot.
|
||||||
|
|
||||||
**3. Deliver compounding, quantifiable ROI for customers.**
|
**3. Deliver compounding, quantifiable ROI for customers.**
|
||||||
Each quarter on Nova must reduce cloud spend, free engineering hours, and
|
Each quarter on Nova must show measurable improvement on four CTO-grade
|
||||||
avoid downtime measurably. If the CFO can't point to a number that
|
metrics, all of which flow into PowerBI views and are captured by the
|
||||||
improves quarter-over-quarter, Nova fails its commercial test, regardless
|
telemetry pipeline:
|
||||||
of how clever the AI is.
|
|
||||||
|
|
||||||
**4. Become the default substrate for agentic infrastructure consumption.**
|
- **Lead Time** — from PR merge to production deployment (downward trend).
|
||||||
AI agents are already becoming the largest consumers of cloud
|
- **Infrastructure Vulnerability Count** — open findings on deployed
|
||||||
infrastructure. Nova must be the platform through which those agents
|
resources (downward trend, demonstrating that proactive scanning +
|
||||||
declare, deploy, and verify infrastructure — not a vendor scrambling into
|
remediation keeps up with the AI-era 0-day pace).
|
||||||
that market two quarters late.
|
- **MTTR** — for platform-detected and platform-remediated incidents.
|
||||||
|
- **Cloud Spend Reduction** — on pilot estates vs. the pre-Nova
|
||||||
|
baseline.
|
||||||
|
|
||||||
|
If leadership cannot point to a number that improves quarter-over-quarter
|
||||||
|
on these four axes, Nova fails its commercial test, regardless of how
|
||||||
|
clever the automation is.
|
||||||
|
|
||||||
|
**4. Integrate with externally owned development platforms — regardless of source.**
|
||||||
|
Nova integrates with externally owned PDLC, SDLC, Agentic, and Citizen
|
||||||
|
Developer platforms with no regard for the source of the intent. Nova
|
||||||
|
provides a set of skills and MCP endpoints that help the developer or AI
|
||||||
|
agent make their application production-grade. Regardless of the source,
|
||||||
|
all intents to deploy to production go through the same rigorous
|
||||||
|
controls, quality gates, attestation, and evidence stream. Nova is the
|
||||||
|
layer any of those platforms reach for first when an agent needs to
|
||||||
|
deploy — not a vendor arriving late to that market.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Anti-Goals (5 — what Nova is fundamentally NOT)
|
## Anti-Goals (4 — what Nova is fundamentally NOT)
|
||||||
|
|
||||||
1. **Not a Terraform, Kubernetes, or hyperscaler competitor.** We
|
1. **Not a general-purpose AI agent platform.** We are purpose-built for
|
||||||
orchestrate them. Replacing them is the most expensive possible
|
|
||||||
distraction from the value we create.
|
|
||||||
2. **Not a general-purpose AI agent platform.** We are purpose-built for
|
|
||||||
infrastructure operations. Breadth here produces shallow tools; depth
|
infrastructure operations. Breadth here produces shallow tools; depth
|
||||||
here wins the category.
|
here wins the category.
|
||||||
3. **Not a system that removes humans from accountability.** Only from
|
2. **Not a system that removes humans from accountability.** Only from
|
||||||
operations. Every AI decision lands in an immutable ledger. Every
|
normal operations. Every automated decision lands in an immutable
|
||||||
stage-gate promotion (qa/prod/dr) requires a human attestation recorded
|
ledger. Every stage-gate promotion (qa/prod/dr) requires a human
|
||||||
with approver identity, separation-of-duties check, and the 8-concern
|
attestation recorded with approver identity, separation-of-duties
|
||||||
evidence matrix. The absence of an operator is never the absence of a
|
check, and the evidence matrix. The absence of an operator in the
|
||||||
record.
|
loop is never the absence of a record.
|
||||||
4. **Not for legacy, untagged, or freeform infrastructure.** Nova requires
|
3. **Not an upstream development platform.** Nova does not own the
|
||||||
Terraform-managed, policy-aligned, fully-tagged inputs. We optimize for
|
product backlog, IDE workflows, code authorship, or application
|
||||||
the disciplined 95%, not the chaotic 5%.
|
business logic. The PDLC is upstream; Nova integrates with it through
|
||||||
5. **Not sold to operators.** Nova is sold to leadership on outcomes —
|
a validated contract boundary — Nova never penetrates it.
|
||||||
cost, velocity, risk. Selling to operators inverts the incentive and
|
4. **Not a replacement for the Product Development Lifecycle (PDLC).**
|
||||||
breaks the autonomy thesis.
|
Nova governs infrastructure + delivery only. Product lifecycle
|
||||||
|
decisions (what to build, when to ship, for whom) remain with the
|
||||||
|
product team. Nova makes their intent production-grade; it does not
|
||||||
|
own the intent.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,157 @@
|
|||||||
|
# Nova — Phase Plan v1.21 (Nova Deck Refinement & Pipeline Hardening)
|
||||||
|
|
||||||
|
> **Milestone:** v1.21 — Nova Deck Refinement & Pipeline Hardening
|
||||||
|
> **Branch:** `milestone/v1.21-deck-refinement` (flat workflow: commits on
|
||||||
|
> main, tags on the v1.20.x line per branch-strategy)
|
||||||
|
> **Tag line:** `v1.20.x` patch line — `v1.20.0` (P0) → `v1.20.1..v1.20.5`
|
||||||
|
> (P1–P5) → `v1.20.6` (P6 final = milestone release). v1.21 is an NFR
|
||||||
|
> milestone (all phases are docs/test/chore/refactor) → progressive patches.
|
||||||
|
> **Phase count:** 7 (P0 pre-execution [DONE] + 5 execution + 1 final).
|
||||||
|
> **Source of truth for requirements:** `.ciagent/REQUIREMENTS.md` §v1.21
|
||||||
|
> (REQ-245..253, 9 requirements).
|
||||||
|
> **Source of truth for decisions:** the 33 review notes in the v1.21
|
||||||
|
> run context (encoded as REQ-245..253).
|
||||||
|
> **Source of truth for research:** existing deck + pipeline + strategic
|
||||||
|
> docs (the domain is known; no new research needed at full autonomy).
|
||||||
|
|
||||||
|
## Wave Ordering
|
||||||
|
|
||||||
|
All 5 execution phases are **sequenced** (flat workflow). The theoretical
|
||||||
|
parallelism is documented for future parallelization-enabled runs.
|
||||||
|
|
||||||
|
| Wave | Phases | Rationale |
|
||||||
|
|------|--------|-----------|
|
||||||
|
| Wave 1 | P1, P2, P4 (**parallelizable**) | P1 (strategic docs: NORTH_STAR, AUTONOMY_THESIS, PROJECT, scope, raci), P2 (slides source-of-truth rewrite + rename), P4 (pipeline hardening: Checkov/Wiz flow). Zero file overlap: P1 touches `.ciagent/` + `docs/scope.md` + `docs/raci.md`; P2 touches `docs/presentations/*.md`; P4 touches `scripts/run_*.sh` + `adapters/wiz/` + `tests/test_pipeline*.py`. In a parallelization-enabled run these three could execute concurrently. |
|
||||||
|
| Wave 2 | P3 | Marp deck + talking points + README synthesis. Depends on P2's updated source-of-truth. Re-renders HTML+PPTX via `render_slides.sh`. Also fixes theme CSS A1 table + footer cleanup (REQ-251). |
|
||||||
|
| Wave 3 | P5 | Render + verify. Depends on P2 (source), P3 (marp), P4 (pipeline). Re-renders mermaid PNGs (slide 1 new diagram, slide 9 expand, Atelier split), HTML, PPTX. Runs `test_slides_pipeline.py`, `test_no_forge_mentions.py`, full `pytest`, `run_platform.sh --check-only`. |
|
||||||
|
| Wave 4 | P6 | Final review + audit + milestone ship. Merge to main, tag `v1.20.6`, create release, attach PPTX. |
|
||||||
|
|
||||||
|
## Phase Summaries
|
||||||
|
|
||||||
|
### Phase P0 — pre-execution (DONE)
|
||||||
|
SPECIFY → CLARIFY → RESEARCH → PLAN. Validated v1.21 requirements
|
||||||
|
(REQ-245..253). Established `active_milestone: "v1.21"`. Synced
|
||||||
|
PROJECT.md strategic-direction pillar. No new research (domain known).
|
||||||
|
|
||||||
|
### Phase P1 — strategic-docs (Wave 1)
|
||||||
|
- `NORTH_STAR.md`: vision polish (item 11); obj #2 deterministic-scoring
|
||||||
|
reword (item 13); obj #3 four CTO metrics (item 14); obj #4 replaced
|
||||||
|
with integration objective (item 17); drop anti-goals 1,4,5; add 2 new
|
||||||
|
anti-goals (item 16); anti-goal #3 reworded (item 7).
|
||||||
|
- `git mv .ciagent/NO_HUMANS_THESIS.md .ciagent/AUTONOMY_THESIS.md` +
|
||||||
|
reframe content (items 7, 8).
|
||||||
|
- `PROJECT.md`: mission/scope sync for item 17 (already partially done
|
||||||
|
in P0; finalize here).
|
||||||
|
- `docs/scope.md`: light sync.
|
||||||
|
- `docs/raci.md`: rename Release Mgmt → SRE; add Quality Engineering role.
|
||||||
|
- **REQs:** REQ-246, REQ-247 (partial).
|
||||||
|
|
||||||
|
### Phase P2 — slides source-of-truth (Wave 1)
|
||||||
|
- `git mv` all 5 deck files `nova-no-humans-platform*` →
|
||||||
|
`nova-autonomous-cloud-delivery*`.
|
||||||
|
- Rewrite `nova-autonomous-cloud-delivery.md` (source of truth):
|
||||||
|
- Slide 1 "The Problem" (items 3,4,5,7,9): broader problem (devs writing
|
||||||
|
terraform, destructive changes, AI-era 0-day pace, bandwidth gaps,
|
||||||
|
tribal knowledge/rockstar operator); no arc; no "18 capabilities
|
||||||
|
verified"; not "humans are the problem".
|
||||||
|
- Slide 2 "Nova's Vision" (item 11): polish for technical audience;
|
||||||
|
"infrastructure operations become visible" as recurring theme.
|
||||||
|
- Slide 3 "Strategic Objectives + Anti-Goals" (items 12,13,14,15,16,17,
|
||||||
|
18): only Obj+Anti-Goals; provable trust = deterministic scripts
|
||||||
|
(functions without AI); ROI = Lead Time + Vuln Trend + MTTR + Spend;
|
||||||
|
drop anti-goals 1,4,5; add "not upstream dev platform", "not PDLC
|
||||||
|
replacement"; replace obj #4 with integration objective; reword benefit.
|
||||||
|
- Slide 4 "Scope" (item 29): refine (moved up).
|
||||||
|
- Slide 5 "RACI" (item 30): add QE column; reassign A from Platform →
|
||||||
|
QA/SRE; rename Release Mgmt → SRE; split release attestation (SRE =
|
||||||
|
Production Readiness); shrink to fit (moved up).
|
||||||
|
- Slide 6 "Pipeline" (item 20): Checkov on static code before plan;
|
||||||
|
Wiz on plan; no Wiz → Checkov on plan; never both.
|
||||||
|
- Slide 7 "Decision Ledger" (items 21,22): drop D-121/122/132; "AI
|
||||||
|
decisions = automated decisions"; focus on value (immutable,
|
||||||
|
queryable, accountable), not sqlite/hash-chain implementation.
|
||||||
|
- Slide 8 "Attestation Matrix" (item 23): drop bullets below table;
|
||||||
|
add Description column per concern; rethink "operator-supplied"
|
||||||
|
label; reword benefit.
|
||||||
|
- Slide 9 "Telemetry & Live Ops" (item 25): expand on value; keep
|
||||||
|
metric flow; drop D-120/125/126; expand on PowerBI live ops
|
||||||
|
dashboard; reword benefit.
|
||||||
|
- Slide 10 "Decision Ledger + Attestation Coverage" (item 27):
|
||||||
|
mandatory by design; no prod change without ledger + human
|
||||||
|
attestation; queryable for auditing; full traceability; improve
|
||||||
|
benefit.
|
||||||
|
- Slide 11 "Cost & ROI": minor polish.
|
||||||
|
- Slide 12 "What's Deferred — and Why" (items 10,19): remove all D-IDs;
|
||||||
|
plain-language blockers.
|
||||||
|
- Slide 13 "Roadmap to the North Star" (items 10,19): drop D-IDs; no
|
||||||
|
status column; roadmap with timelines.
|
||||||
|
- Slide 14 "12-Month Product Roadmap" (item 24): drop `planned` badges.
|
||||||
|
- Slide 15 "Quarter-by-Quarter Outcomes" (item 24): drop badges.
|
||||||
|
- Slide 16 "Production-Grade Guidance via Atelier (1/2)" (item 31):
|
||||||
|
split — Skills + MCP server overview.
|
||||||
|
- Slide 17 "Production-Grade Guidance via Atelier (2/2)" (item 31):
|
||||||
|
split — agentic validation beyond deterministic scanners + vendoring.
|
||||||
|
- Slide 18 "Recap + Ask": refresh recap to match new structure.
|
||||||
|
- Appendix A1 "Metrics Glossary" (item 32): table readability fixed
|
||||||
|
in P3 theme CSS.
|
||||||
|
- Global (items 6,10,24,2): tech-leadership benefits; no D-###/REQ-###/
|
||||||
|
.py paths in audience slides; no badges; no version in footer; final
|
||||||
|
"less is more / no fluff" prose pass.
|
||||||
|
- **REQs:** REQ-245, REQ-248, REQ-249, REQ-252 (partial).
|
||||||
|
|
||||||
|
### Phase P3 — marp deck + talking points + README (Wave 2)
|
||||||
|
- `nova-autonomous-cloud-delivery-marp.md`: synthesize from updated
|
||||||
|
source; frontmatter — title "Nova — The Autonomous Cloud Delivery
|
||||||
|
Platform", footer without version + without "Act N/5", title-slide
|
||||||
|
subtitle "Product Development & Citizen Developer Overview", no badges.
|
||||||
|
- `nova-autonomous-cloud-delivery-talking-points.md`: re-distill to
|
||||||
|
18-slide structure.
|
||||||
|
- `docs/presentations/README.md`: update deck title, audience, slide
|
||||||
|
count (18 main + 1 appendix), directory layout, remove badge docs,
|
||||||
|
update deck table + render commands + filenames.
|
||||||
|
- `docs/presentations/assets/nova-sp-theme.css`: fix Appendix A1 table
|
||||||
|
background (item 32); footer chrome no version.
|
||||||
|
- Update `scripts/render_slides.sh`, `scripts/render_deck.sh`,
|
||||||
|
`workflows-src/slides.yml`, `.github/workflows/slides.yml`,
|
||||||
|
`tests/test_slides_pipeline.py` filename refs.
|
||||||
|
- **REQs:** REQ-251, REQ-252 (partial).
|
||||||
|
|
||||||
|
### Phase P4 — pipeline hardening (Wave 1)
|
||||||
|
- `scripts/run_platform.sh` + `scripts/run_postapply.sh`: item 20 flow —
|
||||||
|
1. Checkov on static code (`main.tf`/TF dir) **before** `terraform
|
||||||
|
plan` → fail-fast dev feedback.
|
||||||
|
2. After plan: if `WIZ_API_TOKEN`+`WIZ_API_URL` → **Wiz against plan**;
|
||||||
|
else **Checkov against plan** (drop-in). **Never both.**
|
||||||
|
- `adapters/wiz/wiz_adapter.py`: support plan-mode input if needed.
|
||||||
|
- Tests: `tests/test_pipeline.py`, `tests/test_pipeline_contract.py`,
|
||||||
|
`tests/test_slides_pipeline.py`, checkov/wiz tests.
|
||||||
|
- `docs/scope.md` policy-enforcement line + slide 6 reflect new flow.
|
||||||
|
- `docs/METRICS.md` policy-stage description if changed.
|
||||||
|
- **REQs:** REQ-250.
|
||||||
|
|
||||||
|
### Phase P5 — render + verify (Wave 3)
|
||||||
|
- Re-render changed/new mermaid diagrams (slide 1 new diagram, slide 9
|
||||||
|
expand, Atelier split) via `scripts/render_slides.sh`.
|
||||||
|
- Render HTML + PPTX.
|
||||||
|
- `tests/test_slides_pipeline.py` passes: 18 main + 1 appendix; no badges;
|
||||||
|
no version in footer; no D-###/REQ-###/.py paths in audience slides;
|
||||||
|
filename refs updated.
|
||||||
|
- `tests/test_no_forge_mentions.py` passes.
|
||||||
|
- Full `pytest` passes (pipeline-hardening tests green).
|
||||||
|
- `run_platform.sh --check-only` passes.
|
||||||
|
- **REQs:** REQ-253 (partial).
|
||||||
|
|
||||||
|
### Phase P6 — final-review-ship (Final Phase, Wave 4)
|
||||||
|
- Multi-persona code review across P1–P5.
|
||||||
|
- Audit: git log vs `.ciagent/` discipline.
|
||||||
|
- Ship: merge to main, tag `v1.20.6` (final patch = milestone release),
|
||||||
|
create release, attach PPTX.
|
||||||
|
- Update `REQUIREMENTS.md` (v1.21 REQs complete) + `ROADMAP.md` (v1.21
|
||||||
|
complete). Clear `CHECKPOINT.json`.
|
||||||
|
- **REQs:** REQ-253 (complete).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
# Nova — Phase Plan v1.18 (Citizen Developer & Production-Grade Guidance)
|
# Nova — Phase Plan v1.18 (Citizen Developer & Production-Grade Guidance)
|
||||||
|
|
||||||
> **Milestone:** v1.18 — Citizen Developer & Production-Grade Guidance
|
> **Milestone:** v1.18 — Citizen Developer & Production-Grade Guidance
|
||||||
|
|||||||
+17
-4
@@ -1266,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, 12–18mo targets (with a
|
objectives, anti-goals, v1.17 non-goals, 12–18mo targets (with a
|
||||||
grounding column), and success criteria. CIAgent reads it in every
|
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
|
||||||
@@ -1334,7 +1347,7 @@ constraints or user-directed scope). New v1.18 decisions:
|
|||||||
| 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-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-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. |
|
| 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 (active — Nova 2nd-Release Sync)
|
## Objective for Milestone v1.19 (complete — Nova 2nd-Release Sync)
|
||||||
|
|
||||||
> **NFR-only chore milestone.** Ships a patch on the v1.18.x line (tag
|
> **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
|
> `v1.18.0`). Single execution phase. Establishes the manual-only "2nd
|
||||||
|
|||||||
@@ -1395,3 +1395,324 @@ with documented schemas.
|
|||||||
| REQ | Phase | Status |
|
| REQ | Phase | Status |
|
||||||
|-----|-------|--------|
|
|-----|-------|--------|
|
||||||
| REQ-229 | P1 | complete |
|
| 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 |
|
||||||
|
|||||||
+98
-2
@@ -1756,7 +1756,7 @@ locked (D-133..D-142).
|
|||||||
Ship tag at milestone COMPLETE: `v1.17.7` (feature milestone; final patch
|
Ship tag at milestone COMPLETE: `v1.17.7` (feature milestone; final patch
|
||||||
IS the release). **DONE.**
|
IS the release). **DONE.**
|
||||||
|
|
||||||
## v1.19 (active — Nova 2nd-Release Sync, tag line `v1.18.x`)
|
## v1.19 (complete — Nova 2nd-Release Sync, tag line `v1.18.x`)
|
||||||
|
|
||||||
> **NFR-only chore milestone.** Single execution phase. Establishes the
|
> **NFR-only chore milestone.** Single execution phase. Establishes the
|
||||||
> manual-only "2nd release" pipeline `~/acdl → ~/nova` (GitLab
|
> manual-only "2nd release" pipeline `~/acdl → ~/nova` (GitLab
|
||||||
@@ -1787,7 +1787,7 @@ IS the release). **DONE.**
|
|||||||
### Phase P2 — final-review-ship (Final Phase)
|
### Phase P2 — final-review-ship (Final Phase)
|
||||||
- **Description:** Final review + audit + milestone ship. Merge to main, tag
|
- **Description:** Final review + audit + milestone ship. Merge to main, tag
|
||||||
`v1.18.0` (first patch on the v1.18.x line), create Gitea release.
|
`v1.18.0` (first patch on the v1.18.x line), create Gitea release.
|
||||||
- **Status:** pending
|
- **Status:** complete
|
||||||
- **Depends on:** [P1]
|
- **Depends on:** [P1]
|
||||||
- **Requirements:** REQ-229
|
- **Requirements:** REQ-229
|
||||||
- **Success Criteria:**
|
- **Success Criteria:**
|
||||||
@@ -1796,3 +1796,99 @@ IS the release). **DONE.**
|
|||||||
`main`.
|
`main`.
|
||||||
- Tag `v1.18.0` created; release notes summarize REQ-229.
|
- Tag `v1.18.0` created; release notes summarize REQ-229.
|
||||||
- Milestone branches deleted; CHECKPOINT cleared.
|
- Milestone branches deleted; CHECKPOINT cleared.
|
||||||
|
|
||||||
|
Ship tag at milestone COMPLETE: `v1.18.1` (NFR milestone; final patch IS the
|
||||||
|
release). **DONE.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## v1.20 — Consumer Cleanup + Transparent Terraform + Slide Pipeline
|
||||||
|
|
||||||
|
> **Multi-concern milestone.** Four user-directed inputs: (1) remove all
|
||||||
|
> gitea/gitlab from synced files — the platform team must never know about
|
||||||
|
> the dev forge; (2) radically simplify documentation for the Platform Team
|
||||||
|
> audience; (3) make terraform runs transparent in workflows with feature-flag
|
||||||
|
> client differentiation; (4) dedicated S&P-themed slide render pipeline +
|
||||||
|
> 12-month product roadmap slides.
|
||||||
|
>
|
||||||
|
> Tags run on the v1.19.x line (milestone v1.20 → tags v1.19.x).
|
||||||
|
|
||||||
|
### Phase P0 — pre-execution
|
||||||
|
- **Description:** Specify → clarify → research → plan. Validate v1.20
|
||||||
|
requirements (REQ-230..244). Establish milestone version in config.json.
|
||||||
|
- **Status:** complete
|
||||||
|
- **Requirements:** REQ-230..244
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `.ciagent/REQUIREMENTS.md` has v1.20 section with all 15 requirements.
|
||||||
|
- `.ciagent/config.json` has `active_milestone: "v1.20"`.
|
||||||
|
- Checkpoint written.
|
||||||
|
|
||||||
|
### Phase P1 — consumer-cleanup (gitea removal + doc simplification)
|
||||||
|
- **Description:** Remove all gitea/gitlab mentions from synced files.
|
||||||
|
Genericize forge-detection code. Drop `.gitea/` byte-identity test
|
||||||
|
assertions. Add `test_no_forge_mentions.py` guard test. Simplify
|
||||||
|
documentation: delete completed migration docs, move thesis to `.ciagent/`,
|
||||||
|
strip ciagent-internal provenance from synced docs.
|
||||||
|
- **Status:** complete
|
||||||
|
- **Requirements:** REQ-230, REQ-231, REQ-232
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `tests/test_no_forge_mentions.py` passes — zero gitea/gitlab mentions in
|
||||||
|
synced subset.
|
||||||
|
- `pytest` passes — all existing tests green after genericization.
|
||||||
|
- Synced docs stripped of REQ-/D-/P- IDs, milestone headers, `.ciagent/`
|
||||||
|
citations.
|
||||||
|
- `docs/NOVA_MIGRATION.md` + `docs/NOVA_AWS_MIGRATION.md` deleted.
|
||||||
|
- `docs/NO_HUMANS_THESIS.md` moved to `.ciagent/`.
|
||||||
|
|
||||||
|
### Phase P2 — slide-pipeline (S&P theme + render automation)
|
||||||
|
- **Description:** Create dedicated S&P theme CSS, render_slides.sh pipeline,
|
||||||
|
CI workflow, tests. Update Marp frontmatter to use dedicated theme. Fix
|
||||||
|
README directory layout.
|
||||||
|
- **Status:** complete
|
||||||
|
- **Requirements:** REQ-239, REQ-240, REQ-241, REQ-242, REQ-243
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `docs/presentations/assets/nova-sp-theme.css` exists with S&P colors.
|
||||||
|
- Marp deck frontmatter references the theme CSS.
|
||||||
|
- `scripts/render_slides.sh` renders mermaid PNGs + HTML + PPTX.
|
||||||
|
- `workflows-src/slides.yml` + `.github/workflows/slides.yml` exist.
|
||||||
|
- `tests/test_slides_pipeline.py` passes.
|
||||||
|
- `docs/presentations/README.md` updated (no retired decks).
|
||||||
|
|
||||||
|
### Phase P3 — product-roadmap (12-month slides)
|
||||||
|
- **Description:** Add 12-month product roadmap as Slide 20 + Slide 21 to the
|
||||||
|
deck. Add matching talking-points sections. Render via new pipeline.
|
||||||
|
- **Status:** complete
|
||||||
|
- **Requirements:** REQ-244
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Slide 20 + 21 in `nova-no-humans-platform-marp.md` + source-of-truth +
|
||||||
|
talking-points.
|
||||||
|
- HTML + PPTX re-rendered via `render_slides.sh`.
|
||||||
|
- 4-quarter product arc grounded in NORTH_STAR + deferred metrics.
|
||||||
|
|
||||||
|
### Phase P4 — transparent-terraform (workflow refactor + feature flags)
|
||||||
|
- **Description:** Split run_platform.sh → run_codegen.sh + run_postapply.sh.
|
||||||
|
Rewrite deploy.yml with native terraform steps. Add var.enabled to all L1
|
||||||
|
modules + L2 composition toggles. Wire forge repo variables as feature
|
||||||
|
flags. Fix stale artifact path.
|
||||||
|
- **Status:** complete
|
||||||
|
- **Requirements:** REQ-233, REQ-234, REQ-235, REQ-236, REQ-237, REQ-238
|
||||||
|
- **Success Criteria:**
|
||||||
|
- `scripts/run_codegen.sh` + `scripts/run_postapply.sh` exist.
|
||||||
|
- `deploy.yml` has native terraform init/validate/plan/apply steps.
|
||||||
|
- Every L1 module has `variable "enabled"` + `count = var.enabled ? 1 : 0`.
|
||||||
|
- L2 `composition.json` supports per-child `enabled`.
|
||||||
|
- `deploy.yml` reads `vars.ENABLE_*` as `-var` flags.
|
||||||
|
- Stale `/tmp/acdl_platform_run_v18` path fixed to `NOVA_WORK_DIR`.
|
||||||
|
- `pytest` passes; `run_platform.sh` shim backward-compat verified.
|
||||||
|
|
||||||
|
### Phase P5 — final-review-ship (Final Phase)
|
||||||
|
- **Description:** Final review + audit + milestone ship. Merge to main,
|
||||||
|
tag `v1.19.4` (final patch = milestone release), create release.
|
||||||
|
- **Status:** complete
|
||||||
|
- **Depends on:** [P1, P2, P3, P4]
|
||||||
|
- **Requirements:** REQ-230..244
|
||||||
|
- **Success Criteria:**
|
||||||
|
- Review + audit clean (no P0).
|
||||||
|
- Milestone branches merged to main.
|
||||||
|
- Tag `v1.19.4` created; release notes summarize all 15 requirements.
|
||||||
|
- CHECKPOINT cleared; milestone branches deleted.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
],
|
],
|
||||||
"active_project": "acdl",
|
"active_project": "acdl",
|
||||||
"active_projects": ["acdl"],
|
"active_projects": ["acdl"],
|
||||||
"active_milestone": "v1.19",
|
"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,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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Nova Slides Render — re-renders presentation deck when source files change.
|
||||||
|
name: Nova Slides Render
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'docs/presentations/**'
|
||||||
|
- 'scripts/render_slides.sh'
|
||||||
|
- 'assets/nova-sp-theme.css'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
render:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with: { fetch-depth: 0 }
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with: { node-version: '20' }
|
||||||
|
- name: Install Chrome
|
||||||
|
run: |
|
||||||
|
npx --yes @marp-team/marp-cli@latest --version
|
||||||
|
npx --yes @mermaid-js/mermaid-cli --version
|
||||||
|
- name: Render slides
|
||||||
|
run: bash scripts/render_slides.sh
|
||||||
|
- name: Commit rendered artifacts
|
||||||
|
run: |
|
||||||
|
git config user.name "nova-slides-bot"
|
||||||
|
git config user.email "bot@nova.local"
|
||||||
|
git add docs/presentations/*.html docs/presentations/*.pptx docs/presentations/assets/png/*.png
|
||||||
|
git diff --cached --quiet || git commit -m "chore(slides): re-render deck [skip ci]"
|
||||||
|
git push
|
||||||
+10
-15
@@ -1,35 +1,30 @@
|
|||||||
# GitHub Workflows — Nova Platform CI/CD Catalog
|
# 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,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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -41,3 +41,4 @@ metrics/lifecycle/
|
|||||||
*.crt
|
*.crt
|
||||||
*.jks
|
*.jks
|
||||||
*.keystore.coverage
|
*.keystore.coverage
|
||||||
|
.coverage
|
||||||
|
|||||||
@@ -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).
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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**:
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,270 +0,0 @@
|
|||||||
# Nova AWS Resource Migration Runbook (REQ-163, P4)
|
|
||||||
|
|
||||||
> **Milestone:** v1.15-Nova (Wave 4, P4). Renames every `acdl-*` AWS
|
|
||||||
> resource name → `nova-*` via Terraform. This is the heaviest Terraform
|
|
||||||
> phase of the rebrand and requires a **maintenance window**.
|
|
||||||
>
|
|
||||||
> **Plan-validated only.** Per A1, `NOVA_LIFECYCLE_MODE` defaults to
|
|
||||||
> `plan` (no live AWS mutation from CI). `terraform validate` passes; the
|
|
||||||
> live apply steps below are executed by a platform operator during the
|
|
||||||
> scheduled maintenance window. Each step has a verification + rollback.
|
|
||||||
|
|
||||||
## Scope (renamed resources)
|
|
||||||
|
|
||||||
| AWS resource | Before | After | Strategy |
|
|
||||||
|---|---|---|---|
|
|
||||||
| KMS alias | `alias/acdl-platform` | `alias/nova-platform` | cheap rename |
|
|
||||||
| SNS topic | `acdl-sod-halt` | `nova-sod-halt` | recreate |
|
|
||||||
| Security group | `acdl-ecs-sg` | `nova-ecs-sg` | recreate |
|
|
||||||
| Lambda (role/policy/function) | `acdl-contract-ingestor` | `nova-contract-ingestor` | recreate |
|
|
||||||
| DynamoDB contracts | `acdl-contracts` | `nova-contracts` | scan + copy |
|
|
||||||
| DynamoDB change-requests | `acdl-change-requests` | `nova-change-requests` | scan + copy |
|
|
||||||
| Secrets Manager secret | `acdl/github-token` | `nova/github-token` | recreate + re-store |
|
|
||||||
| ECR repo | `acdl-microservice` | `nova-microservice` | re-push |
|
|
||||||
| ECS cluster/service/task/role | `acdl-microservice` | `nova-microservice` | recreate |
|
|
||||||
| IAM user + policy | `acdl-spike-runner` (+ `-policy`) | `nova-spike-runner` (+ `-policy`) | re-bootstrap |
|
|
||||||
| IAM act-runner role | `acdl-act-runner-role` | `nova-act-runner-role` | re-bootstrap |
|
|
||||||
| IAM deploy role | `acdl-deploy-<repo>` | `nova-deploy-<repo>` | re-bootstrap |
|
|
||||||
| S3 state bucket | `acdl-tfstate-581513795199-us-east-1` | `nova-tfstate-581513795199-us-east-1` | `-migrate-state` |
|
|
||||||
| DynamoDB outbox | `acdl-outbox` | `nova-outbox` | scan + copy |
|
|
||||||
| Platform VPC/subnet/IGW/RT | `acdl-shared*` | `nova-shared*` | recreate (brief downtime) |
|
|
||||||
| CI VPC/subnet/SG/cluster | `acdl-ci-*` | `nova-ci-*` | recreate (CI-only) |
|
|
||||||
| ALB name prefix | `acdl-alb` | `nova-alb` | recreate (brief downtime, LAST) |
|
|
||||||
|
|
||||||
## Migration ordering (binding)
|
|
||||||
|
|
||||||
Order: **KMS alias → SNS/SG → Lambda → DynamoDB → ECR → IAM → state bucket → ALB**.
|
|
||||||
Each step is independently rollback-able. The ALB is last because it
|
|
||||||
requires the briefest downtime window.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Pre-flight
|
|
||||||
|
|
||||||
1. **Announce the maintenance window** (consumers are notified via the
|
|
||||||
P1 migration guide `docs/NOVA_MIGRATION.md`).
|
|
||||||
2. **Back up state** for every stack (see §State bucket — back up the
|
|
||||||
state JSON *before* `-migrate-state`).
|
|
||||||
3. Confirm `NOVA_LIFECYCLE_MODE=plan` (default) so CI does not mutate
|
|
||||||
AWS during the window.
|
|
||||||
4. Confirm the new `nova-*` destination tables/repos will be created by
|
|
||||||
the same Terraform apply (no manual pre-creation needed).
|
|
||||||
|
|
||||||
## Step 1 — KMS alias (`alias/acdl-platform` → `alias/nova-platform`)
|
|
||||||
|
|
||||||
- **Command (in `terraform/platform/`):**
|
|
||||||
```bash
|
|
||||||
terraform init -upgrade
|
|
||||||
terraform apply -replace=aws_kms_alias.nova_platform
|
|
||||||
```
|
|
||||||
(Terraform destroys the old alias + creates the new one — aliases are
|
|
||||||
cheap; the underlying key ID is unchanged.)
|
|
||||||
- **Verify:** `aws kms list-aliases --query 'Aliases[?AliasName==`alias/nova-platform`]'` returns the new alias; `alias/acdl-platform` is gone.
|
|
||||||
- **Rollback:** `terraform apply -replace=aws_kms_alias.nova_platform` against the prior revision (re-creates `alias/acdl-platform`). Resources encrypted by the key are unaffected (key ID unchanged).
|
|
||||||
|
|
||||||
## Step 2 — SNS topic + Security group (recreate)
|
|
||||||
|
|
||||||
- **Command:** `terraform apply` in `terraform/platform/`.
|
|
||||||
- SNS `acdl-sod-halt` → `nova-sod-halt` (the topic ARN changes; update `NOVA_SOD_HALT_TOPIC_ARN` wherever it is set).
|
|
||||||
- SG `acdl-ecs-sg` → `nova-ecs-sg` (the security group is re-attached to running ECS tasks; brief task restart).
|
|
||||||
- **Verify:** `aws sns list-topics` shows `nova-sod-halt`; `aws ec2 describe-security-groups` shows `nova-ecs-sg`.
|
|
||||||
- **Rollback:** `terraform apply` the prior revision re-creates the `acdl-*` names. The SNS topic has no message backlog (halt artifacts are fire-and-forget); the SG drift resolves on next task deploy.
|
|
||||||
|
|
||||||
## Step 3 — Lambda (recreate)
|
|
||||||
|
|
||||||
- **Command:** `terraform apply` in `terraform/platform/`.
|
|
||||||
- Lambda function `acdl-contract-ingestor` → `nova-contract-ingestor`.
|
|
||||||
- Execution role `acdl-contract-ingestor-role` → `nova-contract-ingestor-role`.
|
|
||||||
- Inline policy `acdl-contract-ingestor-policy` → `nova-contract-ingestor-policy`.
|
|
||||||
- The Lambda env vars (`CONTRACTS_TABLE`, `GITHUB_TOKEN_SECRET_ID`) now resolve to `nova-*` defaults.
|
|
||||||
- **Verify:** `aws lambda list-functions` shows `nova-contract-ingestor`; the Function URL returns 200 on a SigV4-signed invoke. The `consumer_invoke_policy.json` rendered output (Terraform `consumer_invoke_policy_rendered`) now references `function:nova-contract-ingestor` — re-distribute to consumer deploy roles.
|
|
||||||
- **Rollback:** `terraform apply` the prior revision re-creates `acdl-contract-ingestor`. Consumer deploy roles must point back at the old Function ARN (re-distribute the prior `consumer_invoke_policy.json`).
|
|
||||||
|
|
||||||
## Step 4 — DynamoDB (scan + copy)
|
|
||||||
|
|
||||||
DynamoDB table names are immutable post-creation, so the migration is a
|
|
||||||
**scan + copy** (not a rename). The new `nova-*` tables are created by
|
|
||||||
the same Terraform apply (Step 3). The data-migration script copies
|
|
||||||
every item and verifies row counts.
|
|
||||||
|
|
||||||
- **Command (from repo root):**
|
|
||||||
```bash
|
|
||||||
# Dry-run first (no writes):
|
|
||||||
python3 scripts/migrate_dynamodb_data.py
|
|
||||||
# Execute the copy:
|
|
||||||
python3 scripts/migrate_dynamodb_data.py --apply
|
|
||||||
# A single table:
|
|
||||||
python3 scripts/migrate_dynamodb_data.py --table contracts --apply
|
|
||||||
```
|
|
||||||
The script scans `acdl-contracts` → copies to `nova-contracts`, and
|
|
||||||
`acdl-change-requests` → `nova-change-requests`, then verifies the
|
|
||||||
destination row count == source row count (re-scan, not
|
|
||||||
`DescribeTable.ItemCount` which lags ~6h).
|
|
||||||
- **Verify:**
|
|
||||||
```bash
|
|
||||||
# Row counts must match (printed by the script). Manual cross-check:
|
|
||||||
aws dynamodb scan --table-name nova-contracts --select COUNT
|
|
||||||
aws dynamodb scan --table-name acdl-contracts --select COUNT
|
|
||||||
```
|
|
||||||
Then **point consumers at the new tables** (the Lambda already reads
|
|
||||||
`nova-*` defaults; any direct DynamoDB consumers update their env).
|
|
||||||
- **Keep the old tables** (`acdl-contracts`, `acdl-change-requests`)
|
|
||||||
until consumers are verified reading from `nova-*`. **Deletion is a
|
|
||||||
manual post-verification step:**
|
|
||||||
```bash
|
|
||||||
aws dynamodb delete-table --table-name acdl-contracts
|
|
||||||
aws dynamodb delete-table --table-name acdl-change-requests
|
|
||||||
```
|
|
||||||
Only delete after a full soak period confirms `nova-*` reads succeed.
|
|
||||||
- **Rollback:** Re-point consumers at `acdl-*` (the old tables are
|
|
||||||
retained). The copy is additive (no data loss). To roll back a partial
|
|
||||||
copy, re-run `--apply` (idempotent — `PutItem` overwrites).
|
|
||||||
|
|
||||||
### Outbox table (`acdl-outbox` → `nova-outbox`)
|
|
||||||
|
|
||||||
The evidence outbox table follows the same scan+copy pattern (it is
|
|
||||||
created by `terraform/bootstrap/create_state_backend.py`).
|
|
||||||
- **Command:** `python3 scripts/migrate_dynamodb_data.py --source acdl-outbox --dest nova-outbox --apply`
|
|
||||||
- The `core/outbox_writer.py` default + `core/regression_verify.py`
|
|
||||||
CAP-015 probe now reference `nova-outbox` (P4 updated both). The
|
|
||||||
regression gate's live-AWS CAP-015 will return `Verified` once the
|
|
||||||
`nova-outbox` table exists live; until then it is `Decayed` (the gate
|
|
||||||
is re-run at milestone complete after the live migration).
|
|
||||||
|
|
||||||
## Step 5 — ECR (re-push)
|
|
||||||
|
|
||||||
- **Command:** `terraform apply` in `terraform/microservice/` creates
|
|
||||||
the new `nova-microservice` ECR repo. Re-push the image:
|
|
||||||
```bash
|
|
||||||
python3 scripts/push_consumer_image.py # creates nova-microservice + prints docker tag/push
|
|
||||||
```
|
|
||||||
(The script's `ECR_REPO_NAME` is now `nova-microservice`.)
|
|
||||||
- **Verify:** `aws ecr describe-repositories` shows `nova-microservice`; `docker pull <acct>.dkr.ecr.us-east-1.amazonaws.com/nova-microservice:latest` succeeds.
|
|
||||||
- **Rollback:** The old `acdl-microservice` repo is retained until the
|
|
||||||
soak passes. Re-push to it if a rollback is needed. Delete it manually:
|
|
||||||
`aws ecr delete-repository --repository-name acdl-microservice --force`.
|
|
||||||
|
|
||||||
## Step 6 — IAM (re-bootstrap)
|
|
||||||
|
|
||||||
- **Command:**
|
|
||||||
```bash
|
|
||||||
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
|
|
||||||
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
|
|
||||||
python3 terraform/bootstrap/create_state_backend.py # creates nova-outbox (idempotent)
|
|
||||||
python3 terraform/bootstrap/create_iam_user.py # creates nova-spike-runner
|
|
||||||
python3 terraform/bootstrap/apply_iam_baseline.py # creates nova-spike-runner-policy + nova-act-runner-role
|
|
||||||
bash scripts/rotate_spike_key.sh # rotates the nova-spike-runner key
|
|
||||||
```
|
|
||||||
The deploy role `acdl-deploy-<repo>` → `nova-deploy-<repo>` is
|
|
||||||
created by the bootstrap (the deploy workflow
|
|
||||||
`.gitea/.github/workflows/deploy.yml` now references
|
|
||||||
`role/nova-deploy-{1}`).
|
|
||||||
- **Verify:** `aws iam get-user --user-name nova-spike-runner`;
|
|
||||||
`aws iam list-attached-user-policies --user-name nova-spike-runner`
|
|
||||||
shows `nova-spike-runner-policy`;
|
|
||||||
`aws iam get-role --role-name nova-act-runner-role`.
|
|
||||||
- **Rollback:** Re-run the prior bootstrap scripts (they create
|
|
||||||
`acdl-spike-runner` + `acdl-act-runner-role`). The deploy workflow's
|
|
||||||
`role-to-assume` must be reverted to `acdl-deploy-` (prior revision).
|
|
||||||
|
|
||||||
## Step 7 — State bucket (`acdl-tfstate-*` → `nova-tfstate-*`, `-migrate-state`)
|
|
||||||
|
|
||||||
The S3 state backend is renamed. Terraform's `-migrate-state` copies the
|
|
||||||
state objects to the new bucket. **Back up the state JSON first.**
|
|
||||||
|
|
||||||
- **Back up state (per stack):**
|
|
||||||
```bash
|
|
||||||
for stack in platform microservice ci-vpc; do
|
|
||||||
aws s3 cp s3://acdl-tfstate-581513795199-us-east-1/$stack/terraform.tfstate \
|
|
||||||
./backup-$stack.tfstate
|
|
||||||
done
|
|
||||||
```
|
|
||||||
- **Command (per stack):** the backend config in each
|
|
||||||
`terraform/*/terraform.tf` now points at `nova-tfstate-...`.
|
|
||||||
```bash
|
|
||||||
cd terraform/platform
|
|
||||||
terraform init -migrate-state # copies state acdl-tfstate → nova-tfstate
|
|
||||||
cd ../microservice
|
|
||||||
terraform init -migrate-state
|
|
||||||
cd ../ci-vpc
|
|
||||||
terraform init -migrate-state
|
|
||||||
```
|
|
||||||
- **Verify:** `aws s3 ls s3://nova-tfstate-581513795199-us-east-1/`
|
|
||||||
shows the state keys; `terraform state list` in each dir lists the
|
|
||||||
expected resources.
|
|
||||||
- **Rollback:** Point the backend back at `acdl-tfstate-*` and re-run
|
|
||||||
`terraform init -migrate-state` (restores from the backup bucket). The
|
|
||||||
old `acdl-tfstate-*` bucket is retained until the soak passes. Delete
|
|
||||||
it manually:
|
|
||||||
`aws s3 rb s3://acdl-tfstate-581513795199-us-east-1 --force`.
|
|
||||||
|
|
||||||
## Step 8 — ALB (recreate, brief downtime, LAST)
|
|
||||||
|
|
||||||
The ALB is last because its recreation requires the briefest downtime
|
|
||||||
window (the ECS service is re-attached to the new target group).
|
|
||||||
|
|
||||||
- **Command:** `terraform apply` in `terraform/microservice/`. The ALB
|
|
||||||
`acdl-microservice` / `acdl-alb` → `nova-microservice` / `nova-alb`.
|
|
||||||
- **Verify:** `aws elbv2 describe-load-balancers` shows the new ALB;
|
|
||||||
`curl http://<new-alb-dns>/` returns 200.
|
|
||||||
- **Rollback:** `terraform apply` the prior revision re-creates the
|
|
||||||
`acdl-*` ALB (brief downtime again). The old ALB DNS is retained until
|
|
||||||
consumers are re-pointed.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Post-migration
|
|
||||||
|
|
||||||
1. **Soak:** run consumers against `nova-*` for a full verification
|
|
||||||
window (deploy a test contract end-to-end).
|
|
||||||
2. **Delete old resources** (manual, only after soak):
|
|
||||||
- DynamoDB: `acdl-contracts`, `acdl-change-requests`, `acdl-outbox`
|
|
||||||
- ECR: `acdl-microservice`
|
|
||||||
- IAM: `acdl-spike-runner` (+ policy), `acdl-act-runner-role`,
|
|
||||||
`acdl-deploy-<repo>`
|
|
||||||
- S3: `acdl-tfstate-581513795199-us-east-1`
|
|
||||||
- SNS: `acdl-sod-halt`
|
|
||||||
- SG: `acdl-ecs-sg`
|
|
||||||
- Secrets Manager: `acdl/github-token`
|
|
||||||
- KMS alias: `alias/acdl-platform`
|
|
||||||
- ALB: `acdl-alb` / `acdl-microservice`
|
|
||||||
3. **Regression gate:** re-run `bash scripts/run_regression.sh`. The
|
|
||||||
live-AWS CAP-013..016 probes should return `Verified` (the `nova-*`
|
|
||||||
tables + state bucket exist). CAP-015 (outbox) flips from `Decayed`
|
|
||||||
→ `Verified` once `nova-outbox` is live.
|
|
||||||
|
|
||||||
## What P5 owns (not P4)
|
|
||||||
|
|
||||||
- **Remove dual-read fallback:** `core/env.py` `get_env()` drops the
|
|
||||||
`ACDL_*` fallback; shell scripts drop `:-$ACDL_X`. P4 keeps the
|
|
||||||
dual-read (deployments don't break mid-window).
|
|
||||||
- **`nova_tagging.py` hard-fail on `acdl:*`:** P3 set hard mode (no
|
|
||||||
`acdl:*`-only tags); P5 tightens to fail on any `acdl:*` presence. P4
|
|
||||||
leaves P3's behavior.
|
|
||||||
- **Delete `ACDL_*` Gitea secrets:** the `NOVA_*` aliases created in P2
|
|
||||||
are now the only source.
|
|
||||||
- **Finalize `docs/NOVA_MIGRATION.md`:** mark the migration complete
|
|
||||||
(cutoff passed).
|
|
||||||
- **Milestone ship:** tag `v1.15.4`, merge to `main`, Gitea release.
|
|
||||||
|
|
||||||
## Files touched in P4
|
|
||||||
|
|
||||||
- `terraform/platform/main.tf`, `terraform/microservice/main.tf`,
|
|
||||||
`terraform/ci-vpc/main.tf` — resource renames + backend bucket.
|
|
||||||
- `terraform/{platform,microservice,ci-vpc}/terraform.tf` — state bucket.
|
|
||||||
- `terraform/platform/consumer_invoke_policy.json` — Lambda ARN.
|
|
||||||
- `terraform/bootstrap/{create_state_backend,create_iam_user,apply_iam_baseline}.py`,
|
|
||||||
`spike_runner_policy.json`, `.bootstrap_state.json`, `README.md` —
|
|
||||||
IAM/outbox/state-bucket renames.
|
|
||||||
- `modules/l1/*/terraform/**` + `modules/l1/alb/instance.json` — L1
|
|
||||||
resource-name defaults.
|
|
||||||
- `modules/l2/microservice/composition.json` — `nova-app-role` default.
|
|
||||||
- `core/lambda/contract_ingestor.py` — default table names (D-111).
|
|
||||||
- `core/outbox_writer.py`, `core/regression_verify.py`,
|
|
||||||
`core/local_emulators.py` — outbox table consistency (cross-territory,
|
|
||||||
minimal).
|
|
||||||
- `.gitea/workflows/deploy.yml` + `.github/workflows/deploy.yml` —
|
|
||||||
`nova-deploy-` role ARN + artifact names.
|
|
||||||
- `scripts/migrate_dynamodb_data.py` (NEW), `scripts/rotate_spike_key.sh`,
|
|
||||||
`scripts/push_consumer_image.py`.
|
|
||||||
- `tests/**` — fixtures updated to assert `nova-*`.
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
# Nova Migration Guide — What Consumers Must Know
|
|
||||||
|
|
||||||
> **STATUS: COMPLETE (milestone v1.15.4, 2026-07-30).** The Nova rebrand
|
|
||||||
> is fully rolled out. The dual-read / parallel-write grace period has
|
|
||||||
> ended (P5 cutoff passed). All `ACDL_*` env var fallbacks, `.acdl/`
|
|
||||||
> consumer-path fallbacks, `/acdl/` SSM-path fallbacks, `acdl:*` tag-key
|
|
||||||
> fallbacks, and `acdl-*` AWS resource names are removed. Consumers must
|
|
||||||
> use the `NOVA_*` / `.nova/` / `/nova/` / `nova:*` / `nova-*` names
|
|
||||||
> exclusively. If you have not yet migrated, follow the steps below.
|
|
||||||
|
|
||||||
> **Nova** is the new product brand for the platform formerly known as
|
|
||||||
> **ACDL** (Agentic Cloud Delivery Platform). This guide documents the
|
|
||||||
> breaking changes from the rebrand rollout (Phases P2–P4, cutoff P5)
|
|
||||||
> and tells you exactly what to do.
|
|
||||||
|
|
||||||
## What is NOT changing
|
|
||||||
|
|
||||||
- **The Gitea repository name** (`continuous-intelligence/acdl`) is **not**
|
|
||||||
changing. Only the product brand is changing. The `uses:` reference
|
|
||||||
(`acdl/.github/workflows/deploy.yml@vX.Y`) and the GitHub `acdl/acdl` repo
|
|
||||||
path are unchanged for the duration of the rebrand; the workflow
|
|
||||||
`uses:` reference will be migrated in a later, separately-announced step.
|
|
||||||
- **The platform behavior** is unchanged. Same pipeline stages, same
|
|
||||||
contract schema, same confidence model, same evidence stream, same
|
|
||||||
modules. Only the brand, the on-disk path, the env var names, the SSM
|
|
||||||
path, the AWS tag keys, and the AWS resource names are changing.
|
|
||||||
|
|
||||||
## The 5 breaking changes
|
|
||||||
|
|
||||||
Five things that consumers may reference are being renamed. Each is
|
|
||||||
scheduled into a phase, ships with a grace period, and has a cutoff.
|
|
||||||
|
|
||||||
### 1. Consumer contract path — Phase P2
|
|
||||||
|
|
||||||
- **Old:** `.acdl/contract.yml`
|
|
||||||
- **New:** `.nova/contract.yml`
|
|
||||||
- **Phase:** P2 (env vars + consumer path)
|
|
||||||
- **Grace period:** during P2–P4 the deploy workflow reads **both** paths
|
|
||||||
(`.nova/contract.yml` first, falling back to `.acdl/contract.yml` if the
|
|
||||||
new path is absent). Your existing contracts keep working until P5.
|
|
||||||
- **Cutoff:** P5 removes the `.acdl/` fallback. Move your contract file
|
|
||||||
before P5.
|
|
||||||
- **What you must do:** rename the directory in your consumer repo from
|
|
||||||
`.acdl/` to `.nova/` and update any `contract:` workflow input that
|
|
||||||
points at the old path. Nothing else changes in the contract content.
|
|
||||||
|
|
||||||
### 2. Environment variables — Phase P2
|
|
||||||
|
|
||||||
- **Old:** `ACDL_*` (e.g. `ACDL_LIFECYCLE_MODE`, `ACDL_AWS_ACCOUNT_ID`,
|
|
||||||
`ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID`, …)
|
|
||||||
- **New:** `NOVA_*` (e.g. `NOVA_LIFECYCLE_MODE`, `NOVA_AWS_ACCOUNT_ID`,
|
|
||||||
`NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID`, …)
|
|
||||||
- **Phase:** P2 (env vars + consumer path)
|
|
||||||
- **Grace period — dual-read fallback:** during P2–P4 the platform reads
|
|
||||||
**`NOVA_*` first, then falls back to `ACDL_*`** if the Nova variable is
|
|
||||||
unset. This means your CI secrets, workflow env blocks, and local
|
|
||||||
`.env.secrets` keep working unchanged through P4. You do not need to
|
|
||||||
rename everything in one shot — rename a variable and the dual-read picks
|
|
||||||
it up; leave one old and it still resolves.
|
|
||||||
- **Cutoff:** P5 removes the `ACDL_*` fallback. After P5, only `NOVA_*`
|
|
||||||
is read.
|
|
||||||
- **What you must do:** rename your `ACDL_*` CI secrets, workflow `env:`
|
|
||||||
blocks, and any local `.env.secrets` entries to `NOVA_*`. Because of the
|
|
||||||
dual-read, you can do this incrementally across P2–P4 — but it must be
|
|
||||||
complete before P5.
|
|
||||||
|
|
||||||
### 3. SSM parameter path — Phase P3 (DONE)
|
|
||||||
|
|
||||||
- **Old:** `/acdl/{env}/{contractId}/{output}`
|
|
||||||
- **New:** `/nova/{env}/{contractId}/{output}`
|
|
||||||
- **Phase:** P3 (SSM paths + tag keys) — **shipped in P3**
|
|
||||||
- **Grace period — parallel-write:** during P3–P4 the platform **writes
|
|
||||||
every output to both** the `/acdl/…` and `/nova/…` SSM paths, and reads
|
|
||||||
from `/nova/…` first (falling back to `/acdl/…`). Any hardcoded SSM path
|
|
||||||
reads in your application code keep resolving through P4. The P3
|
|
||||||
migration script (`scripts/migrate_ssm_paths.py`) copies existing
|
|
||||||
`/acdl/…` parameters to `/nova/…`, verifies the copy, and deletes the
|
|
||||||
old ones.
|
|
||||||
- **Cutoff:** P5 stops writing to `/acdl/…` and removes the read fallback.
|
|
||||||
After P5 only `/nova/…` exists.
|
|
||||||
- **What you must do:** if your application code or runbooks read deploy
|
|
||||||
outputs from SSM by hardcoded path, update the path prefix from `/acdl/`
|
|
||||||
to `/nova/`. If you consume outputs only via the PR-comment / GitHub
|
|
||||||
issue surface, you do nothing — the platform republishes under the new
|
|
||||||
path automatically.
|
|
||||||
|
|
||||||
### 4. AWS tag keys — Phase P3 (DONE)
|
|
||||||
|
|
||||||
- **Old:** `acdl:owner`, `acdl:environment`, `acdl:contract`,
|
|
||||||
`acdl:cost-center`, `acdl:ref`
|
|
||||||
- **New:** `nova:owner`, `nova:environment`, `nova:contract`,
|
|
||||||
`nova:cost-center`, `nova:ref`
|
|
||||||
- **Phase:** P3 (SSM paths + tag keys) — **shipped in P3**
|
|
||||||
- **Grace period — parallel-tag period:** during P3–P4 the platform
|
|
||||||
**tags every resource with both** the `acdl:*` and `nova:*` keys (same
|
|
||||||
values). The ABAC session policy matches on **either** key set, so your
|
|
||||||
existing scoped permissions keep working. The default cost-center value
|
|
||||||
moves from `acdl-default` to `nova-default` (both written during the
|
|
||||||
parallel-tag period). Terraform now emits `nova:*` keys; old `acdl:*`
|
|
||||||
tags on pre-P3 live resources are removed by the P4 runbook's
|
|
||||||
`scripts/untag_acdl_keys.py` step after the `nova:*` tags are applied
|
|
||||||
live.
|
|
||||||
- **Cutoff:** P5 stops writing the `acdl:*` keys and the ABAC policy matches
|
|
||||||
only on `nova:*`. After P5, resources created before P5 still carry the
|
|
||||||
old `acdl:*` tags (tags are not retroactively rewritten) but **new**
|
|
||||||
resources are tagged `nova:*` only, and the policy no longer grants
|
|
||||||
access via `acdl:*`.
|
|
||||||
- **What you must do:** if you have IAM policies, Cost Explorer filters,
|
|
||||||
or billing groupings that key off `acdl:*` tag keys, add a parallel
|
|
||||||
`nova:*` condition (or migrate to `nova:*`) before P5. The platform
|
|
||||||
handles the dual-tagging; you only need to update your own tag-key
|
|
||||||
references.
|
|
||||||
|
|
||||||
### 5. AWS resource names — Phase P4
|
|
||||||
|
|
||||||
- **Old:** `acdl-*` (DynamoDB tables `acdl-contracts`,
|
|
||||||
`acdl-change-requests`; Lambda `acdl-contract-ingestor`; SNS
|
|
||||||
`acdl-sod-halt`; security group `acdl-ecs-sg`; KMS alias
|
|
||||||
`alias/acdl-platform`; ECS services, ECR repos, IAM user
|
|
||||||
`acdl-spike-runner`, state bucket `acdl-tfstate-*`, ALB `acdl-alb`,
|
|
||||||
`acdl-deploy-*`)
|
|
||||||
- **New:** `nova-*` (the same resources, prefixed `nova-`)
|
|
||||||
- **Phase:** P4 (resource names) — **maintenance window**
|
|
||||||
- **Grace period:** P4 is a **planned maintenance window**. AWS resources
|
|
||||||
cannot be renamed in place, so P4 provisions the `nova-*` resources,
|
|
||||||
migrates data (DynamoDB tables, S3 state), repoints the platform, and
|
|
||||||
tears down the `acdl-*` resources. The platform team schedules and
|
|
||||||
announces the window; consumers do not provision or rename anything
|
|
||||||
themselves.
|
|
||||||
- **Cutoff:** the `acdl-*` resources are decommissioned at the end of the
|
|
||||||
P4 maintenance window. After P4, only `nova-*` resources exist.
|
|
||||||
- **What you must do:** nothing for the resource names themselves — the
|
|
||||||
platform owns the rename. If your application code or runbooks reference
|
|
||||||
a specific `acdl-*` resource by name (e.g. a hardcoded DynamoDB table
|
|
||||||
name or ECR URI), update it to the `nova-*` name during P4. The platform
|
|
||||||
publishes the exact old → new name mapping with the P4 announcement.
|
|
||||||
|
|
||||||
## Timeline at a glance
|
|
||||||
|
|
||||||
| Phase | What ships | Grace period | Cutoff |
|
|
||||||
|-------|------------|--------------|--------|
|
|
||||||
| **P1** (this phase) | Brand prose, docs, decks, schema `$id`, release titles | n/a (prose only) | n/a |
|
|
||||||
| **P2** | `.nova/` contract path + `NOVA_*` env vars | dual-read: `.nova/`→`.acdl/`, `NOVA_*`→`ACDL_*` | **P5** removes fallback |
|
|
||||||
| **P3** | `/nova/` SSM path + `nova:*` tag keys | parallel-write (SSM) + parallel-tag (ABAC matches either) | **P5** removes old path/tags |
|
|
||||||
| **P4** | `nova-*` AWS resource names | maintenance window (platform-owned migration) | end of P4 window |
|
|
||||||
| **P5** | Fallback removal | — | `ACDL_*` env vars, `.acdl/` path, `/acdl/` SSM, `acdl:*` tags stop working |
|
|
||||||
|
|
||||||
## What consumers must do (checklist)
|
|
||||||
|
|
||||||
1. **Before P5 — contract path:** move `.acdl/contract.yml` →
|
|
||||||
`.nova/contract.yml` in your consumer repo; update the `contract:`
|
|
||||||
workflow input. *(Can be done any time in P2–P4.)*
|
|
||||||
2. **Before P5 — env vars:** rename `ACDL_*` CI secrets / workflow `env:`
|
|
||||||
blocks / local `.env.secrets` to `NOVA_*`. *(Incremental during P2–P4;
|
|
||||||
dual-read keeps you green.)*
|
|
||||||
3. **Before P5 — SSM reads:** if you read deploy outputs from SSM by
|
|
||||||
hardcoded `/acdl/…` path, update to `/nova/…`. *(Skip if you consume
|
|
||||||
outputs via PR comments only.)*
|
|
||||||
4. **Before P5 — tag-key references:** if you have IAM policies, Cost
|
|
||||||
Explorer filters, or billing groupings keyed off `acdl:*`, add or
|
|
||||||
migrate to `nova:*`. *(Platform handles dual-tagging.)*
|
|
||||||
5. **During P4 — resource-name references:** if your code or runbooks
|
|
||||||
reference a specific `acdl-*` AWS resource by name, update to the
|
|
||||||
`nova-*` name per the P4 mapping announcement. *(Platform owns the
|
|
||||||
rename itself.)*
|
|
||||||
|
|
||||||
## Questions
|
|
||||||
|
|
||||||
If anything in this guide is unclear, or you are unsure whether your
|
|
||||||
consumer repo references a renamed value, open an issue on the platform
|
|
||||||
repo. The platform team will confirm what you need to change and when.
|
|
||||||
|
|
||||||
> **Note:** the real Gitea repository name (`continuous-intelligence/acdl`)
|
|
||||||
> is **not** changing — only the product brand. The `uses:` workflow
|
|
||||||
> reference and repo path are migrated in a separately-announced later step;
|
|
||||||
> until then, keep your `uses: acdl/.github/workflows/deploy.yml@vX.Y`
|
|
||||||
> reference as-is.
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
# Nova — The No-Humans Infrastructure Platform: Thesis Defensibility Brief
|
|
||||||
|
|
||||||
> v1.17 — Strategic Direction, Leadership Metrics & Unified Story (REQ-213)
|
|
||||||
> Generated: 2026-08-04
|
|
||||||
|
|
||||||
## The thesis
|
|
||||||
|
|
||||||
Nova is the autonomous infrastructure layer that lets product teams
|
|
||||||
ship without engaging an operator, and lets executives trust the AI
|
|
||||||
not because it never fails but because every decision is captured,
|
|
||||||
scored, and accountable.
|
|
||||||
|
|
||||||
**Autonomy in operations; human at stage gates.** The operator is
|
|
||||||
removed from the loop of normal operations. Human attestation remains
|
|
||||||
required at stage gates — QA signs off for production, SRE greenlights
|
|
||||||
based on operational readiness. The absence of an operator is never
|
|
||||||
the absence of a record.
|
|
||||||
|
|
||||||
## Grounded proof (measurable today)
|
|
||||||
|
|
||||||
| Proof | Source | Status |
|
|
||||||
|-------|--------|--------|
|
|
||||||
| 18 capabilities verified, 4 honestly skipped (0 broken) | `REGRESSION_REPORT.json` | grounded |
|
|
||||||
| Decision Ledger captures 100% of AI decisions with outcome backfill | `metrics/decision_ledger.db` | grounded (this milestone) |
|
|
||||||
| Attestation Coverage: 100% of prod/dr promotions attested by a human | `hitl_gates.py` + outbox `approver_*` | grounded |
|
|
||||||
| Confidence-gated policy engine (not an LLM) — 6 weighted inputs, band outcome | `confidence_signal.py` | grounded |
|
|
||||||
| 8-concern attestation matrix with separation-of-duties on prod | `attestation_matrix.py` + `separation_of_duties.py` | grounded |
|
|
||||||
| Pre-apply cost estimates (Infracost, offline) | `infracost_adapter.py` | grounded |
|
|
||||||
| Test suite passes (~656 tests) | `metrics/test-results.xml` | grounded |
|
|
||||||
|
|
||||||
## Deferred proof (measurable when blocking decisions lift)
|
|
||||||
|
|
||||||
| Proof | Blocking Decision | Unblock Requirement |
|
|
||||||
|-------|-------------------|---------------------|
|
|
||||||
| Touchless Resolution Rate ≥99% across production estates | 0 consumers today | Pilot estate activation |
|
|
||||||
| Live infrastructure health (ECS, ALB, RPS) | D-096 | Live AWS re-provisioning |
|
|
||||||
| Onboarding funnel: requested → granted | D-113/D-114/D-119 | Auto-grant implementation |
|
|
||||||
| Drift auto-reversal rate ≥95% | D-096 + no scheduler | Drift detection scheduler |
|
|
||||||
| Predictive vs reactive ratio ≥3:1 | future emitter | ML anomaly-forecasting service |
|
|
||||||
| Tamper-evident ledger checkpoints (S3 Object Lock + JWS) | D-083 | Audit ledger build-out |
|
|
||||||
|
|
||||||
## Anti-claims (what Nova is NOT)
|
|
||||||
|
|
||||||
1. **Nova's "AI" is NOT an LLM planner.** It is a confidence-gated
|
|
||||||
policy engine (confidence_signal + HITL gate). The Decision Ledger
|
|
||||||
captures this real decision path — not a fabricated "AI agent" that
|
|
||||||
doesn't exist yet (D-122). When an LLM planner is added, it will emit
|
|
||||||
richer `alternatives_considered` without schema breakage.
|
|
||||||
2. **Nova does NOT remove humans from accountability.** Only from
|
|
||||||
operations. Every stage-gate promotion (qa/prod/dr) requires a human
|
|
||||||
attestation recorded with approver identity, separation-of-duties
|
|
||||||
check, and the 8-concern evidence matrix (NORTH_STAR Anti-Goal #3).
|
|
||||||
3. **Nova is NOT for legacy, untagged, or freeform infrastructure.** It
|
|
||||||
requires Terraform-managed, policy-aligned, fully-tagged inputs
|
|
||||||
(NORTH_STAR Anti-Goal #4).
|
|
||||||
4. **Nova does NOT fabricate metrics.** Every metric is grounded (cites
|
|
||||||
a source file), derived (documented formula), or deferred (cites a
|
|
||||||
blocking decision ID). No fabricated numbers in any deck slide or
|
|
||||||
METRICS.md entry (the "no fabrication" hard constraint).
|
|
||||||
|
|
||||||
## What "won" looks like
|
|
||||||
|
|
||||||
By month 18, 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.
|
|
||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
# Nova Onboarding — No-Humans Request Path (v1.16, REQ-182..184)
|
# Nova Onboarding — Autonomous Request Path (v1.16, REQ-182..184)
|
||||||
|
|
||||||
The v1.16 milestone implements the **request path** of the no-humans
|
The v1.16 milestone implements the **request path** of the autonomous
|
||||||
onboarding flow (D-113). A consumer can submit an onboarding request
|
onboarding flow (D-113). A consumer can submit an onboarding request
|
||||||
without contacting the platform team; the platform generates an
|
without contacting the platform team; the platform generates an
|
||||||
environment binding + (in a future milestone) provisions the AWS resources.
|
environment binding + (in a future milestone) provisions the AWS resources.
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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 -> resolver -> stack -> adapter<br/>-> security checks -> infrastructure plan -> policy checks<br/>-> confidence -> apply -> evidence event| C
|
B["platform runners<br/>(modules + pipelines + adapters + schemas)"] -->|contract -> resolver -> stack -> adapter<br/>-> security checks -> infrastructure plan -> policy checks<br/>-> confidence -> apply -> evidence event| C
|
||||||
C["your resources in AWS"]
|
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
|
||||||
|
|||||||
@@ -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.
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 \
|
||||||
@@ -102,14 +102,14 @@ 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. As of v1.18 (REQ-228, D-141), PPTX
|
PNG diagrams are embedded in the file. As of v1.18 (REQ-228, D-141), PPTX
|
||||||
files **are committed to the repo** as first-class binary artifacts (no LFS)
|
files **are committed to the repo** as first-class binary artifacts (no LFS)
|
||||||
and are also attached to the phase's Gitea release via
|
and are also attached to the phase's release via
|
||||||
`scripts/attach_release_asset.py`. The render + commit + attach pipeline is
|
`scripts/attach_release_asset.py`. The render + commit + attach pipeline is
|
||||||
automated by `scripts/render_deck.sh`.
|
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:
|
||||||
@@ -144,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
|
||||||
@@ -300,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 \
|
||||||
@@ -311,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
|
||||||
|
|
||||||
@@ -324,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
|
||||||
@@ -352,4 +337,4 @@ attachments to the Gitea release.
|
|||||||
> deck with a 5-act arc (Problem → Vision → How → Proof → Roadmap). v1.18
|
> 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
|
> (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
|
> S&P Global Energy theme is restored (REQ-214, P1). PPTX is committed to
|
||||||
> git + attached to the Gitea release (REQ-228, D-141).
|
> 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,30 +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-family: "Akkurat Pro", "Helvetica Neue", "Arial", sans-serif;
|
|
||||||
font-size: 22px;
|
|
||||||
color: #1B1B1B;
|
|
||||||
}
|
|
||||||
h1 { color: #D6002A; font-size: 34px; margin-bottom: 0.3em; }
|
|
||||||
h2 { color: #D6002A; font-size: 26px; margin-bottom: 0.2em; }
|
|
||||||
section.title { background: #1B1B1B; color: #fff; border-top: 8px solid #D6002A; }
|
|
||||||
section.title h1 { color: #fff; }
|
|
||||||
table { font-size: 18px; width: 100%; }
|
|
||||||
th { background: #F0F0F0; }
|
|
||||||
blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; }
|
|
||||||
img { display: block; margin: 0 auto; max-height: 320px; }
|
|
||||||
.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; }
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- _class: title -->
|
<!-- _class: title -->
|
||||||
@@ -383,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."
|
||||||
|
|||||||
@@ -131,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.0–v1.19) are behind us; this is forward-looking.
|
||||||
|
- Q1 (Pilot Activation): re-provision live AWS, activate the first pilot estate, light up the three post-pilot metrics. Onboarding auto-grant ships.
|
||||||
|
- Q2 (Provable Trust): tamper-evident ledger (Object Lock + JWS), daily checkpoints, live cost reconciliation. Trust is the moat — features can be copied; an immutable decision history cannot.
|
||||||
|
- Q3 (Compounding ROI + Drift): drift detection + auto-reversal, Infracost→CUR reconciliation on the pilot estate. This is the quarter the CFO points to a number that improves quarter-over-quarter.
|
||||||
|
- Q4 (Agentic Substrate + Predictive): ML anomaly-forecasting, AI-agent intent surface, multi-cloud preview. The Future Horizon target moves from aspiration to first measurement.
|
||||||
|
- The roadmap is grounded in the four strategic objectives from the North Star — autonomy, provable trust, ROI, agentic substrate — and the deferred-metric unblock paths from Slide 15.
|
||||||
|
|
||||||
|
**If asked "what about multi-cloud?"**: Q4 preview. AWS-only through Q3; Azure/GCP enters preview in Q4. We optimize for depth first, breadth second.
|
||||||
|
|
||||||
|
**If asked "what about the ML service?"**: Q4. The predictive-vs-reactive ≥3:1 target requires an ML anomaly-forecasting emitter — the most technically ambitious deliverable on the roadmap.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 21 — Quarter-by-Quarter Outcomes
|
||||||
|
|
||||||
|
**Key takeaway:** Each quarter has a concrete deliverable, a target metric, and a strategic-objective grounding. Nothing is hand-waved.
|
||||||
|
|
||||||
|
- Q1: three post-pilot metrics go live (Touchless ≥99%, Escalation <0.1%, Accuracy ≥99.5%). The measurement pipeline is already grounded; the denominator activates when the pilot estate runs.
|
||||||
|
- Q2: Decision Ledger Coverage was already grounded — the *tamper-evidence* is the Q2 upgrade (SQLite hash-chain → S3 Object Lock + JWS). Cost Savings ≥25% becomes CFO-grade with live CUR reconciliation.
|
||||||
|
- Q3: Drift Auto-Reversal ≥95% unblocks when the drift scheduler ships. Spend Reduction ≥25% is the same target, now measured against the pilot baseline.
|
||||||
|
- Q4: Predictive:Reactive ≥3:1 requires the ML forecasting service. AI-Agent Intent Share ≥40% moves from aspiration to first measurement.
|
||||||
|
- The month-18 destination: "Nova is the layer enterprise leadership points to when they say 'we don't have an infrastructure ops team anymore, and the audit trail is stronger than it ever was.'"
|
||||||
|
|
||||||
|
**If asked "are these committed or aspirational?"**: Q1–Q3 are committed (grounded pipeline + known unblock paths). Q4 targets are committed-deliverable, aspirational-metric — the ML service ships, the ≥40% intent share is first measurement (we don't control adoption rate).
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -414,4 +414,40 @@ This appendix shows the real cost figures + the zero-cost steady state.
|
|||||||
---
|
---
|
||||||
|
|
||||||
> **End of deck.** 16 main slides + 2 appendix slides = 18 total.
|
> **End of deck.** 16 main slides + 2 appendix slides = 18 total.
|
||||||
> Both old decks (`how-the-platform-works` + `the-developer-experience`) are retired (D-130).
|
> Both old decks (`how-the-platform-works` + `the-developer-experience`) are retired (D-130).
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 20 — 12-Month Product Roadmap
|
||||||
|
|
||||||
|
**The product arc from pilot activation to agentic substrate — four quarters, four outcomes.**
|
||||||
|
|
||||||
|
This slide shows the 12-month product roadmap — the forward-looking product-outcome arc. It is distinct from Slide 15 (the deferred-metric unblock paths), which explains *why* metrics are deferred and *how* they unblock. This slide shows *what's coming, when* — organized by quarter and board-level outcome.
|
||||||
|
|
||||||
|
**Speaker notes:**
|
||||||
|
|
||||||
|
- The roadmap is organized by product outcome, not by technical milestone. Each quarter activates one strategic objective from the North Star.
|
||||||
|
- Q1 is pilot activation — re-provision live AWS, light up the first pilot estate, and activate the three post-pilot denominator metrics (Touchless Resolution Rate, Human Escalation Frequency, AI Decision Accuracy). The onboarding auto-grant ships, so consumers can self-provision without platform-team intervention.
|
||||||
|
- Q2 is provable trust — the audit substrate goes from a SQLite hash-chain to a tamper-evident ledger (S3 Object Lock + JWS daily checkpoints). Live cost reconciliation (CUR) goes live so the CFO sees real cloud-spend data, not just pre-apply Infracost estimates.
|
||||||
|
- Q3 is compounding ROI + drift — drift detection ships (the scheduler that was deferred), drift auto-reversal activates (≥95% within one detection cycle), and the ≥25% spend-reduction target is measured against the pilot estate's 12-month pre-Nova baseline. This is the quarter the CFO points to a number that improves quarter-over-quarter.
|
||||||
|
- Q4 is agentic substrate + predictive — the ML anomaly-forecasting service ships (predictive-vs-reactive ≥3:1), the AI-agent intent consumption surface goes live, and multi-cloud (Azure/GCP) enters preview. The Future Horizon target (AI-Agent Intent Share ≥40%) moves from aspiration to first measurement.
|
||||||
|
|
||||||
|
**The month-18 destination:** *"Nova is the layer enterprise leadership points to when they say 'we don't have an infrastructure ops team anymore, and the audit trail is stronger than it ever was' — and it is the default substrate their AI engineering teams reach for first when an agent needs to deploy."*
|
||||||
|
|
||||||
|
> **Benefit:** you now know the 12-month product arc — each quarter activates a strategic objective and its corresponding board-level metric, from pilot activation through agentic substrate leadership.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Slide 21 — Quarter-by-Quarter Outcomes
|
||||||
|
|
||||||
|
This slide is the detail table behind Slide 20 — the concrete deliverable, target metric, and strategic-objective grounding for each quarter.
|
||||||
|
|
||||||
|
**Speaker notes:**
|
||||||
|
|
||||||
|
- Each row has a product theme (the narrative), a key deliverable (what ships), a target metric (the board-level number), and a grounding (which strategic objective it activates).
|
||||||
|
- Q1's three target metrics (Touchless ≥99%, Escalation <0.1%, Accuracy ≥99.5%) are the post-pilot metrics from the North Star — their measurement pipeline is grounded this milestone, but the denominator is zero until a pilot estate activates. Q1 is when the denominator goes live.
|
||||||
|
- Q2's Decision Ledger Coverage (100%) is already grounded; the *tamper-evidence* is the Q2 upgrade. Cost Savings ≥25% is the Infracost-grounded target — Q2 is when actual-spend reconciliation (CUR) makes it CFO-grade.
|
||||||
|
- Q3's Drift Auto-Reversal ≥95% is the deferred metric that unblocks when the drift detection scheduler ships. Spend Reduction ≥25% is the same target, now measured against the pilot baseline (not just estimated).
|
||||||
|
- Q4's Predictive:Reactive ≥3:1 requires the ML forecasting service — the most technically ambitious deliverable. AI-Agent Intent Share ≥40% is the Future Horizon target — it moves from aspiration to first measurement when the agentic consumption surface goes live.
|
||||||
|
- The roadmap does NOT duplicate the 19 completed technical milestones (v1.0–v1.19) — it is forward-looking only. It does NOT duplicate Slide 15's deferred-metric unblock paths — it uses them as the mechanism, but the audience sees the product arc, not the decision IDs.
|
||||||
|
|
||||||
|
> **Benefit:** you now know the quarter-by-quarter detail — each quarter has a concrete deliverable, a target metric grounded in a strategic objective, and a path from "honestly deferred" to "shipped and measured."
|
||||||
|
|||||||
Binary file not shown.
+45
-33
@@ -1,13 +1,12 @@
|
|||||||
# RACI — Who Owns What
|
# RACI — Who Owns What
|
||||||
|
|
||||||
> **Source of truth:** `.ciagent/PROJECT.md` § RACI Matrix (v1.18, REQ-215,
|
> This page is the citizen-developer-facing copy.
|
||||||
> D-139). This page is the citizen-developer-facing copy.
|
|
||||||
|
|
||||||
Nova's delivery lifecycle has three roles. This page clarifies who owns
|
Nova's delivery lifecycle has four roles. This page clarifies who owns
|
||||||
what — so the citizen developer knows what they bring, what the platform
|
what — so the citizen developer knows what they bring, what the platform
|
||||||
provides, and what is co-owned.
|
provides, what quality engineering guards, and what is co-owned with SRE.
|
||||||
|
|
||||||
## The Three Roles
|
## The Four Roles
|
||||||
|
|
||||||
### Citizen Developer (CD)
|
### Citizen Developer (CD)
|
||||||
|
|
||||||
@@ -15,40 +14,49 @@ That's you — the consumer (technical developer L3A or non-technical L3B).
|
|||||||
You are **Responsible** for all **Functional Requirements (FRs)** and
|
You are **Responsible** for all **Functional Requirements (FRs)** and
|
||||||
**User Acceptance Testing (UAT)**. You produce the FRs + UAT via your AI
|
**User Acceptance Testing (UAT)**. You produce the FRs + UAT via your AI
|
||||||
coding agent, an upstream agentic SDLC platform, or any upstream
|
coding agent, an upstream agentic SDLC platform, or any upstream
|
||||||
development platform. **The source does not matter** — all are subject
|
development platform. **The source does not matter** — all are subject to
|
||||||
to the same compliance standards (the submission-readiness gate, the
|
the same compliance standards (the submission-readiness gate, the
|
||||||
contract schema, the policy envelope, the immutable audit stream). Nova
|
contract schema, the policy envelope, the immutable audit stream). Nova
|
||||||
validates the submission, not the author.
|
validates the submission, not the author.
|
||||||
|
|
||||||
### Platform (Nova)
|
### Platform (Nova)
|
||||||
|
|
||||||
Nova is **Responsible** for all **Non-Functional Requirements (NFRs)**,
|
Nova is **Responsible** for all **Non-Functional Requirements (NFRs)**,
|
||||||
**Infrastructure** (cloud resource lifecycle, state, IAM), **QA** (the
|
**Infrastructure** (cloud resource lifecycle, state, IAM), and
|
||||||
platform-side quality checks: policy enforcement, confidence scoring,
|
**Production deployments to cloud** (the apply path, the pipeline, the
|
||||||
schema validation), and **Production deployments to cloud** (the apply
|
release mechanics).
|
||||||
path, the pipeline, the release mechanics).
|
|
||||||
|
|
||||||
### Release Management (RM) — co-owned
|
### Quality Engineering (QE)
|
||||||
|
|
||||||
The release is **co-owned**. The platform performs the QA + SRE
|
Quality Engineering is **Responsible** for the platform-side quality
|
||||||
attestations agentically (it runs the confidence signal, the policy
|
checks: policy enforcement, confidence scoring, schema validation, and
|
||||||
checks, the separation-of-duties). The citizen developer **oversees and
|
the functional/contract/non-functional evidence that feeds attestation.
|
||||||
triggers** the actual release — the human attestation at the stage gate
|
QE owns the **quality** of what the platform produces — the gate
|
||||||
is your authorization. The platform runs the checks; you authorize the
|
evidence, not the gate decision.
|
||||||
promotion. This is the "autonomy in operations, human at stage gates"
|
|
||||||
model.
|
### SRE — co-owned with you
|
||||||
|
|
||||||
|
Production readiness is **co-owned**. SRE owns operational readiness:
|
||||||
|
the operational attestation (incident response, capacity, resilience,
|
||||||
|
DR). 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
|
## The Matrix
|
||||||
|
|
||||||
| Work Category | Citizen Developer | Platform | Release Management |
|
| Work Category | Citizen Developer | Platform | Quality Engineering | SRE |
|
||||||
|---|---|---|---|
|
|---|---|---|---|---|
|
||||||
| **Functional Requirements (FRs)** | **R/A** | C | I |
|
| **Functional Requirements (FRs)** | **R/A** | C | I | I |
|
||||||
| **User Acceptance Testing (UAT)** | **R/A** | C | I |
|
| **User Acceptance Testing (UAT)** | **R/A** | C | I | I |
|
||||||
| **Non-Functional Requirements (NFRs)** | I | **R/A** | C |
|
| **Non-Functional Requirements (NFRs)** | I | **R/A** | C | C |
|
||||||
| **Infrastructure (cloud, state, IAM)** | I | **R/A** | C |
|
| **Infrastructure (cloud, state, IAM)** | I | **R/A** | I | C |
|
||||||
| **QA (policy, confidence, schema checks)** | C | **R/A** | I |
|
| **QA (policy, confidence, schema checks)** | C | R | **R/A** | I |
|
||||||
| **Production deployment to cloud** | I | **R/A** | C |
|
| **Production deployment to cloud** | I | **R/A** | C | C |
|
||||||
| **Release attestation (QA + SRE sign-off)** | **A** | R | **R** |
|
| **Quality attestation (QA sign-off)** | **A** | R | **R** | I |
|
||||||
|
| **Production readiness (SRE sign-off)** | **A** | R | C | **R** |
|
||||||
|
|
||||||
**Key:** **R** = Responsible (does the work) · **A** = Accountable (owns
|
**Key:** **R** = Responsible (does the work) · **A** = Accountable (owns
|
||||||
the outcome, sign-off) · **C** = Consulted · **I** = Informed.
|
the outcome, sign-off) · **C** = Consulted · **I** = Informed.
|
||||||
@@ -64,11 +72,15 @@ the outcome, sign-off) · **C** = Consulted · **I** = Informed.
|
|||||||
- The NFRs (security, observability, compliance — baked into the
|
- The NFRs (security, observability, compliance — baked into the
|
||||||
pipeline, not your concern).
|
pipeline, not your concern).
|
||||||
- The infrastructure (cloud resources, state management, IAM scoping).
|
- 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).
|
- The production deployment (the apply path, the pipeline, the release).
|
||||||
|
|
||||||
**You co-own the release:**
|
**Quality Engineering guards:**
|
||||||
- Nova runs the attestations (QA confidence, SRE operational readiness).
|
- The policy enforcement, confidence scoring, schema validation.
|
||||||
|
- The quality attestation evidence that feeds the stage gates.
|
||||||
|
|
||||||
|
**You co-own production readiness with SRE:**
|
||||||
|
- Nova + SRE run the attestations (QA quality sign-off, SRE operational
|
||||||
|
readiness).
|
||||||
- You authorize the promotion at the stage gate. No promotion happens
|
- You authorize the promotion at the stage gate. No promotion happens
|
||||||
without your recorded attestation.
|
without your recorded attestation.
|
||||||
|
|
||||||
@@ -79,7 +91,7 @@ agentic SDLC platform, or a traditional IDE. Nova does not
|
|||||||
differentiate. All submissions pass through the same gate
|
differentiate. All submissions pass through the same gate
|
||||||
(`schemas/submission-readiness.schema.json`): tags, environment
|
(`schemas/submission-readiness.schema.json`): tags, environment
|
||||||
metadata, policy preconditions, profile markers. The compliance
|
metadata, policy preconditions, profile markers. The compliance
|
||||||
standards are the same regardless of how the code was authored. This
|
standards are the same regardless of how the code was authored. This is
|
||||||
is by design: the audit trail is the same, the policy envelope is the
|
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
|
same, the evidence stream is the same. The source does not matter; the
|
||||||
submission does.
|
submission does.
|
||||||
+6
-2
@@ -1,6 +1,5 @@
|
|||||||
# Scope — Nova is Downstream of PDLC
|
# Scope — Nova is Downstream of PDLC
|
||||||
|
|
||||||
> **Source of truth:** `.ciagent/PROJECT.md` § Scope (v1.18, REQ-216).
|
|
||||||
> This page is the citizen-developer-facing copy.
|
> This page is the citizen-developer-facing copy.
|
||||||
|
|
||||||
## The Boundary
|
## The Boundary
|
||||||
@@ -15,7 +14,12 @@ PDLC includes:
|
|||||||
- IDE workflows / developer experience
|
- IDE workflows / developer experience
|
||||||
|
|
||||||
Nova never penetrates the PDLC. Nova's domain is **infrastructure +
|
Nova never penetrates the PDLC. Nova's domain is **infrastructure +
|
||||||
delivery only**.
|
delivery only**. Nova integrates with externally owned PDLC, SDLC,
|
||||||
|
Agentic, and Citizen Developer platforms with no regard for the source
|
||||||
|
of the intent: Nova provides a set of skills and MCP endpoints that help
|
||||||
|
the developer or AI agent make their application production-grade, and
|
||||||
|
all intents to deploy to production go through the same rigorous
|
||||||
|
controls, quality gates, attestation, and evidence stream.
|
||||||
|
|
||||||
## What Nova Does
|
## What Nova Does
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -34,14 +34,14 @@
|
|||||||
|
|
||||||
## Atelier Provenance
|
## Atelier Provenance
|
||||||
|
|
||||||
The skills are derived from [Atelier](https://git.cloudinit.dev/coreci/atelier)
|
The skills are derived from [Atelier](https://example.com/atelier)
|
||||||
— a first-principles docs-as-code engineering framework with 8 core
|
— a first-principles docs-as-code engineering framework with 8 core
|
||||||
principles (C1–C8) and 19 domains, each with 10 derived P-rules. The
|
principles (C1–C8) and 19 domains, each with 10 derived P-rules. The
|
||||||
skills distill the citizen-developer-relevant subset of each domain's
|
skills distill the citizen-developer-relevant subset of each domain's
|
||||||
first-principles, link to the agent-checklist triggers, and map to the
|
first-principles, link to the agent-checklist triggers, and map to the
|
||||||
existing BA.A catalog.
|
existing BA.A catalog.
|
||||||
|
|
||||||
Atelier is vendored under `mcp/atelier/vendor/` (pinned tag, D-136) for
|
Atelier is vendored under `mcp/atelier/vendor/` (pinned tag) for
|
||||||
audit reproducibility — an agentic validation result is replayable
|
audit reproducibility — an agentic validation result is replayable
|
||||||
against the exact principles that produced it.
|
against the exact principles that produced it.
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
# Nova Atelier MCP Server
|
# Nova Atelier MCP Server
|
||||||
|
|
||||||
> **v1.18, REQ-223, REQ-224.** An MCP (Model Context Protocol) server that
|
|
||||||
> exposes Atelier engineering principles to the citizen developer's AI
|
> exposes Atelier engineering principles to the citizen developer's AI
|
||||||
> agent. Plugin-registry architecture (D-140); stdio transport (D-135);
|
> agent. Plugin-registry architecture; stdio transport;
|
||||||
> vendored Atelier (D-136) for audit reproducibility.
|
> vendored Atelier for audit reproducibility.
|
||||||
|
|
||||||
## What This Is
|
## What This Is
|
||||||
|
|
||||||
@@ -22,7 +21,7 @@ observability gaps.
|
|||||||
| `atelier.matrix_lookup(domain)` | Look up the domain→core principle mapping for a given domain. |
|
| `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. |
|
| `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 (D-140)
|
## Architecture — Plugin Registry
|
||||||
|
|
||||||
```
|
```
|
||||||
mcp/atelier/
|
mcp/atelier/
|
||||||
@@ -31,7 +30,7 @@ mcp/atelier/
|
|||||||
│ ├── __init__.py
|
│ ├── __init__.py
|
||||||
│ ├── principles.py # lookup_principle, list_domains, matrix_lookup
|
│ ├── principles.py # lookup_principle, list_domains, matrix_lookup
|
||||||
│ └── validation.py # validate_against_principles
|
│ └── validation.py # validate_against_principles
|
||||||
├── vendor/ # pinned Atelier snapshot (D-136)
|
├── vendor/ # pinned Atelier snapshot
|
||||||
│ ├── VERSION.md # pinned tag + upgrade instructions
|
│ ├── VERSION.md # pinned tag + upgrade instructions
|
||||||
│ ├── core/first-principles.md
|
│ ├── core/first-principles.md
|
||||||
│ ├── domains/security/first-principles.md
|
│ ├── domains/security/first-principles.md
|
||||||
@@ -68,7 +67,7 @@ s.load_plugins()
|
|||||||
result = s.call_tool("atelier_lookup_principle", {"domain": "security", "principle_id": "P4"})
|
result = s.call_tool("atelier_lookup_principle", {"domain": "security", "principle_id": "P4"})
|
||||||
```
|
```
|
||||||
|
|
||||||
## Vendoring (D-136)
|
## Vendoring
|
||||||
|
|
||||||
Atelier is vendored under `vendor/` at a pinned tag (`v0.3.6`, see
|
Atelier is vendored under `vendor/` at a pinned tag (`v0.3.6`, see
|
||||||
`vendor/VERSION.md`). An agentic validation result is only reproducible if
|
`vendor/VERSION.md`). An agentic validation result is only reproducible if
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,3 +1,4 @@
|
|||||||
resource "aws_ecs_cluster" "this" {
|
resource "aws_ecs_cluster" "this" {
|
||||||
|
count = var.enabled ? 1 : 0
|
||||||
name = var.name
|
name = var.name
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
@@ -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."
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)."
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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" {
|
||||||
|
|||||||
@@ -69,3 +69,9 @@ variable "cluster_arn" {
|
|||||||
description = "ECS cluster ARN to deploy the service into."
|
description = "ECS cluster ARN to deploy the service into."
|
||||||
default = ""
|
default = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variable "enabled" {
|
||||||
|
type = bool
|
||||||
|
description = "Feature flag: enable/disable this module. Set to false to skip resource creation."
|
||||||
|
default = true
|
||||||
|
}
|
||||||
|
|||||||
@@ -24,6 +24,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "AWS region the VPC is created in.",
|
"description": "AWS region the VPC 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": {
|
||||||
@@ -57,24 +62,44 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:ec2:vpc",
|
"type": "aws:ec2:vpc",
|
||||||
"description": "The VPC itself.",
|
"description": "The VPC itself.",
|
||||||
"inputs": ["cidr", "name"],
|
"inputs": [
|
||||||
"outputs": ["vpc_id"]
|
"cidr",
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
"vpc_id"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "aws:ec2:subnet",
|
"type": "aws:ec2:subnet",
|
||||||
"description": "One subnet per availability zone (azs split on comma).",
|
"description": "One subnet per availability zone (azs split on comma).",
|
||||||
"inputs": ["cidr", "az", "vpc_id", "name"],
|
"inputs": [
|
||||||
"outputs": ["subnet_ids"]
|
"cidr",
|
||||||
|
"az",
|
||||||
|
"vpc_id",
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
"subnet_ids"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "aws:ec2:routetable",
|
"type": "aws:ec2:routetable",
|
||||||
"description": "Route table bound to the VPC with an internet gateway + default route.",
|
"description": "Route table bound to the VPC with an internet gateway + default route.",
|
||||||
"inputs": ["vpc_id"],
|
"inputs": [
|
||||||
|
"vpc_id"
|
||||||
|
],
|
||||||
"outputs": []
|
"outputs": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"intra_refs": [
|
"intra_refs": [
|
||||||
{"from": "aws:ec2:subnet.vpc_id", "to": "aws:ec2:vpc.vpc_id"},
|
{
|
||||||
{"from": "aws:ec2:routetable.vpc_id", "to": "aws:ec2:vpc.vpc_id"}
|
"from": "aws:ec2:subnet.vpc_id",
|
||||||
|
"to": "aws:ec2:vpc.vpc_id"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "aws:ec2:routetable.vpc_id",
|
||||||
|
"to": "aws:ec2:vpc.vpc_id"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
resource "aws_vpc" "this" {
|
resource "aws_vpc" "this" {
|
||||||
|
count = var.enabled ? 1 : 0
|
||||||
cidr_block = local.cidr_block
|
cidr_block = local.cidr_block
|
||||||
tags = {
|
tags = {
|
||||||
Name = local.name_tag
|
Name = local.name_tag
|
||||||
@@ -11,7 +12,7 @@ resource "aws_vpc" "this" {
|
|||||||
|
|
||||||
resource "aws_subnet" "this" {
|
resource "aws_subnet" "this" {
|
||||||
count = length(local.az_list)
|
count = length(local.az_list)
|
||||||
vpc_id = aws_vpc.this.id
|
vpc_id = aws_vpc.this[0].id
|
||||||
cidr_block = local.subnet_cidrs[count.index]
|
cidr_block = local.subnet_cidrs[count.index]
|
||||||
availability_zone = local.az_list[count.index]
|
availability_zone = local.az_list[count.index]
|
||||||
tags = {
|
tags = {
|
||||||
@@ -20,17 +21,19 @@ resource "aws_subnet" "this" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_internet_gateway" "this" {
|
resource "aws_internet_gateway" "this" {
|
||||||
vpc_id = aws_vpc.this.id
|
count = var.enabled ? 1 : 0
|
||||||
|
vpc_id = aws_vpc.this[0].id
|
||||||
tags = {
|
tags = {
|
||||||
Name = "${local.name_tag}-igw"
|
Name = "${local.name_tag}-igw"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_route_table" "this" {
|
resource "aws_route_table" "this" {
|
||||||
vpc_id = aws_vpc.this.id
|
count = var.enabled ? 1 : 0
|
||||||
|
vpc_id = aws_vpc.this[0].id
|
||||||
route {
|
route {
|
||||||
cidr_block = "0.0.0.0/0"
|
cidr_block = "0.0.0.0/0"
|
||||||
gateway_id = aws_internet_gateway.this.id
|
gateway_id = aws_internet_gateway.this[0].id
|
||||||
}
|
}
|
||||||
tags = {
|
tags = {
|
||||||
Name = "${local.name_tag}-rt"
|
Name = "${local.name_tag}-rt"
|
||||||
@@ -40,5 +43,5 @@ resource "aws_route_table" "this" {
|
|||||||
resource "aws_route_table_association" "this" {
|
resource "aws_route_table_association" "this" {
|
||||||
count = length(local.az_list)
|
count = length(local.az_list)
|
||||||
subnet_id = aws_subnet.this[count.index].id
|
subnet_id = aws_subnet.this[count.index].id
|
||||||
route_table_id = aws_route_table.this.id
|
route_table_id = aws_route_table.this[0].id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
output "vpc_id" {
|
output "vpc_id" {
|
||||||
value = aws_vpc.this.id
|
value = aws_vpc.this[0].id
|
||||||
description = "The VPC id."
|
description = "The VPC 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
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,11 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "AWS region (CloudFront-scoped WAF is always us-east-1; the adapter ignores this for cloudfront scope).",
|
"description": "AWS region (CloudFront-scoped WAF is always us-east-1; the adapter ignores this for cloudfront scope).",
|
||||||
"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": {
|
||||||
@@ -60,8 +65,15 @@
|
|||||||
{
|
{
|
||||||
"type": "aws:wafv2:webacl",
|
"type": "aws:wafv2:webacl",
|
||||||
"description": "WAFv2 Web ACL with managed rules.",
|
"description": "WAFv2 Web ACL with managed rules.",
|
||||||
"inputs": ["name", "scope", "default_action", "rules"],
|
"inputs": [
|
||||||
"outputs": ["web_acl_arn"]
|
"name",
|
||||||
|
"scope",
|
||||||
|
"default_action",
|
||||||
|
"rules"
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
"web_acl_arn"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
resource "aws_wafv2_web_acl" "this" {
|
resource "aws_wafv2_web_acl" "this" {
|
||||||
|
count = var.enabled ? 1 : 0
|
||||||
name = var.name
|
name = var.name
|
||||||
scope = local.scope
|
scope = local.scope
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
output "web_acl_arn" {
|
output "web_acl_arn" {
|
||||||
value = aws_wafv2_web_acl.this.arn
|
value = aws_wafv2_web_acl.this[0].arn
|
||||||
description = "The WAF Web ACL ARN."
|
description = "The WAF Web ACL ARN."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,3 +27,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
|
||||||
|
}
|
||||||
|
|||||||
+5
-5
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Nova uses declarative pipeline contracts (YAML) as the single source of truth. Both Gitea and GitHub workflows implement the same contract (byte-identical). The shell runner (`scripts/run_ci.sh`) mirrors the CI pipeline locally so that every stage that runs in CI can be reproduced on a developer machine without a forge.
|
Nova uses declarative pipeline contracts (YAML) as the single source of truth. GitHub workflows implement the same contract (byte-identical across forges). The shell runner (`scripts/run_ci.sh`) mirrors the CI pipeline locally so that every stage that runs in CI can be reproduced on a developer machine without a forge.
|
||||||
|
|
||||||
## Existing Pipelines
|
## Existing Pipelines
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ Nova uses declarative pipeline contracts (YAML) as the single source of truth. B
|
|||||||
|
|
||||||
## How to Wire a Pipeline
|
## How to Wire a Pipeline
|
||||||
|
|
||||||
1. Create byte-identical workflow YAMLs in `.gitea/workflows/<name>.yml` and `.github/workflows/<name>.yml`.
|
1. Create the workflow YAML in `.github/workflows/<name>.yml`.
|
||||||
2. Both workflows must implement the same stages, commands, triggers, and runner declared in the contract.
|
2. Both workflows must implement the same stages, commands, triggers, and runner declared in the contract.
|
||||||
3. `scripts/run_ci.sh` mirrors `ci.yml` locally so the same stages run without a forge.
|
3. `scripts/run_ci.sh` mirrors `ci.yml` locally so the same stages run without a forge.
|
||||||
4. Consumer repos reference the deploy pipeline via `uses: acdl/.github/workflows/deploy.yml@vX.Y`.
|
4. Consumer repos reference the deploy pipeline via `uses: acdl/.github/workflows/deploy.yml@vX.Y`.
|
||||||
@@ -29,17 +29,17 @@ Nova uses declarative pipeline contracts (YAML) as the single source of truth. B
|
|||||||
|
|
||||||
- `scripts/run_ci.sh` — local CI mirror that runs the `ci.yml` stages.
|
- `scripts/run_ci.sh` — local CI mirror that runs the `ci.yml` stages.
|
||||||
- `scripts/run_platform.sh` — platform pipeline runner that implements the `contract.yml` stages.
|
- `scripts/run_platform.sh` — platform pipeline runner that implements the `contract.yml` stages.
|
||||||
- Workflow YAMLs in `.gitea/workflows/` and `.github/workflows/`.
|
- Workflow YAMLs in `.github/workflows/`.
|
||||||
- Schemas in `schemas/` (`pipeline.schema.json`, `deploy-pipeline.schema.json`).
|
- Schemas in `schemas/` (`pipeline.schema.json`, `deploy-pipeline.schema.json`).
|
||||||
|
|
||||||
## How to Test Pipelines
|
## How to Test Pipelines
|
||||||
|
|
||||||
- `tests/test_pipeline_contract.py` — validates each pipeline YAML against its schema, asserts workflow conformance (byte-identical Gitea/GitHub workflows with the same stages/commands/triggers), and tests `scripts/run_ci.sh` execution against the contract.
|
- `tests/test_pipeline_contract.py` — validates each pipeline YAML against its schema, asserts workflow conformance (byte-identical workflows with the same stages/commands/triggers), and tests `scripts/run_ci.sh` execution against the contract.
|
||||||
|
|
||||||
## Adding a New Pipeline
|
## Adding a New Pipeline
|
||||||
|
|
||||||
1. Create `pipelines/<name>.yml` using the structure above.
|
1. Create `pipelines/<name>.yml` using the structure above.
|
||||||
2. Create or extend the schema in `schemas/` for the new pipeline shape.
|
2. Create or extend the schema in `schemas/` for the new pipeline shape.
|
||||||
3. Create byte-identical workflow YAMLs in `.gitea/workflows/<name>.yml` and `.github/workflows/<name>.yml`.
|
3. Create the workflow YAML in `.github/workflows/<name>.yml`.
|
||||||
4. Extend `scripts/run_ci.sh` if a local mirror of the new pipeline is needed.
|
4. Extend `scripts/run_ci.sh` if a local mirror of the new pipeline is needed.
|
||||||
5. Write or extend tests in `tests/test_pipeline_contract.py` to assert schema validity and workflow conformance.
|
5. Write or extend tests in `tests/test_pipeline_contract.py` to assert schema validity and workflow conformance.
|
||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
# Nova Central CI Pipeline Contract (v1.5)
|
# Nova Central CI Pipeline Contract (v1.5)
|
||||||
#
|
#
|
||||||
# This is the single source of truth for the CI/CD pipeline. Both
|
# This is the single source of truth for the CI/CD pipeline. Both
|
||||||
# .gitea/workflows/ci.yml (Gitea Actions, dev) and
|
# .github/workflows/ci.yml (dev) and
|
||||||
# .github/workflows/ci.yml (GitHub Actions, production) implement the
|
# .github/workflows/ci.yml (GitHub Actions, production) implement the
|
||||||
# stages, commands, triggers, and runner declared here.
|
# stages, commands, triggers, and runner declared here.
|
||||||
# scripts/run_ci.sh mirrors the same stages for shell reproducibility.
|
# scripts/run_ci.sh mirrors the same stages for shell reproducibility.
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#
|
#
|
||||||
# The contract does NOT replace workflow YAML syntax — it declares the
|
# The contract does NOT replace workflow YAML syntax — it declares the
|
||||||
# *intent* that the forge-specific workflows implement. The workflow files
|
# *intent* that the forge-specific workflows implement. The workflow files
|
||||||
# use Gitea/GitHub Actions syntax (checkout, setup-python, run blocks);
|
# use GitHub Actions syntax (checkout, setup-python, run blocks);
|
||||||
# this contract declares what those blocks must contain.
|
# this contract declares what those blocks must contain.
|
||||||
#
|
#
|
||||||
# Validated against schemas/pipeline.schema.json.
|
# Validated against schemas/pipeline.schema.json.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
#
|
#
|
||||||
# This file is the declarative pipeline spec (a contract, not an executable
|
# This file is the declarative pipeline spec (a contract, not an executable
|
||||||
# workflow). The executable workflow is .github/workflows/deploy.yml
|
# workflow). The executable workflow is .github/workflows/deploy.yml
|
||||||
# (GitHub Actions) / .gitea/workflows/deploy.yml (Gitea Actions), which
|
# (GitHub Actions), which
|
||||||
# implements these stages by invoking scripts/run_platform.sh.
|
# implements these stages by invoking scripts/run_platform.sh.
|
||||||
#
|
#
|
||||||
# Validated against schemas/deploy-pipeline.schema.json.
|
# Validated against schemas/deploy-pipeline.schema.json.
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ name: acdl-modules-lifecycle
|
|||||||
# real apply→modify→destroy against live AWS. The CI VPC apply/destroy
|
# real apply→modify→destroy against live AWS. The CI VPC apply/destroy
|
||||||
# jobs are skipped in plan mode (nothing is applied).
|
# jobs are skipped in plan mode (nothing is applied).
|
||||||
#
|
#
|
||||||
# Both Gitea (.gitea/workflows/modules-lifecycle.yml) and GitHub
|
# GitHub
|
||||||
# (.github/workflows/modules-lifecycle.yml) implement this contract
|
# (.github/workflows/modules-lifecycle.yml) implements this contract
|
||||||
# byte-identically.
|
# byte-identically.
|
||||||
|
|
||||||
triggers:
|
triggers:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user