feat(P01): render PPTX + CAP-042 + D-241 record + frontmatter-comment fix

- render_pptx.py: skip leading HTML comments before frontmatter (split_slides + parse_frontmatter)
- docs/presentations/nova-leadership-deck.pptx: 7 slides, footer on every slide
- STATE.md: CAP-042 row appended (leadership deck, single-shot, Aug 2026)
- PROJECT.md: D-241 formal entry (single-shot, discrete, no compression)
- smoke test exits 0 (all 6 assertions a-f pass)

---ci---
project: acdl
phase: 1
milestone: v1.30
status: execute
wave: 4
persona: backend-engineer + lead-developer
---/ci---
This commit is contained in:
CIAgent
2026-08-20 13:23:28 +00:00
parent dc784d576d
commit e3a13e4768
4 changed files with 46 additions and 9 deletions
+16
View File
@@ -691,6 +691,22 @@ New requirements REQ-354..REQ-369 + REQ-371 + REQ-363b — full text in
- **Decisions:** `D-241..D-243` (3 decisions, authored in CLARIFY). - **Decisions:** `D-241..D-243` (3 decisions, authored in CLARIFY).
Max existing D = D-240 (v1.29). Next free: D-244. Max existing D = D-240 (v1.29). Next free: D-244.
- **D-241** — Leadership deck is a single-shot, discrete, hand-
authored artifact (NOT a compression of the existing citizen-
developer pitch `nova-autonomous-cloud-delivery-marp.md`).
Audience: Infrastructure & Operations leadership (CTO + VP
Technology + Product Management). August 2026 presentation
anchor + November 2026 runway anchor. The existing citizen-
developer deck remains untouched. Overrides the post-v1.29
STATE.md intake assumption 3.
- **D-242** — Narrow `scripts/render_pptx.py` extension: accept an
explicit source `.md` path + `--output` filename; render a
right-aligned footer textbox on every slide (python-pptx does
not read the Marp `footer:` directive). Non-REQ-372 prerequisite
per spec §3.3 Edge 2.
- **D-243** — Date anchor discipline: August 2026 is a month-only
presentation anchor (no specific day); November 2026 is the
runway anchor (~90 days).
- **Capabilities:** `CAP-042` (1 capability, appended at ship). - **Capabilities:** `CAP-042` (1 capability, appended at ship).
Max existing CAP = CAP-041 (v1.29). Next free: CAP-043. Max existing CAP = CAP-041 (v1.29). Next free: CAP-043.
+2 -1
View File
@@ -357,6 +357,7 @@
| CAP-039 | Platform ops reposplit | v1.29 / `v1.28.6` | `nova-platform-ops` (out-of-band), `docs/operator-guide-platform-ops.md`, `docs/archive/nova-idp-cfn-v1.28.md` | REQ-369, REQ-OPS-GUIDE, D-232, D-235 | covered-reference | engineering (`acdl/acdl`, GitHub) ends at the artifact; operations (`nova-platform-ops`, Gitea-private, OPER-PRIV) begins at the live platform; tag-pin handoff; CFN archived; covered-reference REQs tracked via cutover gates | | CAP-039 | Platform ops reposplit | v1.29 / `v1.28.6` | `nova-platform-ops` (out-of-band), `docs/operator-guide-platform-ops.md`, `docs/archive/nova-idp-cfn-v1.28.md` | REQ-369, REQ-OPS-GUIDE, D-232, D-235 | covered-reference | engineering (`acdl/acdl`, GitHub) ends at the artifact; operations (`nova-platform-ops`, Gitea-private, OPER-PRIV) begins at the live platform; tag-pin handoff; CFN archived; covered-reference REQs tracked via cutover gates |
| CAP-040 | KJ substrate lockstep | v1.29 / `v1.28.6` | `nova-platform-ops` (out-of-band), `platform/abac/kj-version.txt`, `.github/workflows/publish.yml` | REQ-371, REQ-363, REQ-363b, D-238, D-239 | covered-reference | one ECR image digest shared by Lambda `image_uri` + Fargate task `image`; `lifecycle.precondition` on both resources at `terraform plan`; KJ-STATIC (`CGO_ENABLED=0`, `file(1)` asserts `statically linked`); no second pipeline, no second SHA pin | | CAP-040 | KJ substrate lockstep | v1.29 / `v1.28.6` | `nova-platform-ops` (out-of-band), `platform/abac/kj-version.txt`, `.github/workflows/publish.yml` | REQ-371, REQ-363, REQ-363b, D-238, D-239 | covered-reference | one ECR image digest shared by Lambda `image_uri` + Fargate task `image`; `lifecycle.precondition` on both resources at `terraform plan`; KJ-STATIC (`CGO_ENABLED=0`, `file(1)` asserts `statically linked`); no second pipeline, no second SHA pin |
| CAP-041 | JWKS edge-only | v1.29 / `v1.28.6` | `nova-platform-ops` (out-of-band), `docs/operator-guide-platform-ops.md` | REQ-364, REQ-365, REQ-366, INV-18, D-233 | covered-reference | JWKS is the only public read surface; CloudFront + OAC (`AuthType: AWS_IAM`, NOT `NONE`, `OriginAccessControlOriginType: lambda`, `SigningBehavior: always`); direct Function URL → 403, via-CloudFront → 200; WAF rate-limit 3000/5min + AWSManagedRulesCommonRuleSet; ACM DNS-validated in us-east-1; Route53 A-alias | | CAP-041 | JWKS edge-only | v1.29 / `v1.28.6` | `nova-platform-ops` (out-of-band), `docs/operator-guide-platform-ops.md` | REQ-364, REQ-365, REQ-366, INV-18, D-233 | covered-reference | JWKS is the only public read surface; CloudFront + OAC (`AuthType: AWS_IAM`, NOT `NONE`, `OriginAccessControlOriginType: lambda`, `SigningBehavior: always`); direct Function URL → 403, via-CloudFront → 200; WAF rate-limit 3000/5min + AWSManagedRulesCommonRuleSet; ACM DNS-validated in us-east-1; Route53 A-alias |
| CAP-042 | Leadership presentation deck (single-shot) | v1.30 / `v1.29.3` | `docs/presentations/nova-leadership-deck-marp.md`, `docs/presentations/nova-leadership-deck.pptx`, `scripts/check_leadership_deck.sh` | REQ-372.1..REQ-372.12, D-241, D-242, D-243 | local | Single-shot 7-slide PPTX deck for Infrastructure & Operations leadership (CTO + VP Technology + Product Management); presented August 2026; November 2026 runway anchor; discrete hand-authored artifact (NOT a compression of the citizen-developer pitch per D-241); rendered via existing `scripts/render_pptx.py` (narrowly extended per D-242); smoke test on-demand (NOT a CI gate); vision `[1]` grounding in slides 3/5/7 |
## Archive pointers ## Archive pointers
@@ -497,7 +498,7 @@ Locked Decisions: D-001..D-240 (full ledger in PROJECT.md + CLAR
Active Invariants: INV-1..INV-18 (full text above). New in v1.28: INV-12 (mode observability), INV-13 (mode determinism), INV-14 (credential type encodes role), INV-15 (no AWS-managed identity), INV-16 (Argon2id password storage), INV-17 (ABAC discipline fail-closed). New in v1.29: INV-18 (JWKS-EDGE-ONLY) + 10 NFR constraints (KJ-STATIC, KJ-LOCKSTEP, KJ-WARMUP-HEALTH, OPER-PRIV, IAM-NARROW, DRIFT-DETECT, IMPORT-IDEMPOTENT, TFM-HITL, JWKS-SLO, JWKS-ROTATION) Active Invariants: INV-1..INV-18 (full text above). New in v1.28: INV-12 (mode observability), INV-13 (mode determinism), INV-14 (credential type encodes role), INV-15 (no AWS-managed identity), INV-16 (Argon2id password storage), INV-17 (ABAC discipline fail-closed). New in v1.29: INV-18 (JWKS-EDGE-ONLY) + 10 NFR constraints (KJ-STATIC, KJ-LOCKSTEP, KJ-WARMUP-HEALTH, OPER-PRIV, IAM-NARROW, DRIFT-DETECT, IMPORT-IDEMPOTENT, TFM-HITL, JWKS-SLO, JWKS-ROTATION)
Standing Capability Gate: CAP-001..CAP-041 — all Verified (32 from v1.0..v1.27 + 6 from v1.28 + 3 from v1.29 covered-reference). Gate enforced by `core/regression_verify.py` + CI merge gates. CAP-033..038 added v1.28 (CLI surface, delegation AST, layer/wheel match, auth flow, KMS sign, PAT revocation). CAP-039..041 added v1.29 (platform-ops-reposplit, kj-substrate-lockstep, jwks-edge-only — covered-reference, live cutover pending operator action in nova-platform-ops). Standing Capability Gate: CAP-001..CAP-042 — all Verified (32 from v1.0..v1.27 + 6 from v1.28 + 3 from v1.29 covered-reference + 1 from v1.30 single-shot deck). Gate enforced by `core/regression_verify.py` + CI merge gates. CAP-033..038 added v1.28 (CLI surface, delegation AST, layer/wheel match, auth flow, KMS sign, PAT revocation). CAP-039..041 added v1.29 (platform-ops-reposplit, kj-substrate-lockstep, jwks-edge-only — covered-reference, live cutover pending operator action in nova-platform-ops). CAP-042 added v1.30 (leadership-deck — single-shot, on-demand smoke test, NOT a CI gate).
Anti-Goals Touched: `docs/vision.md` §7 / `NORTH_STAR.md` §Anti-Goals — (1) not an upstream dev platform; (2) not a general-purpose AI; (3) not a legacy infra bridge; (4) not a permissive delivery highway; (5) not a mutable audit log. v1.29 honored all 5 (no PDLC reach, narrow CLI autonomy, no VMs, ABAC fail-closed + HITL gates intact, immutable outbox). Anti-Goals Touched: `docs/vision.md` §7 / `NORTH_STAR.md` §Anti-Goals — (1) not an upstream dev platform; (2) not a general-purpose AI; (3) not a legacy infra bridge; (4) not a permissive delivery highway; (5) not a mutable audit log. v1.29 honored all 5 (no PDLC reach, narrow CLI autonomy, no VMs, ABAC fail-closed + HITL gates intact, immutable outbox).
Binary file not shown.
+28 -8
View File
@@ -70,13 +70,21 @@ def parse_frontmatter(md_text: str):
(no nested structures) sufficient for Marp deck frontmatter (no nested structures) sufficient for Marp deck frontmatter
(marp, theme, footer, paginate, size). The `style:` block (multi- (marp, theme, footer, paginate, size). The `style:` block (multi-
line `|`) is skipped (not needed by the python-pptx renderer). line `|`) is skipped (not needed by the python-pptx renderer).
Skips leading HTML comments before the frontmatter fence.
""" """
if not md_text.lstrip().startswith("---"): text = md_text.lstrip()
# Skip leading HTML comments before frontmatter.
while text.startswith("<!--"):
end = text.find("-->")
if end == -1:
return {}
text = text[end + 3 :].lstrip()
if not text.startswith("---"):
return {} return {}
end = md_text.find("\n---", 3) end = text.find("\n---", 3)
if end == -1: if end == -1:
return {} return {}
fm_text = md_text[3:end] fm_text = text[3:end]
fm = {} fm = {}
in_multiline = False in_multiline = False
for line in fm_text.splitlines(): for line in fm_text.splitlines():
@@ -104,14 +112,26 @@ def parse_frontmatter(md_text: str):
def split_slides(md_text: str): def split_slides(md_text: str):
"""Strip YAML frontmatter, then split the deck into slide source strings.""" """Strip leading HTML comments + YAML frontmatter, then split into slides.
A Marp deck may carry a header HTML comment before the frontmatter
(e.g. the REQ-372 related-artifacts comment). Skip leading comments
before detecting the `---` frontmatter fence.
"""
text = md_text.lstrip()
# Skip leading HTML comments (<!-- ... -->) before frontmatter.
while text.startswith("<!--"):
end = text.find("-->")
if end == -1:
break
text = text[end + 3 :].lstrip()
# Strip YAML frontmatter (between first pair of `---` lines). # Strip YAML frontmatter (between first pair of `---` lines).
if md_text.lstrip().startswith("---"): if text.startswith("---"):
end = md_text.find("\n---", 3) end = text.find("\n---", 3)
if end != -1: if end != -1:
md_text = md_text[end + 4 :] text = text[end + 4 :]
# Normalize slide separators. Marp uses `\n---\n` on its own line. # Normalize slide separators. Marp uses `\n---\n` on its own line.
parts = re.split(r"\n---\s*\n", md_text) parts = re.split(r"\n---\s*\n", text)
slides = [] slides = []
for p in parts: for p in parts:
p = p.strip("\n") p = p.strip("\n")