23 Commits

Author SHA1 Message Date
jonathan 55b35c86db Merge pull request 'docs(P00): .ciagent/STATE.md re-take — v1.1 phase 0 execution' (#12) from phase/00-pre-execution into main
CI / ci (push) Successful in 42s
Reviewed-on: #12
Reviewed-by: jonathan <jonathan@coreci.dev>
2026-11-03 02:48:13 +00:00
coreci-nova-po 810a9da6fa docs(P00): restore the ratified v1.1 phase-0 re-take STATE.md (nova-platform#11 c16383) after main merge — PR#12 conflict resolved (Chairman c17054)
CI / ci (push) Successful in 33s
CI / ci (pull_request) Successful in 31s
2026-11-02 14:12:58 +00:00
coreci-nova-po 3d13fa7b71 Merge branch 'main' into phase/00-pre-execution
CI / ci (push) Successful in 33s
CI / ci (pull_request) Successful in 34s
2026-11-02 14:12:57 +00:00
coreci-nova-po 5cdc8fb0a7 docs(P00): align .ciagent/STATE.md with main intake version — pre-step to dissolve the add/add conflict (Chairman request PR#12 c17054)
CI / ci (push) Successful in 33s
2026-11-02 14:12:56 +00:00
coreci-nova-po 2bfd32fc8f docs(P00): state re-take — stamp the RE-TAKEN header line with its UTC timestamp 2026-10-31 22:09:51 +00:00
coreci-nova-po 9b88105bff docs(P00): state re-take — refresh .ciagent/STATE.md ground truth to 2026-10-31 (v1.1 plan ratified, nova-platform#11 comment 16383) 2026-10-31 22:09:35 +00:00
jonathan 8a92f57b04 Merge pull request 'ci: run the pipeline on the ubuntu-latest label (the node job image has no test deps and no pypi)' (#9) from ci/ubuntu-runner into main
CI / ci (push) Successful in 24s
Reviewed-on: #9
2026-10-26 23:17:19 +00:00
coreci-nova-po 0a82a8c2eb ci: run the pipeline on the ubuntu-latest label (pypi reachable there, deps absent on the node image)
CI / ci (push) Successful in 23s
CI / ci (pull_request) Successful in 23s
2026-10-26 05:08:46 +00:00
jonathan 906a18c98c Merge pull request 'ci: make the workflow parse and run on a label the fleet serves (first run)' (#8) from ci/standup-node into main
CI / ci (push) Failing after 3s
Reviewed-on: #8
Reviewed-by: jonathan <jonathan@coreci.dev>
2026-10-26 02:31:16 +00:00
coreci-nova-po dc455c3c6c ci: quote the two plain scalars that made this workflow invalid YAML
CI / ci (push) Failing after 4s
CI / ci (pull_request) Failing after 3s
A plain YAML scalar cannot contain a colon followed by a space. Two step
values did, so the whole file failed to parse and Gitea skipped it: every
push left actions/runs and actions/tasks at zero. Quoting both values
makes the workflow parse; run 50815 is the same file with both values
quoted and it was picked up by the runner.

run: hourly RUN 2 2026-10-26T02:2xZ
2026-10-26 02:05:05 +00:00
coreci-nova-po e672543e0f ci: drop the push branch filter so the workflow actually fires
A push filter of branches: ["**"] produced zero run records in this
repository across three pushes (docs/ci-pipeline, ci-pipeline-docs,
ci/standup-node). The same push event with no branch filter created runs
50762 and 50763. The filter shape is therefore not matching branch names
here, and a pipeline that never fires reads as CI that is up while
nothing gates. The filter is now absent, so every branch push runs the
workflow, which is what the task order asked for.

run: hourly RUN 2 2026-10-26T02:1xZ
2026-10-26 01:59:19 +00:00
coreci-nova-po 6d3b4b16dc docs: document the CI pipeline (task order nova-platform#3)
The workflow landed in PR #6 but nothing in the repo pointed a reader at it.
This states what the three legs gate, what a loud failure means, and the
local equivalent. No product behaviour changes.

run: hourly RUN 2 2026-10-26T02:0xZ
2026-10-26 01:58:14 +00:00
coreci-nova-po fdfe9d5b18 ci: run the workflow on the fleet label node
ubuntu-latest produces no run and no task record in this repository: three
push events with the merged workflow (branches docs/ci-pipeline and
ci-pipeline-docs) left actions/runs and actions/tasks at zero. A probe
branch with one trivial job per candidate label produced run 50763
(runs-on: node, completed success) and run 50762 (runs-on: employee-py,
still queued), so node is the label that serves this repo today.

run: hourly RUN 2 2026-10-26T02:0xZ
2026-10-26 01:58:13 +00:00
jonathan 2822f14d4f Merge pull request 'ci: stand up CI workflow on nova-platform (task order #3, hip c12363)' (#6) from ci/standup into main
Reviewed-on: #6
Reviewed-by: jonathan <jonathan@coreci.dev>
2026-10-26 00:48:27 +00:00
coreci-nova-po a20980517f ci: stand up CI workflow - offline mirror of scripts/run_ci.sh (task order nova-platform#3, hip c12363)
Stages: lint (py_compile) -> test (pytest, not-shell markers) -> check-only
(run_platform.sh --check-only). Per-leg availability detection: a leg
that cannot run fails loud with its reason; no silent skips. Deps install
from requirements-test.txt when pypi is reachable. Evidence rows land on
nova-platform#3 per the order's contract.
2026-10-23 06:37:10 +00:00
jonathan 86da39cd4d Merge pull request 'docs: TL;DR prologues — README + consumer-guide (comms-etiquette v1.0)' (#2) from docs/comms-tldr into main
Reviewed-on: #2
2026-10-19 22:57:31 +00:00
coreci-nova-po 3ce57dcb9a Update modules/README.md 2026-10-19 21:01:47 +00:00
coreci-nova-po d57bec9bf5 docs(comms): TL;DR prologue for consumer guide (comms-etiquette v1.0) 2026-10-19 19:40:08 +00:00
coreci-nova-po bbed068a0c docs(comms): TL;DR prologue for README (comms-etiquette v1.0) 2026-10-19 19:40:08 +00:00
CIAgent 7a03b0e1e5 docs(P00): merge PDLC state intake to main (STATE.md)
---ci---
project: nova-platform
milestone: v1.1
status: complete
---/ci---

Merge PDLC Phase 0 state intake. STATE.md is the single-pass
ground-truth snapshot for Product Owner review.
2026-08-28 19:19:11 +00:00
CIAgent f8cf46b94c docs(P00): PDLC state intake — STATE.md created
---ci---
project: nova-platform
phase: 0
milestone: v1.1
status: specify
---/ci---

PDLC Phase 0 state intake. STATE.md populated with absolute ground
truth: architecture state (13 L1 + 2 L2 modules, 4-layer architecture),
technical stack (Python 3.11, Terraform 1.9, bash scripts), 38 locked
decisions (D-001..D-038), active constraints, recent history (v1.0
complete, v0.1.6), known tensions (forge token blank, only dev.json
env, no coverage), agent assumptions.
2026-08-28 19:19:03 +00:00
CIAgent fe9ab7e444 docs(ship): milestone v1.0 complete (release v0.1.6, local-only)
---ci---
project: nova-platform
milestone: v1.0
status: complete
---/ci---

v1.0 MILESTONE COMPLETE. 38 REQ-IDs all complete. 38 decisions
(D-001..D-038). 76 tests passing. Merged to main 2443909. Tag
v0.1.6 (milestone release on v0.1 patch line). All milestone
branches deleted (local-only; no remote configured). NOVA_FORGE_TOKEN
blank → Gitea release pending (local-only ship). Checkpoint cleared —
next run starts a new milestone.
2026-08-25 02:19:21 +00:00
CIAgent 2443909362 docs(milestone): complete v1.0-nova-platform (release v0.1.6)
---ci---
project: nova-platform
milestone: v1.0
status: complete
requirements:
  covered: [REQ-01,REQ-02,REQ-03,REQ-04,REQ-05,REQ-06,REQ-07,REQ-08,REQ-09,REQ-10,REQ-11,REQ-12,REQ-13,REQ-14,REQ-15,REQ-16,REQ-17,REQ-18,REQ-19,REQ-20,REQ-21,REQ-22,REQ-23,REQ-24,REQ-25,REQ-26,REQ-27,REQ-28,REQ-29,REQ-30,REQ-31,REQ-32,REQ-33,REQ-34,REQ-35,REQ-36,REQ-37,REQ-38]
  partial: []
---/ci---

v1.0 milestone complete: simplified infrastructure-delivery platform
derived from Nova (acdl). 6 phases (P0-P5 + P6 final). 38 REQ-IDs.
38 decisions (D-001..D-038). 76 tests pass. Engine boundary holds.
Happy paths green (check-only + CI). 13 L1 + 2 L2 modules. 5 terraform
roots. Shell reproducibility. Zero OOS files.

Tags: v0.1.0 (P0) → v0.1.1..v0.1.5 (P1..P5) → v0.1.6 (P6 = milestone
release on v0.1 patch line).
2026-08-25 02:18:35 +00:00
18 changed files with 1307 additions and 22 deletions
+10 -17
View File
@@ -1,21 +1,14 @@
{
"phase": 4,
"stage": "verify",
"milestone": "v1.0",
"phase_role": "execution",
"phase": 0,
"stage": "specify",
"milestone": "v1.1",
"phase_role": "pre_execution",
"attempts": 0,
"updated_at": "2026-08-20T20:15:00Z",
"updated_at": "2026-08-28T19:09:26Z",
"project": "nova-platform",
"milestone_branch": "milestone/v1.0-nova-platform",
"phase_branch": "phase/04-l2-patterns-bootstrap-platform",
"phase_0_ship": {"tag": "v0.1.0", "local_only": true},
"phase_1_ship": {"tag": "v0.1.1", "local_only": true},
"phase_2_ship": {"tag": "v0.1.2", "local_only": true},
"phase_3_ship": {"tag": "v0.1.3", "local_only": true},
"phase_4_verify": {
"tests_pass": true,
"tests_count": 68,
"reqs_covered": ["REQ-12", "REQ-14", "REQ-15", "REQ-16", "REQ-17", "REQ-18", "REQ-19", "REQ-22"]
},
"next_phase": "phase/05-shell-reproducibility-tests-docs"
"milestone_branch": "milestone/v1.1-pdlc-state",
"phase_branch": "phase/00-pre-execution",
"milestone_complete": false,
"pdlc_intake": true,
"state_md_created": true
}
+6 -3
View File
@@ -58,9 +58,12 @@ documented. Ships as `v0.1.5`.
### Phase 6 — Final Review + Ship (milestone release)
REVIEW + AUDIT + milestone SHIP. Merges `phase/06` →
`milestone/v1.0-nova-platform` → `main`. Tags `v1.0.0` (major — the
initial release). Creates the Gitea release. Deletes all milestone
branches.
`milestone/v1.0-nova-platform` → `main`. Tags `v0.1.6` (the milestone
release on the v0.1 patch line — v1.0 is the milestone *label*, tags
run on the previous minor's patch line per branch-strategy.md; since
there is no prior minor, v0.1.x is the patch line and v0.1.6 IS the
v1.0 milestone release). Creates the Gitea release. Deletes all
milestone branches.
## Coverage (init estimate — refined by PLAN)
+170
View File
@@ -0,0 +1,170 @@
# STATE — Nova Platform
> PDLC Phase 0 intake. Absolute ground truth as of 2026-08-28T19:09Z.
> RE-TAKEN 2026-10-31T22:09Z — phase 0 execution of the ratified v1.1 plan (fresh single
> pass by PO coreci-nova-po; ratification: coreci/nova-platform#11 comment
> 16383, 2026-10-31T20:39:34Z). The 2026-08-28 intake text is preserved
> below; every moved fact is marked (re-take) inline and catalogued in
> §0 Re-take Deltas. Nothing is rewritten silently.
> Single-pass discovery for the Product Owner/Manager.
---
## 0. Re-take Deltas (added by the phase 0 re-take; evidence ids on every move)
This section is the phase 0 re-take delta record (PO coreci-nova-po,
2026-10-31T22:09Z). The v1.1 five-phase plan is ratified in full
(coreci/nova-platform#11, comment 16383, 2026-10-31T20:39:34Z); this
refresh is that ratification's phase 0 execution work.
Moved since 2026-08-28 (3 facts):
1. Forge release — intake tension (1) reads "v0.1.6 shipped local-only,
gitea release pending". MOVED: release v0.1.6 EXISTS on the forge
(API-verified 2026-10-31: tag v0.1.6, created 2026-08-25T02:18:39Z).
No claim is made here about the .env.secrets token state.
2. CI/CD — §3's "Local shell only; NO .github/workflows (OOS per
D-007)" is stale in fact: the repo carries .gitea/workflows/ci.yml
and main runs green (run 30, success, 2026-10-26T23:17:26Z — PR #9
merge commit 8a92f57b). The D-007 canon correction itself is phase-2
work and rides there as a dated addition; this re-take does not
rewrite any locked decision.
3. Forge host/org — §3 names git.cloudinit.dev/continuous-intelligence;
the forge is now git.coreci.dev, repo coreci/nova-platform (verified
by the API host this re-take's reads answered on).
Not moved since 2026-08-28 (verified 2026-10-31):
- Environments: only core/environments/dev.json exists; qa/prod/dr still
resolve nowhere — phase 1's whole target, unchanged.
- Coverage: pyproject.toml carries no coverage config and no pytest-cov
reference — RULED OUT of v1.1 (comment 16383); D-030 stands; if the
missing floor ever bites it reopens through its own lane.
Rulings carried into the record by comment 16383 (same verdict):
- NORTH_STAR.md stays OUT of v1.1 (gap noted for the next product
review, not minted mid-milestone).
- Phase tag numbering stays with the product's own harness at phase 0.
- Phase 4 (nova-bcx consumer unblock) shape DECIDED 2026-10-31 (hip's
comment 16379 on coreci/nova-platform#10): adapter option 1 mirror;
execution filed as MR coreci/nova-blockchain-exchange#3; the merge rides
the gate with no bypass (merge-path ruling comment 16503: the fleet lane
serves the runner plane via coreci/company#139) until a green nova-bcx
deploy run newer than 6210 lands.
This re-take does NOT touch .ciagent/CHECKPOINT.json (the harness's own
state file) and does NOT rewrite any locked decision. The original intake
sections below are preserved as the 2026-08-28 record.
---
## 1. Header
Project: nova-platform
Initiative: PDLC Phase 0 — State Intake for next milestone planning
Initiator: CIAgent (automated harness)
Date (UTC): 2026-08-28T19:09:26Z
Current Version: v1.0 milestone complete; release v0.1.6 on main; no active milestone in progress
(re-take 2026-10-31: milestone v1.1 is ACTIVE — five-phase plan ratified
2026-10-31T20:39:34Z, coreci/nova-platform#11 comment 16383; phase 0
execution is this re-take)
System Health: GREEN — 76/76 tests pass, engine boundary holds, zero OOS files, both happy paths green
Raw Idea (≤ 3 sentences):
Nova Platform v1.0 shipped a simplified infrastructure-delivery platform (contract → resolve → terraform adapter → apply) derived from the Nova/acdl reference, with security/audit/identity/CI-workflow machinery deliberately excluded.
This intake was triggered by the PDLC system instruction to produce a STATE.md for the Product Owner to review before planning the next milestone.
The desired outcome is a single-pass ground-truth snapshot enabling the PO to ask pointed Phase 1 questions without back-and-forth.
---
## 2. Architecture State
Active Layers (which exist and are stable):
[x] Core Primitives — 13 L1 modules: s3, vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf, rds, kms-key, dynamodb, uptime
[x] Domain Modules — 2 L2 patterns: microservice (vpc+ecs-cluster+ecs-service+iam-role+ecr+alb per D-038), static-assets (s3+cloudfront+kms-key per D-038)
[x] API/Dev Surface — contract schema (array infrastructure D-015), stack schema (flat, no source D-037), environment schema (simplified D-017/D-018); resolver (core/contract_resolver.py), environment_check (core/environment_check.py), terraform adapter (adapters/terraform/adapter.py)
[ ] UI/Agent Surface — N/A (no frontend, no agent UI; frontend-engineer persona deactivated per D-006)
Compute Topology (per environment):
local: abstract (offline --check-only: contract → resolve → adapter → validate; no AWS, no terraform binary)
dev: serverless (ECS Fargate + ALB + S3 + CloudFront via terraform apply; platform-managed VPC in terraform/platform/)
staging: N/A — UNKNOWN — needs investigation (no qa.json environment file created; only dev.json exists)
prod: N/A — UNKNOWN — needs investigation (no prod.json environment file created)
dr: N/A — UNKNOWN — needs investigation (no dr.json environment file created)
Identity Stack in Force:
auth: N/A — out of scope (Nova-idp, PAT lifecycle, ABAC evaluator explicitly OOS per D-007)
token-vend: N/A — out of scope
signing: N/A — out of scope (KMS signing, JWS attestation explicitly OOS)
session: N/A — out of scope
Audit Stream:
source of truth: N/A — out of scope (outbox writer, audit ledger, attestation matrix explicitly OOS per D-007)
in-repo fallback: no
retention policy: N/A
---
## 3. Technical Stack (concrete, not aspirational)
Language(s) and runtime(s): Python 3.11 (>=3.11 required per pyproject.toml); Bash (shell scripts); HCL (Terraform 1.9.* pinned)
Build / packaging: pyproject.toml (setuptools backend); no CLI package (D-027 — scripts invoked via shell, no [project.scripts]); requirements-test.txt pins 5 deps
CI / CD: Local shell only (scripts/run_ci.sh: lint → test → check-only); NO .github/workflows (OOS per D-007); NO pipelines/ central contract (OOS)
(re-take 2026-10-31: stale — .gitea/workflows/ci.yml is live and main runs
green, run 30 success 2026-10-26T23:17:26Z, merge commit 8a92f57b; the
D-007 canon correction is phase-2, as a dated addition)
Infrastructure: AWS (S3 state backend, DynamoDB lock table, ECS Fargate, ALB, CloudFront, WAF, RDS, KMS, ECR, IAM); terraform/bootstrap/ scripts create S3 bucket nova-tfstate-<account>-<region> + DynamoDB nova-tfstate-locks (D-022); only dev.json environment exists (account_id placeholder 000000000000 for offline)
Data stores: S3 (state backend, static assets), DynamoDB (state locking — nova-tfstate-locks; also L1 dynamodb primitive for consumer tables), RDS (L1 rds primitive)
Secrets / KMS: Static AWS key only (NOVA_AWS_* in .ciagent/.env.secrets, chmod 600, gitignored); NO OIDC (OOS per D-025); KMS via L1 kms-key primitive (consumer-side, not platform-side); NOVA_FORGE_TOKEN for gitea release (currently blank in .env.secrets — release pending)
External integrations in scope: gitea @ https://git.cloudinit.dev/continuous-intelligence/nova-platform (release forge; token NOVA_FORGE_TOKEN); AWS (boto3 for bootstrap scripts + terraform apply)
(re-take 2026-10-31: the forge is now git.coreci.dev, repo
coreci/nova-platform)
---
## 4. Active Constraints (the load-bearing ones)
Locked Decisions: D-001 (milestone type major, first release), D-002 (all 13 L1 + 2 L2 in v1.0), D-003 (branching_strategy phase), D-004 (auto_commit/auto_push true), D-005 (test_first false), D-006 (personas: lead+data+backend active, frontend deactivated), D-007 (config.policy removed, ideation drops security), D-008 (secrets.scopes keeps forge + model-backend), D-009 (ship.confirm_before_ship true), D-010 (telemetry.persist true), D-011 (resolver modules_dir param, file reads permitted), D-012 (L2 opaque, flat stack, no children/wires expansion), D-013 (L2 interface.json + terraform_dir in registry), D-014 (interface.json: no nfrs, no intra_refs), D-015 (infrastructure is ARRAY, version optional), D-016 (interpolation ${env.*}/${contract.*} kept), D-017 (environment.schema.json kept simplified), D-018 (env fields: name/account_id/region/state_backend/network; no runner_role_arn/autonomy/confidence_threshold), D-019 (check() returns dict, raises EnvironmentNotFoundError), D-020 (run_platform.sh stages: check-only/plan-only/apply), D-021 (run_ci.sh glob py_compile), D-022 (lock table nova-tfstate-locks, NOT nova-outbox), D-023 (platform/main.tf = ONLY shared VPC), D-024 (ci-vpc = short-lived test VPC), D-025 (onboarding = IAM role cross-account assume, NOT OIDC, NOT user), D-026 (runner policy account parameterized, NOT hardcoded), D-027 (no CLI package, scripts only), D-028 (docs/modules/index links to modules/*/README.md), D-029 (drop STANDARDS.md, keep README-TEMPLATE.md trimmed), D-030 (moto pinned, minimal use), D-031 (run_platform.sh flags: --check-only/--plan-only/--quiet/--help only), D-032 (NOVA_* env prefix, NOVA_AWS_* → AWS_* copy then unset), D-033 (per-env contract variants differ ONLY in environment field), D-034 (engine-boundary test scans .py stripped of docstrings/comments), D-035 (index_document is L2 static-assets input, NOT L1 s3), D-036 (L2 children: microservice=vpc+ecs-cluster+ecs-service+iam-role+ecr+alb, static-assets=s3+cloudfront+kms-key), D-037 (adapter loads registry, no source in stack — grill C-1 fix), D-038 (L2 compositions locked, grill C-2 fix)
Active Invariants: No INV-* IDs formally registered (v1.0 used D-* decisions, not INV-* invariants). Load-bearing invariants: (1) engine-agnostic core — no aws_*/terraform/module "/provider "/resource " in .py outside adapters/terraform/ (verified by tests/test_engine_boundary.py); (2) structural conventions preserved with 9 locked deviations (D-012, D-013, D-015, D-017, D-018, D-019, D-022, D-023, D-025, D-027); (3) no OOS-creep — zero files from the PROJECT.md/REQUIREMENTS.md OOS list exist in the repo.
Standing Capability Gate: N/A — no GATE-* IDs registered
Anti-Goals Touched: PROJECT.md §"Out of scope" + REQUIREMENTS.md §"Out of scope (locked — do NOT implement in v1.0)": security/policy (kyverno, Wiz, Checkov, PolicyEngine), confidence signal + evidence outbox, identity/ABAC (Nova-idp, PAT, JWS, KMS signing, SoD, HITL, attestation, submission_readiness), CI/CD pipeline (.github/workflows, pipelines/, pipeline schemas), metrics/telemetry, leadership decks, decommission/env_transition/mode_resolver/onboarding flow beyond bootstrap, multi-project mode, OIDC federation.
Out-of-Scope (hard): kyverno-json adapter, Wiz adapter, Checkov custom rules, core/policy_engine.py, core/confidence_signal.py, core/outbox_writer.py, schemas/policy_check_result.schema.json, schemas/metrics_*.schema.json, audit ledger, attestation matrix, core/abac_evaluator.py, core/auth_store.py, core/jws_attestation.py, core/kms_signing.py, core/pat_lifecycle.py, core/separation_of_duties.py, core/hitl_gates.py, core/attestation_matrix.py, core/submission_readiness.py, .github/workflows/ci.yml, .github/workflows/deploy.yml, pipelines/, schemas/pipeline.schema.json, schemas/deploy-pipeline.schema.json, metrics/, core/metrics/, core/regression_verify*.py, modules/STANDARDS.md, PPTX/marp slides, core/env_transition.py, core/decommission_transform.py, core/mode_resolver.py, core/onboarding.py, OIDC federation.
---
## 5. Recent History & Quality Gates (last 1-2 milestones)
Last Shipped: v1.0 milestone — 2026-08-25 (tag v0.1.6 on main); delivered: simplified infrastructure-delivery platform with 13 L1 + 2 L2 modules, contract surface, resolver, terraform adapter, engine boundary, 5 terraform roots, bootstrap scripts, shell reproducibility, 76 tests, docs; 38 REQ-IDs all complete; 38 decisions (D-001..D-038)
In Progress: No active milestone in progress (v1.0 complete; checkpoint cleared; next /ci-run starts a new milestone)
Coverage Floor: UNKNOWN — needs investigation (pytest-cov not installed; no coverage measurement configured in pyproject.toml addopts; D-030 dropped pytest-cov as OOS)
Recent Incidents: none
Known Tensions: (1) NOVA_FORGE_TOKEN blank in .ciagent/.env.secrets — v0.1.6 shipped local-only (tag + merge complete, gitea release pending); the forge repo was created during this intake but the token must be set before releases can be created. (2) Only dev.json environment exists — qa/prod/dr environment files were not created in v1.0 (contracts reference them via enum but environment_check will raise EnvironmentNotFoundError for qa/prod/dr). (3) Coverage measurement is absent — pytest-cov was dropped as OOS but this means no coverage floor is enforceable.
(re-take 2026-10-31: (1) release v0.1.6 EXISTS on the forge (created
2026-08-25T02:18:39Z, API-verified) — release pending is resolved; the
.env.secrets token state is not re-asserted from this seat. (2) unmoved —
phase 1. (3) unmoved — ruled OUT of v1.1 (comment 16383); D-030 stands.)
---
## 6. Agent Context & Assumptions (Agent Initiators Only)
Missing Context: (1) qa/prod/dr environment JSON files — not created in v1.0 (only core/environments/dev.json exists); the contract schema allows these environment values but no environment record exists for them. (2) Coverage percentage — pytest-cov is not installed; no coverage data available. (3) NORTH_STAR.md — not created (the reference acdl has one; nova-platform deferred it to a future /ci-run specify stage). (4) Live AWS verification — bootstrap scripts + terraform roots were authored but never applied against real AWS (offline-only verification via --check-only).
(re-take 2026-10-31: (1) unmoved — phase 1. (2) unmoved — ruled out of
v1.1 (comment 16383). (3) unmoved as a v1.1 item — ruled out (comment
16383). (4) unmoved — phase 3; no live-apply evidence exists today.)
Agent Assumptions: (1) Assumed the gitea forge token from the acdl reference repo (/home/opencode/acdl/.env.secrets) is valid for the nova-platform org — verified via API (200 auth). (2) Assumed the next milestone is v1.1 (incrementing from v1.0) — no ROADMAP.md entry exists for a next milestone yet. (3) Assumed PDLC Phase 0 STATE.md work belongs on a new milestone/phase-0 branch hierarchy (milestone/v1.1-pdlc-state → phase/00-pre-execution) per the branch gate convention. (4) Assumed "merge to the forge upstream but DO NOT perform a release" means: push main + branches to origin, do NOT create a gitea release (no tag push, no release API call).
---
## 7. Canonical State References (Version/Hash)
Vision/Strategy doc: UNKNOWN — needs investigation (NORTH_STAR.md not created; PROJECT.md §"Vision / Core Value" serves as the de facto vision but is not versioned as a strategy doc)
Architecture document: v0.1.6 (tag on main commit fe9ab7e); .ciagent/ARCHITECTURE.md + docs/architecture.md are the architecture source of truth
Last approved SPEC: N/A — no SPEC-ID system in use; REQUIREMENTS.md (38 REQ-IDs, REQ-01..REQ-38) is the requirements source of truth at commit fe9ab7e
Decision log: fe9ab7e (last synced commit on main); decisions D-001..D-035 in .ciagent/CLARIFY.md; D-036..D-038 (grill fixes) in .ciagent/CLARIFY.md §"Grill fixes"; D-001..D-010 also in .ciagent/PROJECT.md
Invariants catalog: N/A — no INV-* IDs formally registered; load-bearing invariants documented in STATE.md §4 above
+78
View File
@@ -0,0 +1,78 @@
# nova-platform CI — offline mirror of scripts/run_ci.sh (task order coreci/nova-platform#3, hip c12363)
#
# Stages mirror the local CI script: lint (py_compile) -> test (pytest,
# not shell markers) -> check-only (run_platform.sh --check-only).
# Every leg either runs for real or fails loud with its reason — no leg
# is skipped silently and nothing skipped is presented as passed.
name: CI
on:
push:
pull_request:
branches: [main]
workflow_dispatch:
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: "fetch (probe: stock actions/checkout@v4)"
id: actfetch
continue-on-error: true
uses: actions/checkout@v4
- name: fetch (git-clone fallback, run token)
if: ${{ steps.actfetch.outcome == 'failure' }}
run: |
set -euo pipefail
REF="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME}}"
git clone --depth 1 -b "$REF" "https://coreci-nova-po:${{ secrets.GITEA_TOKEN }}@git.coreci.dev/${{ github.repository }}.git" .
- name: toolchain probe (reports; never gates)
run: |
set -u
python3 --version
python3 -m pytest --version 2>/dev/null || echo "pytest: ABSENT"
for m in yaml jsonschema boto3 moto; do
python3 -c "import $m" 2>/dev/null && echo "$m: OK" || echo "$m: ABSENT"
done
timeout 25 pip download --no-deps --dest /tmp/piptest pytest >/dev/null 2>&1 && echo "pypi: REACHABLE" || echo "pypi: UNREACHABLE"
- name: deps (requirements-test.txt when pypi reachable)
run: |
if timeout 25 pip download --no-deps --dest /tmp/piptest pytest >/dev/null 2>&1; then
pip install -q -r requirements-test.txt && echo "deps: installed from requirements-test.txt"
else
echo "deps: pypi UNREACHABLE - proceeding on the runner's preinstalled toolchain only"
fi
- name: lint (stage 1/3 - py_compile, stdlib-only)
run: |
set -euo pipefail
python3 -m py_compile $(find core/ adapters/ scripts/ -name '*.py' -not -path '*/__pycache__/*')
echo "lint: OK"
- name: test (stage 2/3 - pytest, offline markers)
run: |
set -euo pipefail
MISSING=""
python3 -m pytest --version >/dev/null 2>&1 || MISSING="$MISSING pytest"
for m in yaml jsonschema boto3 moto; do
python3 -c "import $m" 2>/dev/null || MISSING="$MISSING $m"
done
if [ -n "$MISSING" ]; then
echo "FAIL: test leg cannot run - toolchain absent on runner vantage:$MISSING" >&2
exit 1
fi
python3 -m pytest tests/ -q --tb=short -m "not shell"
echo "test: OK"
- name: check-only (stage 3/3 - run_platform.sh --check-only)
run: |
set -euo pipefail
MISSING=""
for m in yaml jsonschema; do
python3 -c "import $m" 2>/dev/null || MISSING="$MISSING $m"
done
if [ -n "$MISSING" ]; then
echo "FAIL: check-only leg cannot run - toolchain absent on runner vantage:$MISSING" >&2
exit 1
fi
bash scripts/run_platform.sh --check-only contracts/static-assets.yml
echo "check-only: OK"
- name: summary
run: 'echo "=== CI stages complete: lint, test, check-only ==="'
+150
View File
@@ -0,0 +1,150 @@
# Nova Platform
> Nova Platform — infrastructure delivery, simplified. A consumer declares
> intent in a YAML contract; the platform resolves it to a stack, compiles
> it through the Terraform adapter, and applies it. Every deployment is
> reproducible from the shell, not just in CI.
**TL;DR** — Nova Platform turns a small YAML contract into AWS
infrastructure: declare modules and inputs, and the platform resolves the
contract, compiles Terraform, and applies it. The last tagged release
(v0.1.6) predates CoreCI adoption; nothing has shipped under CoreCI yet.
**What** —
- One contract file per deployment: module names and versions,
environment, and module-specific inputs.
- The platform resolves the contract to a stack, compiles it through the
Terraform adapter, and applies it.
- Offline checks ship in the repo: `bash scripts/run_ci.sh` runs lint,
tests, and a check-only pass — no AWS required.
**Why** — Contract-driven deploys are reproducible and auditable; a
consumer never writes infrastructure code.
**Action** — No action — FYI. Consumers start at
[`docs/consumer-guide.md`](docs/consumer-guide.md); the product owner is
coreci-nova-po.
**Details** — Design: [`docs/architecture.md`](docs/architecture.md);
runbook: "How to run" below.
Nova Platform is the **infrastructure-delivery core** of the Nova model.
The DevSecOps, identity, audit-ledger, and central CI-pipeline-contract
machinery of the reference are intentionally removed. What remains: a
consumer writes a small YAML contract that names one or more modules by
name + version, selects an environment, and supplies module-specific
inputs. The platform resolves the contract to a stack instance, compiles
it through the Terraform adapter, and applies it.
- **Consumer guide:** [`docs/consumer-guide.md`](docs/consumer-guide.md)
- **Contracts:** [`docs/contracts/`](docs/contracts/index.md)
- **Environments:** [`docs/environments/`](docs/environments/index.md)
- **Architecture:** [`docs/architecture.md`](docs/architecture.md)
- **Modules:** [`docs/modules/`](docs/modules/index.md)
## How to run
### Quick start (offline, no AWS required)
The fastest way to verify the platform works — no AWS credentials, no
bootstrap, no cost.
```bash
# Install test dependencies
pip install -r requirements-test.txt
# 1. Run the test suite (all offline)
python3 -m pytest tests/ -q
# 2. Run the platform in check-only mode (offline — contract -> resolve ->
# adapter -> structure validation). Uses the default sample contract.
bash scripts/run_platform.sh --check-only contracts/static-assets.yml
# Expected: "=== PLATFORM CHECK OK ==="
# 3. Reproduce the full CI pipeline locally (lint -> test -> check-only)
bash scripts/run_ci.sh
# Expected: "=== CI PIPELINE OK ==="
# Show all run_platform.sh flags:
bash scripts/run_platform.sh --help
```
### Run against live AWS (requires credentials + bootstrap)
> Prerequisites: a platform-managed environment (see
> [docs/environments/](docs/environments/index.md); `core/environments/dev.json`
> is the sample), AWS credentials for dev (in `.ciagent/.env.secrets`,
> gitignored; see [Credentials](#credentials)), `terraform` (pin `>= 1.9, < 1.10`),
> `python3` + `boto3` + `jsonschema` + `pyyaml`.
```bash
# 1. Bootstrap the AWS state backend + runner IAM user (one-time, idempotent)
# See terraform/bootstrap/README.md for the full runbook.
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID="<root key>"
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY="<root secret>"
export AWS_DEFAULT_REGION="us-east-1"
python3 terraform/bootstrap/create_state_backend.py
python3 terraform/bootstrap/create_iam_user.py # prints the initial key
bash scripts/rotate_spike_key.sh # writes .ciagent/.env.secrets
# 2. Run the full platform pipeline (contract -> environment check -> stack ->
# adapter -> terraform init/validate/plan -> apply).
bash scripts/run_platform.sh contracts/microservice.yml
# Expected: "=== PLATFORM APPLY OK ==="
# Or plan-only (contract -> stack -> adapter -> terraform plan; no apply):
bash scripts/run_platform.sh --plan-only contracts/static-assets.yml
# Add --quiet to suppress streaming (output to log files only):
bash scripts/run_platform.sh --quiet contracts/static-assets.yml
```
### run_platform.sh flags (D-031)
| Flag | Mode | AWS required | Description |
|------|------|--------------|-------------|
| `--check-only` | offline | no | contract → resolve → adapter → structure validation |
| `--plan-only` | AWS | yes | above + `terraform init`/`validate`/`plan` (no apply) |
| `--quiet` | any | — | suppress streaming output |
| `-h`, `--help` | — | — | show usage |
| *(none)* | apply | yes | full path: above + `terraform apply -auto-approve` |
## Repository layout
| Path | Purpose | Status |
|------|---------|--------|
| `core/` | Platform code: contract resolver, environment check, environments | active |
| `schemas/` | JSON Schemas (draft 2020-12): contract, stack, environment | active |
| `adapters/terraform/` | The Terraform adapter — the only engine-specific code | active |
| `terraform/` | State backend (S3 + DynamoDB) + bootstrap scripts + platform/onboarding/ci-vpc | active |
| `modules/` | L1 primitives (13) + L2 patterns (2) + `registry.json`. Each module has `interface.json` + `terraform/` | active |
| `contracts/` | Sample consumer contracts (`static-assets.yml`, `microservice.yml`) + per-env variants | active |
| `scripts/` | `run_platform.sh` (pipeline runner), `run_ci.sh` (local CI mirror), `rotate_spike_key.sh` | active |
| `tests/` | Pytest suite (all offline — resolver, adapter, schemas, engine boundary, environment check) | active |
| `docs/` | Documentation: consumer guide, contracts, environments, architecture, modules | active |
| `.ciagent/` | CIAgent config + locked decisions (`ARCHITECTURE.md`, `PROJECT.md`, `CLARIFY.md`) | active |
## Credentials
Nova Platform uses a **static AWS key** for dev/local operation. There is no
zero-trust federation layer in v1.0 — that is out of scope.
- The runner key is stored in **`.ciagent/.env.secrets`** (gitignored,
`chmod 600`) using the `NOVA_AWS_*` prefix (D-032):
`NOVA_AWS_ACCESS_KEY_ID`, `NOVA_AWS_SECRET_ACCESS_KEY`.
- `scripts/run_platform.sh` copies `NOVA_AWS_*` to the standard `AWS_*`
env vars before invoking Terraform, then unsets the `NOVA_*` copies.
- Bootstrap uses a one-shot root key via `NOVA_BOOTSTRAP_AWS_*` env vars
(never committed, never echoed). See
[`terraform/bootstrap/README.md`](terraform/bootstrap/README.md) for the
full bootstrap runbook (state backend + runner IAM user + key rotation).
- Onboarding creates a per-consumer IAM **role** (cross-account assume-role
pattern, D-025) — not a user, not federation. See
`terraform/onboarding/main.tf`.
## Consumer guide
A step-by-step guide for a consumer to create a repo, write a contract,
validate it offline, and run it against AWS is at
[`docs/consumer-guide.md`](docs/consumer-guide.md). The guide is generic
across all modules; `static-assets` is the worked example.
+1
View File
@@ -153,6 +153,7 @@ def resolve(contract, registry, modules_dir, environments_dir=None,
def main(argv=None):
import sys
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
argv = argv or sys.argv[1:]
if len(argv) < 2:
print("usage: contract_resolver.py <contract.yaml> [out.json]",
+130
View File
@@ -0,0 +1,130 @@
# Architecture
> **Status:** v1.0 (current). This document mirrors `.ciagent/ARCHITECTURE.md`.
> Where the two conflict, `.ciagent/ARCHITECTURE.md` wins.
## 0. Purpose
Nova Platform is the **infrastructure-delivery core**. A consumer declares
intent in a YAML contract; the platform resolves it to a stack, compiles it
through the Terraform adapter, and applies it. The DevSecOps, identity,
audit-ledger, and central CI-pipeline-contract machinery of the Nova
reference are intentionally removed — see the OOS list below.
## 1. Layers (4)
```
┌──────────────────────────────────────────────────────┐
│ 1. Contract Surface schemas/contract.schema.json
│ contracts/*.yml (samples)
├──────────────────────────────────────────────────────┤
│ 2. Resolution core/contract_resolver.py
│ core/environment_check.py
│ schemas/stack.schema.json
├──────────────────────────────────────────────────────┤
│ 3. Engine Adapter adapters/terraform/ (the only
│ (only engine-specific) engine-specific code)
├──────────────────────────────────────────────────────┤
│ 4. Apply terraform/ (bootstrap, modules)
│ scripts/run_platform.sh
└──────────────────────────────────────────────────────┘
```
### Layer 1 — Contract Surface
A consumer writes a small YAML contract. The `infrastructure` field is an
**array** (D-015), not a map:
```yaml
id: stsi
name: My Static Site
environment: dev
infrastructure:
- module: static-assets
version: "1.0.0"
inputs:
bucket_name: my-static-site-assets
index_document: index.html
```
Validated against `schemas/contract.schema.json`. The contract is the only
consumer-facing surface. It is engine-agnostic — no `aws_*` terms.
### Layer 2 — Resolution
`core/contract_resolver.py` resolves a validated contract to a Stack
instance (a typed structure conforming to `schemas/stack.schema.json`).
Resolution is pure: contract in, stack out. No I/O beyond local file reads
for module metadata (D-011). No engine terms.
`core/environment_check.py` validates that the named environment exists in
`core/environments/*.json` and returns its definition. Environments are
platform-managed (consumers provide no AWS account, VPC, or state bucket).
Interpolation (D-016): the resolver expands `${env.<field>}` and
`${contract.<field>}` tokens after the environment is loaded. Unknown tokens
raise `ValueError`.
### Layer 3 — Engine Adapter
`adapters/terraform/` is the only engine-specific code. It takes a Stack
and emits Terraform (`module "x" { source = "../../modules/..." }` blocks).
The adapter is a stateless assembler — lifecycle ownership belongs to
Terraform via the shell orchestrator. This is the only place `aws_*` /
Terraform terms appear.
### Layer 4 — Apply
`scripts/run_platform.sh` orchestrates: contract → resolve → adapter →
`terraform init` → `terraform plan` → `terraform apply`. Modes (D-031):
`--check-only` (offline, structure validation), `--plan-only` (no apply),
full (apply). `--quiet` suppresses streaming.
## 2. Engine Boundary (Enforced)
The engine boundary is strict. Code outside `adapters/terraform/` MUST NOT
contain engine-specific terms (`aws_s3_bucket`, `aws_*`, Terraform HCL).
This invariant is verified by tests (`tests/test_engine_boundary.py`,
scope per D-034: `.py` files in `core/`, `schemas/`, `contracts/`,
`tests/`, `scripts/`, root — excluding `adapters/terraform/`, `modules/`,
`.tf`/`.md`/`.json` data files).
## 3. What is NOT here (intentionally removed vs the reference)
Nova Platform is a simplified, infrastructure-only platform. The following
reference features are **out of scope** for v1.0:
- No confidence signal — no score gating apply.
- No audit outbox — no hash-chained evidence events.
- No policy engine / policy adapter — no policy checks.
- No identity layer, no attribute-based authorization, no human-in-the-loop
approval gates.
- No reusable CI workflow — local shell only (`scripts/run_ci.sh`).
- No central pipeline contract — no `pipelines/` directory.
- No platform telemetry / metrics.
- No decommission transform, env-transition transform, or onboarding flow
beyond bootstrap.
- No leadership decks or slide rendering.
## 4. Module Catalog
L1 primitives (single resources) + L2 patterns (composites of primitives).
Each module has an `interface.json` (inputs/outputs, no engine terms) and a
`terraform/` directory. `modules/registry.json` indexes every module +
version.
**L1 (primitives — 13):** `s3`, `vpc`, `ecs-cluster`, `ecs-service`,
`iam-role`, `alb`, `ecr`, `cloudfront`, `waf`, `rds`, `kms-key`,
`dynamodb`, `uptime`.
**L2 (patterns — 2):**
| Module | Composes (D-038) | Description |
|--------|------------------|-------------|
| `microservice` | vpc + ecs-cluster + ecs-service + iam-role + ecr + alb (6 L1s) | Container microservice with a public ALB |
| `static-assets` | s3 + cloudfront + kms-key (3 L1s) | Static site fronted by CloudFront |
L2 modules are opaque stack entries (D-012): the resolver does not expand
their children. The L2's `terraform/main.tf` composes L1 modules internally
via `module` blocks. See [`docs/modules/index.md`](modules/index.md) for the
full catalog and each module's README.
+26
View File
@@ -0,0 +1,26 @@
# Continuous integration - nova-platform
**TL;DR -** Every push and every pull request to `main` runs the CI
workflow (`.gitea/workflows/ci.yml`). It is the offline mirror of
`scripts/run_ci.sh`: lint, test, check-only. A leg that cannot run fails
loud - nothing skipped is reported as passed.
**What** -
- **lint** - `python3 -m py_compile` over `core/`, `adapters/`, `scripts/`
(stdlib only, always runs).
- **test** - `pytest tests/ -q -m "not shell"`, with the test dependencies
from `requirements-test.txt` installed first when pypi is reachable; a
missing toolchain fails the leg with its absence list.
- **check-only** - `bash scripts/run_platform.sh --check-only
contracts/static-assets.yml` (needs `yaml` + `jsonschema`).
**Why** - the line shipped without a pipeline, so every change went out
ungated. The workflow runs the same three legs as the local script, so a
green local run and a green pipeline mean the same thing.
**Action** - none - FYI. The pipeline was ordered under
`coreci/nova-platform#3`; the workflow itself landed in PR #6.
**Details** - local equivalent: `bash scripts/run_ci.sh`. Run history:
the repository's Actions tab.
+228
View File
@@ -0,0 +1,228 @@
# Consumer Guide — Declare intent, deploy to AWS
**TL;DR** — This guide takes a consumer from an empty repository to
applied AWS infrastructure: create a repo, write one YAML contract,
validate offline, then run the platform script against AWS.
**What** —
- Step-by-step contract authoring with a worked static-site example,
generic across all modules.
- A field-by-field reference for contract and module inputs.
- Offline validation first (no AWS); the live AWS run second.
**Why** — The contract is the whole consumer interface; infrastructure
code stays platform-side.
**Action** — No action — FYI. Follow Steps 1-6 below when ready to
deploy.
**Details** — The platform README (commands and credentials);
[`docs/contracts/`](contracts/index.md) (contract shapes).
This guide walks a consumer through creating a repo, writing a contract,
validating it offline, and running it against AWS. It is **generic** across
all modules in the registry; `static-assets` is the worked example, but
every step applies to `microservice` and any future module.
## The model
You write a contract YAML file and the platform does the rest. Your
repository contains only your application code and your contracts. You do
not write infrastructure modules or adapter code.
```mermaid
flowchart LR
A["your repo<br/>(app code + contract)"] -->|run_platform.sh| B
B["platform<br/>(resolver + adapter + modules)"] -->|contract -> stack -> terraform -> apply| C
C["your resources in AWS"]
```
## Prerequisites
- **A consumer repository** for your application code + contract.
- **A platform-managed environment** bound to your repo. The platform team
provisions the AWS account, network, and state backend. See
[Environments](environments/index.md).
- **AWS credentials** for the target environment, in `.ciagent/.env.secrets`
(gitignored) using the `NOVA_AWS_*` prefix. See the
[platform README](../README.md#credentials) and
[`terraform/bootstrap/README.md`](../terraform/bootstrap/README.md).
## Step 1 — Create a consumer repo
Create a repository for your application. The top level holds your app
code; your contract lives at the repo root (or wherever you point
`run_platform.sh`). Example for a static site:
```
my-static-site/
index.html
assets/
style.css
logo.png
contract.yml
```
Example for a microservice:
```
my-microservice/
app.py
Dockerfile
contract.yml
```
## Step 2 — Define the contract
Write `contract.yml`. The `static-assets` example:
```yaml
id: stsi
name: Static Assets Site
environment: dev
infrastructure:
- module: static-assets
version: "1.0.0"
inputs:
bucket_name: "${env.environment}-${contract.id}-assets"
index_document: index.html
```
A `microservice` example:
```yaml
id: msvc
name: Microservice
environment: dev
infrastructure:
- module: microservice
version: "1.0.0"
inputs:
service_name: "${env.environment}-${contract.id}-svc"
desired_count: 2
```
### Contract fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | yes | Short operational acronym (`^[a-z][a-z0-9-]{2,5}$`, 3-6 chars). Becomes the stack name used for the Terraform state key and resource naming prefix. Stable across deploys and environment promotions. |
| `name` | string | yes | Full human-readable stack name (min 3 chars). |
| `environment` | string | yes | The platform-managed environment to deploy to (`dev`/`qa`/`prod`/`dr`). See [Environments](environments/index.md). |
| `infrastructure` | array | yes | **Array** of modules to deploy (D-015). Each entry carries a `module` name (matching a registry key), an optional `version` (defaults to latest non-deprecated), and required `inputs`. One entry = single-module deploy; N entries = multi-module manifest. |
### Infrastructure item fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `module` | string | yes | Module name from `modules/registry.json` (`^[a-z][a-z0-9-]*$`). |
| `version` | string | no | Semver pin `X.Y.Z`. Omitted = latest non-deprecated version. |
| `inputs` | object | yes | Module-specific inputs (see the module's README). No `aws_*` keys — the contract is engine-agnostic. |
Each module declares its inputs in its `interface.json`. Consult the
[module catalog](modules/index.md) for the full list, or read the module's
own README under `modules/l1/<name>/` or `modules/l2/<name>/`.
The contract is validated against `schemas/contract.schema.json`. An invalid
contract (missing field, unknown module, wrong type) fails at the
validate-contract stage with a clear error.
### Interpolation reference (D-016)
The resolver expands `${env.*}` and `${contract.*}` tokens after the
environment is loaded. Unknown tokens raise `ValueError` (fail loud).
Expansion is recursive (nested map/list values expand too).
| Token | Resolves to | Example |
|-------|-------------|---------|
| `${env.name}` | the environment name | `dev` |
| `${env.region}` | the environment's AWS region | `us-east-1` |
| `${env.account_id}` | the environment's AWS account id | `000000000000` |
| `${env.state_backend.bucket}` | the environment's state bucket | `nova-tfstate-dev-us-east-1` |
| `${env.state_backend.lock_table}` | the environment's lock table | `nova-tfstate-locks` |
| `${env.network.vpc_cidr}` | the environment's VPC CIDR | `10.0.0.0/16` |
| `${contract.id}` | the contract's operational acronym | `stsi` |
| `${contract.name}` | the contract's name field | `Static Assets Site` |
| `${contract.environment}` | the contract's environment field | `dev` |
## Step 3 — Validate offline (no AWS required)
Before touching AWS, validate the contract end-to-end offline. Clone the
Nova Platform repo and run `--check-only` against your contract:
```bash
bash scripts/run_platform.sh --check-only path/to/your/contract.yml
# Expected: "=== PLATFORM CHECK OK ==="
```
This runs: environment check → contract schema validation → resolve to
stack → adapter compiles to HCL → output structure validation. No AWS
credentials are needed.
## Step 4 — Run against live AWS
Once the contract validates offline, run the full pipeline against AWS.
Ensure your credentials are in `.ciagent/.env.secrets` (see
[Credentials](../README.md#credentials)):
```bash
bash scripts/run_platform.sh contracts/static-assets.yml
# Expected: "=== PLATFORM APPLY OK ==="
```
The full path: environment check → validate contract → resolve to stack →
adapter compiles to HCL → load AWS credentials → `terraform init` →
`terraform validate` → `terraform plan` → `terraform apply -auto-approve`.
To stop before apply (review the plan only):
```bash
bash scripts/run_platform.sh --plan-only contracts/static-assets.yml
# Expected: "=== PLATFORM PLAN OK ==="
```
## Step 5 — What gets created
After a successful `dev` run, the resources declared by your module's
pattern exist in your AWS account.
For the `static-assets` example (s3 + cloudfront + kms-key, D-038):
- An **S3 bucket** (named via your `bucket_name` input, interpolation
expanded) with versioning enabled.
- A **CloudFront distribution** with the S3 bucket as the origin.
- A **KMS key** for SSE.
For other modules, consult the module's README
(`modules/l1/<name>/README.md` or `modules/l2/<name>/README.md`) for the
exact resources created.
## Step 6 — Upload your content (static-assets example)
The platform provisions the infrastructure; you upload your content. For the
`static-assets` module:
```bash
aws s3 sync ./assets s3://<your-bucket-name>/
```
For a `microservice`, the platform provisions the ECS service and ALB; you
push your container image to the ECR repo the platform created.
## Reference
| Resource | Path | Description |
|----------|------|-------------|
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
| Module catalog | [modules/](modules/index.md) | All primitives and modules. |
| Sample contract | `contracts/static-assets.yml` | The reference example contract. |
| Sample contract | `contracts/microservice.yml` | The microservice example contract. |
| Module examples | `modules/<name>/examples/` | Validated per-module example contracts. |
| Contract resolver | `core/contract_resolver.py` | Resolves contracts to stack instances. |
| Terraform adapter | `adapters/terraform/adapter.py` | Compiles stack instances to Terraform. |
| Pipeline runner | `scripts/run_platform.sh` | The pipeline runner. |
| Environments | [environments/](environments/index.md) | Platform-managed environments. |
| Platform README | `README.md` | How the platform works + how to run it. |
| Credentials | `README.md#credentials` | The static-key model + bootstrap runbook. |
+139
View File
@@ -0,0 +1,139 @@
# Contracts
A consumer declares intent in a **contract** — a small YAML file that names
infrastructure (one or more modules), selects an environment, and supplies
module-specific inputs. The platform validates, resolves, and deploys it.
## The contract file
A minimal example (the `static-assets` module):
```yaml
id: stsi
name: Static Assets Site
environment: dev
infrastructure:
- module: static-assets
version: "1.0.0"
inputs:
bucket_name: "${env.environment}-${contract.id}-assets"
index_document: index.html
```
A `microservice` example:
```yaml
id: msvc
name: Microservice
environment: dev
infrastructure:
- module: microservice
version: "1.0.0"
inputs:
service_name: "${env.environment}-${contract.id}-svc"
desired_count: 2
```
## Array-based infrastructure (D-015)
The `infrastructure` field is an **array** of module entries, not a map
keyed by module name. This is a locked deviation from the Nova reference
(D-015). Each entry is an object with `module`, `version` (optional), and
`inputs`.
One entry = single-module deploy. N entries = multi-module manifest deployed
in one pipeline run:
```yaml
id: app
name: Pricing Service API
environment: dev
infrastructure:
- module: microservice
version: "1.0.0"
inputs: { ... }
- module: static-assets
version: "1.0.0"
inputs: { ... }
```
All modules deploy to the same `environment` in one pipeline run.
## Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | string | yes | Short operational acronym (`^[a-z][a-z0-9-]{2,5}$`, 3-6 chars). Becomes the stack name used for the Terraform state key and resource naming prefix. Stable across deploys and environment promotions. |
| `name` | string | yes | Full human-readable stack name (min 3 chars). |
| `environment` | string | yes | The platform-managed environment to deploy to (`dev`/`qa`/`prod`/`dr`). See [Environments](../environments/index.md). |
| `infrastructure` | array | yes | Array of modules to deploy (D-015). `minItems: 1`. |
### Infrastructure item fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `module` | string | yes | Module name from `modules/registry.json` (`^[a-z][a-z0-9-]*$`). |
| `version` | string | no | Semver pin (`^\d+\.\d+\.\d+$`). Omitted = latest non-deprecated version from the registry. |
| `inputs` | object | yes | Module-specific inputs (see the module's README / `interface.json`). No `aws_*` keys — the contract is engine-agnostic. |
## Validation
The contract is validated against
[`schemas/contract.schema.json`](../../schemas/contract.schema.json). An
invalid contract (missing field, unknown module, wrong type) fails at the
validate-contract stage with a clear error.
## Interpolation tokens (D-016)
The resolver expands `${env.*}` and `${contract.*}` tokens after the
environment is loaded. Unknown tokens raise `ValueError` (fail loud).
Expansion is recursive (nested map/list values expand too).
| Token | Resolves to | Example |
|-------|-------------|---------|
| `${env.name}` | the environment name | `dev` |
| `${env.region}` | the environment's AWS region | `us-east-1` |
| `${env.account_id}` | the environment's AWS account id | `000000000000` |
| `${env.state_backend.bucket}` | the environment's state bucket | `nova-tfstate-dev-us-east-1` |
| `${env.state_backend.lock_table}` | the environment's lock table | `nova-tfstate-locks` |
| `${env.network.vpc_cidr}` | the environment's VPC CIDR | `10.0.0.0/16` |
| `${env.network.azs}` | the environment's availability zones | `["us-east-1a","us-east-1b"]` |
| `${contract.id}` | the contract's operational acronym | `stsi` |
| `${contract.name}` | the contract's name field | `Static Assets Site` |
| `${contract.environment}` | the contract's environment field | `dev` |
## Sample contracts
Two reference examples exist in `contracts/`:
- [`contracts/static-assets.yml`](../../contracts/static-assets.yml) — the
`static-assets` module (s3 + cloudfront + kms-key, D-038).
- [`contracts/microservice.yml`](../../contracts/microservice.yml) — the
`microservice` module (vpc + ecs-cluster + ecs-service + iam-role + ecr +
alb, D-038).
Additionally, every module has a `modules/<name>/examples/` directory with
validated example contracts. See the [module catalog](../modules/index.md)
for the full list.
## Per-environment variants (D-033)
Each sample contract has per-environment variants that differ **only** in
the `environment` field — all other fields are identical. Interpolation
resolves environment-specific values at resolver time.
| File | Environment |
|------|-------------|
| `contracts/static-assets.yml` | dev (default) |
| `contracts/static-assets.dev.yml` | dev |
| `contracts/static-assets.qa.yml` | qa |
| `contracts/static-assets.prod.yml` | prod |
| `contracts/static-assets.dr.yml` | dr |
| `contracts/microservice.yml` | dev (default) |
| `contracts/microservice.dev.yml` | dev |
| `contracts/microservice.qa.yml` | qa |
| `contracts/microservice.prod.yml` | prod |
| `contracts/microservice.dr.yml` | dr |
Promotion = running the pipeline against the matching variant. See the
[Consumer Guide](../consumer-guide.md) for the end-to-end flow.
+104
View File
@@ -0,0 +1,104 @@
# Environments
A consumer does **not** provide an AWS account, a VPC, a subnet, or an S3
state bucket. The platform manages environments.
## What an environment is
A named environment is a **platform-owned** bundle of:
- An **AWS account** (or a scoped partition of one).
- A **network** (VPC + subnets / AZs).
- A **state backend** (an S3 bucket + DynamoDB lock table for Terraform
state).
A consumer selects an environment **by name** in their contract:
```yaml
environment: dev
```
The platform resolves the name to the underlying account/network/state
backend at run time. The consumer never sees the raw credentials.
## Environment definition shape (D-018)
Each environment is a JSON file in `core/environments/`. The field set is
reduced from the reference — the IAM role, autonomy, and confidence
threshold fields are out of scope for v1.0.
`core/environments/dev.json` (the sample):
```json
{
"name": "dev",
"description": "Sample dev environment for offline/local testing. account_id placeholder (000000000000) for offline mode.",
"account_id": "000000000000",
"region": "us-east-1",
"state_backend": {
"bucket": "nova-tfstate-dev-us-east-1",
"lock_table": "nova-tfstate-locks"
},
"network": {
"vpc_cidr": "10.0.0.0/16",
"azs": ["us-east-1a", "us-east-1b"]
}
}
```
### Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | yes | The environment name (`dev`/`qa`/`prod`/`dr`). |
| `description` | string | no | Human-readable description. |
| `account_id` | string | yes | The AWS account id (placeholder `000000000000` for offline dev). |
| `region` | string | yes | The AWS region. |
| `state_backend.bucket` | string | yes | The S3 state bucket name. |
| `state_backend.lock_table` | string | yes | The DynamoDB lock table name (`nova-tfstate-locks` per D-022). |
| `network.vpc_cidr` | string | yes | The VPC CIDR block. |
| `network.azs` | array | yes | The availability zones. |
### Dropped from the reference (D-018)
- `runner_role_arn` — identity/authorization is out of scope.
- `autonomy` — human-in-the-loop gates are out of scope.
- `confidence_threshold` — the confidence signal is out of scope.
## State backend (D-022)
The Terraform state backend uses a dedicated DynamoDB lock table named
`nova-tfstate-locks` (NOT `nova-outbox` — the audit outbox is out of scope
for v1.0). The S3 state bucket is named `nova-tfstate-<account>-<region>`
with versioning enabled. See
[`terraform/bootstrap/README.md`](../../terraform/bootstrap/README.md) for
the bootstrap runbook that creates both.
## Autonomy by environment
| Environment | Autonomy | Operator action |
|-------------|----------|-----------------|
| dev | Fully autonomous | None — `terraform apply -auto-approve` runs automatically. |
| qa | Manual | An operator runs `run_platform.sh` against the `qa` contract. |
| prod | Manual | An operator runs `run_platform.sh` against the `prod` contract. |
| dr | Manual | An operator runs `run_platform.sh` against the `dr` contract. |
`dev` is the only autonomous environment. Higher environments require a
human operator to invoke the pipeline against the environment's contract
variant. There are no automated gates or attestation steps — those are out
of scope for v1.0. Staging does not exist.
## Onboarding scaffold (current state)
The platform repo ships a minimal onboarding scaffold:
- [`core/environments/`](../../core/environments/) — environment definitions
(a sample `dev.json`).
- `core/environment_check.py` — checks whether an environment is defined
for a given contract's environment name; raises `EnvironmentNotFoundError`
when none is defined (D-019).
- `scripts/run_platform.sh` calls the check before contract validation.
The scaffold is minimal: provisioning a new environment is a platform-team
action today (bootstrap the state backend + network). Self-service
environment provisioning is a future milestone.
+8 -2
View File
@@ -1,5 +1,11 @@
# Nova Modules
**TL;DR** — Nova's module library: two tiers of engine-agnostic modules,
whose contract is declared in `interface.json` and translated by an adapter
(Terraform today) to concrete infrastructure. This file explains how
modules compose and how to add one; the L1/L2 tables and adapter detail
follow below.
Nova ships a two-tier module library. Modules are **engine-agnostic**:
their contract is declared in `interface.json` (stack types like
`aws:s3:bucket`), and an adapter translates the contract to a concrete
@@ -123,5 +129,5 @@ in this repo ship a Terraform adapter under `terraform/`.
| Module | Composes | Description |
|------------------|-------------------------------------------|----------------------------------------------|
| `microservice` | vpc + ecs-cluster + ecs-service + alb + ecr | Container microservice with public ALB |
| `static-assets` | s3 + cloudfront | Static site fronted by CloudFront |
| `microservice` | vpc + ecs-cluster + ecs-service + iam-role + ecr + alb | Container microservice with public ALB (D-038) |
| `static-assets` | s3 + cloudfront + kms-key | Static site fronted by CloudFront (D-038) |
+38
View File
@@ -0,0 +1,38 @@
[project]
name = "nova-platform"
version = "1.0.0"
description = "Nova Platform — simplified infrastructure delivery (contract -> resolve -> terraform adapter -> apply)"
requires-python = ">=3.11"
dependencies = [
"boto3>=1.34",
"jsonschema>=4.20",
"pyyaml>=6.0",
]
[project.optional-dependencies]
test = [
"pytest>=8.0",
"moto[dynamodb]>=5.0",
"jsonschema>=4.20",
"pyyaml>=6.0",
"boto3>=1.34",
]
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --tb=short"
markers = [
"offline: offline tests (no AWS)",
"slow: slow tests",
"shell: shell-invocation tests (excluded from run_ci.sh to avoid recursion)",
]
filterwarnings = [
"ignore::DeprecationWarning:botocore.*",
]
[build-system]
requires = ["setuptools>=68"]
build-backend = "setuptools.build_meta"
[tool.setuptools.packages.find]
include = ["core*", "adapters*"]
+5
View File
@@ -0,0 +1,5 @@
pytest>=8.0
moto[dynamodb]>=5.0
jsonschema>=4.20
pyyaml>=6.0
boto3>=1.34
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
# Nova Platform — run_ci.sh
#
# Local CI pipeline mirror: lint (py_compile) -> test (pytest) ->
# check-only (run_platform.sh --check-only).
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
QUIET=0
[ "${1:-}" = "--quiet" ] && QUIET=1
banner() { [ "$QUIET" -eq 0 ] && echo "── $1 ──"; }
banner "Stage 1/3: lint (py_compile)"
python3 -m py_compile $(find core/ adapters/ scripts/ -name '*.py' -not -path '*/__pycache__/*')
echo "lint: OK"
banner "Stage 2/3: test (pytest)"
python3 -m pytest tests/ -q --tb=short -m "not shell"
echo "test: OK"
banner "Stage 3/3: check-only (run_platform.sh --check-only)"
bash scripts/run_platform.sh --check-only contracts/static-assets.yml
echo "check-only: OK"
echo "=== CI PIPELINE OK ==="
echo "3 stages passed: lint, test, check-only"
+116
View File
@@ -0,0 +1,116 @@
#!/usr/bin/env bash
# Nova Platform — run_platform.sh
#
# Orchestrates: contract -> resolve -> adapter -> apply.
#
# Flags (per D-031): --check-only, --plan-only, --quiet, --help (-h).
# Default mode (no flag) = apply.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
CHECK_ONLY=0
PLAN_ONLY=0
QUIET=0
CONTRACT=""
_print_help() {
cat <<EOF
usage: run_platform.sh [--check-only|--plan-only|--quiet|--help] [contract.yml]
--check-only Offline validation: contract -> resolve -> adapter -> validate. No AWS.
--plan-only AWS: above + terraform init/validate/plan. No apply.
--quiet Suppress streaming output.
-h, --help Show this help.
Default (no flag): full apply path (AWS required).
Default contract: contracts/static-assets.yml
EOF
}
while [ $# -gt 0 ]; do
case "$1" in
--check-only) CHECK_ONLY=1; shift;;
--plan-only) PLAN_ONLY=1; shift;;
--quiet) QUIET=1; shift;;
-h|--help) _print_help; exit 0;;
--*) echo "FAIL: unknown flag $1" >&2; exit 1;;
*) CONTRACT="$1"; shift;;
esac
done
[ -z "$CONTRACT" ] && CONTRACT="contracts/static-assets.yml"
fail() { echo "FAIL: $*" >&2; exit 1; }
WORK="${NOVA_WORK_DIR:-/tmp/nova_platform_run}"
TF_DIR="$WORK/tf"
rm -rf "$WORK"; mkdir -p "$TF_DIR"
banner() { [ "$QUIET" -eq 0 ] && echo "=== $* ==="; }
banner "Step 0: environment check"
python3 -c "
import sys, json, yaml
from pathlib import Path
sys.path.insert(0, '.')
from core import environment_check
contract = yaml.safe_load(open('$CONTRACT'))
env = environment_check.check(contract['environment'], Path('core/environments'))
print(f'env: {env[\"name\"]} ({env[\"region\"]})')
"
banner "Step 1: validate contract"
python3 -c "
import json, yaml, jsonschema
schema = json.load(open('schemas/contract.schema.json'))
contract = yaml.safe_load(open('$CONTRACT'))
jsonschema.validate(contract, schema)
print('contract valid')
"
banner "Step 2: resolve contract -> stack"
python3 core/contract_resolver.py "$CONTRACT" "$WORK/stack.json"
python3 -c "import json; s=json.load(open('$WORK/stack.json')); print(f'resources: {len(s[\"resources\"])}')"
banner "Step 3: adapter compiles stack -> HCL"
python3 adapters/terraform/adapter.py "$WORK/stack.json" "$TF_DIR/main.tf"
[ "$QUIET" -eq 0 ] && head -20 "$TF_DIR/main.tf"
if [ "$CHECK_ONLY" -eq 1 ]; then
python3 -c "
from pathlib import Path
main = Path('$TF_DIR/main.tf')
assert main.exists() and main.stat().st_size > 0, 'main.tf missing or empty'
content = main.read_text()
assert 'module \"' in content, 'no module blocks'
print('output structure valid')
"
echo "=== PLATFORM CHECK OK ==="
exit 0
fi
banner "Loading AWS credentials"
if [ -f .ciagent/.env.secrets ]; then
set -a
. .ciagent/.env.secrets
set +a
fi
export AWS_ACCESS_KEY_ID="${NOVA_AWS_ACCESS_KEY_ID:-${AWS_ACCESS_KEY_ID:-}}"
export AWS_SECRET_ACCESS_KEY="${NOVA_AWS_SECRET_ACCESS_KEY:-${AWS_SECRET_ACCESS_KEY:-}}"
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-us-east-1}"
unset NOVA_AWS_ACCESS_KEY_ID NOVA_AWS_SECRET_ACCESS_KEY NOVA_FORGE_TOKEN 2>/dev/null || true
[ -z "$AWS_ACCESS_KEY_ID" ] && fail "no AWS credentials (set NOVA_AWS_* in .ciagent/.env.secrets)"
banner "Step 4: terraform init + validate + plan"
( cd "$TF_DIR" && terraform init -backend=false && terraform validate && terraform plan -out=tfplan )
if [ "$PLAN_ONLY" -eq 1 ]; then
echo "=== PLATFORM PLAN OK ==="
exit 0
fi
banner "Step 5: terraform apply"
( cd "$TF_DIR" && terraform apply -auto-approve tfplan )
echo "=== PLATFORM APPLY OK ==="
+32
View File
@@ -0,0 +1,32 @@
"""Test scripts/run_ci.sh — REQ-30."""
import subprocess
import pytest
from tests.conftest import ROOT
pytestmark = pytest.mark.shell
class TestRunCi:
def test_exits_zero(self):
result = subprocess.run(
["bash", str(ROOT / "scripts" / "run_ci.sh")],
capture_output=True, text=True, cwd=str(ROOT),
)
assert result.returncode == 0, f"stderr: {result.stderr}"
def test_banner(self):
result = subprocess.run(
["bash", str(ROOT / "scripts" / "run_ci.sh")],
capture_output=True, text=True, cwd=str(ROOT),
)
assert "=== CI PIPELINE OK ===" in result.stdout
def test_three_stages_message(self):
result = subprocess.run(
["bash", str(ROOT / "scripts" / "run_ci.sh")],
capture_output=True, text=True, cwd=str(ROOT),
)
assert "3 stages passed: lint, test, check-only" in result.stdout
+37
View File
@@ -0,0 +1,37 @@
"""Test scripts/run_platform.sh --check-only — REQ-29."""
import subprocess
from pathlib import Path
import pytest
from tests.conftest import ROOT
pytestmark = pytest.mark.shell
class TestRunPlatformCheckOnly:
def test_check_only_exits_zero(self):
result = subprocess.run(
["bash", str(ROOT / "scripts" / "run_platform.sh"),
"--check-only", "contracts/static-assets.yml"],
capture_output=True, text=True, cwd=str(ROOT),
)
assert result.returncode == 0, f"stderr: {result.stderr}"
def test_check_only_banner(self):
result = subprocess.run(
["bash", str(ROOT / "scripts" / "run_platform.sh"),
"--check-only", "contracts/static-assets.yml"],
capture_output=True, text=True, cwd=str(ROOT),
)
assert "=== PLATFORM CHECK OK ===" in result.stdout
def test_check_only_microservice(self):
result = subprocess.run(
["bash", str(ROOT / "scripts" / "run_platform.sh"),
"--check-only", "contracts/microservice.yml"],
capture_output=True, text=True, cwd=str(ROOT),
)
assert result.returncode == 0
assert "=== PLATFORM CHECK OK ===" in result.stdout