docs(P00): create phase plans — v1.24 (4 phases, 4 waves)

---ci---
project: acdl
phase: 0
milestone: v1.24
status: plan
---/ci---
This commit is contained in:
Jon Chery
2026-08-12 14:23:24 +00:00
parent 5dc97673e5
commit d8240588c9
2 changed files with 173 additions and 470 deletions
+4 -3
View File
@@ -1,13 +1,14 @@
{
"phase": 0,
"stage": "research",
"stage": "plan",
"milestone": "v1.24",
"phase_role": "pre_execution",
"attempts": 0,
"updated_at": "2026-08-12T02:10:00Z",
"updated_at": "2026-08-12T02:15:00Z",
"project": "acdl",
"milestone_complete": false,
"tag_line": "v1.23.x",
"requirements": ["REQ-276","REQ-277","REQ-278","REQ-279","REQ-280","REQ-281","REQ-282","REQ-283","REQ-284","REQ-285","REQ-286","REQ-287","REQ-288","REQ-289","REQ-290"],
"decisions": ["D-201","D-202","D-203","D-204","D-205","D-206","D-207","D-208"]
"phases": ["P1:consumer-guide-fixes","P2:env-transition-detect-and-destroy","P3:env-transition-tests","P4:final-review-ship"],
"waves": 4
}
+166 -464
View File
@@ -1,478 +1,180 @@
# PLAN — v1.23 (Nova Deck Cleanup & Python PPTX)
# PLAN — v1.24 (Consumer Guide Accuracy & Env-Promotion Lifecycle Enforcement)
> NFR milestone (docs/render/test only). Tags on v1.22.x line:
> v1.22.0 (P0) → v1.22.1..v1.22.4 (P1P4) → v1.22.5 (P5 final = milestone
> release). 13 requirements (REQ-263..275), 6 phases.
>
> **GRILL revisions applied** (G-001..G-004, conf 0.78-0.85):
> - G-001: "penetrate" purge covers the whole repo (`docs/` +
> `.ciagent/`), not just `docs/presentations/`.
> - G-002: P3→P4 serialized (not parallel) — P4's parser depends on
> P3's stable `render_slides.sh`; P4's trimmed deck is what P3b's
> parser consumes.
> - G-003: P3 split into P3a (inline_images + render_slides.sh +
> pyproject — low-risk) + P3b (render_pptx.py + parser +
> attach_release_asset.py — high-risk, isolated).
> - G-004: P5+P6 merged — NFR docs milestone; dedicated review/ship
> phase is ceremonial overhead. P5 absorbs review/audit/ship. Tag
> v1.22.5 = milestone release.
> Feature milestone (one `feat` phase: env-transition destroy enforcement;
> the rest are `fix`/`docs`/`test`). Tags on the **v1.23.x** line:
> v1.23.0 (P0) → v1.23.1 (P1) → v1.23.2 (P2) → v1.23.3 (P3) → v1.23.4 (P4
> final = milestone release). 15 requirements (REQ-276..290), 4 phases +
> P0 pre-execution.
## Wave Ordering
## Phase breakdown
### Phase P1 — consumer-guide-fixes (Wave 1, lead-developer)
**Type:** `docs` + `fix` + `test` (consumer guide accuracy + guide test updates)
**Requirements:** REQ-276, REQ-277, REQ-278, REQ-279, REQ-280, REQ-281, REQ-290
**Must-haves:**
- `docs/consumer-guide.md` Step 3 contract fields table corrected (REQ-276)
- `docs/consumer-guide.md` Step 4 caller consistent with Step 2 (REQ-277)
- `docs/consumer-guide.md` Step 5 stage 8 "(dev only)" → "(autonomous in dev; higher environments apply after HITL attestation)" (REQ-278)
- `docs/consumer-guide.md` Step 8 rewritten with destroy-then-rebuild semantics + cross-ref to Shape B (REQ-279)
- `docs/consumer-guide.md` Per-env section gains Shape B lead sentence (REQ-280)
- `docs/consumer-guide.md` Reference table `@v1.19` wording corrected (REQ-281)
- `tests/test_consumer_guide_per_env_section.py` updated: rename `test_consumer_guide_states_no_field_editing``test_consumer_guide_documents_both_promotion_shapes`; add `test_consumer_guide_documents_destroy_on_env_change` (REQ-290)
**Vertical slice:** A reader of `docs/consumer-guide.md` can promote via
either shape (A: edit environment + platform destroys prior; B: per-env
caller workflow) without contradiction. The guide's field table, caller
examples, and stage descriptions match the actual schema and platform
behavior. All consumer-guide tests pass.
**Files touched:**
- `docs/consumer-guide.md`
- `tests/test_consumer_guide_per_env_section.py`
**Verification:** `pytest tests/test_consumer_guide_per_env_section.py -v`
(all 7 tests pass). Manual read of `docs/consumer-guide.md` end-to-end
for internal consistency.
---
### Phase P2 — env-transition-detect-and-destroy (Wave 2, backend-engineer)
**Type:** `feat` (new platform feature: env-transition detect-and-destroy)
**Requirements:** REQ-282, REQ-283, REQ-284, REQ-285, REQ-286, REQ-287
**Must-haves:**
- `core/env_transition.py` new module: `detect_prior_env()` + `record_applied_env()` with boto3 DynamoDB queries (REQ-282, REQ-283)
- `scripts/run_platform.sh` Step 0b: environment-transition check — detect prior env, re-resolve with `environment_override=prior_env` + `deletion_protection=false`, `terraform init -reconfigure` + `terraform destroy -auto-approve` against prior state key, emit `nova.env.destroyed` evidence event, fail closed on destroy failure (REQ-284)
- `scripts/run_platform.sh` records applied env after successful apply (REQ-285)
- `.github/workflows/deploy.yml` passes `NOVA_CONSUMER_REPO=${{ github.repository }}` to `run_platform.sh` (REQ-286)
- `adapters/terraform/adapter.py` state-key block gains doc comment (REQ-287)
**Vertical slice:** When a consumer changes `environment:` on a stable
`contract.id`, the pipeline detects the prior env from DynamoDB, destroys
the prior env's Terraform state (with `deletion_protection=false`), emits
an evidence event, and only then applies the new env. If the destroy
fails, the pipeline exits non-zero (no orphan path). If no prior env
exists (first deploy or Shape B), the pipeline proceeds normally.
**Files touched:**
- `core/env_transition.py` (NEW)
- `scripts/run_platform.sh`
- `.github/workflows/deploy.yml`
- `adapters/terraform/adapter.py` (doc comment only)
**Verification:** `python3 -m py_compile core/env_transition.py`.
`pytest tests/test_pipeline.py tests/test_deploy_workflow_env_input.py -v`
(existing tests still pass). The new tests in P3 validate the behavior.
**Key implementation notes (from RESEARCH §5 pitfalls):**
1. The destroy step must re-resolve with `environment_override=prior_env`
so the emitted TF matches the prior env's resources.
2. `terraform init -reconfigure` is required when switching state backends.
3. `deletion_protection: false` must be injected (same as decommission
Step 2 in `scripts/run_decommission.sh:34-37`) or `prevent_destroy`
blocks the destroy.
4. DynamoDB unreachable in local/CI → log warning + return `None`
(conservative, no prior env assumed).
5. The state key `spike/{id}/{env}/terraform.tfstate` stays as-is — the
env segment is what lets the destroy target the prior env.
---
### Phase P3 — env-transition-tests (Wave 3, backend-engineer + lead-developer)
**Type:** `test` (new test coverage for env-transition + pipeline integration)
**Requirements:** REQ-288, REQ-289
**Must-haves:**
- `tests/test_env_transition.py` (NEW): `detect_prior_env` returns `None` when no record; returns prior env when record differs; returns `None` when record matches; `record_applied_env` writes record. Uses moto for DynamoDB (REQ-288)
- `tests/test_run_platform_env_transition.py` (NEW): asserts `run_platform.sh` has Step 0b; calls `env_transition.py detect`; calls `terraform destroy` on prior env; fails closed on destroy failure; records applied env after success (REQ-289)
**Vertical slice:** The env-transition detect-and-destroy behavior is
fully covered by automated tests. The DynamoDB query logic is unit-tested
with moto. The pipeline orchestration is tested via shell-text assertions
(pattern from `tests/test_pipeline.py:79-95`).
**Files touched:**
- `tests/test_env_transition.py` (NEW)
- `tests/test_run_platform_env_transition.py` (NEW)
**Verification:** `pytest tests/test_env_transition.py tests/test_run_platform_env_transition.py -v` (all new tests pass). Full suite: `pytest tests/ -k "env_transition or consumer_guide or pipeline or adapter or deploy_workflow" -v`.
---
### Phase P4 — final-review-ship (Wave 4, lead-developer)
**Type:** `docs` (review + audit + milestone ship)
**Requirements:** (none new — milestone completion)
**Must-haves:**
- Multi-persona code review across P1-P3 changes (ci-code-reviewer)
- Project health audit (ci-doc-verifier + ci-audit)
- Milestone ship: tag v1.23.4 (final phase patch = milestone release), merge to main, Gitea release
- Update REQUIREMENTS.md traceability (all REQ-276..290 → complete)
- Update ROADMAP.md (v1.24 → complete)
---
## Wave ordering
```
Wave 1 (P1): consolidate-docs (edits -marp.md, deletes plain .md)
Wave 1 (P1): consumer-guide-fixes [lead-developer]
Wave 2 (P2): restore-clean-style (edits -marp.md frontmatter +
↓ benefit callouts; edits render_slides.sh; retires
↓ nova-sp-theme.css from render)
Wave 2 (P2): env-transition-detect-and-destroy [backend-engineer]
Wave 3 (P3a): inline-images (inline_images.py + render_slides.sh
↓ step + pyproject — low-risk, mechanical)
Wave 3 (P3): env-transition-tests [backend-engineer + lead-developer]
Wave 4 (P3b): python-pptx-generator (render_pptx.py + parser +
↓ attach_release_asset.py — high-risk, isolated)
Wave 5 (P4): trim-wordcount + remove "penetrate" (edits -marp.md +
↓ talking-points.md + docs/scope.md + docs/vision.md +
↓ .ciagent/*.md "penetrate" purge)
Wave 6 (P5): ci-tests-readme + review + audit + milestone ship
(merged P5+P6 per G-004)
Wave 4 (P4): final-review-ship [lead-developer]
```
**Rationale (C8 revised by G-002/G-003/G-004):** P1→P2 serial (both
edit `-marp.md`). P3a→P3b serial (P3b's parser benefits from P3a's
stable `render_slides.sh`). P4 serial after P3b (P4's trimmed deck is
what P3b's parser must handle; P4's verify render depends on P3a's
stable script). P5 final (merged review/audit/ship per G-004 — NFR docs
milestone doesn't warrant a dedicated P6).
**Dependencies:**
- P2 depends on P1: the Step 8 rewrite in P1 documents the destroy
semantics that P2 implements. Doing P1 first ensures the docs and code
land in the right order (docs describe the intended behavior, then code
implements it).
- P3 depends on P2: the tests validate the env-transition module and
pipeline Step 0b that P2 creates.
- P4 depends on P1+P2+P3: the final review covers all changes.
---
**Parallelization:** P1 and P2 could run in parallel (different
territories: docs vs code), but the wave ordering is sequential for
safety — if P1's Step 8 rewrite reveals a design issue, P2's
implementation should follow the corrected design. With
`parallelization.enabled=true` and `min_plans_for_parallel=2`, the
orchestrator *could* run them concurrently; however, the dependency
(P2 follows P1's design) makes sequential the safer choice. P3 must
follow P2 (tests validate the code). P4 must follow all.
## Phase P1 — consolidate-docs (tag v1.22.1)
## Requirement → Phase mapping
**Requirements:** REQ-263, REQ-264
**Persona:** lead-developer (deck markdown + .ciagent)
**Branch:** `phase/01-consolidate-docs`
| REQ | Phase | Type | Description |
|-----|-------|------|-------------|
| REQ-276 | P1 | docs | Contract fields table corrected |
| REQ-277 | P1 | docs | Step 4 caller consistent with Step 2 |
| REQ-278 | P1 | docs | Step 5 stage 8 "dev only" corrected |
| REQ-279 | P1 | docs | Step 8 rewritten with destroy semantics |
| REQ-280 | P1 | docs | Per-env section Shape B lead sentence |
| REQ-281 | P1 | docs | Reference table @v1.19 wording corrected |
| REQ-282 | P2 | feat | env_transition.py detect_prior_env() |
| REQ-283 | P2 | feat | env_transition.py record_applied_env() |
| REQ-284 | P2 | feat | run_platform.sh Step 0b detect-and-destroy |
| REQ-285 | P2 | feat | run_platform.sh records applied env |
| REQ-286 | P2 | feat | deploy.yml passes NOVA_CONSUMER_REPO |
| REQ-287 | P2 | docs | adapter.py state-key doc comment |
| REQ-288 | P3 | test | test_env_transition.py |
| REQ-289 | P3 | test | test_run_platform_env_transition.py |
| REQ-290 | P1 | test | consumer guide test updates |
### Tasks
## Tag plan
1. **Fold speaker notes into the deck** (REQ-263):
- Read `docs/presentations/nova-autonomous-cloud-delivery.md` (the
plain source-of-truth, 747 lines).
- For each `## Slide N — Title` section, extract the
`> **Speaker notes:**` and `> **Transition:**` blockquote blocks.
- In `docs/presentations/nova-autonomous-cloud-delivery-marp.md`,
insert `<!-- Speaker notes: ... -->` (and `<!-- Transition: ... -->`
where present) as a Marp HTML comment immediately after the
slide's content, before the next `---` separator.
- The slide's visible content is unchanged.
2. **Fold talking points into the deck** (REQ-264):
- Read `docs/presentations/nova-autonomous-cloud-delivery-talking-points.md`
(145 lines, 3-6 bullets per slide + key takeaway).
- For each slide, insert `<!-- Talking points: ... -->` (the bullets
joined) as a Marp HTML comment after the speaker-notes comment.
- Update the `talking-points.md` header (line 4): change "distilled
from the source of truth (`nova-autonomous-cloud-delivery.md`)" →
"mirrors the `<!-- Talking points: -->` comments in
`nova-autonomous-cloud-delivery-marp.md` (the sole source of
truth)".
3. **Delete the plain `.md`** (REQ-263):
- `git rm docs/presentations/nova-autonomous-cloud-delivery.md`.
4. **Verify** (must-have):
- `bash scripts/render_slides.sh` still renders (22 slides:
title + 20 main + 1 appendix — unchanged).
- HTML slide count unchanged (test_marp_deck_slide_count passes).
- `grep -c "<!-- Speaker notes:" nova-autonomous-cloud-delivery-marp.md`
returns ≥20 (one per main slide).
- `grep -c "<!-- Talking points:" ...` returns ≥20.
- The plain `.md` no longer exists.
- `talking-points.md` still exists with updated header.
---
## Phase P2 — restore-clean-style (tag v1.22.2)
**Requirements:** REQ-265, REQ-266, REQ-267
**Persona:** lead-developer (deck markdown + CSS) + backend-engineer
(render_slides.sh)
**Branch:** `phase/02-restore-clean-style`
**Depends on:** P1 (consolidated doc)
### Tasks
1. **Revert frontmatter to `theme: default` + inline `style:`** (REQ-265):
- In `nova-autonomous-cloud-delivery-marp.md` frontmatter:
- `theme: nova-sp``theme: default`.
- Add `style: |` block porting the S&P visual language (from the
research-extracted reference CSS + `nova-sp-theme.css`):
```yaml
style: |
section { font-family: "Akkurat Pro", "Helvetica Neue", "Arial", sans-serif; font-size: 22px; color: #1B1B1B; padding: 48px 56px 40px; overflow: auto; }
h1 { color: #D6002A; font-size: 34px; margin-bottom: 0.3em; }
h2 { color: #D6002A; font-size: 26px; margin-bottom: 0.2em; }
h3 { color: #D6002A; font-size: 22px; margin-bottom: 0.2em; }
section.title { background: #1B1B1B; color: #fff; border-top: 8px solid #D6002A; }
section.title h1, section.title h2 { color: #fff; }
section.title header, section.title footer { display: none; }
table { font-size: 18px; width: 100%; border-collapse: collapse; }
th { background: #F0F0F0; border-bottom: 2px solid #D6002A; padding: 4px 8px; text-align: left; }
td { border-bottom: 1px solid #F0F0F0; padding: 4px 8px; }
blockquote { border-left: 4px solid #D6002A; color: #2E2E2E; font-size: 20px; padding-left: 12px; }
pre { background: #1B1B1B; color: #fff; border-radius: 4px; padding: 12px; font-size: 16px; }
code { background: #F0F0F0; color: #1B1B1B; border-radius: 2px; padding: 1px 4px; font-size: 18px; }
pre code { background: transparent; color: inherit; }
img { display: block; margin: 0 auto; max-width: 100%; max-height: 380px; object-fit: contain; }
strong { color: #D6002A; }
.benefit { margin-top: 0.6em; padding-top: 0.4em; border-top: 1px solid #D6002A; color: #1B1B1B; font-size: 20px; font-style: italic; }
section.title .benefit { color: #fff; }
@media print { section { overflow: hidden; } }
```
- Keep: `paginate: true`, `size: 16x9`, `footer:`, no `header:`.
2. **Retire `nova-sp-theme.css` from render** (REQ-266):
- Add header comment to `docs/presentations/assets/nova-sp-theme.css`:
"Retained as the S&P visual-language reference for future styling
work. The live deck uses Marp `default` theme + inline `style:`
block in the -marp.md frontmatter. Not loaded at render time."
- `scripts/render_slides.sh`: remove `THEME_CSS` variable usage and
the `--theme "$THEME_CSS"` argument from both marp-cli invocations
(lines 70, 73). Keep `--allow-local-files`.
3. **Restyle benefit callouts** (REQ-267):
- In `nova-autonomous-cloud-delivery-marp.md`, convert every
`**Benefit:** text` line → `<div class="benefit">text</div>`.
- This applies to all 20 main slides + appendix (21 callouts).
- The `**Benefit:**` prefix is removed; the `.benefit` class (defined
in the inline `style:` block) provides the visual emphasis.
4. **Verify** (must-have):
- `bash scripts/render_slides.sh` renders; HTML opens with S&P red
h1s, black title slide with red top border, benefit callouts with
red top-rule + italic.
- `grep -c "theme: default" nova-autonomous-cloud-delivery-marp.md`
returns 1.
- `grep -c "theme: nova-sp" ...` returns 0.
- `grep -c 'class="benefit"' ...` returns ≥21.
- `grep -c "\\*\\*Benefit:\\*\\*" ...` returns 0.
- `nova-sp-theme.css` still exists (reference).
- `grep "Not loaded at render" nova-sp-theme.css` returns a match.
---
## Phase P3a — inline-images (tag v1.22.3)
**Requirements:** REQ-268
**Persona:** backend-engineer (scripts + pyproject)
**Branch:** `phase/03a-inline-images`
**Depends on:** P2 (render_slides.sh `--theme` drop)
### Tasks
1. **`scripts/inline_images.py`** (REQ-268):
- Stdlib only (`base64`, `re`, `mimetypes`, `sys`, `pathlib`).
- Reads the rendered HTML path (argv[1]).
- Finds all `<img ... src="(assets/...)" ...>` (relative paths only;
skip absolute/`http(s)://`).
- Reads each referenced file, replaces `src` with
`data:image/<mime>;base64,<base64>`.
- MIME by extension: `.png`→`image/png`, `.svg`→`image/svg+xml`,
`.jpg/.jpeg`→`image/jpeg`, `.gif`→`image/gif`; fallback
`application/octet-stream`.
- Writes HTML back in place; prints count inlined to stderr.
- Usage: `python scripts/inline_images.py <html-path>`.
2. **`scripts/render_slides.sh`** (REQ-268):
- After Step 2 (MARP render), add Step 3: inline images into HTML
(`python scripts/inline_images.py "$HTML"`).
- Update Step 5 (stage): no change yet (python-pptx is P3b).
- Order now: Step 1 mermaid → Step 2 MARP HTML+PPTX → Step 3 inline
images → Step 4 stage.
3. **Verify** (must-have):
- `bash scripts/render_slides.sh` produces `.html` with inlined
base64 images.
- Open the HTML from a different directory (no `assets/` folder) —
images display.
- `grep -c 'src="assets/' nova-autonomous-cloud-delivery.html`
returns 0 (all inlined).
- `grep -c 'data:image' nova-autonomous-cloud-delivery.html`
returns ≥2 (the deck's 2 images).
---
## Phase P3b — python-pptx-generator (tag v1.22.4)
**Requirements:** REQ-269, REQ-270
**Persona:** backend-engineer (scripts + pyproject) + lead-developer
(deck structure consultation)
**Branch:** `phase/03b-python-pptx`
**Depends on:** P3a (stable render_slides.sh with inline step)
### Tasks
1. **`scripts/render_pptx.py`** (REQ-269):
- Dependency: `python-pptx>=0.6.23` (import at module load; if
absent, print "pip install -e .[slides]" and exit 1).
- Parses `nova-autonomous-cloud-delivery-marp.md`:
- Frontmatter (skip YAML between `---` markers).
- `---` slide separators.
- `#`/`##` headings → slide title.
- `<!-- ... -->` HTML comments → skipped (speaker notes/talking
points).
- `- `/`* ` bullets → bullet list.
- `**bold lead**` (first paragraph after title) → bold lead.
- `> blockquote` → blockquote-styled paragraph.
- ` ```lang ... ``` ` → code block (monospace text frame).
- `![alt](path)``add_picture` (embedded in ppt/media/).
- `| ... |` tables → native PPTX table (`add_table`).
- `<div class="benefit">text</div>` → benefit callout
(italic text frame with red top border accent).
- Produces 16:9 PPTX (`prs.slide_width = Inches(13.333)`,
`prs.slide_height = Inches(7.5)`):
- Title slide: black background, red top bar
(`MSO_SHAPE.RECTANGLE`, fill `#D6002A`, height ~0.4"), white H1
(Arial — Akkurat Pro not installed; PowerPoint does OS
substitution).
- Content slides: blank layout; red H2 title text box at top;
bold lead paragraph (red `**strong**`); bullets; blockquote
(indent + red left accent via thin rectangle); embedded PNGs
(centered, scaled to fit content area); native tables (grey
header, red bottom border); benefit callout (italic, red
top-rule accent).
- Font: "Akkurat Pro" (PowerPoint falls back to OS default if
absent — acceptable; the HTML is the pixel-perfect artifact).
- Output: `docs/presentations/nova-autonomous-cloud-delivery-python.pptx`.
- Usage: `python scripts/render_pptx.py [deck-name]` (defaults to
`nova-autonomous-cloud-delivery`).
2. **`pyproject.toml`** (REQ-269):
- Add to `[project.optional-dependencies]`:
```toml
slides = ["python-pptx>=0.6.23"]
```
3. **`scripts/render_slides.sh`** (REQ-270):
- Add Step 4: python-pptx render → `*-python.pptx`.
- Update Step 5 (stage): add `*-python.pptx`.
- Final order: Step 1 mermaid → Step 2 MARP HTML+PPTX → Step 3
inline images → Step 4 python-pptx → Step 5 stage.
4. **`scripts/attach_release_asset.py`** (REQ-270):
- Extend to accept multiple asset paths (both `.pptx` +
`-python.pptx`). Backward-compatible (single arg still works; or
accept a list). MARP PPTX is the primary attachment (first).
5. **Verify** (must-have):
- `bash scripts/render_slides.sh` produces 3 artifacts: `.html`
(with inlined base64 images), `.pptx` (MARP), `-python.pptx`
(structured).
- Open `-python.pptx` — 16:9, title slide black with red bar, content
slides with red titles + bullets + tables + images.
- `-python.pptx` is a valid zip (PPTX signature).
---
## Phase P4 — trim-wordcount + repo-wide "penetrate" purge (tag v1.22.5)
**Requirements:** REQ-271, REQ-272
**Persona:** lead-developer (deck markdown + talking-points + docs +
.ciagent)
**Branch:** `phase/04-trim-wordcount`
**Depends on:** P3b (stable render_slides.sh; trimmed deck is what P3b's
parser already handles — P4 does not break P3b's parser because the
parser handles the pre-trim structure and P4 trims prose, not
structure)
### Tasks
1. **Targeted word-count trim** (REQ-271) on ~8 verbose slides:
- **Slide 1 (The Problem)**: cut the "Every hour a developer
spends..." restatement paragraph (lines 29-ish); tighten bullet
sub-clauses. Target ~25% reduction.
- **Slide 5 (Scope)**: collapse bullets 1 + 3 (both say "PDLC is
upstream") to 3 bullets.
- **Slide 7 (Pipeline)**: condense bullet 1's pipe-chain; deduplicate
bullet 3's "Wiz on the plan" repetition.
- **Slide 8 (Decision Ledger)**: tighten the "AI decisions" bullet
from a paragraph to one sentence.
- **Slide 13 (Cost & ROI)**: collapse the "Honest caveat" bullet to
one line.
- **Slide 14 (Deferred)**: cut the preamble paragraph to one line.
- **Slide 20 (Recap + Ask)**: split the 60-word ask run-on into two
short sentences.
- **Appendix A1**: tighten the benefit callout to one line.
- Tables (slides 6, 9, 10, 15, 16, 17, A1) untouched.
- Short slides (2, 3, 4, 11, 12, 18, 19) untouched.
- Benefit callouts (all slides): already restyled in P2; here just
tighten each to one short sentence (preserve the spirit).
2. **Remove "penetrate" — repo-wide purge** (REQ-272, G-001):
- `docs/presentations/nova-autonomous-cloud-delivery-marp.md` slide 5:
"Nova never penetrates it. Integration is through one validated
contract." → "Integration is through one validated contract."
- `docs/presentations/nova-autonomous-cloud-delivery-talking-points.md:40`:
"Nova never penetrates it" → match slide 5's trimmed wording.
- `docs/scope.md`: reword "Nova never penetrates the PDLC" →
"Nova never reaches into the PDLC" (or restructure — the scope
boundary is already established by "downstream of PDLC").
- `docs/vision.md`: reword "It does not penetrate upstream product or
software development lifecycles" → "It does not reach into upstream
product or software development lifecycles".
- `.ciagent/PROJECT.md`: reword the 3 "penetrate" occurrences in the
Core Tenets #2 + Scope sections.
- Note: `.ciagent/RESEARCH.md` + `.ciagent/PLAN.md` meta-references
to "penetrate removal" are NOT purged (they describe the removal;
purging them would erase the decision history).
3. **Verify** (must-have):
- `grep -ri penetrat docs/ .ciagent/PROJECT.md .ciagent/CLARIFY.md`
returns nothing (the term is gone from all audience-facing +
project-spec docs; RESEARCH.md/PLAN.md/GRILL.md meta-references
are exempt as decision-history).
- Slide count unchanged (22).
- `bash scripts/render_slides.sh` renders; no broken slides.
- Word count of trimmed slides reduced ~20-30% (manual spot-check).
---
## Phase P5 — ci-tests-readme + review + audit + ship (Final Phase, tag v1.22.6)
**Requirements:** REQ-273, REQ-274, REQ-275 (+ review/audit/ship for all REQ-263..275)
**Persona:** backend-engineer (CI + tests) + lead-developer (README + review coordination)
**Branch:** `phase/05-ci-tests-readme-ship`
**Depends on:** P1, P2, P3a, P3b, P4 (asserts the final state; merged
P5+P6 per G-004 — NFR docs milestone doesn't warrant a dedicated
review/ship phase)
### Tasks
1. **CI workflows** (REQ-273):
- `workflows-src/slides.yml`: add `setup-python` step + `pip install
-e .[slides]` (or `pip install python-pptx>=0.6.23`). Pin
`@marp-team/marp-cli@4.5.0` + `@mermaid-js/mermaid-cli@11.16.0`
(match render_slides.sh). Update `git add` commit list: add
`*-python.pptx`.
- Sync to `.github/workflows/slides.yml` + `.gitea/workflows/slides.yml`
via `scripts/sync_workflows.py`.
2. **Tests** (REQ-274):
- `tests/test_slides_pipeline.py` updates (per research finding 5):
- Delete/invert `test_marp_deck_not_using_default_theme` (we now
USE `theme: default`).
- `test_marp_deck_uses_sp_theme` → rewrite to assert the inline
`style:` block has S&P properties (`#D6002A`, `#1B1B1B`,
`section.title`).
- `test_theme_css_*` trio → retarget from standalone CSS file to
inline `style:` block (or delete if the inline-style assertion
covers it).
- `test_html_embeds_theme` → assert literal `#D6002A` (not
`--sp-red`).
- `test_source_md_*` → delete (plain `.md` is gone).
- `test_render_slides_pins_cli_versions` → keep, extend to assert
`python-pptx` is in `pyproject.toml`.
- `test_no_penetrate_term` (new, G-001): assert
`grep -ri penetrat docs/ .ciagent/PROJECT.md .ciagent/CLARIFY.md`
returns nothing (RESEARCH.md/PLAN.md/GRILL.md meta-references
exempt as decision-history).
- `test_html_images_inlined_as_base64` (new): assert rendered
HTML has zero `src="assets/` references and ≥1
`data:image/png;base64` per image the deck uses.
- `test_python_pptx_exists` (new): assert
`nova-autonomous-cloud-delivery-python.pptx` exists + is a valid
zip (PPTX signature).
- `test_nova_sp_theme_css_retained_as_reference` (new): assert
the file exists + has the "Not loaded at render" comment.
- `test_benefit_callouts_use_class` (new): assert `-marp.md` has
`class="benefit"` and no `**Benefit:**` prefix.
- New `tests/test_pptx_generator.py`:
- `test_slide_count`: python PPTX slide count == 22 (title + 20 +
appendix).
- `test_title_slide_colors`: title slide background black, red bar
present.
- `test_expected_slide_titles`: spot-check 3-5 slide titles match
the markdown.
- `test_table_rendering`: a slide with a table (e.g., RACI) has a
native PPTX table shape.
- `test_image_embedding`: a slide with an image has a picture
shape.
- `test_benefit_callout_present`: ≥1 slide has italic benefit text.
3. **README** (REQ-275):
- Rewrite `docs/presentations/README.md` process section:
- 3-step process (was 4): 1) author `*-marp.md` (sole source of
truth, with `<!-- Speaker notes: -->` + `<!-- Talking points: -->`
comments); 2) render HTML + dual PPTX (`render_slides.sh`:
mermaid → MARP HTML+PPTX → inline images → python-pptx); 3)
talking points (mirrored from deck comments).
- Drop the plain `.md` row from the decks table.
- Add `inline_images.py` + `render_pptx.py` + `-python.pptx` rows
to the tooling section.
- Note `nova-sp-theme.css` retired (reference only; inline `style:`
is the live styling).
- Document the dual-PPTX output (MARP image-of-slide primary +
python structured for comparison/editing).
4. **Multi-persona code review** (`ciagent-review`):
- Review all changes in `milestone/v1.23-deck-cleanup-python-pptx`.
- Auto-apply P0 fixes; flag P1+ for post-hoc review.
- If P1+ found: fix in this phase.
5. **Audit** (`ciagent-audit`):
- Reconstruction test: git log matches `.ciagent/` files.
- `.ciagent/` file discipline + branch hygiene + commit discipline.
6. **Ship** (`ciagent-ship` — milestone ship):
- Merge `phase/05` → `milestone/v1.23-deck-cleanup-python-pptx`.
- Merge `milestone/v1.23-deck-cleanup-python-pptx` → `main`.
- Tag `v1.22.6` (final patch = milestone release).
- Create release with full milestone summary (all phases, all 13
requirements).
- Attach both PPTX files (MARP + python) to the release.
- Delete all milestone branches (local + remote). Tags preserve
history.
7. **Complete the milestone**:
- Update `REQUIREMENTS.md` — mark all v1.23 requirements complete.
- Update `ROADMAP.md` — mark v1.23 complete.
- Commit: `docs(milestone): complete v1.23 — Nova Deck Cleanup & Python PPTX`.
8. **Verify** (must-have):
- `pytest tests/test_slides_pipeline.py tests/test_pptx_generator.py`
all pass.
- CI workflow YAML is valid.
- README reflects the new pipeline.
---
## Versioning
- NFR milestone → progressive patches on v1.22.x line.
- `v1.22.0` (P0) → `v1.22.1` (P1) → `v1.22.2` (P2) → `v1.22.3` (P3a) →
`v1.22.4` (P3b) → `v1.22.5` (P4) → `v1.22.6` (P5 final = milestone
release). No separate minor tag. 6 phases total (was 7; P5+P6 merged
per G-004, P3 split into P3a+P3b per G-003).
## Risk Mitigations (from RESEARCH + GRILL)
1. **Default theme padding** — explicitly set
`section { padding: 48px 56px 40px }` in the inline `style:` block
(don't rely on default's `56px 64px` which doesn't reserve
header/footer space).
2. **python-pptx font fallback** — use "Akkurat Pro" in the PPTX
(PowerPoint does OS substitution); the HTML is the pixel-perfect
artifact, the python PPTX is for comparison/editing.
3. **Marp version drift** — marp-cli pinned at 4.5.0 (already).
4. **Speaker notes stripped** — Marp strips HTML comments from rendered
slides but they persist in the source `-marp.md` (acceptable; they're
for authors, not the audience).
5. **`test_html_embeds_theme`** — update to assert literal `#D6002A`
(not `--sp-red`).
6. **P3b parser overflow** (G-003) — isolated in its own phase so a
parser setback doesn't block inline-images (P3a) or trim (P4).
7. **"penetrate" purge scope** (G-001) — repo-wide, not just
`docs/presentations/`; RESEARCH.md/PLAN.md/GRILL.md exempt as
decision-history.
- P0 (this phase): `v1.23.0` — pre-execution patch
- P1: `v1.23.1` — consumer guide fixes
- P2: `v1.23.2` — env-transition detect-and-destroy
- P3: `v1.23.3` — env-transition tests
- P4: `v1.23.4` — final review + ship = **milestone release**