5 Commits

Author SHA1 Message Date
CIAgent 756100ab17 docs(P04): complete l2-patterns-bootstrap-platform phase
---ci---
project: nova-platform
phase: 4
milestone: v1.0
status: complete
---/ci---

P4 complete: L2 patterns (D-038), bootstrap (D-022/026), platform
(D-023), ci-vpc (D-024), onboarding (D-025), microservice root.
68 tests pass. REQ-12,14,15,16,17,18,19,22 covered.
2026-08-24 17:47:24 +00:00
CIAgent 11e342a9f7 docs(P03): complete l1-primitives-registry phase
---ci---
project: nova-platform
phase: 3
milestone: v1.0
status: complete
---/ci---

P3 complete: 13 L1 primitives + registry + module docs.
68 tests pass. REQ-10,11,13,34 covered.
2026-08-24 17:41:43 +00:00
CIAgent 14be01d971 docs(P02): complete terraform-adapter-engine-boundary phase
---ci---
project: nova-platform
phase: 2
milestone: v1.0
status: complete
---/ci---

P2 complete: adapter (loads registry D-037), engine-boundary test (D-034).
58 tests pass. REQ-07,08,09,25,26 + REQ-10 (registry) covered.
2026-08-24 17:33:22 +00:00
CIAgent 97691fd752 docs(P01): complete contract-surface-schemas-resolver phase
---ci---
project: nova-platform
phase: 1
milestone: v1.0
status: complete
phase_role: execution
---/ci---

P1 complete: contract schema, stack schema, environment schema,
resolver, env_check, 10 sample contracts, 36 tests. REQ-01..06,
23..28 covered.
2026-08-24 17:31:00 +00:00
CIAgent 7835c2aa2a docs(P00): complete pre-execution phase (specify+clarify+research+plan+grill)
---ci---
project: nova-platform
phase: 0
milestone: v1.0
status: complete
phase_role: pre_execution
---/ci---

Phase 0 complete: 5 pre-execution stages.
SPECIFY: 38 REQ-IDs validated.
CLARIFY: D-011..D-035 (3 escalated).
RESEARCH: 22 in-scope items + PERSONAS.md.
PLAN: 6 phases, ~45 tasks, 13 waves.
GRILL: PROCEED-WITH-CONDITIONS (0.82) → PROCEED (6 conditions resolved).
MVP/UX CHECK: PASS (3 sections in PLAN.md).
Grill fixes: D-037 (adapter loads registry), D-038 (L2 children locked).
2026-08-24 17:26:06 +00:00
16 changed files with 22 additions and 1085 deletions
+17 -10
View File
@@ -1,14 +1,21 @@
{
"phase": 0,
"stage": "specify",
"milestone": "v1.1",
"phase_role": "pre_execution",
"phase": 4,
"stage": "verify",
"milestone": "v1.0",
"phase_role": "execution",
"attempts": 0,
"updated_at": "2026-08-28T19:09:26Z",
"updated_at": "2026-08-20T20:15:00Z",
"project": "nova-platform",
"milestone_branch": "milestone/v1.1-pdlc-state",
"phase_branch": "phase/00-pre-execution",
"milestone_complete": false,
"pdlc_intake": true,
"state_md_created": true
"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"
}
+3 -6
View File
@@ -58,12 +58,9 @@ 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 `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.
`milestone/v1.0-nova-platform``main`. Tags `v1.0.0` (major — the
initial release). Creates the Gitea release. Deletes all milestone
branches.
## Coverage (init estimate — refined by PLAN)
-101
View File
@@ -1,101 +0,0 @@
# STATE — Nova Platform
> PDLC Phase 0 intake. Absolute ground truth as of 2026-08-28T19:09Z.
> Single-pass discovery for the Product Owner/Manager.
---
## 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
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)
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)
---
## 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.
---
## 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).
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
-127
View File
@@ -1,127 +0,0 @@
# 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.
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,7 +153,6 @@ 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
@@ -1,130 +0,0 @@
# 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.
-208
View File
@@ -1,208 +0,0 @@
# Consumer Guide — Declare intent, deploy to AWS
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
@@ -1,139 +0,0 @@
# 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
@@ -1,104 +0,0 @@
# 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.
+2 -2
View File
@@ -123,5 +123,5 @@ in this repo ship a Terraform adapter under `terraform/`.
| Module | Composes | Description |
|------------------|-------------------------------------------|----------------------------------------------|
| `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) |
| `microservice` | vpc + ecs-cluster + ecs-service + alb + ecr | Container microservice with public ALB |
| `static-assets` | s3 + cloudfront | Static site fronted by CloudFront |
-38
View File
@@ -1,38 +0,0 @@
[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
@@ -1,5 +0,0 @@
pytest>=8.0
moto[dynamodb]>=5.0
jsonschema>=4.20
pyyaml>=6.0
boto3>=1.34
-29
View File
@@ -1,29 +0,0 @@
#!/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
@@ -1,116 +0,0 @@
#!/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
@@ -1,32 +0,0 @@
"""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
@@ -1,37 +0,0 @@
"""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