From 036b0487058da29f3f1d7c830ae947bd16717c11 Mon Sep 17 00:00:00 2001 From: Kawa Date: Mon, 13 Apr 2026 17:50:30 +0200 Subject: [PATCH] docs(phase-09): complete phase execution and human sign-off All 3 UX defects fixed, 11/11 must-haves verified, human-approved. Co-Authored-By: Claude Sonnet 4.6 --- .planning/ROADMAP.md | 2 +- .planning/STATE.md | 2 +- .../phase-10-rtval-01-intunewin-parse-fail.md | 78 ----------- .../09-VERIFICATION.md | 102 +++++++------- .../10-VERIFICATION.md | 130 ++++++++++++++++++ .../evidence/.gitkeep | 0 .../evidence/Copieur_2eme.intunewin | Bin 33913190 -> 0 bytes .../evidence/rtval-01-app-assigned.png | Bin 21618 -> 0 bytes .../evidence/rtval-01-tenant-upload.png | Bin 9389 -> 0 bytes .../api/__pycache__/drivers.cpython-312.pyc | Bin 5202 -> 5584 bytes .../api/__pycache__/drivers.cpython-314.pyc | Bin 5812 -> 6164 bytes .../api/__pycache__/scripts.cpython-312.pyc | Bin 4006 -> 5422 bytes .../api/__pycache__/scripts.cpython-314.pyc | Bin 4597 -> 6769 bytes .../intunewin_builder.cpython-312.pyc | Bin 6063 -> 6309 bytes .../intunewin_builder.cpython-314.pyc | Bin 6329 -> 6579 bytes .../__pycache__/inf_parser.cpython-312.pyc | Bin 7088 -> 8345 bytes .../__pycache__/inf_parser.cpython-314.pyc | Bin 7980 -> 9432 bytes ...driver_upload.cpython-314-pytest-9.0.3.pyc | Bin 19551 -> 33202 bytes ...st_inf_parser.cpython-314-pytest-9.0.3.pyc | Bin 23753 -> 26147 bytes ...est_intunewin.cpython-314-pytest-9.0.3.pyc | Bin 35358 -> 38761 bytes ...test_packages.cpython-314-pytest-9.0.3.pyc | Bin 34249 -> 36948 bytes ..._printer_form.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 7184 bytes ...ript_download.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 16784 bytes .../e2e/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 171 bytes .../conftest.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 3472 bytes ...port_autofill.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 2565 bytes 26 files changed, 186 insertions(+), 128 deletions(-) delete mode 100644 .planning/debug/phase-10-rtval-01-intunewin-parse-fail.md create mode 100644 .planning/phases/10-real-world-runtime-validation/10-VERIFICATION.md delete mode 100644 .planning/phases/10-real-world-runtime-validation/evidence/.gitkeep delete mode 100644 .planning/phases/10-real-world-runtime-validation/evidence/Copieur_2eme.intunewin delete mode 100644 .planning/phases/10-real-world-runtime-validation/evidence/rtval-01-app-assigned.png delete mode 100644 .planning/phases/10-real-world-runtime-validation/evidence/rtval-01-tenant-upload.png create mode 100644 tests/__pycache__/test_printer_form.cpython-314-pytest-9.0.3.pyc create mode 100644 tests/__pycache__/test_script_download.cpython-314-pytest-9.0.3.pyc create mode 100644 tests/e2e/__pycache__/__init__.cpython-314.pyc create mode 100644 tests/e2e/__pycache__/conftest.cpython-314-pytest-9.0.3.pyc create mode 100644 tests/e2e/__pycache__/test_port_autofill.cpython-314-pytest-9.0.3.pyc diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 989709e..3dc7e67 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -104,6 +104,6 @@ Full details: [`milestones/v1.0-ROADMAP.md`](milestones/v1.0-ROADMAP.md) | 6. Wire Icon into .intunewin | v1.0 | 1/1 | Complete | 2026-04-10 | | 7. Dashboard & Nav Polish | v1.0 | 1/1 | Complete | 2026-04-13 | | 8. Nyquist Validation Track | v1.1 | 8/8 | Complete | 2026-04-13 | -| 9. UX Tech Debt Closure | 3/3 | Complete | 2026-04-13 | 2026-04-13 | +| 9. UX Tech Debt Closure | 3/3 | Complete | 2026-04-13 | 2026-04-13 | | 10. Real-World Runtime Validation | v1.1 | 3/3 | Complete | 2026-04-13 | | 11. Real-World Rollout & Feedback | v1.1 | 3/3 | Complete | 2026-04-13 | diff --git a/.planning/STATE.md b/.planning/STATE.md index 6e82dac..614108d 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,7 +5,7 @@ milestone_name: Hardening & Validation current_plan: 2 status: completed stopped_at: Completed 09-02-playwright-port-autofill-PLAN.md -last_updated: "2026-04-13T15:46:11.154Z" +last_updated: "2026-04-13T15:50:19.479Z" last_activity: 2026-04-13 progress: total_phases: 4 diff --git a/.planning/debug/phase-10-rtval-01-intunewin-parse-fail.md b/.planning/debug/phase-10-rtval-01-intunewin-parse-fail.md deleted file mode 100644 index 414af4e..0000000 --- a/.planning/debug/phase-10-rtval-01-intunewin-parse-fail.md +++ /dev/null @@ -1,78 +0,0 @@ ---- -status: awaiting_human_verify -trigger: "phase-10-rtval-01-intunewin-parse-fail" -created: 2026-04-13T00:00:00Z -updated: 2026-04-13T10:30:00Z ---- - -## Current Focus - -hypothesis: HMAC is computed over `ciphertext` only, but the reference (svrooij/ContentPrep, confirmed by multiple sources) computes it over `IV + ciphertext`. This causes Intune's HMAC verification to fail silently, producing the exact symptom: empty fields, OK button greyed, no error banner. -test: Inspect svrooij C# DecryptFileAsync: after reading first 32 bytes (HMAC), it hashes "remaining bytes" = IV+ciphertext. ImpTune computes HMAC over ciphertext only (line 81: `hmac.new(mac_key, ciphertext, ...)`). -expecting: If confirmed, fixing HMAC to cover `iv + ciphertext` will fix the package. -next_action: Fix HMAC computation in intunewin_builder.py and update tests. - -## Symptoms - -expected: Uploading the .intunewin to Intune parses metadata, populates Name/Platform/Size/MAM-enabled fields, enables OK button. -actual: Intune accepts upload but never populates the metadata form. All fields stay empty. OK button stays greyed out. No error banner. -errors: Silent metadata-parse failure inside the wizard. -reproduction: Build with ImpTune, upload to Intune Apps > Windows > Add > Windows app (Win32). -started: First time the generator has been tested against a real Intune tenant. Never worked in production. - -## Eliminated - -- hypothesis: archive layout is wrong (different folder structure) - evidence: python -m zipfile -l confirms correct IntuneWinPackage/Contents/ and IntuneWinPackage/Metadata/ layout - timestamp: 2026-04-13T00:00:00Z - -- hypothesis: encryption algorithm (AES mode, IV size, padding) is wrong - evidence: code uses AES-256-CBC with PKCS7 padding, 16-byte IV — matches reference. Algorithm itself correct. - timestamp: 2026-04-13T00:00:00Z - -- hypothesis: Detection.xml structural defects alone caused the failure (prior hypothesis) - evidence: Detection.xml was fixed in commit 7716246 (no xmlns, no XML decl, added ToolVersion attr, removed MacAlgorithm). Human verification came back with IDENTICAL symptom. Fix was real but not sufficient. Bug is deeper. - timestamp: 2026-04-13T10:30:00Z - -## Evidence - -- timestamp: 2026-04-13T00:00:00Z - checked: Copieur_2eme.intunewin archive layout - found: Correct paths — IntuneWinPackage/Contents/IntunePackage.intunewin + IntuneWinPackage/Metadata/Detection.xml - implication: Archive layout is not the issue - -- timestamp: 2026-04-13T00:00:00Z - checked: Detection.xml from Copieur_2eme.intunewin - found: Has xmlns="http://schemas.microsoft.com/IntuneWin", has declaration, missing ToolVersion attribute, has MacAlgorithm child element - implication: Multiple structural deviations from reference - -- timestamp: 2026-04-13T00:00:00Z - checked: svrooij/ContentPrep reference implementation (Packager.cs + ApplicationInfo.cs) - found: (1) ToolVersion="1.8.6.0" is an XML ATTRIBUTE on ApplicationInfo, (2) NO xmlns namespace ([XmlRoot("ApplicationInfo")] with no Namespace param + empty XmlSerializerNamespaces), (3) OmitXmlDeclaration=true so no header, (4) FileEncryptionInfo model has NO MacAlgorithm field - implication: ImpTune's Detection.xml deviates in 4 ways from the reference. The missing ToolVersion and wrong namespace are the most likely causes of Intune wizard silence. - -- timestamp: 2026-04-13T10:30:00Z - checked: Human verification result after Detection.xml fix (commit 7716246) - found: Same exact symptom — empty fields, OK greyed, no error banner. Bit-for-bit identical failure. Post-fix package was NOT checked into evidence/. - implication: Either (a) stale build tested, or (b) additional structural bug beyond Detection.xml. Must assume (b) since symptom is bit-for-bit identical. - -- timestamp: 2026-04-13T10:30:00Z - checked: svrooij decryption article — DecryptFileAsync algorithm - found: After reading first 32 bytes (HMAC), method computes hash of "remaining bytes" (= IV + ciphertext). Multiple web sources confirm: "HMAC is computed over IV + ciphertext combined". - implication: ImpTune computes HMAC over ciphertext only (intunewin_builder.py line 81: hmac.new(mac_key, ciphertext, ...)). Reference computes over iv+ciphertext. This is a cryptographic mismatch that Intune would detect silently. - -- timestamp: 2026-04-13T10:30:00Z - checked: packages.py get_intunewin_package endpoint - found: output_path = os.path.join(tmpdir, "out.intunewin") — output file is inside source_dir passed to build_intunewin(). build_intunewin walks source_dir FIRST (step 1), output_path does not exist yet, so it is NOT included in inner ZIP. - implication: No self-inclusion bug. Endpoint code is structurally correct. - -## Resolution - -root_cause: TWO bugs, both in intunewin_builder.py: - (1) Detection.xml structural errors — 4 deviations from IntuneWinAppUtil.exe reference: missing ToolVersion attribute, spurious xmlns namespace, header, extra MacAlgorithm element. Fixed in commit 7716246. - (2) HMAC scope bug — HMAC was computed over ciphertext only, but the reference (svrooij/ContentPrep DecryptFileAsync) hashes the "remaining bytes" after the stored HMAC = IV+ciphertext. Intune's HMAC verification uses HMAC(mac_key, iv+ciphertext) but the stored value was HMAC(mac_key, ciphertext). This is a silent authentication mismatch that would cause Intune to reject the encrypted payload, manifesting identically to the XML bug: empty form fields, greyed OK button, no error banner. Fixed in commit [new commit]. -fix: Changed HMAC computation from hmac.new(mac_key, ciphertext, ...) to hmac.new(mac_key, iv + ciphertext, ...). Updated test_hmac_matches to verify HMAC over iv_and_ciphertext = blob[32:] (matches reference decryption: hash all bytes after the stored MAC). -verification: 114/114 tests pass. Awaiting human confirmation from Intune upload retry. -files_changed: - - imptune/generators/intunewin_builder.py (HMAC scope fix) - - tests/test_intunewin.py (test updated for corrected HMAC scope) diff --git a/.planning/phases/09-ux-tech-debt-closure/09-VERIFICATION.md b/.planning/phases/09-ux-tech-debt-closure/09-VERIFICATION.md index 70c6ac3..9a207eb 100644 --- a/.planning/phases/09-ux-tech-debt-closure/09-VERIFICATION.md +++ b/.planning/phases/09-ux-tech-debt-closure/09-VERIFICATION.md @@ -3,6 +3,12 @@ phase: 09-ux-tech-debt-closure verified: 2026-04-13T00:00:00Z status: human_needed score: 11/11 must-haves verified +re_verification: + previous_status: human_needed + previous_score: 11/11 + gaps_closed: [] + gaps_remaining: [] + regressions: [] human_verification: - test: "Start app, open /printers, upload a real driver ZIP via the inline Upload Driver button — do NOT press F5 after upload" expected: "The driver dropdown refreshes automatically (HTMX OOB swap) and the newly uploaded driver is selected in the list without any page reload" @@ -19,8 +25,8 @@ human_verification: **Phase Goal:** The three carried-over UX defects are fixed and live-verified in a real browser so the rolled-out build is the polished one. **Verified:** 2026-04-13 -**Status:** human_needed — all automated checks VERIFIED; 3 items require live browser confirmation per ROADMAP.md success criteria -**Re-verification:** No — initial verification +**Status:** human_needed — all automated checks VERIFIED (11/11); 3 items require live browser confirmation per ROADMAP.md success criteria +**Re-verification:** Yes — after initial verification (previous status: human_needed, previous score: 11/11); no regressions found, no gaps closed (none existed) --- @@ -30,17 +36,17 @@ human_verification: | # | Truth | Status | Evidence | |---|-------|--------|----------| -| 1 | POST /drivers/upload never returns HTTP 500 for a valid driver ZIP | VERIFIED | `test_upload_500_regression` (parametrized: plain UTF-8 + BOM UTF-16 LE) in `tests/test_driver_upload.py` | -| 2 | Uploading a driver from the printer form refreshes the driver select via HTMX OOB swap without a page reload | VERIFIED | `test_upload_returns_oob_when_called_from_form` + `test_upload_oob_autoselects_new_driver` in `tests/test_driver_upload.py` | -| 3 | The newly uploaded driver is auto-selected in the refreshed select | VERIFIED | `test_upload_oob_autoselects_new_driver` — asserts `