Compare commits

...

10 Commits

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

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

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

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

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

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

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

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

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

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

---ci---
project: acdl
phase: 0
milestone: v1.18
status: specify
---/ci---
2026-08-06 14:54:22 +00:00
Jon Chery 0ad6a88c4b docs(P5): render unified deck to HTML (Step 3 of 4-step deck process)
acdl-ci / Lint (push) Successful in 8s
acdl-ci / Test (push) Failing after 23s
acdl-ci / Platform check-only (offline) (push) Successful in 22s
---ci---
project: acdl
phase: 5
milestone: v1.17
status: complete
---/ci---
2026-08-05 01:58:59 +00:00
14 changed files with 3587 additions and 1481 deletions
+8 -9
View File
@@ -1,13 +1,12 @@
{ {
"phase": 7, "phase": 0,
"stage": "complete", "stage": "complete",
"milestone": "v1.17", "milestone": "v1.18",
"phase_role": "final", "phase_role": "pre_execution",
"attempts": 0, "attempts": 0,
"updated_at": "2026-08-04T22:30:00Z", "updated_at": "2026-08-06T00:35:00Z",
"milestone_complete": true, "milestone_complete": false,
"tag": "v1.16.7", "tag": "v1.17.0",
"release_id": null, "release_id": 522,
"requirements": ["REQ-185", "REQ-186", "REQ-187", "REQ-188", "REQ-189", "REQ-190", "REQ-191", "REQ-192", "REQ-193", "REQ-194", "REQ-195", "REQ-196", "REQ-197", "REQ-198", "REQ-199", "REQ-200", "REQ-201", "REQ-202", "REQ-203", "REQ-204", "REQ-205", "REQ-206", "REQ-207", "REQ-208", "REQ-209", "REQ-210", "REQ-211", "REQ-212", "REQ-213"], "notes": "v1.18 P0 complete. 5 pre-execution stages done. Tag v1.17.0, release 522."
"notes": "v1.17 milestone complete. 3 pillars: (A) NORTH_STAR.md authored + wired into CIAgent context-loading, (B) Leadership Metrics + PowerBI (CloudEvents envelope, SQLite cold store, Decision Ledger hash-chain, Infracost, 8 placeholder views, trust snapshot, metrics catalog), (C) Unified Narrative Deck (18 slides, x3 arc, per-slide benefits, old decks retired). 29 requirements satisfied. 94 tests pass. CAP-023 + CAP-024 Verified. Interactive GRILL: 12 binding decisions applied. 13 decisions locked (D-120..D-132). Hard constraint honored: DO NOT make anything up."
} }
+102 -351
View File
@@ -1,33 +1,31 @@
--- ---
project: acdl project: acdl
milestone: v1.17 milestone: v1.18
generated_at: 2026-08-04 generated_at: 2026-08-06
generator: lead-developer generator: lead-developer
verification_toolchain: verification_toolchain:
typecheck: "terraform validate && python3 -m py_compile core/**/*.py && python3 -m jsonschema schemas/*.schema.json" typecheck: "python3 -m py_compile core/submission_readiness.py mcp/atelier/server.py && python3 -m jsonschema schemas/submission-readiness.schema.json"
test: "bash scripts/run_regression.sh # 22-capability gate (D-091/D-118) + CAP-023/024 (v1.17)" test: "pytest tests/test_submission_readiness.py tests/test_atelier_mcp.py # REQ-220 + REQ-225"
build: "bash scripts/run_ci.sh # full local CI reproduction (lint+test+check-only)" build: "bash scripts/render_deck.sh docs/presentations/nova-no-humans-platform-marp.md # HTML + PPTX (D-142)"
note: | note: |
v1.17 adds a telemetry/observability layer (metrics emitters, SQLite v1.18 adds the Citizen Developer & Production-Grade Guidance surface:
cold store, PowerBI export, Decision Ledger) + a unified narrative submission-readiness gate, Atelier-derived skills, the Atelier MCP server
deck + a durable NORTH_STAR.md. Three active personas: lead-developer (plugin-registry, stdio), and PPTX-as-first-class-artifact deck automation.
(coordination + deck narrative co-author), backend-engineer (event Three active personas: lead-developer (coordination + decks + RACI/scope
emitters, outbox_writer extension, Infracost adapter), data-engineer docs), backend-engineer (MCP server + submission-readiness validator +
(SQLite store, schemas, PowerBI views, metrics collector). frontend- render/attach scripts), data-engineer (submission-readiness schema if it
engineer stays deactivated (no Nova web UI — dashboards are PowerBI, touches contract storage / DynamoDB shape). frontend-engineer stays
not a Nova-built frontend; decks are markdown = lead-developer deactivated (v1.18 has no frontend; decks are markdown = lead-developer
territory). No new custom personas needed — the metrics domain maps territory). The MCP plugin-registry is a backend pattern, so a separate
cleanly to data-engineer (schema/store/export) + backend-engineer mcp-engineer persona is NOT added — it folds into backend-engineer.
(emitters/instrumentation).
--- ---
# ACDL — Persona Roster (project-level, v1.11 RESTART) # ACDL — Persona Roster (v1.18 Citizen Developer & Production-Grade Guidance)
> v1.11 is a restart (D-097). The v1.9 roster is superseded. Three > v1.18 roster. Three active personas + one deactivated. The MCP server
> structural corrections: (1) stateless adapter (D-098), (2) terraform > plugin-registry (D-140) is a backend pattern, not a new persona — it
> owns lifecycle (D-101), (3) pipeline-driven testing (D-102). The roster > folds into backend-engineer. v1.17 precedent (frontend-engineer
> is simplified to the three active domains: data (terraform foundation), > deactivated, decks are markdown = lead-developer territory) is upheld.
> backend (adapter/resolver), general (pipelines/workflows).
## Active personas ## Active personas
@@ -35,351 +33,104 @@ verification_toolchain:
- **Domain:** coordination - **Domain:** coordination
- **Active:** true - **Active:** true
- **Phase-specific:** false - **Phase-specific:** false
- **Reason:** Owns CIAgent metadata, cross-phase verification scripts, the v1.11 phase orchestration (D-107: P56a + P56b split), and arbitrates persona conflicts. Resolves the milestone decomposition and the STANDARDS.md §8 rewrite (the adapter extension pattern is replaced by the per-module terraform subdir pattern). - **Frameworks:** [] (no framework — owns process + narrative, not code)
- **Constraints:** ["pragmatic", "battle-tested defaults", "no fabrication (NORTH_STAR honesty model)"]
- **Territory:**
- `docs/presentations/**` (Step 1/2/4 markdown + the deck automation trigger)
- `.ciagent/**` (PROJECT, ROADMAP, REQUIREMENTS, RESEARCH, PLAN, GRILL, PERSONAS, REVIEW, CHECKPOINT)
- `PROJECT.md` (RACI matrix + PDLC-scope statement, REQ-215/216)
- `ROADMAP.md`
- `REQUIREMENTS.md`
- `docs/raci.md` (REQ-215)
- `docs/scope.md` (REQ-216)
- `docs/skills.md` (REQ-222 — the index page, not the skill files themselves)
- `docs/submission-readiness.md` (REQ-219 — citizen-developer-facing copy; co-owned with backend-engineer for the reason-code catalog)
- **Reason:** Owns CIAgent metadata, the milestone narrative, the RACI +
PDLC-scope statements (REQ-215/216), the deck (21 slides, S&P theme
regression check vs P1, CAP-024), the skills index page (REQ-222), and
the citizen-developer-facing submission-readiness doc (REQ-219). Is
the only persona that touches `.ciagent/**` and the deck markdown.
- **Phase-specific flag:** none (active for all of P0P7).
### backend-engineer ### backend-engineer
- **Domain:** backend - **Domain:** backend
- **Active:** true - **Active:** true
- **Phase-specific:** false - **Phase-specific:** false
- **Reason:** Owns the adapter rewrite (D-098: stateless assembler — deletes TYPE_MAP/INPUT_MAP/OUTPUT_MAP + 39 type-specific branches, becomes a ~80-line assembler that emits `module "x" { source = "..." ... }` blocks) and the contract resolver env-aware state keys (D-106: `spike/{id}/{env}/terraform.tfstate`). The adapter holds no module content; the engine binding lives in the per-module `terraform/` subdir. Co-authoring expected on the adapter + `run_platform.sh` boundary (general adds `--apply`/`--destroy` modes that invoke the adapter). - **Frameworks:** ["mcp (Python SDK v2)", "pydantic", "jsonschema", "urllib"]
- **Territory:** `adapters/terraform/adapter.py` (rewrite to stateless assembler), `core/contract_resolver.py` (env-aware state keys, deterministic composition), `schemas/stack.schema.json` (if the stack instance shape changes), `tests/test_adapter*.py` (regression baseline — the s3 instance.json round-trip must still pass). - **Constraints:** ["api-first", "strict-typing", "plugin-registry extensible (D-140)", "stdio now / HTTP-ready (D-135)", "no stack traces to citizen developers (REQ-218)"]
- **Territory:**
- `mcp/atelier/server.py` (REQ-223)
- `mcp/atelier/plugins/**/*.py` (REQ-223 — principles.py, validation.py)
- `mcp/atelier/vendor/**` (REQ-224 — vendored Atelier snapshot)
- `mcp/atelier/VERSION.md` + `mcp/atelier/README.md` (REQ-224)
- `scripts/update_atelier_vendor.sh` (REQ-224)
- `core/submission_readiness.py` (REQ-218 — the validator, invoked as `contract_ingestor.py --check-readiness`)
- `scripts/render_deck.sh` (REQ-228 — HTML + PPTX render)
- `scripts/attach_release_asset.py` (REQ-228 — Gitea release asset upload)
- `tests/test_atelier_mcp.py` (REQ-225)
- `tests/test_submission_readiness.py` (REQ-220)
- `docs/submission-readiness.md` (REQ-219 — reason-code catalog section; co-owned with lead-developer for the narrative)
- **Reason:** Owns the MCP server (plugin-registry, stdio, vendored
Atelier), the submission-readiness validator (extends
`contract_ingestor.py --check-readiness`, D-133), the render/attach
scripts (D-142 trigger), and the two new test files. The MCP
plugin-registry (D-140) is a backend pattern — no separate
mcp-engineer persona is created; backend-engineer owns it.
- **Phase-specific flag:** none (active for P1 deck-render, P3 validator,
P5 MCP server, P6 scripts).
### data-engineer ### data-engineer
- **Domain:** data - **Domain:** data
- **Active:** true - **Active:** true
- **Phase-specific:** false - **Phase-specific:** false
- **Reason:** Reactivated for v1.11. Owns the heaviest territory: the per-module `terraform/` subdirs (D-098/D-099/D-100 — the engine binding) for all 12 L1 modules, plus the single platform VPC (D-105: `terraform/platform` owns ONE VPC; the microservice composition drops its `vpc` child and references the platform VPC via data source). Each L1 module ships a real terraform module dir (versions/variables/locals/main/outputs.tf) owning its resource shape, nested blocks, and defaults. `locals.tf` is used heavily to centralize default interpolation (D-099). Multi-resource modules get the full 5-file split; trivial single-resource modules may inline locals in main.tf. This is the binding constraint — the stateless adapter cannot be written until the reference s3 module exists (D-107: P56a proves the design with s3 first). - **Frameworks:** ["jsonschema", "dynamodb (item shape)"]
- **Territory:** `terraform/` (platform VPC, D-105), `modules/l1/*/terraform/` (per-module terraform subdirs — the engine binding), `modules/l1/*/interface.json` (defaults move from adapter to interface inputs), `modules/registry.json` (terraform_dir field), `modules/l2/microservice/composition.json` (drop the vpc child, D-105), `modules/STANDARDS.md` §8 (rewrite the adapter extension pattern → per-module terraform subdir pattern). - **Constraints:** ["schema-first", "superset-gate NOT duplicate (PROJECT.md hard constraint)", "W3.E per-env mandatory table is the source of truth"]
- **Territory:**
### general (lead-developer + backend-engineer pipeline work) - `schemas/**` (REQ-217 — `submission-readiness.schema.json` is the new schema; existing schemas untouched)
- **Domain:** coordination + pipelines - `core/lambda/contract_ingestor.py` (the `--check-readiness` subcommand wiring, D-133 — the validator is in `core/submission_readiness.py` but the ingestor dispatches to it; co-owned with backend-engineer)
- **Active:** true - **Reason:** Owns the submission-readiness JSON Schema (REQ-217) — it
- **Phase-specific:** false is a schema artifact, data-engineer territory. The schema is a
- **Reason:** Owns the pipeline-driven testing (D-102/D-103/D-104) and the terraform lifecycle modes (D-101). The modules-lifecycle pipeline (Gitea + GitHub, byte-identical) matrix-runs each L1 module's `examples/{simple,complex}.yml` contracts through apply→modify→destroy against live AWS. `run_platform.sh` gains `--apply` and `--destroy` modes; Python never runs terraform. `verify_deploy_microservice.py` is deleted (D-101). Co-authoring expected on the `run_platform.sh` boundary (backend-engineer rewrites the adapter that `run_platform.sh` invokes). *superset gate above* `contract.schema.json`, not a duplicate (it
- **Territory:** `pipelines/modules-lifecycle.yml`, `.gitea/workflows/modules-lifecycle.yml` + `.github/workflows/modules-lifecycle.yml` (byte-identical, D-102), `scripts/run_platform.sh` (`--apply`/`--destroy` modes, D-101), `scripts/run_primitive_plan.sh` (if extended for lifecycle), `scripts/run_pattern_plan.sh` (if extended), `pipelines/README.md` (document the new pipeline), `schemas/deploy-pipeline.schema.json` (if the lifecycle stages are added to the contract). references contract fields, does not redefine them). The
per-env-mandatory table comes from W3.E (the locked decision). The
## Deactivated personas ingestor wiring is co-owned with backend-engineer (the dispatch point
is backend; the schema it validates against is data).
### lambda-engineer (custom, v1.9 — deactivated for v1.11) - **Phase-specific flag:** none (active for P3 schema + ingestor wiring).
- **Domain:** serverless
- **Active:** false
- **Phase-specific:** false
- **Reason:** No per-module Python this milestone (D-102: testing is pipeline-driven, not pytest). The v1.9 Lambda (`core/lambda/contract_ingestor.py`) and the `terraform/platform/main.tf` Lambda/DynamoDB/KMS/Secrets definitions persist from v1.9 but are not touched in v1.11. The `acdl-sod-halt` SNS topic and the attestation matrix are out of scope. Removed from the roster for v1.11; reactivates if a future milestone touches the Lambda.
### platform-engineer (custom, v1.9 — folded into data-engineer for v1.11)
- **Domain:** infra
- **Active:** false
- **Phase-specific:** false
- **Reason:** The v1.11 scope (D-097..D-107) is terraform module authoring + adapter rewrite + pipelines — not the v1.9-era L1/L2 IR-typed module authoring or the AWS OIDC bootstrap. The platform-engineer's v1.9 territory (`adapters/terraform/**`, `modules/**`, `terraform/**`) is split: the adapter goes to backend-engineer (rewrite), the per-module terraform subdirs + platform VPC go to data-engineer (the heaviest v1.11 work). Folded into data-engineer for v1.11; reactivates if a future milestone does IR-shaped module authoring or OIDC bootstrap work.
### security-engineer (custom, v1.9 — deactivated for v1.11)
- **Domain:** security
- **Active:** false
- **Phase-specific:** false
- **Reason:** The v1.11 scope does not touch Wiz/Kyverno/Checkov adapters, the HITL matrix, separation-of-duties, or the audit ledger. The security-engineer's v1.9 territory persists but is not touched. Removed from the roster for v1.11; reactivates if a future milestone touches security adapters or HITL gates.
### frontend-engineer
- **Domain:** frontend
- **Active:** false
- **Phase-specific:** false
- **Reason:** The evidence timeline UI (`evidence-ui/**`) is unchanged from v1.0 and not touched in v1.11. Removed from the active roster; reactivates if a future milestone touches the timeline UI.
### data-engineer (v1.9 — was deactivated, reactivated for v1.11)
- **Domain:** data
- **Active:** true (reactivated)
- **Phase-specific:** false
- **Reason:** See the active `data-engineer` entry above. The v1.9 deactivation rationale ("No ORM/persistence framework") no longer applies — v1.11's data-engineer owns terraform module authoring, not a data persistence layer.
### infra-stub-engineer (custom, v1.0 only)
- **Domain:** backend
- **Active:** false
- **Reason:** Owned L1 stub modules in the v1.0 demo. The demo is archived to `demo/`; real L1 modules are owned by data-engineer (v1.11). Not reactivated.
## Phase-specific overrides
| Phase | Personas active | Notes |
|-------|------------------|-------|
| 56a adapter-rewrite-and-s3-reference-module | data-engineer (lead: s3 reference terraform module — proves the design), backend-engineer (lead: stateless adapter rewrite — emits module blocks for s3), general (run_platform.sh --apply/--destroy skeleton) | security/lambda/frontend idle |
| 56b remaining-11-l1-module-terraform-subdirs | data-engineer (lead: author 11 L1 module terraform subdirs — vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf, rds, kms-key, uptime), backend-engineer (adapter: confirm each module round-trips through the assembler), general (modules-lifecycle pipeline wiring) | security/lambda/frontend idle |
| (modules-lifecycle pipeline) | general (lead: byte-identical Gitea+GitHub workflow + matrix apply→modify→destroy), data-engineer (examples/{simple,complex}.yml contracts as the modify variants), backend-engineer (adapter confirms the lifecycle cells resolve) | security/lambda/frontend idle |
| (platform VPC + composition drop) | data-engineer (lead: terraform/platform VPC + microservice composition drops vpc child, D-105), backend-engineer (resolver: env-aware state keys, D-106) | general/security/lambda/frontend idle |
| verify | lead-developer (lead: 4-layer verification), all active personas (review their territory) | — |
| review-audit-complete | lead-developer (lead: review + audit + milestone completion), all active personas (review participation) | — |
## Domain priority (used by TaskDecomposer)
`data → backend → general`
Rationale: in v1.11, the terraform foundation (per-module `terraform/`
subdirs + platform VPC) is the binding constraint — the stateless adapter
cannot be written until the reference s3 module exists (D-107: P56a
proves the design with s3 first). Backend (adapter/resolver) follows once
the module shape is proven. General (pipelines/workflows) wires the
lifecycle modes last, once the adapter + modules produce valid terraform.
## Conflict resolutions (lead-developer arbitration)
- `backend-engineer` vs `data-engineer` over `modules/l1/*/interface.json`:
data-engineer owns the interface defaults (defaults move from the
adapter to the interface inputs, D-100); backend-engineer owns the
adapter that reads them. Co-authoring is expected; conflict goes to
lead-developer.
- `backend-engineer` vs `general` over `scripts/run_platform.sh`:
backend-engineer rewrites the adapter that `run_platform.sh` invokes;
general adds the `--apply`/`--destroy` modes. The interface (the CLI
flags + the adapter invocation) is co-authored; conflicts go to
lead-developer.
- `data-engineer` vs `general` over `modules/l1/*/examples/`:
data-engineer owns the example contracts (the modify variants,
D-103); general owns the pipeline that matrix-runs them. Co-authoring
is expected; conflicts go to lead-developer.
- `lead-developer` vs any: lead-developer owns `.ciagent/**` + `docs/**`
meta + verification scripts + `modules/STANDARDS.md` §8 rewrite; persona
engineers do not edit CIAgent metadata or the vision/architecture
source docs.
## Territory enforcement mode
`warn` — config.json has no `personas.territory_enforcement` field, so the
default per execute.md is `warn`. Cross-territory edits are logged in the
commit message but do not fail the task. v1.11's scope means co-authoring
across territories is likely (e.g. backend + general on the adapter +
`run_platform.sh` boundary; data + general on the examples + pipeline
boundary); `warn` keeps it frictionless.
---
## v1.15 Persona Addendum — Nova Rebrand (2026-07-30)
**Milestone:** v1.15-Nova. The roster carries forward from v1.11/v1.14
unchanged — the rebrand touches existing territories, no new domains.
**frontend-engineer** remains deactivated (no UI; decks are markdown =
lead-developer territory). No **security-engineer** persona is activated
— the ABAC session-policy + tag-key migration (REQ-162) is data-engineer
territory (terraform IAM) with lead-developer review.
### v1.15 territory assignments
| Phase | Lead | Contributors | Territory |
|-------|------|---------------|-----------|
| P1 docs-decks-prose | lead-developer | — | `README.md`, `docs/**`, `.ciagent/*.md`, deck `.md`/`-marp.md`/`-talking-points.md`/`.html`, `docs/presentations/assets/mmd/*.mmd` (+ PNG re-export), `pyproject.toml`, `schemas/*.schema.json` `$id` (D-110), `docs/NOVA_MIGRATION.md`, `.github/workflows/release.yml` title, `modules/STANDARDS.md` |
| P2 code-envvars-consumer-path | backend-engineer | lead-developer (docs/runbook) | `core/env.py` (NEW dual-read helper, D-108), `core/*.py` (call-site migration), `scripts/*.py` + `*.sh`, `adapters/**`, `tests/**`, `.gitea/workflows/**` + `.github/workflows/**`, `.env` + `.env.secrets` (key rename), `schemas/tagging-standard.json`, `adapters/terraform/policy/custom_rules/acdl_tagging.py``nova_tagging.py` (D-109: warn mode) |
| P3 ssm-tagkeys | data-engineer | backend-engineer (readers) | `core/output_publisher.py` (SSM path `/nova/`), `core/contract_resolver.py` (SSM reads), `scripts/migrate_ssm_paths.py` (NEW), `terraform/**` (tag keys `nova:*`), `adapters/terraform/policy/custom_rules/nova_tagging.py` (D-109: hard mode), ABAC session-policy terraform |
| P4 aws-resource-migration | data-engineer | lead-developer (runbook) | `terraform/platform/main.tf`, `terraform/microservice/main.tf`, `terraform/ci-vpc/main.tf`, `terraform/bootstrap/**`, `modules/l1/alb/instance.json`, `scripts/migrate_dynamodb_data.py` (NEW), `docs/NOVA_AWS_MIGRATION.md` (NEW runbook), `core/lambda/contract_ingestor.py` (default table names → `nova-*`, D-111) |
| P5 final-review-ship | lead-developer | all active (review) | `.ciagent/**` (REQUIREMENTS/ROADMAP/PROJECT complete), `core/env.py` (remove dual-read fallback), `nova_tagging.py` (hard-fail `acdl:*`), review + audit |
### v1.15 domain priority
`lead → backend → data` (inverted from v1.11)
Rationale: the rebrand is docs/prose-first (P1 establishes the
vocabulary, no runtime impact), then code/env-vars/consumer-path (P2),
then SSM/tag-keys (P3), then the heavy terraform/AWS migration (P4).
Lead-developer owns the docs + runbooks + verification + final ship;
backend-engineer owns the dual-read helper + call-site migration +
contract resolver; data-engineer owns the terraform resource/tag/SSM
migration (the heaviest terraform territory). Co-authoring expected at:
`core/env.py` + `core/*.py` boundary (backend + lead on the helper
design), `nova_tagging.py` + `schemas/tagging-standard.json` boundary
(backend authors the rule, data-engineer owns the tag-key schema),
`core/output_publisher.py` SSM path + `terraform` outputs boundary
(backend writes the reader, data-engineer owns the terraform that
produces the outputs).
### v1.15 verification toolchain (unchanged from v1.14)
```
typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
test: bash scripts/run_regression.sh # 16-capability gate
build: bash scripts/run_ci.sh # full local CI reproduction
```
The regression gate (CAP-001..CAP-016) must stay **16/16 Verified**
throughout the rebrand — the rebrand must not regress any capability.
P2/P3/P4 update test fixtures that reference `ACDL`/`acdl` so the gate
stays green.
## v1.16 Persona Addendum — Nova Simplification (2026-07-30)
**Milestone:** v1.16-Nova-Simplification (NFR). Roster carries forward
unchanged — NFR work touches existing territories, no new domains. The
onboarding request-path (P18P20) is backend-engineer (Lambda action +
onboarding.py) + data-engineer (cross-account Terraform) territory.
**frontend-engineer** remains deactivated. No **security-engineer**
persona — the ingestor defense-in-depth (P10) is backend-engineer with
lead-developer review; IAM/ABAC (P20) is data-engineer territory.
### v1.16 territory assignments
| Phase | Lead | Contributors | Territory |
|-------|------|---------------|-----------|
| P1 state-bucket+kyverno fix | backend-engineer | data-engineer (kyverno policy) | `adapters/terraform/adapter.py:117`, `adapters/kyverno/policies/require-resource-labels.yml` |
| P2 user-facing brand sweep | lead-developer | backend-engineer | `core/environment_check.py`, `core/lambda/contract_ingestor.py`, `scripts/post_stage_comment.sh`, `scripts/run_ci.sh`, module docstrings, `adapters/README.md` |
| P3 dead-code+stale-prefix | lead-developer | — | `scripts/run_platform.sh`, `core/local_emulators.py`, `core/regression_verify.py`, lifecycle scripts |
| P4 migrate-ssm except | backend-engineer | — | `scripts/migrate_ssm_paths.py` |
| P5 regression-verify dedup | backend-engineer | — | `core/regression_verify.py` |
| P6 run-platform deadcode+hitl-fn | lead-developer | — | `scripts/run_platform.sh` |
| P7 contract-resolver envloader+kind | backend-engineer | — | `core/contract_resolver.py`, `modules/registry.json` |
| P8 workflow generator | lead-developer | backend-engineer (test) | `scripts/sync_workflows.py` (NEW), `tests/test_pipeline_contract.py`, `.gitea/workflows/**`, `.github/workflows/**` |
| P9 run-platform split | lead-developer | — | `scripts/run_platform.sh`, `scripts/run_decommission.sh` (NEW), `scripts/run_uptime.sh` (NEW) |
| P10 ingestor defense-in-depth | backend-engineer | lead-developer (review) | `core/lambda/contract_ingestor.py`, `core/environments/` |
| P11 ingestor payload validation | backend-engineer | — | `core/lambda/contract_ingestor.py` |
| P12 split contract-resolver | backend-engineer | — | `core/contract_resolver.py``core/contract_resolve.py` + `core/decommission_transform.py` + `core/contract_resolver_cli.py` |
| P13 split regression-verify | backend-engineer | — | `core/regression_verify.py` → split modules |
| P14 schema-driven outputs+cache | backend-engineer | data-engineer (interface.json) | `core/output_publisher.py`, `core/contract_resolver.py`, `modules/l1/*/interface.json` |
| P15 run-platform --help+flags | lead-developer | — | `scripts/run_platform.sh`, `README.md` |
| P16 workflows README catalog | lead-developer | — | `.github/workflows/README.md` (NEW) |
| P17 getting-started consolidation | lead-developer | — | `README.md` |
| P18 onboarding schema+lambda | backend-engineer | lead-developer (schema) | `schemas/onboarding.schema.json` (NEW), `core/lambda/contract_ingestor.py` |
| P19 onboarding envfile autogen | backend-engineer | lead-developer (docs) | `core/onboarding.py` (NEW), `core/environment_check.py`, `core/environments/README.md` |
| P20 cross-account role offline | data-engineer | backend-engineer (ABAC) | `terraform/onboarding/` (NEW), `terraform/platform/main.tf` |
| P21 final-review-ship | lead-developer | all active (review) | `.ciagent/**`, review + audit + ship |
### v1.16 domain priority
`backend → lead → data` (the simplification + security + ingestor work
is backend-heavy; lead-developer owns docs/DX/splits; data-engineer owns
the P20 cross-account Terraform only).
### v1.16 verification toolchain
```
typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
test: bash scripts/run_regression.sh # 22-capability gate (D-118: P9 + P21)
build: bash scripts/run_ci.sh # full local CI reproduction
```
The regression gate (22 capabilities) must stay **22/22 Verified**
throughout v1.16 — simplification must not regress any capability
(D-118). P9 (end of Wave 2) and P21 (milestone complete) run the gate;
P14 (end of Wave 3) is an offline mid-milestone checkpoint.
---
# v1.17 Persona Roster — Strategic Direction, Leadership Metrics & Unified Story
> v1.17 adds a telemetry/observability layer (P1P3), a metrics catalog
> + NORTH_STAR integration (P4), a unified narrative deck (P5), a
> regression capability (P6), and a final review/ship (P7). Three
> active personas; frontend-engineer stays deactivated (no Nova web UI
> — dashboards are PowerBI, not a Nova-built frontend).
## Active personas
### lead-developer
- **Domain:** coordination + deck narrative
- **Active:** true
- **Phase-specific:** false
- **Reason:** Owns CIAgent metadata, the NORTH_STAR.md authoring
process (P0), the milestone decomposition, the unified narrative deck
co-authoring (P5 — the deck is markdown, which is lead-developer
territory per the established convention), and the final review/ship
(P7). Arbitrates persona conflicts (e.g., backend vs data on the
emitter/store boundary).
- **Territory:** `.ciagent/NORTH_STAR.md`, `.ciagent/PROJECT.md`,
`.ciagent/REQUIREMENTS.md`, `.ciagent/PLAN.md`, `.ciagent/RESEARCH.md`,
`.ciagent/ARCHITECTURE.md`, `docs/presentations/nova-no-humans-platform.md`
(NEW — unified deck source of truth), `docs/presentations/nova-no-humans-platform-marp.md`,
`docs/presentations/nova-no-humans-platform-talking-points.md`,
`docs/METRICS.md`, `docs/metrics/*.md` (per-KPI definition docs).
### backend-engineer
- **Domain:** backend (event emitters + instrumentation)
- **Active:** true
- **Phase-specific:** false
- **Reason:** Owns the event emitters (P1): the CloudEvents envelope,
the per-run manifest writer, the `outbox_writer.py` extension to the
SQLite Decision Ledger, the Infracost post-processor, the
`hitl_gates.py` attestation event emission, the `confidence_signal.py`
decision event emission, the `checkov_adapter.py` policy event
emission, and the pytest `--junitxml` addopts change. Also owns the
`regression_verify.py` CAP-023/024 additions (P6). The emitter work
is the bridge between existing Nova components and the new metrics
layer — it touches the code paths that already exist.
- **Territory:** `core/metrics/event_envelope.py` (NEW),
`core/metrics/run_manifest.py` (NEW),
`core/metrics/infracost_adapter.py` (NEW),
`core/metrics/decision_ledger.py` (NEW — extends outbox_writer),
`core/outbox_writer.py` (extend to SQLite),
`core/hitl_gates.py` (emit attestation.recorded),
`core/confidence_signal.py` (emit ai.decision.made),
`adapters/terraform/policy/checkov_adapter.py` (emit policy.evaluated),
`scripts/run_platform.sh` (invoke manifest writer + Infracost),
`core/regression_verify.py` (CAP-023/024),
`pyproject.toml` (addopts --junitxml),
`tests/test_metrics_emitters.py` (NEW),
`tests/test_decision_ledger.py` (NEW).
### data-engineer
- **Domain:** data (schema, SQLite store, PowerBI export)
- **Active:** true
- **Phase-specific:** false
- **Reason:** Reactivated with a new territory for v1.17: the metrics
collector (P2) and the PowerBI export (P3). Owns the schema design
(metrics_*.schema.json), the SQLite cold store (nova_metrics.db), the
fact/dimension table design, the 8 deferred placeholder views, and
the CSV/JSON export. The data-engineer's schema-first constraint
applies: all event types and fact/dim tables have JSON Schema
definitions before any code is written. The collector reads files +
events → SQLite; the export reads SQLite → CSV/JSON. This is the
heaviest data-territory work since v1.11's terraform modules.
- **Territory:** `core/metrics/collector.py` (NEW),
`core/metrics/powerbi_export.py` (NEW),
`schemas/metrics_*.schema.json` (NEW — event + fact/dim schemas),
`metrics/nova_metrics.db` (NEW — SQLite cold store),
`metrics/powerbi/` (NEW — CSV/JSON export dir),
`docs/METRICS_VIEWS.md` (NEW — schema doc for PowerBI views),
`tests/test_metrics_collector.py` (NEW),
`tests/test_powerbi_export.py` (NEW).
## Deactivated personas ## Deactivated personas
### frontend-engineer ### frontend-engineer
- **Domain:** frontend
- **Active:** false - **Active:** false
- **Phase-specific:** false - **Domain:** frontend
- **Reason:** v1.17 has no Nova web UI. The leadership dashboards are - **Frameworks:** ["react", "next.js"] (inert — no territory)
PowerBI (an external tool that ingests CSV/JSON files), not a - **Constraints:** ["component-first", "server-components", "minimal-client-js"] (inert)
Nova-built frontend. The decks are markdown (lead-developer - **Territory:** [] (no territory in v1.18)
territory). frontend-engineer stays deactivated, consistent with - **Reason:** v1.18 has no frontend; decks are markdown (lead-developer
v1.11v1.16. Reactivates if a future milestone builds a Nova web UI. territory); deactivated per PERSONAS.md v1.17 precedent. v1.18's
observability stays PowerBI / external (Out of Scope: "A Nova-built
frontend / dashboard"). The MCP server exposes tools to an AI agent,
not a web UI. No reactivation trigger in this milestone.
### lambda-engineer, platform-engineer, security-engineer ## Roster decisions
- **Active:** false (carried forward from v1.11)
- **Reason:** v1.17 does not touch the Lambda (beyond emitting events
from the existing hitl_gates/attestation_matrix), does not do IR-
shaped module authoring, and does not touch security adapters beyond
emitting policy.evaluated events. The existing components are
instrumented, not rewritten.
## v1.17 phase assignment ### D-143 (0.90): Fold mcp-engineer into backend-engineer
The MCP plugin-registry (D-140: `plugins/<name>.py register(mcp)`) is a
backend code pattern — Python modules, type hints, stdio transport,
urllib for the Gitea asset API. It shares nothing with the data domain
(schemas/DynamoDB) and is not a new engineering discipline. Creating a
separate `mcp-engineer` persona would fragment ownership of the server +
its tests + the render/attach scripts (all backend). **Decision:** fold
into backend-engineer. backend-engineer's `frameworks` list gains
`mcp (Python SDK v2)`. Confidence 0.90 — the only counter-argument is
that MCP is a distinct protocol skill, but the SDK v2 API surface
(`@mcp.tool()` + type hints) is small and well within backend-engineer's
range (it's the same Pydantic/FastAPI-style pattern the persona already
knows).
| Phase | Primary persona | Supporting | Territory | ### Territory-overlap resolution (co-ownership)
|-------|----------------|------------|-----------|
| P0 pre-execution | lead-developer | — | `.ciagent/NORTH_STAR.md`, `PROJECT.md`, `REQUIREMENTS.md`, `RESEARCH.md`, `ARCHITECTURE.md`, `PERSONAS.md`, `PLAN.md` |
| P1 event-emitters | backend-engineer | data-engineer (schemas) | `core/metrics/event_envelope.py`, `run_manifest.py`, `decision_ledger.py`, `infracost_adapter.py`, `outbox_writer.py`, `hitl_gates.py`, `confidence_signal.py`, `checkov_adapter.py`, `run_platform.sh`, `pyproject.toml` |
| P2 metrics-collector | data-engineer | backend-engineer (event formats) | `core/metrics/collector.py`, `schemas/metrics_*.schema.json`, `metrics/nova_metrics.db` |
| P3 powerbi-export | data-engineer | — | `core/metrics/powerbi_export.py`, `metrics/powerbi/`, `docs/METRICS_VIEWS.md` |
| P4 metrics-catalog + north-star-integration | lead-developer | data-engineer (metric definitions) | `docs/METRICS.md`, `docs/metrics/*.md`, `PROJECT.md`, `ARCHITECTURE.md`, `config.json` |
| P5 deck-rebuild | lead-developer | — | `docs/presentations/nova-no-humans-platform*.md`, retire old decks |
| P6 regression-capability | backend-engineer | data-engineer (CAP-023 schema) | `core/regression_verify.py` (CAP-023, CAP-024) |
| P7 final-review-ship | lead-developer | all active (review) | `.ciagent/**`, review + audit + ship |
## v1.17 domain priority | Path | Primary | Co-owner | Why |
|------|---------|----------|-----|
`backend → data → lead` (the emitter work in P1 is the foundation; | `docs/submission-readiness.md` | lead-developer (narrative + examples) | backend-engineer (reason-code catalog, REQ-218 codes) | The doc is citizen-developer-facing copy (lead) but the reason-code catalog (MISSING_TAGS, ENV_MISSING_MANDATORY, AGENTIC_MISSING_INTENT, MISSING_APP_SOURCE, POLICY_PRECONDITION_MISSING) is backend (it mirrors the validator's return codes). |
data-engineer's collector + export in P2P3 depends on P1's event | `core/lambda/contract_ingestor.py` | backend-engineer (dispatch wiring) | data-engineer (the schema it validates against) | D-133 places the `--check-readiness` subcommand on the ingestor (backend dispatch), but the readiness schema it loads is data-engineer territory. |
formats; lead-developer's catalog + deck in P4P5 depends on the | `schemas/submission-readiness.schema.json` | data-engineer (schema artifact) | backend-engineer (the validator must match it) | The schema is data-engineer's; the validator (REQ-218) is backend-engineer's and must stay in sync with it. |
metrics being grounded).
## v1.17 verification toolchain
```
typecheck: terraform validate && python3 -m py_compile core/**/*.py adapters/**/*.py
test: bash scripts/run_regression.sh # 22-capability gate + CAP-023/024 (v1.17)
build: bash scripts/run_ci.sh # full local CI reproduction
```
The regression gate (22 capabilities + CAP-023 metrics collector +
CAP-024 deck structure) must pass at P6 and P7. CAP-009 (offline pytest
suite) must remain Verified after the `--junitxml` addopts change
(assumption A5).
+875 -1109
View File
File diff suppressed because it is too large Load Diff
+103 -1
View File
@@ -598,6 +598,90 @@ utility, unrelated to presentations).
No code changes; 494 tests pass; `run_ci.sh` + `run_platform.sh --check-only` No code changes; 494 tests pass; `run_ci.sh` + `run_platform.sh --check-only`
green. PPTX files uploaded to Gitea release. green. PPTX files uploaded to Gitea release.
## Objective for Milestone v1.18 (active — Citizen Developer & Production-Grade Guidance)
v1.18 advances Nova from a platform that governs infrastructure delivery
to one that **instructs the citizen developer on production-grade
engineering** and defines a **clear, machine-checkable contract for what
is acceptable to start**. Five user-directed inputs drive the milestone:
1. **S&P Global theme restoration.** The v1.17 P5 deck rebuild consolidated
two decks into one unified narrative deck but lost the S&P Global Energy
brand visual identity (introduced v1.9.2 / P45, commit `ae0cb58`). The
Marp `style:` block (red-core `#D6002A`, grey-90 `#1B1B1B`, Akkurat Pro
font, 8px top accent bar) is restored to the unified deck. The mermaid
`sp-theme.json` survived; only the Marp CSS theme was lost.
2. **PDLC-upstream scope made explicit.** Core Tenet #2 already states the
platform "does not penetrate upstream product/SDLC" and Anti-Goal #1 says
"Not an upstream development platform." v1.18 promotes this from a
buried tenet to a dedicated, unmissable scope statement in PROJECT.md +
`docs/scope.md` + a deck slide: **the PDLC (Product Development
Lifecycle — product backlog, code authorship, IDE) is upstream of Nova;
Nova governs infra + delivery only; integration is through the validated
contract boundary.**
3. **RACI matrix.** A three-role responsibility matrix clarifies who owns
what: **Citizen Developer** (Responsible for all Functional Requirements
+ User Acceptance Testing, via their AI coding agent / upstream agentic
SDLC / upstream development platform — the source does not matter as all
are subject to the same compliance standards), **Platform** (Responsible
for all NFRs + Infrastructure + QA + Production deployments to cloud),
**Release Management** (co-owned: QA + SRE attestations required by the
actual release, performed agentically but overseen & triggered by the
Citizen Developer). Source of truth in PROJECT.md + `docs/raci.md` + a
deck slide.
4. **Nova input contract — "what is acceptable to start."** A JSON Schema
(`schemas/submission-readiness.schema.json`) defines the
acceptable-to-start gate as a superset *above* contract-schema validity:
schema-valid contract + required Nova tags + per-env mandatory metadata
(per W3.E) + declared policy preconditions + (for L3B) `profile:agentic`
markers + `appSource` pointer. A validator (`core/submission_readiness.py`,
invoked as `contract_ingestor.py --check-readiness`) returns a structured
`ReadinessResult` with reason codes. On fail → citizen-developer-facing
error (not a stack trace); on pass → proceeds to existing ingestion.
5. **Atelier integration — production-grade guidance + agentic validation.**
Nova consumes `coreci/atelier` (a first-principles docs-as-code
engineering framework — 8 core principles, 19 domains, 190 P-rules) via
two surfaces: **skills** (markdown files under `skills/` keyed to Atelier
domain paths, surfaced to the citizen developer's AI agent, extending the
BA.A 5-skill catalog) and an **MCP server** (`mcp/atelier/server.py`,
plugin-registry architecture, stdio transport, vendored Atelier snapshot
for audit reproducibility) exposing tools for principle-lookup,
domain-listing, matrix-lookup, and agentic validation against the
Atelier agent-checklist — validation that goes beyond deterministic
scanners (Wiz/Checkmarx/Mend) by catching correctness/clarity/simplicity/
observability gaps.
**Deck automation (cross-cutting):** any phase modifying
`docs/presentations/*-marp.md` or `docs/presentations/assets/` MUST
re-render HTML + PPTX, **commit the PPTX to git** (binary, no LFS), and
attach it to the phase's Gitea release. New scripts:
`scripts/render_deck.sh` (HTML + PPTX render) and
`scripts/attach_release_asset.py` (Gitea release asset upload).
**Milestone type:** Feature (P1 S&P theme restoration + P3 readiness
schema/validator + P5 MCP server are new code/features). Tags run on the
**v1.17.x** patch line (previous minor per branch-strategy): `v1.17.0` (P0)
`v1.17.1..v1.17.6` (P1P6) → `v1.17.7` (P7 final = milestone release).
**Phase count:** 8 (P0 pre-execution + 6 execution + 1 final).
**Hard constraints:**
- DO NOT make anything up (NORTH_STAR.md honesty model).
- The submission-readiness schema is a superset gate above
`contract.schema.json`, NOT a duplicate — it references but does not
redefine contract fields.
- The MCP server is plugin-registry extensible (future capabilities drop
in as new plugin files, no `server.py` edits).
- Atelier is vendored (pinned tag) for audit reproducibility — an agentic
validation result must be replayable against the exact principles that
produced it.
- PPTX is a first-class artifact: committed (history) + attached (download)
— both always, not optional.
## Requirements ## Requirements
### v1.0 (Prior milestone — the demo) ### v1.0 (Prior milestone — the demo)
@@ -1133,4 +1217,22 @@ P7 review+audit+ship). Tags on the v1.16.x line: `v1.16.0` (P0) →
| D-129 | PowerBI delivery = CSV/JSON files, folder connector. | Nova is offline-first; no live connector to a running service. PowerBI ingests via the folder connector. | P3 emits CSV/JSON to metrics/powerbi/. | | D-129 | PowerBI delivery = CSV/JSON files, folder connector. | Nova is offline-first; no live connector to a running service. PowerBI ingests via the folder connector. | P3 emits CSV/JSON to metrics/powerbi/. |
| D-130 | Deck arc = Problem → Vision → How → Proof → Roadmap. | The unified narrative deck's 5-act structure. x3 arc at deck + slide level. Per-slide benefit callouts. Fluid transitions. Both old decks retired. | P5 builds the unified deck; old decks deleted. | | D-130 | Deck arc = Problem → Vision → How → Proof → Roadmap. | The unified narrative deck's 5-act structure. x3 arc at deck + slide level. Per-slide benefit callouts. Fluid transitions. Both old decks retired. | P5 builds the unified deck; old decks deleted. |
| D-131 | MTTR scope = platform-run MTTR. | The <60s MTTR target refers to platform-run failures (apply.failed → successful retry), not infra-incident MTTR (no incident detection system). Infra-incident MTTR deferred. | P4 grounds platform-run MTTR. | | D-131 | MTTR scope = platform-run MTTR. | The <60s MTTR target refers to platform-run failures (apply.failed → successful retry), not infra-incident MTTR (no incident detection system). Infra-incident MTTR deferred. | P4 grounds platform-run MTTR. |
| D-132 | Attestation instrumentation = emit attestation.recorded events. | The attestation system (hitl_gates.py + attestation_matrix.py + separation_of_duties.py) already exists. Instrument it: emit attestation.recorded events into the Decision Ledger + PowerBI. Attestation Coverage = 100% target grounded from outbox approver_* attributes. | P1 emits attestation events; P4 grounds Attestation Coverage. | | D-132 | Attestation instrumentation = emit attestation.recorded events. | The attestation system (hitl_gates.py + attestation_matrix.py + separation_of_duties.py) already exists. Instrument it: emit attestation.recorded events into the Decision Ledger + PowerBI. Attestation Coverage = 100% target grounded from outbox approver_* attributes. | P1 emits attestation events; P4 grounds Attestation Coverage. |
## Key Decisions (v1.18)
Resolved at the CLARIFY stage (full autonomy — all within locked
constraints or user-directed scope). New v1.18 decisions:
| ID | Decision | Rationale | Outcome |
|----|----------|-----------|---------|
| D-133 | Submission-readiness validator location = extend `contract_ingestor.py --check-readiness`. | Adding a new CLI binary is unnecessary; the ingestor is the existing entry point for contract submission. The validator is a subcommand that runs before ingestion proceeds. No new binary, no new entry point to maintain. | P3 implements the subcommand; no new CLI binary. |
| D-134 | Deck slide budget = 18 → 21 slides (no act restructure). | The 3 new slides (scope/RACI/atelier) are leadership-relevant and append after the existing 18. The 5-act arc (D-130) is preserved; the new slides are append-only context, not a new act. | P6 appends 3 slides → 21 total. |
| D-135 | Atelier MCP transport = stdio now; HTTP-ready (same server object). | stdio is the local-agent transport (the citizen developer's AI agent spawns the server as a subprocess). The MCP Python SDK v2 supports Streamable HTTP on the same `MCPServer` object, so adding HTTP later is a transport-only change in `server.py`, not a rewrite. | P5 ships stdio; HTTP deferred (documented in README). |
| D-136 | Atelier source = vendor pinned tag under `mcp/atelier/vendor/`. | An agentic validation result is only reproducible if the principles that produced it are pinned. Live-fetch breaks replayability (Atelier `main` drifts). Vendoring matches the v1.16 P15 offline-first precedent and the Nova thesis (provable trust). `mcp/atelier/vendor/VERSION.md` records the pinned tag; `scripts/update_atelier_vendor.sh` is the intentional upgrade path. | P5 vendors Atelier; live-fetch not implemented. |
| D-137 | MCP server language = Python (MCP Python SDK v2, `modelcontextprotocol/python-sdk`). | Nova's `core/` is Python. The MCP Python SDK v2 (23.9k stars, MIT, stable) matches the codebase; type hints become JSON Schema automatically (`@mcp.tool()` decorator). | P5 uses Python SDK v2. |
| D-138 | Skill catalog format = markdown files under `skills/` keyed to Atelier domain paths. | Markdown is the established Nova docs format (Jekyll Pages, 4-step deck process). Each skill file names the Atelier source path, distills the first-principles, links to agent-checklist triggers, and maps to the BA.A catalog. | P4 authors 9 markdown skill files. |
| D-139 | RACI role names = Citizen Developer / Platform / Release Management (co-owned). | User-specified. The 3 roles are the columns of the RACI table. Release Management is co-owned: QA + SRE attestations are required by the actual release (performed agentically, overseen & triggered by the Citizen Developer). | P2 authors the RACI with these 3 roles. |
| D-140 | MCP server extensibility = plugin-registry (`plugins/<name>.py` implementing `register(mcp)`). | Future capabilities (new scanners, policy evaluators, cost tools) drop in as new plugin files — no `server.py` edits. `server.py` scans `plugins/` and calls `register` on each. This is the extensibility insurance: plugins are decoupled from the server entrypoint. | P5 implements the plugin-registry; initial plugins are `principles.py` + `validation.py`. |
| D-141 | PPTX storage = commit binary directly to `docs/presentations/` (no LFS). | Decks are small (~1-5 MiB); git handles binary blobs. LFS requires server-side support (unverified for git.cloudinit.dev) + client config. Committing directly is simplest and works without any repo/server config. Binary diffs are not delta-friendly, but deck changes are infrequent. | P1/P2/P6 commit .pptx directly. |
| D-142 | Deck render trigger = any phase modifying `docs/presentations/*-marp.md` or `docs/presentations/assets/` must re-render HTML + PPTX, commit PPTX, and attach to the Gitea release. | PPTX was previously manual + release-only (not committed). v1.18 makes it a first-class artifact: committed (history) + attached (download), both always, not optional. Automated via `scripts/render_deck.sh` + `scripts/attach_release_asset.py`. | P1/P2/P6 run the render+commit+attach pipeline. |
+174
View File
@@ -1180,3 +1180,177 @@ with documented schemas.
- A third deck — the two existing decks merge into one; no new - A third deck — the two existing decks merge into one; no new
standalone metrics deck. standalone metrics deck.
- A Nova web UI — dashboards are PowerBI, not a Nova-built frontend. - A Nova web UI — dashboards are PowerBI, not a Nova-built frontend.
---
## v1.18 — Citizen Developer & Production-Grade Guidance
> **Milestone type:** Feature. Tags run on the v1.17.x patch line (previous
> minor per branch-strategy). `v1.17.0` (P0) → `v1.17.1..v1.17.6` (P1P6) →
> `v1.17.7` (P7 final = milestone release).
> **Active milestone:** v1.18. **Branch:**
> `milestone/v1.18-citizen-developer-guidance`.
### Requirements
- **REQ-214** — S&P Global Energy Marp theme restored in the unified deck
(`docs/presentations/nova-no-humans-platform-marp.md`). The `style:` block
from commit `ae0cb58` (v1.9.2 / P45) is ported: H1/H2 `#D6002A`
(S&P red-core), title-slide bg `#1B1B1B` (grey-90) with 8px `#D6002A` top
accent bar, body text `#1B1B1B`, blockquote border `#D6002A`,
table headers `#F0F0F0`, font `'Akkurat Pro'` with web-safe fallbacks. The
current Nova header/footer text is preserved (rebrand is not touched —
only the visual theme is restored). HTML re-rendered with the S&P theme.
- **REQ-215** — RACI matrix authored in `PROJECT.md` (new `## RACI Matrix`
section) and `docs/raci.md` (citizen-developer-facing copy). Three roles:
**Citizen Developer** (Responsible for all Functional Requirements + User
Acceptance Testing — via their AI coding agent / upstream agentic SDLC /
upstream development platform; the source does not matter as all are
subject to the same compliance standards), **Platform** (Responsible for
all NFRs + Infrastructure + QA + Production deployments to cloud),
**Release Management** (co-owned: QA + SRE attestations required by the
actual release, performed agentically but overseen & triggered by the
Citizen Developer). Rendered as a table: rows = work categories (FRs, UAT,
NFRs, Infra, QA, Prod deploy, Release attestation), columns = R/A/C/I per
role. Includes the compliance-standard-equivalence note.
- **REQ-216** — PDLC-upstream scope statement made explicit in `PROJECT.md`
(new `## Scope: Nova is Downstream of PDLC` subsection under Domain
Boundaries) and `docs/scope.md`. States that the PDLC (Product Development
Lifecycle — product backlog, code authorship, IDE) is upstream of Nova;
Nova governs infra + delivery only; integration is through the validated
contract boundary. Promotes Core Tenet #2 + Anti-Goal #1 from buried
tenets to a dedicated, unmissable scope statement.
- **REQ-217** — `schemas/submission-readiness.schema.json` (JSON Schema
draft 2020-12) defines what is acceptable to start — a superset gate
*above* `contract.schema.json` validity. Required fields: `contractId`
(non-empty), `environment` (dev/qa/prod/dr) with the W3.E per-env mandatory
table enforced (dev: stack+environment; qa: +validation.e2eSuite
+validation.loadTest; prod: +runbook+dashboard+oncall; dr: +drDrillRef),
`tags` (the 5 required Nova tags per D-054: `nova:owner`, `nova:contract`,
`nova:environment`, `nova:cost-center`, `nova:ref`), `policyPreconditions`
(declared policy expectations the platform will enforce, e.g.,
`public-ingress: false`), `profile` (`developer` or `agentic`; if
`agentic`, requires `naturalLanguageIntent`, `confidenceAtSubmission`,
`agentTrace` per REQ-22 / W3.E), `appSource` (repo + ref pointer for
runtime fetch).
- **REQ-218** — `core/submission_readiness.py` validator, invoked as
`contract_ingestor.py --check-readiness` subcommand (decision D-133). Returns
a structured `ReadinessResult` (pass/fail per check, with reason codes).
On fail → the ingestor rejects with a citizen-developer-facing error
(not a stack trace). On pass → proceeds to existing contract ingestion.
Calls `contract.schema.json` validation first, then the readiness checks.
Reason codes: `MISSING_TAGS`, `ENV_MISSING_MANDATORY:<env>:<field>`,
`AGENTIC_MISSING_INTENT`, `MISSING_APP_SOURCE`, `POLICY_PRECONDITION_MISSING`.
- **REQ-219** — `docs/submission-readiness.md` citizen-developer-facing doc
explaining what is acceptable to start, with good + rejected examples and
the reason-code catalog. References `schemas/submission-readiness.schema.json`
as the source of truth.
- **REQ-220** — `tests/test_submission_readiness.py` covers: good contract
passes; missing tags fail with `MISSING_TAGS`; missing env mandatory fails
with `ENV_MISSING_MANDATORY:<env>:<field>`; agentic profile missing intent
fails with `AGENTIC_MISSING_INTENT`; missing appSource fails with
`MISSING_APP_SOURCE`.
- **REQ-221** — `skills/` directory with 9 Atelier-derived skill files mapped
to the BA.A citizen-developer catalog: `skills/api.md` (domains/api/),
`skills/security.md` (domains/security/), `skills/data.md` (domains/data/),
`skills/testing.md` (domains/testing/), `skills/observability.md`
(domains/observability/), `skills/errors.md` (domains/errors/),
`skills/devops.md` (domains/devops/), `skills/infrastructure-as-code.md`
(domains/infrastructure-as-code/), `skills/compliance.md`
(domains/compliance/). Each names the Atelier source path, distills the
first-principles to the citizen-developer-relevant subset, links to
agent-checklist triggers, and maps to the BA.A 5-skill catalog (web API,
worker, scheduled job, static asset, basic observability bootstrap).
- **REQ-222** — `docs/skills.md` index page listing the skill catalog, the
Atelier provenance, and how the citizen developer's AI agent consumes them
(read before completing a task; run `review/agent-checklist.md` before
finishing). `PROJECT.md` BA.A decision extended with the Atelier-derived
skill catalog reference.
- **REQ-223** — `mcp/atelier/server.py` MCP server (stdio transport,
decision D-135) with a **plugin-registry architecture** (decision D-140):
`plugins/<name>.py` modules each expose `register(mcp: MCPServer) -> None`
and call `@mcp.tool()` for their tools; `server.py` scans `plugins/` and
calls `register` on each. Initial plugins: `principles.py`
(`atelier.lookup_principle`, `atelier.list_domains`, `atelier.matrix_lookup`)
and `validation.py` (`atelier.validate_against_principles` — agentic
validation against the Atelier agent-checklist, beyond Wiz/Checkmarx/Mend).
Uses the MCP Python SDK v2 (`modelcontextprotocol/python-sdk`).
- **REQ-224** — `mcp/atelier/vendor/` vendored Atelier snapshot (pinned tag,
decision D-136) for audit reproducibility. `mcp/atelier/vendor/VERSION.md`
records the pinned tag + a `scripts/update_atelier_vendor.sh` helper for
intentional upgrades. `mcp/atelier/README.md` documents the server: how to
run, transport, tool catalog, plugin-authoring guide, vendoring policy.
- **REQ-225** — `tests/test_atelier_mcp.py` covers: tool registration (all 4
tools discoverable via `tools/list`), `atelier.lookup_principle` returns
the principle text + core C-rule, `atelier.validate_against_principles`
catches a planted C1 (correctness) + C7 (observability) violation in a
known-bad snippet and passes a known-good snippet, `atelier.matrix_lookup`
returns the domain→core mapping, plugin discovery loads all plugins in
`plugins/`.
- **REQ-226** — 3 new deck slides added to the unified deck
(`docs/presentations/nova-no-humans-platform-marp.md`) → 21 slides total:
Slide 19 "Scope: Downstream of PDLC", Slide 20 "RACI: Who Owns What",
Slide 21 "Production-Grade Guidance via Atelier". Arc Preview slide
updated to reflect 21-slide count. Talking points
(`nova-no-humans-platform-talking-points.md`) synced for the 3 new slides.
S&P theme preserved (regression check vs P1). CAP-024 deck structure
regression passes.
- **REQ-227** — `docs/presentations/README.md` slide count + deck table
updated to reflect 21 slides + the 3 new slide titles.
- **REQ-228** — `scripts/render_deck.sh` (renders HTML + PPTX from a Marp
deck, commits both to git) and `scripts/attach_release_asset.py` (uploads
a file to a Gitea release via the API). Any phase modifying
`docs/presentations/*-marp.md` or `docs/presentations/assets/` MUST
re-render HTML + PPTX, commit the PPTX binary to `docs/presentations/`,
and attach it to the phase's Gitea release. PPTX is stored as a committed
binary (no LFS, decision D-141).
### Out of Scope (v1.18)
- **Streamable HTTP transport for the MCP server** — stdio ships now; HTTP
is a future milestone (the SDK supports it on the same server object, so
adding it later is a transport-only change, not a rewrite).
- **A Nova-built frontend / dashboard** — observability stays PowerBI /
external; no Nova web UI.
- **Replacing the existing BA.A 5-skill catalog** — the Atelier-derived
skills extend it, not replace it.
- **Live AWS re-provisioning** (D-096, still deferred) — submission-readiness
validates the contract shape, not a live AWS deployment.
- **A second forge adapter** (GitLab) — BA.F cross-platform evolution is
future work.
- **Atelier live-fetch mode** — vendoring is the only mode this milestone;
live-fetch (with its reproducibility trade-offs) is not implemented.
### v1.18 Traceability
| REQ | Phase | Status |
|-----|-------|--------|
| REQ-214 | P1 | pending |
| REQ-215 | P2 | pending |
| REQ-216 | P2 | pending |
| REQ-217 | P3 | pending |
| REQ-218 | P3 | pending |
| REQ-219 | P3 | pending |
| REQ-220 | P3 | pending |
| REQ-221 | P4 | pending |
| REQ-222 | P4 | pending |
| REQ-223 | P5 | pending |
| REQ-224 | P5 | pending |
| REQ-225 | P5 | pending |
| REQ-226 | P6 | pending |
| REQ-227 | P6 | pending |
| REQ-228 | P1/P2/P6 | pending |
+853
View File
@@ -1493,3 +1493,856 @@ Total: ~1216 slides. Opening = arc preview; closing = recap + ask.
cost estimate. No live AWS access required. If Infracost is not cost estimate. No live AWS access required. If Infracost is not
available, the `cost.estimated` event is omitted (degraded mode, not available, the `cost.estimated` event is omitted (degraded mode, not
a failure). a failure).
---
## v1.18 Research — Citizen Developer & Production-Grade Guidance
> Phase 0 RESEARCH. Autonomy = full. Findings are evidence-grounded
> (fetched from live sources, not assumed). The Atelier repo, the MCP
> Python SDK v2 docs, the existing Nova schemas/ingestor, and the Marp
> CLI README were all fetched directly. Decisions are logged with
> confidence scores; low-confidence items are flagged.
### 1. Atelier Integration Reference
#### 1.1 The 8 core principles (C1C8)
Source: `core/first-principles.md` (fetched 2026-08-06 from
`https://git.cloudinit.dev/coreci/atelier/raw/branch/main/core/first-principles.md`).
Precedence is a **total order** — a lower-numbered principle is never
sacrificed for a higher-numbered one (C1 never sacrificed; C2 only for
C1; C3 only for C1/C2; C4C8 tradeable among themselves but always below
C1C3).
| ID | Principle | One-line description |
|----|-----------|----------------------|
| **C1** | Correctness | The system does what it is supposed to do, and nothing else. Highest principle; never overridden. Security is a subset (exploitable code is incorrect). Includes temporal correctness (a late answer is wrong when the deadline mattered). |
| **C2** | Clarity | The intent of the code is obvious to its reader. Optimize for the reader; names reveal intent; comments explain *why* not *what*. Unclear code is where bugs hide. |
| **C3** | Simplicity | The solution is as simple as possible, and no simpler. Complexity is the enemy of correctness; every line is a liability. Not laziness — the result of removing everything unnecessary. |
| **C4** | Locality | Decisions and their consequences live near each other. State, logic, side effects that depend on each other live near each other. A change needing many distant files is a locality violation. |
| **C5** | Reversibility | Every decision can be undone, and the cost of undoing is known. Migrations/deploys/schema/API changes reversible by default. Versioning, feature flags, rollback paths are the mechanisms. |
| **C6** | Composability | Parts combine into wholes, and the parts are reusable in new wholes. A part that does one thing well composes; the boundary is its contract. Composable parts are understandable in isolation. |
| **C7** | Observability | The system's behavior is visible to the people who must understand it. Logs/metrics/traces are first-class, designed in. An observable system answers "what/why/what next" without reading source. |
| **C8** | Economy | The system uses no more resources than the task requires (time, memory, attention, money, complexity). Most tradeable principle; unbounded growth in any resource is a defect. |
The precedence string (from `core/first-principles.md` §3):
`C1 Correctness > C2 Clarity > C3 Simplicity > C4 Locality > C5 Reversibility > C6 Composability > C7 Observability > C8 Economy`.
Conflict resolution (`core/conflict-resolution.md`, fetched): a
deterministic 6-step procedure. The **hierarchy** is
`core/first-principles.md` > `domains/<x>/first-principles.md` >
`domains/<x>/<topic>.md` > `languages/<lang>.md` > `examples/<x>.md`.
Same-level conflicts resolve by core derivation (via the matrix), then
by specificity, then by filing an issue (a tie is a defect). A domain's
"non-tradeable" declaration (e.g. Security: 8 of 10) promotes those
rules to **C1-equivalent** — a binding escalation recorded in the
matrix's derivation.
#### 1.2 The 19 domains and Nova-citizen-dev relevance
Source: `matrix/principles-matrix.md` (fetched) + the releases page
(v0.4 milestone = 19 domains, 190 P-rules, confirmed in the v0.3.6
release notes and the matrix Coverage Summary).
| # | Domain | Atelier path | P-rules | Nova-citizen-dev relevant? | Reason |
|---|--------|--------------|---------|------------------------------|--------|
| 1 | UI/UX | `domains/uiux/` | 10 | **NO** — excluded | v1.18 has no frontend (Out of Scope: "A Nova-built frontend / dashboard"). Decks are markdown, not a UI. |
| 2 | API Design | `domains/api/` | 10 | **YES** | A citizen developer building a web API / worker / scheduled job touches API contracts. Maps to `skills/api.md`. |
| 3 | Security | `domains/security/` | 10 | **YES** | Zero-trust, input validation, secret hygiene, fail-securely — universal for any production-grade service. Maps to `skills/security.md`. |
| 4 | Data | `domains/data/` | 10 | **YES** | Schema-as-truth, migration safety, referential integrity — applies to any stateful service. Maps to `skills/data.md`. |
| 5 | Testing | `domains/testing/` | 10 | **YES** | Tests-as-specification, determinism, edge-case coverage — required for a citizen developer's UAT. Maps to `skills/testing.md`. |
| 6 | Performance | `domains/performance/` | 10 | **YES (reference, not a skill)** | Measure-first, bounded operations, no N+1, timeouts. NOT one of the 9 REQ-221 skills; Performance principles are cited inside the 9 skills + the index. |
| 7 | Observability | `domains/observability/` | 10 | **YES** | Structured logs, correlation IDs, no secrets in logs — the "basic observability bootstrap" BA.A skill. Maps to `skills/observability.md`. |
| 8 | Errors | `domains/errors/` | 10 | **YES** | Errors are data, fail loudly + specifically, preserve context — production-grade error handling. Maps to `skills/errors.md`. |
| 9 | Documentation | `domains/documentation/` | 10 | **YES (reference, not a skill)** | Docs-as-code, audience awareness, examples mandatory. REQ-221 does NOT list `skills/documentation.md`; a self-referential "documentation skill" is redundant. Principles cited inside `docs/skills.md` index. |
| 10 | Concurrency | `domains/concurrency/` | 10 | **YES (reference, not a skill)** | Immutability, bounded queues, timeouts — advanced for a citizen developer's first 5 skills. REQ-221 does NOT list `skills/concurrency.md`. Top rules cross-referenced inside `skills/api.md` + `skills/errors.md`. |
| 11 | DevOps | `domains/devops/` | 10 | **YES** | Reproducibility, rollback-first, config-as-code — the citizen developer co-owns Release Management (RACI). Maps to `skills/devops.md`. |
| 12 | Infrastructure as Code | `domains/infrastructure-as-code/` | 10 | **YES** | Declarative intent, idempotence, plan-before-apply, no secrets in HCL — directly relevant to the Nova contract→Terraform path. Maps to `skills/infrastructure-as-code.md`. |
| 13 | Kubernetes | `domains/kubernetes/` | 10 | **NO** — excluded | Nova emits Terraform (ECS/Fargate per the architecture), not K8s manifests. Kyverno adapter is "ready but inactive" (D-053). Not citizen-dev-relevant. |
| 14 | GitOps + Operators | `domains/gitops-operators/` | 10 | **NO** — excluded | Nova uses a push pipeline (contract → resolve → plan → apply), not a pull-based reconciler. Not citizen-dev-relevant. |
| 15 | AI/ML | `domains/ai-ml/` | 10 | **YES (reference, not a skill)** | Reproducibility, data versioning, drift detection — relevant *to Nova itself* (Nova is an agentic platform), but a citizen developer on Nova is NOT building ML models; they consume Nova's agentic capability. REQ-221 does NOT list `skills/ai-ml.md`; the Atelier AI/ML domain is platform-team guidance, not citizen-dev guidance. |
| 16 | i18n | `domains/i18n/` | 10 | **NO** — excluded | Not relevant to a citizen developer's first production-grade service on Nova. |
| 17 | Compliance | `domains/compliance/` | 10 | **YES** | Audit logs append-only, policy-as-code, evidence-by-operation — directly relevant (Nova's compliance posture is a selling point). Maps to `skills/compliance.md`. |
| 18 | Edge | `domains/edge/` | 10 | **NO** — excluded | Nova does not deploy edge/CDN for the citizen developer's first 5 skills; Route53/ACM/CloudFront are consumer-supplied extension points (D-049). |
| 19 | Messaging | `domains/messaging/` | 10 | **NO** — excluded | The citizen developer's first 5 skills (web API / worker / scheduled job / static asset / observability bootstrap) do not require a broker; messaging is a future capability. |
**Relevant count:** 13 of 19 are relevant to *some* Nova audience
(YES or YES-reference). Of those, **9 become skills** (per REQ-221, the
planned count). The other 4 relevant domains (Performance, Documentation,
Concurrency, AI/ML) are **reference-only** — their principles are cited
inside skills or the `docs/skills.md` index, but they do NOT get their
own skill file. This matches REQ-221's exact 9-skill list.
**Excluded count:** 6 of 19 (UI/UX, Kubernetes, GitOps, i18n, Edge,
Messaging) are not relevant to a Nova citizen developer building a
production-grade application — confirmed.
#### 1.3 Atelier domain → Nova skill mapping (final 9-skill list)
REQ-221 names exactly 9 skills. The research **confirms the planned 9**
— no adjustment needed. The mapping (each skill cites its Atelier source
path + distills the citizen-developer-relevant subset + links to
agent-checklist triggers + maps to the BA.A 5-skill catalog):
| Nova skill file | Atelier domain path | P-rules distilled | BA.A catalog skill it extends |
|-----------------|----------------------|-------------------|--------------------------------|
| `skills/api.md` | `domains/api/` | P1 Contract Fidelity, P2 Clarity, P5 Versioning, P6 Idempotency, P8 Security, P9 Error Transparency | web API |
| `skills/security.md` | `domains/security/` | P1 Zero Trust, P2 Least Privilege, P4 Input Validation, P6 Crypto Correctness, P8 Fail Securely, P9 Secret Hygiene | all 5 (cross-cutting) |
| `skills/data.md` | `domains/data/` | P1 Truth, P3 Invariants in Schema, P4 Migration Safety, P7 Type Fidelity, P9 Referential Integrity | web API, worker, scheduled job |
| `skills/testing.md` | `domains/testing/` | P1 Tests as Specification, P3 Determinism, P5 Coverage of Behavior, P9 Edge Case Coverage, P10 No Test Theater | all 5 (UAT is a citizen-developer RACI responsibility) |
| `skills/observability.md` | `domains/observability/` | P1 Structured by Default, P2 Correlation, P6 No Secrets in Obs, P7 Actionable Alerts | basic observability bootstrap |
| `skills/errors.md` | `domains/errors/` | P1 Errors are Data, P2 Fail Loudly, P3 Fail Specifically, P4 Preserve Context, P5 Recoverable When Possible | web API, worker, scheduled job |
| `skills/devops.md` | `domains/devops/` | P1 Reproducibility, P4 Rollback First, P5 Progressive Delivery, P6 Config as Code, P8 Security at Every Layer | scheduled job, worker (deploy/release is co-owned Release Mgmt) |
| `skills/infrastructure-as-code.md` | `domains/infrastructure-as-code/` | P1 Declarative Intent, P2 Idempotence, P4 Plan Before Apply, P5 Version Everything, P10 Secrets Never in Code | static asset (the contract→Terraform path) |
| `skills/compliance.md` | `domains/compliance/` | P1 Audit Logs Append-Only, P2 Every Significant Action Logged, P4 Policy is Code, P5 Policy is Evaluated as a Gate, P9 Secrets Redacted in Audit | all 5 (cross-cutting; Nova's compliance posture) |
**Final recommendation: 9 skills, exactly as REQ-221 planned.**
Confidence 0.95 — the planned list maps cleanly to the relevant Atelier
domains and to the BA.A 5-skill catalog; the 4 "reference-only" domains
(Performance, Documentation, Concurrency, AI/ML) are correctly *not*
elevated to skills (a citizen developer's first production-grade service
does not need a standalone Concurrency or AI/ML skill; Performance and
Documentation principles are cited inside the 9 skills + the index).
#### 1.4 Agent-checklist → MCP `atelier.validate_against_principles` checks
Source: `review/agent-checklist.md` (fetched). The checklist has a
**Core (C1C8)** section (8 subsections, ~30 boolean items) plus
**domain-specific trigger sections** (one per domain; Nova-relevant
ones: API, Security, Data, Testing, Performance, Observability, Errors,
Concurrency, DevOps, IaC, Compliance).
The MCP `atelier.validate_against_principles` tool (REQ-223, in
`plugins/validation.py`) runs the relevant checklist items against a
code/diff snippet. The tool input model:
```python
class ValidateInput(BaseModel):
snippet: str # the code/diff to validate
language: str # e.g. "python", "terraform", "yaml"
domains: list[str] # e.g. ["security", "api"] — which domain triggers to run
run_core: bool = True # always run C1C8 unless explicitly skipped
```
The structured output model (Pydantic, returned as `structured_content`):
```python
class Violation(BaseModel):
principle: str # e.g. "C1", "security/P9"
checklist_item: str # the verbatim checklist question
severity: str # "C1" (blocking) | "non-tradeable" | "tradeable"
evidence: str # the snippet substring + why it fails
fix_hint: str # the principle's remediation guidance
class ValidateResult(BaseModel):
snippet_id: str # hash of the snippet for replay
passed: bool
violations: list[Violation]
domains_checked: list[str]
core_checked: bool
```
**Checklist → check mapping** (the validation plugin encodes each
checklist item as a boolean predicate over the snippet + language):
| Checklist section | MCP check behavior | Nova-relevant? |
|-------------------|--------------------|-----------------|
| **C1 Correctness** (4 items) | Run all 4; any fail → `severity: "C1"` (blocking). | YES — always run (core) |
| **C2 Clarity** (4 items) | Heuristic checks: name smell (`data/temp/x/doStuff`), comment-why ratio. | YES — always run |
| **C3 Simplicity** (4 items) | Dead-code heuristic, function-length, premature-abstraction. | YES — always run |
| **C4 Locality** (3 items) | Cross-file-change heuristic (for diffs); within-file coupling. | YES — always run |
| **C5 Reversibility** (3 items) | Migration-has-down, deploy-has-rollback presence checks. | YES — always run |
| **C6 Composability** (3 items) | Single-responsibility heuristic, boundary-typed check. | YES — always run |
| **C7 Observability** (4 items) | Log-presence, error-context, metric, **no-secrets-in-logs** (hard check). | YES — always run |
| **C8 Economy** (3 items) | Unbounded-growth, no-timeout, resource-leak heuristics. | YES — always run |
| If API | 5 items: nouns-plural-lowercase, status codes, structured errors, schema validation, auth-required. | YES — when `domains` includes "api" |
| If Security | 5 items: no-secrets-in-code/logs/URLs, input-validation, output-encoding, vetted-crypto, authz-checked. **All 5 are non-tradeable** (Security domain §3). | YES — when "security" |
| If Data | 5 items: schema-reflects-domain, constraints-in-schema, migration-up-down, domain-types, no-SELECT-star. | YES — when "data" |
| If Testing | 4 items: independence, determinism, edge-cases, failure-specificity. | YES — when "testing" |
| If Performance | 4 items: no-unbounded, no-N+1, timeouts, cache-invalidation. | YES — when "performance" |
| If Observability | 4 items: structured-logs, correlation-id, no-high-cardinality, alerts-have-runbooks. | YES — when "observability" |
| If Errors | 4 items: not-swallowed, specific, context-preserved, recovery-attempted. | YES — when "errors" |
| If Concurrency | 5 items: shared-state-minimized, minimal-locks, bounded-queues, timeouts, cancellation. | YES — when "concurrency" |
| If DevOps | 4 items: pipeline-is-process, rollback-known, config-in-code, env-parity. | YES — when "devops" |
| If IaC | 8 items: declarative, pinned-providers, remote-locked-state, plan-before-apply, no-secrets-in-HCL, versioned-modules, drift-is-incident, least-priv-providers. | YES — when "infrastructure-as-code" |
| If Compliance | 10 items: append-only-audit, a-priori-action-set, retention-as-policy, policy-as-code, policy-as-gate, continuous-evidence, attributable-identity, subject-access, redacted-secrets, observable-posture. | YES — when "compliance" |
The validation plugin reads the vendored `review/agent-checklist.md`
(frozen at the pinned tag — §1.6) so the checks are replayable against
the exact checklist version that produced a result. The plugin maps each
checklist line to a predicate function keyed by `(language, principle)`
so a "no secrets in code" check runs differently for Python (ast scan for
string-constant assignment) vs Terraform (HCL scan for hardcoded
provider keys) vs YAML (scan for `api_key:` literals).
#### 1.5 Principle-lookup query model
`atelier.lookup_principle(domain: str, principle_id: str)` (REQ-223, in
`plugins/principles.py`) resolves a principle reference to its full
text + core derivation + checklist items. Resolution model:
**Input:**
```python
class LookupInput(BaseModel):
domain: str # "security" | "api" | "data" | ... | "core"
principle_id: str # "P4" | "C1" (core) | "P9"
```
**Resolution path (the lookup algorithm):**
1. If `domain == "core"`: load `vendor/core/first-principles.md`, parse
the `### C<n>. <Name>` section for `principle_id` (e.g. `C1`
the "C1. Correctness" section). Return the full principle text.
2. Else: load `vendor/domains/<domain>/first-principles.md`, parse the
`### P<n>. <Name>` section for `principle_id` (e.g. `security/P4`
the "P4. Input Validation" section).
3. **Cross-reference the matrix:** load
`vendor/matrix/principles-matrix.md`, find the row for
`<Domain> P<n>`, extract the `Core` column (e.g. Security P4 → `C1`).
This is the core derivation.
4. **Cross-reference the checklist:** load
`vendor/review/agent-checklist.md`, find the `If <Domain>` section,
extract the checklist items tagged with `P<n>` (the IaC section
tags items with `(P1)`, `(P10)` etc.; the Security section items map
to P9, P4, P5, P6, P1/P10 by content).
5. **Check non-tradeable status:** load
`vendor/domains/<domain>/first-principles.md` §3 (Conflict
Resolution); if the principle is listed as "never sacrificed", mark
`non_tradeable: true` (escalates it to C1-equivalent per
`core/conflict-resolution.md` §6).
**Return (structured output):**
```python
class PrincipleLookup(BaseModel):
domain: str # "security"
principle_id: str # "P4"
name: str # "Input Validation"
text: str # full principle body
core_derivation: list[str] # ["C1"] (from the matrix)
non_tradeable: bool # True for security P1-P8, P9; False for P10
checklist_items: list[str] # the verbatim checklist questions for this P-rule
source_path: str # "domains/security/first-principles.md" (relative to vendor/)
```
**Example resolution — `atelier.lookup_principle("security", "P4")`:**
- `name`: "Input Validation"
- `text`: "All input is untrusted until proven otherwise. Validation
happens at the boundary, against a schema, with explicit failure
modes."
- `core_derivation`: `["C1"]` (matrix row: Security P4 → C1)
- `non_tradeable`: `true` (Security §3 lists P4 as "never sacrificed")
- `checklist_items`: `["Input is validated at the boundary", "Output is
encoded for its context"]` (from `review/agent-checklist.md` If Security)
- `source_path`: `"domains/security/first-principles.md"`
The two companion tools:
- `atelier.list_domains()` → returns the 19 domain names + their
P-rule counts + relevance flag (the plugin hardcodes the
Nova-relevance table from §1.2 so the citizen developer's agent can
filter to the 13 relevant / 9 skill-bearing domains).
- `atelier.matrix_lookup(domain: str)` → returns the full domain→core
mapping for one domain (all 10 P-rules → their core C-rule(s)), used
by `validate_against_principles` to set `severity` and by conflict
resolution when two findings collide.
#### 1.6 Recommended Atelier pinned tag to vendor
**Recommendation: vendor tag `v0.3.6`** (the v0.4 milestone release).
Evidence (from `https://git.cloudinit.dev/coreci/atelier/releases`,
fetched 2026-08-06):
- The latest release is **v0.3.6**, dated 2026-08-05 16:22:58 +00:00,
tagged `v0.3.6` (commit `66b4767d25`), marked **Stable**, with the
title "v0.3.6 — v0.4 milestone: Edge + Messaging + Language-Derived
Docs".
- It is the **v0.4 milestone release** (the release notes state:
"v0.4 — Edge + Messaging + Language-Derived Docs (Milestone
Release). Tag: v0.3.6 (NFR milestone — final patch IS the deliverable;
no separate minor tag per branch-strategy.md)").
- The matrix is at its complete state: **19 domains, 190 P-rules**
(the Coverage Summary in `matrix/principles-matrix.md` confirms this
exactly; the v0.3.6 release notes confirm "170 → 190 P-rules across
19 domains"). All 190 P-rules trace to ≥1 core C-rule (no orphans —
verified in the release audit).
- `-11 commits to main since this release` — there is post-release
activity on `main`, which is exactly why pinning matters: vendoring
`main` HEAD would be a moving target. `v0.3.6` is the frozen,
audited, reproducible snapshot. This satisfies D-136 (vendor for audit
reproducibility) — an agentic validation result must be replayable
against the exact principles that produced it.
**Vendoring mechanics (for REQ-224):**
- `mcp/atelier/vendor/` = a clean copy of the Atelier repo at tag
`v0.3.6` (the `core/`, `domains/`, `matrix/`, `review/` directories —
the docs the MCP tools read; `examples/` and `languages/` are optional
but cheap to include for completeness).
- `mcp/atelier/vendor/VERSION.md` records: tag `v0.3.6`, commit
`66b4767d25`, date 2026-08-05, milestone "v0.4 Edge + Messaging +
Language-Derived Docs", P-rule count 190, domain count 19.
- `scripts/update_atelier_vendor.sh` = a helper that takes a tag arg,
fetches the tarball from
`https://git.cloudinit.dev/coreci/atelier/archive/<tag>.tar.gz`,
extracts the doc directories into `mcp/atelier/vendor/`, and updates
`VERSION.md`. Intentional upgrades only (re-run + re-audit).
Confidence: 0.95. The only risk is that a v0.5 milestone lands before
P5 ships — but the pinning model (VERSION.md + update script) makes a
future upgrade a deliberate, audited action, not a silent drift.
---
### 2. MCP Python SDK v2 Reference
Source: `https://py.sdk.modelcontextprotocol.io/` (the official Python
SDK docs, fetched 2026-08-06) + the Tools page
(`.../servers/tools/`) + the Structured Output page
(`.../servers/structured-output/`). The docs document **v2, the current
stable release line** (Python 3.10+).
#### 2.1 Confirmed API patterns
1. **Server creation + import path.** The v2 high-level server class is
`MCPServer` (NOT `FastMCP` — that was v1; v2 renamed/restructured):
```python
from mcp.server import MCPServer
mcp = MCPServer("atelier") # one arg = server name
```
This is the exact pattern shown in the docs' landing-page example and
the Tools-page example. There is no `FastMCP` import in v2.
2. **`@mcp.tool()` decorator — inputSchema from type hints.** Confirmed
verbatim from the docs: "No JSON Schema. `a: int, b: int` *is* the
schema." The SDK reads three things from the function:
- **name** = the function name (`search_books`)
- **description** = the docstring (the model sees this)
- **arguments** = the type hints (`query: str`, `limit: int`)
The SDK generates the JSON Schema and sends it during `tools/list`.
Type hints are **the contract** — if a client sends `"limit": "ten"`,
the SDK rejects it *before the function runs*. Optional args =
default values (`limit: int = 10` → leaves `required`, gains
`default: 10`). Richer constraints via
`Annotated[int, Field(ge=1, le=50, description="...")]`. Enums via
`Literal["a", "b"]`. Pydantic `BaseModel` parameter = structured
"body" (nested as `$defs`).
3. **Multiple tools / dynamic registration (plugin-registry).** The
`@mcp.tool()` decorator is called on the `mcp` object. A plugin
receives `mcp` and calls `@mcp.tool()` on it — this is plain Python
decorator application, no registration magic. The plugin-registry
pattern (D-140):
```python
# plugins/principles.py
from mcp.server import MCPServer
def register(mcp: MCPServer) -> None:
@mcp.tool()
def atelier_lookup_principle(domain: str, principle_id: str) -> PrincipleLookup:
"""Look up an Atelier principle by domain + ID."""
...
```
`server.py` scans `plugins/`, imports each module, calls
`register(mcp)`. Each plugin's `@mcp.tool()` calls register the tool
on the shared `mcp` object. **This is the confirmed dynamic-
registration pattern** — no `add_tool()` API is needed; the decorator
does it.
4. **stdio transport.** The landing-page example shows `uv run mcp dev
server.py` (Inspector). For stdio transport (D-135: stdio now), the
server runs over stdio via the SDK's run entry point. The v2 server
object supports stdio as the default transport. The exact run call is
`mcp.run()` (the SDK handles the transport based on how the process
is launched — stdio when invoked by an MCP host over stdio). The
README's "no protocol handling" promise means `mcp.run()` is the only
call needed. (HTTP transport is on the same server object — Out of
Scope for v1.18, future milestone; the server object is
transport-agnostic so adding HTTP later is a transport-only change,
confirming D-135.)
5. **outputSchema / structured output.** Confirmed: **the return type
annotation IS the output schema.** From the Structured Output page:
"the return type annotation is the output schema. It's published in
`tools/list` as `output_schema`." A Pydantic `BaseModel` return type
produces an unwrapped object schema (no `result` wrapper); a
`TypedDict` or `dataclass` works identically. The result carries
both `content` (text, for the model) and `structured_content` (data,
for the application). **Validation is enforced**: whatever the
function returns is validated against the schema before it leaves the
server — a mismatch is a tool error (not a corrupt result). This is
exactly what `atelier.validate_against_principles` needs: a
`ValidateResult(BaseModel)` return type gives the host a structured
`violations` list while giving the model a JSON-text rendering of the
same object. `structured_output=False` opts out (text-only); we do
NOT opt out for the validation tool.
6. **`listChanged` capability / dynamic tool registration.** The v2
docs (Tools page + landing page) describe tool registration as
declarative (`@mcp.tool()` at import time). The docs do NOT document
a runtime `listChanged` notification API on the high-level
`MCPServer`. For Nova's use case (plugins loaded once at server
startup, not added/removed at runtime), this is fine — all 4 tools
are registered before `mcp.run()`. A future milestone that adds
tools at runtime would need the low-level Server
(`advanced/low-level-server/`) for explicit notification control.
**Conclusion: no `listChanged` needed for v1.18; the plugin-registry
loads at startup, before the stdio loop.** Confidence 0.85 (the docs
are silent on a high-level `listChanged`; the low-level server has
it, but we use the high-level server).
#### 2.2 Skeleton for `mcp/atelier/server.py` (P5 basis)
This is the 15-line pattern Nova's server should follow (the basis for
P5 implementation):
```python
import importlib, pathlib
from mcp.server import MCPServer
mcp = MCPServer("atelier") # server name; stdio transport is the default
# Plugin-registry: scan plugins/, import each, call register(mcp).
for p in sorted(pathlib.Path(__file__).parent.glob("plugins/*.py")):
if p.stem != "__init__": importlib.import_module(f".plugins.{p.stem}", __package__).register(mcp)
@mcp.tool()
def atelier_list_domains() -> list[dict]:
"""List the 19 Atelier domains with P-rule counts + Nova-relevance."""
return [{"domain": "security", "p_rules": 10, "nova_relevant": True}, ...]
if __name__ == "__main__":
mcp.run() # stdio transport (D-135); HTTP-ready on the same object (future)
```
**Notes on the skeleton:**
- `MCPServer("atelier")` — one import, one constructor arg (the name).
- The plugin loop uses `importlib` + a `register(mcp)` convention (D-140).
Each plugin's `register` body contains `@mcp.tool()` calls that
register that plugin's tools on the shared `mcp` object. `sorted()`
makes plugin load order deterministic (audit reproducibility — a
plugin load order that changes between runs would break replay).
- The sample tool shows the pattern: `@mcp.tool()`, type hints ARE the
input schema, docstring IS the description, return type IS the
output schema. The real `atelier_list_domains` returns a
`list[DomainInfo]` (a `list[BaseModel]` → wrapped in `{"result": [...]}`,
per the Structured Output docs).
- `mcp.run()` — the single entry point; stdio is the default. No
transport boilerplate. Adding HTTP later = a transport argument or a
different run call on the same object (D-135, Out of Scope for v1.18).
- The vendored Atelier snapshot (`mcp/atelier/vendor/`) is read by the
plugin tool functions (not shown in the skeleton); the plugins load
the markdown files lazily on first tool call and cache the parsed
structure in module-level dicts (C8 Economy — don't re-parse the
matrix on every lookup).
---
### 3. Submission-Readiness Gap Analysis
#### 3.1 `contract.schema.json` defines SHAPE, not the readiness gate
Confirmed by reading `/root/acdl/schemas/contract.schema.json` (51
lines). The schema defines the **contract shape** only:
- `required`: `["id", "name", "environment", "infrastructure"]`
- `id`: pattern `^[a-z][a-z0-9-]{2,5}$` (36 char acronym)
- `name`: minLength 3
- `environment`: enum `["dev", "qa", "prod", "dr"]`
- `infrastructure`: map keyed by module name, each entry has `version`
(optional semver) + `inputs` (required, additionalProperties allowed)
- `additionalProperties: false` (top-level + per-module)
**What it does NOT define (the gap):**
- ❌ No `tags` field (the 5 required Nova tags per D-054)
- ❌ No per-env mandatory metadata (the W3.E table: dev=stack+environment;
qa+=e2eSuite+loadTest; prod+=runbook+dashboard+oncall; dr+=drDrillRef)
- ❌ No `policyPreconditions` field (declared policy expectations)
- ❌ No `profile` field (`developer` | `agentic`; agentic requires
`naturalLanguageIntent`, `confidenceAtSubmission`, `agentTrace`)
- ❌ No `appSource` field (repo + ref pointer for runtime fetch)
- ❌ No `contractId` field at the top level (the ingestor payload has
`contractId` in the Lambda envelope, but the contract *blob* itself
does not — the readiness schema promotes it to a required field per
REQ-217)
The schema's own description confirms this is the shape: "A consumer
contract declares intent: which infrastructure to deploy, in which
environment, with which inputs." It is the *intent shape*, not the
*ready-to-start gate*.
#### 3.2 The readiness schema is a SUPERSET gate ABOVE contract-schema validity
Confirmed by PROJECT.md (lines 635643, the v1.18 scope statement) and
REQ-217. The relationship:
```
contract.schema.json (SHAPE — id/name/environment/infrastructure)
│ references but does NOT redefine contract fields
submission-readiness.schema.json (GATE — superset above shape validity)
= contract-shape-valid (delegate to contract.schema.json)
+ tags (5 required Nova tags, D-054)
+ per-env mandatory (W3.E table)
+ policyPreconditions (declared policy expectations)
+ profile (developer | agentic + agentic markers)
+ appSource (repo + ref pointer)
+ contractId (non-empty, promoted to required)
```
PROJECT.md hard constraint (line 674676): "The submission-readiness
schema is a superset gate above `contract.schema.json`, NOT a
duplicate — it references but does not redefine contract fields."
This means `submission-readiness.schema.json` uses
`$ref` to `contract.schema.json` for the contract shape (or validates
the contract blob against it as a first step), then adds the gate
fields *alongside* it. The validator (REQ-218) calls
`contract.schema.json` validation **first** (the existing
`_validate_contract_schema` in the ingestor), then the readiness
checks. This is a two-layer gate, not a merged schema.
#### 3.3 Fields the new `schemas/submission-readiness.schema.json` must add
Per REQ-217 + W3.E (PROJECT.md line 888) + D-054 (tagging standard):
| Field | Type | Required | Source / rule |
|-------|------|----------|---------------|
| `contractId` | string (non-empty) | **YES** | REQ-217. Promoted from the Lambda envelope to a contract-level required field. |
| `environment` | enum `dev/qa/prod/dr` | **YES** | Already in `contract.schema.json`; the readiness schema references it (does not redefine) and uses it to select the per-env mandatory set. |
| `tags` | object | **YES** | D-054 / `schemas/tagging-standard.json`. Required keys: `nova:owner`, `nova:contract`, `nova:environment`, `nova:cost-center` (`nova:ref` optional). The readiness schema references `tagging-standard.json`'s `required_tags` shape. |
| `policyPreconditions` | object (map of string→boolean/string) | **YES** | REQ-217. Declared policy expectations the platform will enforce (e.g. `{"public-ingress": false}`). |
| `profile` | enum `developer` \| `agentic` | **YES** | REQ-217 / W3.E. |
| `profile` == `agentic` → requires: `naturalLanguageIntent` (string), `confidenceAtSubmission` (number 01), `agentTrace` (object/string) | per W3.E | **conditional** | REQ-22 / W3.E. These are "optional everywhere" per W3.E (a `developer` profile omits them) but **required when profile is `agentic`**. |
| `appSource` | object `{repo: string, ref: string}` | **YES** | REQ-217. Repo + ref pointer for runtime fetch. |
| **Per-env mandatory (W3.E):** | | | |
| `dev` | `stack` + `environment` | **YES** | W3.E. (These are the base contract fields; the readiness schema enforces their presence for dev.) |
| `qa` adds | `validation.e2eSuite` + `validation.loadTest` | **YES for qa** | W3.E. |
| `prod` adds | `runbook` + `dashboard` + `oncall` | **YES for prod** | W3.E. |
| `dr` adds | `drDrillRef` | **YES for dr** | W3.E. |
| `inputs` map | object | optional everywhere | W3.E ("inputs map is always optional"). |
The per-env mandatory table is a **conditional `allOf`** in JSON Schema
draft 2020-12: an `if`/`then` keyed on `environment` that requires the
env-specific fields. The reason code
`ENV_MISSING_MANDATORY:<env>:<field>` (REQ-218) maps directly to this
conditional check.
#### 3.4 How `contract_ingestor.py` currently works (P3 wiring point)
Read `/root/acdl/core/lambda/contract_ingestor.py` (502 lines). The
current entry point + dispatch:
- **Entry point:** `lambda_handler(event, context)` (line 460). Parses
`event["body"]` (JSON string) → `payload`. Reads `action` (default
`"submit_contract"`).
- **Identity validation:** `_validate_caller_identity(event, payload)`
(line 293) — checks IAM caller ARN, `consumerRepo` format,
`contractId` format (regex `^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$`),
`environment` enum (from `core/environments/*.json`, P10/REQ-174),
error-length cap. Fails closed if no IAM identity (P10).
- **Action dispatch (line 475):**
- `submit_contract``_submit_contract(payload)` (line 135):
validates required fields (`consumerRepo`, `contractId`, `contract`,
`environment`), size-caps the contract blob (256 KB, P11/REQ-175),
calls `_validate_contract_schema(contract)` (line 57 — validates
against `schemas/contract.schema.json` via `jsonschema`; no-op if
schema/jsonschema unavailable; bypassed by `NOVA_LAMBDA_LOCAL_BYPASS`),
writes to DynamoDB `nova-contracts` (PK `consumerRepo`, SK
`contractId#submittedAt`).
- `report_error``_report_error` (D-055, GitHub/Gitea issue).
- `validate_change_request``_validate_change_request` (REQ-93).
- `onboard_consumer``_onboard_consumer` (P18/REQ-182, validates
against `schemas/onboarding.schema.json`).
- **Error mapping:** ValueError → 400 (or 401 for identity failures);
other Exception → 500 (defensive top-level guard, `pragma: no cover`).
**Where P3 adds `--check-readiness` (D-133):**
The ingestor is a **Lambda handler**, not a CLI. D-133 says the
validator is "invoked as `contract_ingestor.py --check-readiness`
subcommand" — this is a **local CLI mode** for citizen-developer
pre-flight validation, NOT a new Lambda action. The implementation
pattern (confirmed by the existing code structure):
1. Add a `if __name__ == "__main__":` block at the bottom of
`contract_ingestor.py` that parses `sys.argv` (argparse or manual).
The existing file has NO `__main__` block (it's Lambda-only); P3
adds one.
2. The `--check-readiness` subcommand loads a contract file (or reads
stdin), validates it against
`schemas/submission-readiness.schema.json` (REQ-217) via the new
`core/submission_readiness.py` validator (REQ-218), and prints a
structured `ReadinessResult` (pass/fail per check + reason codes).
3. The validator (`core/submission_readiness.py`) calls
`_validate_contract_schema(contract)` first (reusing the existing
function — the shape gate), then runs the readiness checks (tags,
per-env mandatory, policyPreconditions, profile:agentic markers,
appSource).
4. On fail → the CLI exits non-zero with a **citizen-developer-facing
error** (not a stack trace) — REQ-218. On pass → proceeds to
existing ingestion (in the Lambda path, the readiness check would
be a pre-write gate; in the CLI path, it's a pre-flight check that
returns 0).
**Reason codes (REQ-218, the validator's return vocabulary):**
`MISSING_TAGS`, `ENV_MISSING_MANDATORY:<env>:<field>`,
`AGENTIC_MISSING_INTENT`, `MISSING_APP_SOURCE`,
`POLICY_PRECONDITION_MISSING`. Each maps to a failed check in the
schema's conditional `allOf`. The validator returns a list of these
(not a single error) so a citizen developer sees *all* gaps at once,
not one-at-a-time (C2 Clarity — the reader understands the full scope
of fixes needed).
#### 3.5 Gitea release-asset API endpoint (for `scripts/attach_release_asset.py`)
Confirmed from the existing `scripts/ship_phase.sh` (line 38) which
already uses the Gitea releases API, and from the Gitea API swagger
(`https://gitea.com/api/swagger`, fetched — the OpenAPI/Swagger JSON is
published there; the endpoint is standard Gitea).
**Release creation (existing pattern, `ship_phase.sh` line 38):**
```
POST https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases
Authorization: token <NOVA_GITEA_TOKEN>
Content-Type: application/json
Body: {"tag_name": "...", "name": "...", "body": "..."}
Response: {"id": <release_id>, ...}
```
**Release asset attachment (the new endpoint, for
`attach_release_asset.py`):**
```
POST https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases/{release_id}/assets
Authorization: token <NOVA_GITEA_TOKEN>
Content-Type: multipart/form-data
Form fields:
name = <filename, e.g. "nova-no-humans-platform.pptx">
attachment = <the file, multipart>
Response: {"id": <asset_id>, "name": "...", "size": ..., "download_count": 0, ...}
```
The Gitea API endpoint is `POST
/api/v1/repos/{owner}/{repo}/releases/{id}/assets` with a **multipart
form** containing `name` (the display filename) and `attachment` (the
file binary). The `{id}` is the numeric release ID returned by the
release-creation call (the `d.get('id')` in `ship_phase.sh` line 40).
`attach_release_asset.py` (REQ-228) takes a release tag (or ID) + a
file path, resolves the tag → release ID (GET
`/api/v1/repos/.../releases/tags/{tag}` if only the tag is known), then
POSTs the multipart form. The token comes from `.env.secrets`
(`NOVA_GITEA_TOKEN`, same as `ship_phase.sh` line 35).
**Implementation note:** `urllib` (used throughout `contract_ingestor.py`
and `ship_phase.sh`) does not natively produce multipart form bodies —
`attach_release_asset.py` must either (a) construct the multipart
boundary + body manually (the standard `urllib` pattern), or (b) use
`requests` if available. The repo's convention is stdlib-only
(`urllib`, no `requests` dependency in the ingestor), so the script
should construct the multipart body manually (C3 Simplicity — no new
dependency for one script; C8 Economy — stdlib is sufficient). A
~30-line `multipart_encode(fields, files)` helper is the standard
stdlib pattern.
---
### 4. Marp PPTX Theme Fidelity
#### 4.1 The PPTX export path and inline-CSS survival
Source: the Marp CLI README (`https://github.com/marp-team/marp-cli`,
fetched) + the existing `docs/presentations/README.md` (lines 93105)
+ the v1.9.2 theme commit `ae0cb58` (verified via `git show`).
**Confirmed export command (from `docs/presentations/README.md` line
9699):**
```bash
CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
npx --yes @marp-team/marp-cli@latest --allow-local-files \
docs/presentations/nova-no-humans-platform-marp.md \
-o <output-path>.pptx
```
**How PPTX export works (from the Marp CLI README, `--pptx` section):**
The default (non-editable) PPTX "consists of **pre-rendered background
images**." Marp renders each slide in a headless browser (Chrome/Chromium
via the `--browser-path` / `CHROME_PATH` env), captures the rendered
slide as a high-resolution image (default scale factor 2x — the README
states: "By default, Marp CLI will use 2 as the default scale factor in
PPTX"), and embeds those images as full-slide background pictures in the
PPTX. Presenter notes are supported; the PPTX opens in PowerPoint,
Keynote, Google Slides, LibreOffice Impress.
**Inline `style:` CSS survival — CONFIRMED YES.** Because the slides
are **rasterized in a headless browser**, the browser's rendering engine
applies the inline `style:` CSS block (H1/H2 `#D6002A`, title-slide bg
`#1B1B1B` with 8px `#D6002A` accent, body text `#1B1B1B`, blockquote
border `#D6002A`, table headers `#F0F0F0`, font `'Akkurat Pro'` +
fallbacks) exactly as it does for HTML export. The CSS is *baked into
the pixels* of each slide image. The PPTX is a sequence of images, not
editable PPTX shapes — so there is no "CSS stripping" step. The S&P
Global Energy theme **survives PPTX export** in the standard
(non-editable) path.
The current unified deck (`docs/presentations/nova-no-humans-platform-marp.md`)
already has the `style: |` block in its frontmatter (verified: line 8
`style: |`, line 2 `marp: true`, line 3 `theme: default`). So the S&P
theme is already inline; PPTX export will honor it.
**Caveat — `--pptx-editable` (NOT used):** The experimental
`--pptx-editable` flag generates editable PPTX (texts/shapes, not
images), and the README warns: "If the theme and inline styles are
providing complex styles into the slide, `--pptx-editable` may throw an
error or output the incomplete result." Nova does NOT use
`--pptx-editable` (the S&P theme is complex inline CSS); the standard
image-based PPTX is the path. REQ-228 specifies `--pptx
--allow-local-files`, not `--pptx-editable`.
#### 4.2 Fallback (NOT needed, documented for completeness)
If PPTX export ever strips inline CSS (it does NOT in the standard
path, per §4.1), the fallback is a **Marp custom theme CSS file**
referenced via `--theme <path>`:
```bash
CHROME_PATH=... npx @marp-team/marp-cli@latest --allow-local-files \
--theme docs/presentations/assets/sp-theme.css \
docs/presentations/nova-no-humans-platform-marp.md \
-o output.pptx
```
Marp CLI supports custom theme CSS files via `--theme <path>` (the
README's "Use custom theme" section: "A custom theme created by user
also can use easily by passing the path of CSS file"). The CSS file
would be `docs/presentations/assets/sp-theme.css` containing the same
rules currently in the inline `style:` block, prefixed with the
`@theme` meta comment (Marpit convention: `/* @theme sp-energy */`).
The deck's frontmatter `theme:` directive would then be set to the
custom theme name instead of `default`.
**Recommendation: do NOT use the fallback.** The inline `style:` block
survives the standard PPTX path (rasterized images). The fallback adds
a file to maintain in sync with the inline block (a DRY violation —
two sources of truth for the S&P colors). REQ-214 restores the S&P
theme *in the unified deck's inline `style:` block* (the v1.9.2
pattern); the PPTX export uses the same deck file. **The S&P colors
survive PPTX export via the inline `style:` block. No `--theme` flag,
no separate CSS file needed.** Confidence 0.90 (the only residual risk
is a Marp CLI version regression that changes the rasterization path —
mitigated by `@marp-team/marp-cli@latest` pinning in the render script
and the PPTX slide-count/media verification step already in
`docs/presentations/README.md` lines 332340).
#### 4.3 `ship_phase.sh` release pattern + `attach_release_asset.py` extension
Confirmed from `scripts/ship_phase.sh` (read in full, 46 lines):
- **Line 38:** `POST
https://git.cloudinit.dev/api/v1/repos/continuous-intelligence/acdl/releases`
with `Authorization: token <NOVA_GITEA_TOKEN>` (read from
`.env.secrets`, line 35) + JSON body `{"tag_name", "name", "body"}`
(line 37). The response's `id` is the release ID (line 40:
`d.get('id')`).
- The script creates the tag, pushes, creates the release, prints
`release_id: <id> tag: <tag>`.
**How `attach_release_asset.py` extends it (REQ-228):**
`attach_release_asset.py` is a **separate script** (not a modification
to `ship_phase.sh`) that runs *after* the release exists. It takes a
release tag (or ID) + a file path, then:
1. **Resolve tag → release ID** (if only the tag is known): `GET
/api/v1/repos/continuous-intelligence/acdl/releases/tags/{tag}` →
the release object's `id`.
2. **Upload the asset:** `POST
/api/v1/repos/continuous-intelligence/acdl/releases/{id}/assets`
with multipart form (`name` = filename, `attachment` = file binary)
+ `Authorization: token <NOVA_GITEA_TOKEN>` (same `.env.secrets`
source).
3. **Print** `asset_id: <id> release: <tag> file: <name>` for the
ship log.
The render+attach flow (REQ-228, triggered by any
`docs/presentations/*-marp.md` or `docs/presentations/assets/` change,
D-142):
```
render_deck.sh → HTML (committed) + PPTX (committed, D-141)
attach_release_asset.py → PPTX uploaded to the phase's Gitea release
```
PPTX is a **first-class artifact** (PROJECT.md line 682683): committed
to git (history) + attached to the release (download) — both always,
not optional. This is the D-141 decision (no LFS — the binary is
committed directly).
---
### 5. Assumptions logged (v1.18)
- **A1 (0.92):** The Atelier `v0.3.6` tag is the correct pin. It is the
latest release (2026-08-05), the v0.4 milestone release, and the
complete matrix state (19 domains, 190 P-rules). `-11 commits to main
since this release` confirms `main` is a moving target — pinning is
required for audit reproducibility (D-136). Risk: a v0.5 lands before
P5 ships — mitigated by VERSION.md + update script (deliberate
upgrade, not silent drift).
- **A2 (0.88):** The MCP Python SDK v2 high-level server class is
`MCPServer` (import `from mcp.server import MCPServer`), NOT
`FastMCP`. The docs (landing page + Tools page) use `MCPServer`
consistently; `FastMCP` was the v1 name. D-137 (MCP Python SDK v2)
resolves to this import. Risk: the v1→v2 rename — if a future SDK
patch restores a `FastMCP` alias, both imports would work, but the v2
canonical name is `MCPServer`.
- **A3 (0.85):** `mcp.run()` starts the stdio transport by default (no
explicit transport argument needed for the stdio path). The docs
show `uv run mcp dev server.py` (Inspector) and the "no protocol
handling" promise implies `mcp.run()` is the single entry point. The
exact `run()` signature for stdio vs HTTP is not spelled out on the
landing page (it's in the "Running your server" section, not fetched
in full); the D-135 decision (stdio now, HTTP-ready on the same
object) is consistent with a single `run()` entry point. P5
implementation should verify the exact run call from the
"Running your server" docs page.
- **A4 (0.90):** The standard (non-editable) PPTX export bakes inline
`style:` CSS into the rasterized slide images. The Marp README
states PPTX "consists of pre-rendered background images" — the
browser rendering applies the CSS before rasterization. The S&P
theme survives PPTX export. The `--pptx-editable` path (NOT used) is
the only path that could strip CSS, and Nova does not use it.
- **A5 (0.88):** The Gitea release-asset endpoint is `POST
/api/v1/repos/{owner}/{repo}/releases/{id}/assets` with multipart
`name` + `attachment`. This is the standard Gitea API (the swagger at
`gitea.com/api/swagger` publishes the OpenAPI spec); the existing
`ship_phase.sh` uses the sibling `.../releases` endpoint, confirming
the API root + auth pattern. The `{id}` is the numeric release ID
(resolvable from the tag via `GET .../releases/tags/{tag}`).
- **A6 (0.85):** The submission-readiness schema uses JSON Schema draft
2020-12 conditional `allOf` / `if-then` for the per-env mandatory
table (W3.E). This is the standard pattern for "if environment=qa
then require validation.e2eSuite + validation.loadTest." The
`jsonschema` library (already a dependency, used in
`contract_ingestor.py`) supports draft 2020-12 conditionals. The
validator (`core/submission_readiness.py`) may implement the per-env
check in Python (clearer reason codes) rather than relying solely on
schema conditionals — the schema is the *shape*, the validator is
the *gate* with the citizen-developer-facing reason codes (REQ-218).
- **A7 (0.80):** The `--check-readiness` CLI mode is added as a
`if __name__ == "__main__":` block in `contract_ingestor.py` (which
currently has none — it's Lambda-only). D-133 says "invoked as
`contract_ingestor.py --check-readiness`" — this is a local
pre-flight CLI, not a new Lambda action. The validator lives in
`core/submission_readiness.py` (REQ-218); the ingestor dispatches to
it. This keeps the Lambda path unchanged (the readiness gate is a
pre-write step in `_submit_contract` only if desired; the CLI path
is the citizen-developer pre-flight). Risk: the exact wiring (does
the Lambda also gate on readiness, or only the CLI?) is a P3
implementation decision — REQ-218 says "On pass → proceeds to
existing contract ingestion," implying the gate is in the
submission path, but the CLI mode is the pre-flight surface.
- **A8 (0.90):** The 9-skill list in REQ-221 is final (no adjustment).
The research confirms the 9 Atelier domains map cleanly to the BA.A
5-skill catalog; the 4 "reference-only" domains (Performance,
Documentation, Concurrency, AI/ML) are correctly NOT elevated to
skills. Adding a 10th skill would break REQ-221's exact list and the
BA.A mapping.
- **A9 (0.88):** The `mcp-engineer` persona is NOT needed — it folds
into backend-engineer. The MCP plugin-registry (D-140) is a Python
backend pattern (decorators, type hints, stdio, urllib). The SDK v2
API surface is small and FastAPI/Pydantic-style (already in
backend-engineer's range). D-143 logged in PERSONAS.md records this.
+59
View File
@@ -1683,3 +1683,62 @@ deferred (D-113/D-114).
Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS Ship tag at milestone COMPLETE: `v1.15.26` (NFR milestone; final patch IS
the release). **DONE.** the release). **DONE.**
## v1.18 (active — Citizen Developer & Production-Grade Guidance, tag line `v1.17.x`)
Nova advances from a platform that governs infrastructure delivery to one
that **instructs the citizen developer on production-grade engineering**
and defines a **clear, machine-checkable contract for what is acceptable
to start**. Five user-directed inputs drive the milestone:
1. **S&P Global theme restoration** (P1) — the v1.17 P5 deck rebuild lost
the S&P Global Energy brand visual identity (introduced v1.9.2 / P45).
The Marp `style:` block (`#D6002A` red, `#1B1B1B` grey-90, Akkurat Pro,
8px accent bar) is restored to the unified deck.
2. **PDLC-upstream scope** (P2) — promotes Core Tenet #2 + Anti-Goal #1
from buried tenets to a dedicated, unmissable scope statement: the PDLC
is upstream of Nova; Nova governs infra + delivery only.
3. **RACI matrix** (P2) — three-role responsibility matrix (Citizen
Developer / Platform / Release Management co-owned) clarifies who owns
what, with the compliance-standard-equivalence note.
4. **Nova input contract** (P3) — `schemas/submission-readiness.schema.json`
+ `core/submission_readiness.py` validator define "what is acceptable to
start" as a superset gate above contract-schema validity.
5. **Atelier integration** (P4+P5) — skills (markdown, extending BA.A) + an
MCP server (plugin-registry, vendored Atelier, agentic validation
beyond Wiz/Checkmarx/Mend).
**Milestone type:** Feature (P1 theme restoration + P3 schema/validator +
P5 MCP server are new code). Tags run on the v1.17.x patch line:
`v1.17.0` (P0) → `v1.17.1..v1.17.6` (P1P6) → `v1.17.7` (P7 final =
milestone release).
**Deck automation (cross-cutting, REQ-228):** any phase modifying
`docs/presentations/*-marp.md` or `docs/presentations/assets/` re-renders
HTML + PPTX, commits the PPTX binary to git, and attaches it to the
phase's Gitea release.
**Phase count:** 8 (P0 pre-execution + 6 execution + 1 final).
**Phases:**
- **P1 — sp-theme-restoration** (feat): restore S&P Global Marp theme to
unified deck + HTML re-render + PPTX commit + release attach. REQ-214,228.
- **P2 — pdlc-scope-raci** (docs): PDLC-upstream scope + RACI matrix +
2 deck slides + HTML/PPTX re-render. REQ-215,216,228.
- **P3 — submission-readiness** (feat): JSON Schema + validator + docs +
tests. REQ-217,218,219,220.
- **P4 — atelier-skills** (docs): 9 Atelier-derived skill files + index +
BA.A extension. REQ-221,222.
- **P5 — atelier-mcp** (feat): plugin-registry MCP server + vendored
Atelier + 4 tools + tests. REQ-223,224,225.
- **P6 — deck-slides-atelier** (docs): 3 new deck slides (scope/RACI/atelier)
→ 21 slides + talking points + HTML/PPTX re-render + README. REQ-226,227,228.
- **P7 — final-review-ship** (final): review + audit + milestone ship.
**Requirements:** REQ-214..228 (15 requirements). See
`.ciagent/REQUIREMENTS.md` §v1.18.
**Open decisions to lock (CLARIFY/GRILL):** D-133 (validator location),
D-134 (deck slide budget), D-135 (MCP transport), D-136 (Atelier vendoring),
D-137 (MCP server language), D-138 (skill format), D-139 (RACI roles),
D-140 (MCP plugin-registry), D-141 (PPTX storage), D-142 (deck render trigger).
+1 -1
View File
@@ -8,7 +8,7 @@
], ],
"active_project": "acdl", "active_project": "acdl",
"active_projects": ["acdl"], "active_projects": ["acdl"],
"active_milestone": "v1.17", "active_milestone": "v1.18",
"autonomy": { "autonomy": {
"level": "full", "level": "full",
"escalation_hooks": ["deploy", "delete_data", "merge_to_main"], "escalation_hooks": ["deploy", "delete_data", "merge_to_main"],
+5 -3
View File
@@ -100,9 +100,11 @@ CHROME_PATH=/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
``` ```
The `--allow-local-files` flag is **required** for PPTX export so the local The `--allow-local-files` flag is **required** for PPTX export so the local
PNG diagrams are embedded in the file. PPTX files are not committed to the PNG diagrams are embedded in the file. As of v1.18 (REQ-228, D-141), PPTX
repo (binary, no meaningful diffs) — they are uploaded to the Gitea release files **are committed to the repo** as first-class binary artifacts (no LFS)
as downloadable attachments. and are also attached to the phase's Gitea release via
`scripts/attach_release_asset.py`. The render + commit + attach pipeline is
automated by `scripts/render_deck.sh`.
### Step 4 — Talking points (presenter cues) ### Step 4 — Talking points (presenter cues)
@@ -6,13 +6,25 @@ size: 16x9
header: 'Nova — The No-Humans Infrastructure Platform' header: 'Nova — The No-Humans Infrastructure Platform'
footer: 'Act %{page}/5 — v1.17' footer: 'Act %{page}/5 — v1.17'
style: | style: |
section { font-size: 0.85em; } section {
h1 { color: #1a1a2e; } font-family: "Akkurat Pro", "Helvetica Neue", "Arial", sans-serif;
h2 { color: #16213e; } font-size: 22px;
table { font-size: 0.75em; } color: #1B1B1B;
.badge { padding: 2px 8px; border-radius: 3px; font-size: 0.8em; } }
.badge.planned { background: #fff3cd; color: #856404; } h1 { color: #D6002A; font-size: 34px; margin-bottom: 0.3em; }
section.title { background: #1a1a2e; color: white; } h2 { color: #D6002A; font-size: 26px; margin-bottom: 0.2em; }
section.title { background: #1B1B1B; color: #fff; border-top: 8px solid #D6002A; }
section.title h1 { color: #fff; }
table { font-size: 18px; width: 100%; }
th { background: #F0F0F0; }
blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; }
img { display: block; margin: 0 auto; max-height: 320px; }
.badge {
display: inline-block; padding: 2px 8px; border-radius: 4px;
font-size: 14px; font-weight: 600;
}
.badge.today { background: #c6f6d5; color: #22543d; }
.badge.planned { background: #fef3c7; color: #78350f; }
--- ---
<!-- _class: title --> <!-- _class: title -->
File diff suppressed because one or more lines are too long
Binary file not shown.
+80
View File
@@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""scripts/attach_release_asset.py — upload a file as a Gitea release attachment.
REQ-228 (v1.18): PPTX (and any deck artifact) is attached to the phase's
Gitea release. Uses the Gitea API:
POST /api/v1/repos/{owner}/{repo}/releases/{id}/assets
multipart form: name=<filename>, attachment=<file bytes>
Usage:
python3 scripts/attach_release_asset.py <file-path> <release-id>
python3 scripts/attach_release_asset.py docs/presentations/nova-no-humans-platform.pptx 522
Token resolution: reads NOVA_GITEA_TOKEN (or ACDL_GITEA_TOKEN) from .env.secrets
/ .env, matching the ship_phase.sh pattern. Never uses shell env tokens.
"""
import os
import sys
import json
import urllib.request
import urllib.error
from pathlib import Path
GITEA_BASE = "https://git.cloudinit.dev"
OWNER = "continuous-intelligence"
REPO = "acdl"
def resolve_token() -> str:
for fn in (".env.secrets", ".env"):
try:
for line in Path(fn).read_text().splitlines():
if line.startswith("NOVA_GITEA_TOKEN=") or line.startswith("ACDL_GITEA_TOKEN="):
return line.split("=", 1)[1].strip()
except (FileNotFoundError, PermissionError):
continue
raise RuntimeError("No Gitea token found in .env.secrets or .env (NOVA_GITEA_TOKEN/ACDL_GITEA_TOKEN)")
def attach_asset(file_path: str, release_id: str) -> dict:
token = resolve_token()
p = Path(file_path)
if not p.is_file():
raise FileNotFoundError(f"Asset file not found: {file_path}")
url = f"{GITEA_BASE}/api/v1/repos/{OWNER}/{REPO}/releases/{release_id}/assets"
filename = p.name
boundary = "----NovaBoundary7MAgYbk"
body = (
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="name"\r\n\r\n'
f"{filename}\r\n"
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="attachment"; filename="{filename}"\r\n'
f"Content-Type: application/octet-stream\r\n\r\n"
).encode() + p.read_bytes() + f"\r\n--{boundary}--\r\n".encode()
req = urllib.request.Request(
url,
data=body,
headers={
"Authorization": f"token {token}",
"Content-Type": f"multipart/form-data; boundary={boundary}",
},
method="POST",
)
try:
resp = urllib.request.urlopen(req, timeout=60)
return json.loads(resp.read())
except urllib.error.HTTPError as e:
err = e.read().decode()[:300]
raise RuntimeError(f"HTTP {e.code} attaching {filename} to release {release_id}: {err}") from e
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: attach_release_asset.py <file-path> <release-id>")
sys.exit(1)
result = attach_asset(sys.argv[1], sys.argv[2])
print(f"Attached: {result.get('name')} → release {sys.argv[2]} (asset id {result.get('id')})")
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
# scripts/render_deck.sh — render a Marp deck to HTML + PPTX, commit both to git.
# REQ-228 (v1.18): PPTX is now a first-class committed artifact + release attachment.
#
# Usage:
# bash scripts/render_deck.sh <deck-name>
# bash scripts/render_deck.sh nova-no-humans-platform
#
# Renders:
# docs/presentations/<deck-name>-marp.md → docs/presentations/<deck-name>.html (committed)
# → docs/presentations/<deck-name>.pptx (committed, binary)
#
# The PPTX is also attached to the current phase's Gitea release via
# scripts/attach_release_asset.py (call separately after ship, or this script
# will invoke it if NOVA_GITEA_RELEASE_ID is set).
set -euo pipefail
DECK="${1:?Usage: render_deck.sh <deck-name>}"
cd "$(git rev-parse --show-toplevel)"
SRC="docs/presentations/${DECK}-marp.md"
HTML="docs/presentations/${DECK}.html"
PPTX="docs/presentations/${DECK}.pptx"
if [ ! -f "$SRC" ]; then
echo "ERROR: source deck $SRC not found" >&2; exit 1
fi
CHROME=""
for c in \
/root/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome \
/usr/bin/chromium \
/usr/bin/chromium-browser \
/usr/bin/google-chrome; do
if [ -x "$c" ]; then CHROME="$c"; break; fi
done
if [ -z "$CHROME" ]; then
echo "WARNING: no Chrome/Chromium found — skipping render (HTML/PPTX will need manual re-render)" >&2
exit 0
fi
export CHROME_PATH="$CHROME"
echo "Rendering HTML → $HTML"
npx --yes @marp-team/marp-cli@latest --allow-local-files "$SRC" -o "$HTML" 2>&1 | tail -3
echo "Rendering PPTX → $PPTX"
npx --yes @marp-team/marp-cli@latest --allow-local-files "$SRC" -o "$PPTX" 2>&1 | tail -3
git add "$HTML" "$PPTX"
echo "Staged $HTML + $PPTX for commit."
if [ -n "${NOVA_GITEA_RELEASE_ID:-}" ]; then
echo "Attaching PPTX to Gitea release $NOVA_GITEA_RELEASE_ID..."
python3 scripts/attach_release_asset.py "$PPTX" "$NOVA_GITEA_RELEASE_ID" || \
echo "WARNING: attach failed — PPTX is still committed; attach manually."
fi