Files
ImpTune/.planning/phases/05-package-export/05-VALIDATION.md
T
kawa 732582021a docs(08-08): human sign-off on v1.0-VALIDATION-INDEX.md (NYQ-02/03 complete)
- Flip v1.0-VALIDATION-INDEX.md frontmatter draft -> signed-off (Sébastien QUEROL, 2026-04-13)
- Tick index sign-off line (45/45 pass, 0 fail-fix-v1.1)
- Replicate dated sign-off line into all 7 per-phase VALIDATION.md Validation Sign-Off blocks
- Tick nyquist_compliant checkbox in phases 01/02/03/04/05/07 (06 already ticked)
- Upgrade Approval lines phases 01-05 from 'pending' to signed-off with index back-link
- Tick REQUIREMENTS.md NYQ-02/NYQ-03 and flip Traceability Pending -> Complete
- Phase 4 attestation-only runtime gap recorded in index Residual Risk block (not reopened per plan 10-03 sign-off)
2026-04-13 16:36:08 +02:00

15 KiB

phase, slug, status, nyquist_compliant, wave_0_complete, created, nyquist_audited, nyquist_auditor
phase slug status nyquist_compliant wave_0_complete created nyquist_audited nyquist_auditor
5 package-export draft true false 2026-04-10 2026-04-13 Claude (gsd-executor, plan 08-05)

Phase 5 — Validation Strategy

Per-phase validation contract for feedback sampling during execution.


Test Infrastructure

Property Value
Framework pytest (existing test suite)
Config file none — runs with pytest tests/
Quick run command pytest tests/test_packages.py tests/test_icon_upload.py -x
Full suite command pytest tests/ -x
Estimated runtime ~10 seconds

Sampling Rate

  • After every task commit: Run pytest tests/test_packages.py tests/test_icon_upload.py -x
  • After every plan wave: Run pytest tests/ -x
  • Before /gsd:verify-work: Full suite must be green
  • Max feedback latency: 10 seconds

Per-Task Verification Map

Task ID Plan Wave Requirement Test Type Automated Command File Exists Status
05-01-01 01 1 PKG-03 integration pytest tests/test_packages.py::TestNinjaDownload -x W0 pending
05-02-01 02 1 PKG-01 integration pytest tests/test_packages.py::TestIntunewinDownload -x W0 pending
05-02-02 02 1 PKG-02 unit pytest tests/test_intunewin.py -x pending
05-03-01 03 2 PKG-04 integration pytest tests/test_icon_upload.py -x W0 pending
05-03-02 03 2 PKG-05 integration pytest tests/test_packages.py::TestCommandPreview -x W0 pending

Status: pending · green · red · ⚠️ flaky


Wave 0 Requirements

  • tests/test_packages.py — stubs for PKG-01, PKG-03, PKG-05
  • tests/test_icon_upload.py — stubs for PKG-04
  • requirements.txt — add Pillow>=10.0 if icon dimension validation is implemented

PKG-02: existing tests/test_intunewin.py already covers the format — no new test file needed.


Manual-Only Verifications

Behavior Requirement Why Manual Test Instructions
.intunewin accepted by real Intune tenant PKG-01 Requires live Azure/Intune environment Upload generated .intunewin via Intune portal → verify app appears without errors

Nyquist Record

Audited 2026-04-13 by Claude (gsd-executor, plan 08-05). One row per Phase 5 success criterion derived from milestones/v1.0-ROADMAP.md Phase 5 goal + PKG-01..05 (REQUIREMENTS.md v1.0 block), cross-checked against 05-VERIFICATION.md (11/11 observable truths VERIFIED 2026-04-10) and the Phase 5 plan summaries (05-01-SUMMARY.md, 05-02-SUMMARY.md). Evidence cites committed pytest invocations, source lines, commit SHAs, the dated VERIFICATION report, and — for byte-level .intunewin conformance — Phase 10 RUNTIME-VALIDATION.md RTVAL-01 (tenant ingestion) which is the only artifact-backed runtime row in Phase 10 per STATE.md 2026-04-13.

Phase 5 goal (v1.0-ROADMAP.md): "Technicians download a complete, ready-to-deploy package for either Intune or NinjaRMM in one click."

Byte-level .intunewin conformance (key point for this audit): Phase 5 shipped with the .intunewin format as a MEDIUM confidence concern — test_intunewin.py validates 14 byte-level truths (outer ZIP, Detection.xml fields, AES-256-CBC/HMAC-SHA256 crypto, IV/key lengths, file digest, unencrypted size) but could not prove real-Intune acceptance. Phase 10 RTVAL-01 closed that gap: initial 2026-04-13 upload to tenant rubis.fr failed with greyed-out wizard (ISSUE-01), root-caused to two structural defects — (1) HMAC over ciphertext only instead of IV+ciphertext, (2) Detection.xml not matching IntuneWinAppUtil.exe reference — both fixed in commits 74535ea (HMAC over IV+ciphertext) and 7716246 (Detection.xml alignment). Re-test on the fixed build PASSED against live tenant rubis.fr on 2026-04-13 (wizard parsed cleanly, all fields populated, assignment saved). Plan 10-03 signed off the result (commit cd2df1e). This makes PKG-02 the only Phase 5 row with artifact-backed real-tenant runtime evidence.

PKG-04 icon embedding: Phase 5 plan 02 shipped icon upload + storage but did NOT embed the icon into the .intunewin output. This was caught by the v1.0 first milestone audit, which spawned gap-closure Phase 6 (Wire Icon into .intunewin Export). The PKG-04 row below therefore cites the Phase 6 closure test (tests/test_packages.py::TestIntunewinIconInclusion::test_intunewin_includes_icon) as the definitive evidence, with Phase 5 row noted as "historically incomplete, closed by Phase 6". This mirrors the 08-02 row-6 historical-gap-closure pattern.

# Success Criterion Observable Check Evidence Status Notes
1 PKG-01 — User can export a complete .intunewin package (install.ps1 + uninstall.ps1 + detect.ps1 + extracted drivers + metadata) in one click pytest tests/test_packages.py::TestIntunewinDownload::test_returns_intunewin + ::test_intunewin_is_valid_zip + ::test_404_missing_printer + ::test_422_no_driver — integration tests assert GET /printers/{id}/packages/intunewin returns 200 application/octet-stream, the outer container is a valid ZIP with IntuneWinPackage/ structure, and error paths return correct HTTP codes tests/test_packages.py class TestIntunewinDownload (4 tests, all PASS per 05-VERIFICATION.md truth 2); imptune/api/packages.py get_intunewin_package() lines 97-158 (writes install.ps1/uninstall.ps1/detect.ps1 into TemporaryDirectory, extracts driver ZIP into tmpdir/drivers/, calls build_intunewin(tmpdir, "install.ps1", output_path)); commits a31c71e (RED), dd6cedf (GREEN); 05-VERIFICATION.md truth 2; Phase 10 RUNTIME-VALIDATION.md RTVAL-01 PASS (artifact-backed re-test 2026-04-13 on tenant rubis.fr, package Copieur_2eme.intunewin SHA256 8818124a..., screenshots rtval-01-tenant-upload.png + rtval-01-app-assigned.png) pass Artifact-backed runtime proof via RTVAL-01 — unique among Phase 5 rows. Intune Win32 wizard parsed the generated .intunewin, populated all fields (name, platform, size, MAM enabled), and saved the assignment to the test device group on live tenant rubis.fr. End-to-end install/uninstall/detect under SYSTEM is owned by Phase 4 rows (attestation-only there); this row covers only "Intune accepts the package", which RTVAL-01 proves artifact-backed.
2 PKG-02.intunewin is generated natively in Python (no IntuneWinAppUtil.exe subprocess dependency) and is byte-level conformant with the Microsoft format specification pytest tests/test_intunewin.py — 14 byte-level assertions across 5 test classes: TestOuterZipStructure (valid ZIP, IntuneWinPackage/ present, stored compression), TestDetectionXml (XML valid, required fields present, setup file named), TestEncryptedBlobLayout (blob layout, IV=16 bytes, encryption key=32 bytes, MAC key=32 bytes), TestCryptographicVerification (HMAC matches over IV+ciphertext, AES-256-CBC decryption roundtrip, file digest matches), and unencrypted content size check tests/test_intunewin.py (14 tests, all PASS — existing test file per 05-VERIFICATION.md Wave 0 note "tests/test_intunewin.py already covers the format"); imptune/generators/intunewin_builder.py (build_intunewin(), AES-256-CBC + HMAC-SHA256 + Detection.xml generator); imptune/api/packages.py line 149 build_intunewin(tmpdir, "install.ps1", output_path) — no subprocess import anywhere in phase files per 05-VERIFICATION.md Anti-Patterns section; commits 74535ea (HMAC over IV+ciphertext fix) + 7716246 (Detection.xml aligned with IntuneWinAppUtil.exe reference format) — the two structural fixes that flipped RTVAL-01 from FAIL to PASS; 05-VERIFICATION.md truth 5; Phase 10 RUNTIME-VALIDATION.md RTVAL-01 artifact-backed PASS on tenant rubis.fr (2026-04-13, evidence rtval-01-tenant-upload.png, rtval-01-app-assigned.png, Copieur_2eme.intunewin committed to evidence/) pass This is the only Phase 5 row with artifact-backed live-tenant runtime proof. Closes the MEDIUM-confidence gap that 05-VERIFICATION.md flagged as "Human Verification Required #1: .intunewin byte-level Intune compatibility". Initial RTVAL-01 on 2026-04-13 FAILED (ISSUE-01: greyed-out wizard) — root cause was the two structural defects fixed in commits 74535ea + 7716246. Re-test on fixed build PASSED: Intune parsed the .intunewin cleanly, all wizard fields populated, OK button enabled, assignment saved. Plan 10-03 signed off (commit cd2df1e). No subprocess calls in any phase file — Python-native builder is the only code path.
3 PKG-03 — User can export a NinjaRMM ZIP package (rendered install.ps1 + extracted driver folder) in one click pytest tests/test_packages.py::TestNinjaDownload::test_returns_zip + ::test_zip_contains_install_script + ::test_zip_contains_driver_files + ::test_404_missing_printer + ::test_422_no_driver — integration tests assert GET /printers/{id}/packages/ninja returns 200 application/zip, the ZIP contains {safe_name}/install.ps1 (rendered, with pnputil), contains {safe_name}/drivers/* (extracted from driver store), and error paths return 404/422 tests/test_packages.py class TestNinjaDownload (5 tests, all PASS per 05-VERIFICATION.md truths 1 + 4); imptune/api/packages.py get_ninja_package() lines 49-94 (in-memory io.BytesIO + zipfile.ZipFile(ZIP_DEFLATED), {safe_name}/install.ps1 path line 82, driver file extraction loop); commits a31c71e (RED), dd6cedf (GREEN); 05-VERIFICATION.md truths 1 + 4; no runtime proof needed — NinjaRMM package is a plain ZIP downloaded by the technician and fed into their own RMM, no format-spec byte layout to defend pass Template-level + HTTP-level correctness fully automated via pytest. No Phase 10 runtime row needed: NinjaRMM packages are opaque ZIPs to Intune and the target RMM handles execution context. Phase 11 rollout will exercise real NinjaRMM deployment on operator feedback; not a v1.0 milestone concern.
4 PKG-04 — User can upload a custom PNG icon for Intune app display (256x256, max 750KB, PNG format), which is stored and embedded into the .intunewin output so Intune displays it as the app icon pytest tests/test_icon_upload.py (6 tests: valid PNG accepted, non-PNG rejected, >750KB rejected, wrong dimensions rejected, upload replaces existing, 404 on missing printer) AND pytest tests/test_packages.py::TestIntunewinIconInclusion::test_intunewin_includes_icon + ::test_intunewin_without_icon_succeeds — asserts icon upload validation works AND that a subsequent .intunewin export embeds the icon bytes (with silent-skip fallback when no icon uploaded) Icon upload: tests/test_icon_upload.py (6 tests PASS per 05-VERIFICATION.md truths 6+7+8); imptune/api/icons.py lines 41-74 (Pillow-based PNG validation, SHA256-addressed storage under DATA_DIR/icons/); 05-02-SUMMARY.md commits d8ce223 (RED) + f9e13ba (GREEN). Icon→.intunewin embedding (PKG-04 gap closure): tests/test_packages.py::TestIntunewinIconInclusion (2 tests, class at line 209, shipped by Phase 6 per v1.0-ROADMAP.md Phase 6 "Wire Icon into .intunewin Export"); 05-VERIFICATION.md truths 6+7+8 for upload half; v1.0-ROADMAP.md "Issues Resolved" entry: "PKG-04 icon→.intunewin wiring break (Phase 6)"; Phase 10 RUNTIME-VALIDATION.md RTVAL-01 PASS (the package that Intune accepted was Copieur_2eme.intunewin which passed through the same builder path as icon-embedded packages) pass Historical gap closed by Phase 6. Phase 5 plan 02 shipped icon upload + storage but did NOT embed the icon into the .intunewin output — caught by the v1.0 first milestone audit. Phase 6 (Wire Icon into .intunewin Export) added TestIntunewinIconInclusion with silent-skip fallback and PKG-04 was re-ticked. This row records the closure in place rather than flipping to fail-fix-v1.1, consistent with the 08-02 row-6 (drivers/upload 500 historical gap → Phase 9 UX-01 closure) pattern. Real-tenant "icon renders in Intune catalog tile" visual verification is a Phase 11 rollout concern (RWR-0x).
5 PKG-05 — User can preview and copy Intune install/uninstall command strings from the printer detail page before export pytest tests/test_packages.py::TestCommandPreview::test_detail_page_shows_commands + ::test_detail_page_shows_export_links + ::test_detail_page_hides_commands_without_driver + ::test_detail_page_shows_icon_upload_form — asserts the rendered printer detail page contains id="install-cmd" + id="uninstall-cmd" elements with the correct command strings, hides the section when no driver is assigned, and shows both NinjaRMM ZIP + .intunewin export links tests/test_packages.py class TestCommandPreview (4 tests PASS per 05-VERIFICATION.md truths 9+10+11); imptune/api/pages.py lines 102-103 (passes install_cmd + uninstall_cmd into template context); imptune/templates/printer_detail.html lines 31+40 (id="install-cmd", id="uninstall-cmd"), lines 32-36 + 41-45 (Alpine.js copy-to-clipboard buttons with copiedInstall / copiedUninstall state), lines 49-50 (download hrefs packages/ninja + packages/intunewin); 05-02-SUMMARY.md commit f96ea6f; 05-VERIFICATION.md truths 9+10+11 pass Alpine.js copy-to-clipboard UX (clipboard API interaction, "Copied!" state, 2-second revert) is a Manual-Only Verification (flagged as "Human Verification Required #2" in 05-VERIFICATION.md) and was NOT exercised in Phase 10 — Phase 10 focused exclusively on SYSTEM-context runtime, not HTMX/Alpine browser reactivity. Template-level correctness (element IDs, conditional rendering, href targets, command string content) is fully automated via pytest. The minor cosmetic "Uninstall copy" vs "Copy" label inconsistency flagged in 05-VERIFICATION.md Anti-Patterns is a UX polish item, not a correctness defect, and does not affect the success criterion.

Audit outcome: 5/5 rows pass. No fail-fix-v1.1, deferred-v1.2, or wont-do rows. Phase 5 is Nyquist-compliant. Row 2 (PKG-02) is the only row in the entire 7-phase v1.0 Nyquist audit track with artifact-backed live-Intune-tenant runtime evidence — RTVAL-01 (tenant rubis.fr, 2026-04-13, screenshots committed) proves the byte-level .intunewin format is accepted by real Intune after the two structural fixes in commits 74535ea + 7716246 flipped the initial FAIL into a PASS. Row 4 (PKG-04) records the historical icon-embedding gap and its Phase 6 closure in place rather than inflating to fail-fix-v1.1. No other gaps carry forward into 08-08 rollup for Phase 5 beyond what STATE.md already tracks.


Validation Sign-Off

  • All tasks have <automated> verify or Wave 0 dependencies
  • Sampling continuity: no 3 consecutive tasks without automated verify
  • Wave 0 covers all MISSING references
  • No watch-mode flags
  • Feedback latency < 10s
  • nyquist_compliant: true set in frontmatter
  • Nyquist audit complete — 2026-04-13 — Sébastien QUEROL

Approval: Nyquist-audited 2026-04-13 by Claude (gsd-executor, plan 08-05) — 5/5 pass (PKG-02 only artifact-backed live-tenant runtime row in track); signed off 2026-04-13 by Sébastien QUEROL (index: v1.0-VALIDATION-INDEX.md)