V5 — Client Cloud Posture Report — Test Runbook¶
Asset: V5 (
apps/posture-report/) — offline composer that bundles E0/S2/S4/L5/V2 (+ optional K4) into one branded, deliverable-organized client posture report (markdown + HTML, optional PDF). Owner: SH · Milestone: M4 · Cloud cost: $0 (offline-only).V5 is a pure composition tool — it never touches Azure. This runbook is offline-only (Parts A + B); there is no live phase, mirroring S4.md / L5.md.
Part A — Offline build + test gate (~3 min, $0)¶
Proves the composer builds, typechecks, and its pure core passes its suite.
cd apps/posture-report
npm ci
npm run typecheck # tsc over src + tests, --noEmit
npm test # jest: compose + render suites
Acceptance:
- [ ] npm run typecheck exits 0.
- [ ] npm test is green — composition, honest-missing-input, tier tagging,
grade-source (S4 scorecard preferred, E0 fallback), the never-fabricate
cost section, trend-sign derivation, HTML self-containment + injection
safety, and determinism.
- [ ] The app is auto-discovered by .github/workflows/app-tests.yml (it has a
package.json with a test script), so CI runs the same suite on PRs.
Part B — Worked composition (~4 min, $0)¶
Compose the report from the committed example fixtures (the same data the
posture-report.yml workflow uses for its offline demo).
cd apps/posture-report
npm run build
node dist/index.js \
--client-name "Acme Corp" \
--branding examples/branding.json \
--scorecard examples/scorecard.json \
--snapshot examples/snapshot.json \
--dashboard examples/dashboard.json \
--rto-rpo examples/rto-rpo.json \
--incidents examples/incidents.json \
--diagram examples/architecture.d2 \
--out-dir /tmp/posture \
--now 2026-06-09T12:00:00.000Z
Acceptance — composition is re-presentation, never fabrication:
- [ ] Writes posture-report.md, posture-report.html, and model.json to
/tmp/posture. Stdout reports grade B for Acme Corp.
- [ ] Headline grade comes from the S4 scorecard verbatim — the derivation
line begins "From the S4 scorecard:". Re-run without --scorecard and
confirm the grade is instead derived from the E0 snapshot (derivation says
"derived here from the E0 snapshot").
- [ ] Sections are organized by deliverable and tagged:
Executive summary [Baseline], Your guardrails [Baseline], Your data is
protected [Baseline], Your compliance posture [Advanced], Incidents we
handled [Advanced], Cost governance [Baseline], Architecture
[Baseline].
- [ ] Honest missing inputs: re-run with only --snapshot + --client-name
+ --out-dir. The compliance-posture and incidents sections render a
"not supplied / Advanced package" note, and an upsell teasers section
("What else SnowOps can light up") appears.
- [ ] Cost governance is never fabricated — it always renders as a roadmap
note referencing the not-yet-wired U4/U5 FinOps feed, even when every other
input is supplied.
- [ ] HTML is self-contained — grep -c 'http' /tmp/posture/posture-report.html
returns 0 when no logoUrl is set (inline CSS, no JS, no network refs;
safe to upload as a CI artifact). The d2 diagram is HTML-escaped into a
<pre> (no markup injection); an .svg diagram is embedded inline.
- [ ] Optional: --pdf renders posture-report.pdf via Pandoc where available;
a Pandoc failure is logged but never fails the run.
Delivery: .github/workflows/posture-report.yml (monthly + dispatch) runs
this composition and uploads a PRIVATE artifact (14-day retention) — posture
data is sensitive. The QBR delivery guide is under
docs/client-guides/.
Notes¶
- No cross-package imports (D37/D40/D49): V5 re-declares the E0/S2/S4/L5/K4 input contracts and the V2 diagram shape; it depends on the versioned artifact shapes, not the producing packages.
- Cost + incidents data feeds: incidents reuse the K4 report when supplied; cost has no SnowOps producer yet (U4/U5 postponed), so it degrades honestly. Wiring them is a follow-on, not a blocker (D65).