psadt-deploy-skill 0.27.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +39 -546
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -425,549 +425,42 @@ pre-flight fails on non-ASCII without a BOM), and anything that lands in a packa
425
425
 
426
426
  ## Changelog
427
427
 
428
- Recent releases below; the complete history is in **[CHANGELOG.md](CHANGELOG.md)** (nothing is ever removed
429
- from either).
430
-
431
- ### 0.27.1 - 10.09.2026
432
- - **Fixed: das Dossier erfand Fakten über das Paket und druckte sie als Aussage.** Beim Paketieren zweier
433
- echter Apps zum Test von 0.27.0 aufgefallen. Ohne `-Metadata` behauptete der Report `_Beschreibung folgt._`
434
- als Company-Portal-Text — der wird unverändert nach Intune übernommen und liest sich wie ein fertiges Feld —
435
- sowie `Start-ADTMsiProcess` und „Nutzerdaten bleiben erhalten" als Hook-Inhalt, für welches Paket auch immer
436
- gerade berichtet wurde. Ein WinMerge-Paket, das ausschließlich `Start-ADTProcess` mit Inno-Switches aufruft,
437
- wurde so viermal mit einem Cmdlet beschrieben, das es nie verwendet. Nichts kennzeichnete das als Vermutung.
438
- - **Hooks und Cmdlet-Liste werden jetzt per AST aus dem echten Launcher gelesen**, je `*-ADTDeployment`-Funktion.
439
- Kein Launcher vorhanden: „nicht ermittelbar" statt einer plausiblen Liste. Ein explizites `-Metadata` gewinnt
440
- weiterhin.
441
- - **Eine fehlende Beschreibung warnt und wird sichtbar markiert — und wird bei `decisions.upload = true`
442
- verweigert**, in derselben Form wie das bestehende SYSTEM-Test-Gate. `-AllowMissingDescription` ist der
443
- bewusste, sichtbare Ausweg.
444
- - **Der Header-Status wird aus den Belegen abgeleitet** statt auf „Upload-bereit · getestet" zu defaulten.
445
- - **SKILL.md Phase 8** sagt jetzt, dass die Beschreibung Pflicht ist; vorher stand dort nur, wie sie zu
446
- formatieren wäre. Suite 474 → 484.
447
- ### 0.27.0 - 10.09.2026
448
- - **Fixed: after auto-compaction, half of SKILL.md was gone.** Claude Code re-attaches only the **first
449
- 5000 tokens** of an invoked skill after a summary. SKILL.md was ~10 10900, so the cut fell at line 198 — the
450
- middle of Phase 2. In exactly the sessions long enough to compact, the skill lost Phases 3–12, the whole
451
- troubleshooting table, every anti-pattern and the reference map. The fix is **ordering, not size**: the
452
- operating mode, the four gates, the conventions and Phases 0–6 now sit ahead of the cut, and Phase 6 ends
453
- at byte 17 457 of a 17 500-byte budget — with a test that fails if it ever crosses back.
454
- - **Fixed: `--ref <tag>` returned HTTP 404 on the tarball route.** The installer built
455
- `tar.gz/refs/heads/<ref>`, and `refs/heads` only resolves *branches* — so pinning worked with git and failed
456
- on exactly the machines without it, which are the ones most likely to need a pinned release.
457
- `Update-PsadtSkill.ps1` had the same latent bug in its archive path.
458
- - **Fixed: a failed install exited 127 instead of 1**, with a libuv assertion after the error message. A
459
- mistyped `--ref` printed a correct explanation and then looked like a crash.
460
- - **Changed: the default install is the newest release tag, not `main`.** This skill registers an Entra app
461
- with admin consent and writes to a tenant; installing whatever last landed on a branch is not a defensible
462
- default for that. `--ref main` and `--ref v0.27.0` both remain. The update check now distinguishes a
463
- release-pinned installation (counts *releases* behind) from one following a branch.
464
- - **Changed: the 2942-line deployment guide is now nineteen files**, one per domain, with
465
- `references/README.md` as the map. Section numbering is unchanged, so every "App. L.1" and "Phase 6.2"
466
- still resolves.
467
- - **Changed (behaviour): Phase 11 no longer re-runs Phase 6.** Phase 6 answers *does the package work* (the
468
- gate, in a throwaway Sandbox); Phase 11 answers *does the delivery work* (Intune test group, real device,
469
- `AppWorkload.log`). A package that passes 6 and fails 11 now tells you something.
470
- - **Changed: `"update skill"` is gone from the description** — un-namespaced, it made this skill answer for
471
- every other updatable skill on the machine. `"psadt update"` stays.
472
- - **Added `SECURITY.md`** — the risk surface next to the control that covers each part of it, each naming the
473
- file and the test that implement it.
474
- - **Added: researched content is data, never instructions** (`references/research-trust.md`) — Phase 2
475
- researches on the open web and the result runs as SYSTEM. The install4j case generalised.
476
- - **Added three guards and CI**: rule anchors proving no binding rule was lost in the move, a two-directional
477
- doc cross-reference check that also reads `scripts/`, a context-budget test, and the suite on a clean
478
- Windows runner for every push. Suite 441 → 474.
479
- - **Added `evals/`** — 20 trigger and behaviour cases. Not yet run: `claude plugin eval` is in early access.
480
- ### 0.26.7 - 08.09.2026
481
- - **Fixed: the control plane did not know MSIX exists.** L.8/L.9 landed in 0.26.5, but Gate 1's package-type list
482
- never mentioned MSIX — so a `.msix` fell through to "native installer (default)", the one route that is wrong for
483
- it, and nothing pointed at the new appendix. Gate 1 now carries **MSIX/AppX** with the decision attached: Intune
484
- takes it **natively as a line-of-business app**, so the default is to use that and *not* build a PSADT package;
485
- the wrapper is the exception, and L.8 is read first when it is used.
486
- - **Added `tests/SKILL.Tests.ps1`**, a coherence guard: every appendix SKILL.md points at must exist, and the Gate 1
487
- decision must actually route MSIX. Research that the router cannot reach is dead weight — now a test failure.
488
- ### 0.26.6 - 08.09.2026
489
- - **Fixed: the sandbox host timeout force-killed the viewer, then told you to close the window.** A timed-out run
490
- was torn down like a finished one. That **orphans `vmmemWindowsSandbox`** — the Hyper-V compute service owns it,
491
- so the host cannot kill it, and it keeps the work folder locked until the next run throws on it — and it removes
492
- the only window through which the guest could still be shut down cleanly. The script's own comments name this
493
- mechanism as the reason the *guest* must shut itself down; the timeout path did it anyway. A timeout now touches
494
- nothing and explains what to close and which knob to raise.
495
- - **Fixed: the warning claimed 60 seconds while the code waited 180** — a literal duplicating a parameter default.
496
- One declared value now feeds both.
497
- - **Appendix G finding 6 corrected**: "no recovery path" is now scoped to a run killed from *outside* the script.
498
- ### 0.26.5 - 08.09.2026
499
- - **L.8, new: MSIX/AppX**, verified against Microsoft Learn and against the live cmdlets. MSIX is a two-step
500
- model — machine-wide **staging**, then **per-user registration at logon** — and three traps follow from it:
501
- **`Add-AppxPackage` as SYSTEM registers the app for the SYSTEM account and reports success** (nobody can launch
502
- it), **`Get-AppxPackage` is the wrong detection cmdlet** (finds nothing right after provisioning, so Intune
503
- reinstalls forever), and **de-provisioning does not remove the app from existing users** — a complete uninstall
504
- needs `Remove-AppxProvisionedPackage` *and* `Remove-AppxPackage -AllUsers`. Plus signing: the certificate
505
- Subject must equal the manifest Publisher (so a vendor MSIX cannot just be re-signed), self-signed certs belong
506
- in `LocalMachine\TrustedPeople`, and missing timestamping is what makes an expired certificate break installs.
507
- And the decision that comes first: **usually don't wrap MSIX in PSADT** — Intune takes it natively.
508
- - **L.9, new: App-V is not end of life.** The client and sequencer are **no longer deprecated** (fixed extended
509
- support, no end date, no extra cost); only the **server components** end in **April 2026**. Also: `-Global` is
510
- the device-context switch, and a package in use goes *pending* — a global task applies only after a restart.
511
- - **The MSIX row in L.2 was misleading and is rewritten**; L.2 gains an App-V row.
512
- ### 0.26.4 - 08.09.2026
513
- - **L.7, new: Inno Setup and NSIS**, verified against vendor documentation. `/VERYSILENT` **reboots the
514
- machine by itself** when a restart is needed — so `/NORESTART` is mandatory, and `/RESTARTEXITCODE=3010`
515
- turns the fact into the code Intune already understands. And `Uninstall.exe /S` in NSIS **returns before
516
- the uninstall has finished** (it copies itself to temp and detaches), so any verification afterwards races
517
- it — `_?=<installdir>` makes it synchronous, must be last, must not be quoted.
518
- - **Advanced Installer wired into L.1 and L.2**, not just the new L.6 — with a measured fingerprint
519
- (`AI_*` custom actions, `aicustact.dll`, `OLDPRODUCTS;AI_NEWERPRODUCTFOUND`).
520
- ### 0.26.3 - 08.09.2026
521
- - **Appendix L gains three sections**, researched against vendor documentation instead of written from
522
- memory: **MSP patches** (the full `/p` matrix, why `/i` and `/p` never combine, and that exit 1642 means
523
- *either* "not installed" *or* "wrong patch revision" — so it must not be treated as a blanket success),
524
- **WiX Burn bundles** (BundleProviderKey rather than a ProductCode for detection; `/layout` is a Burn
525
- action but the bootstrapper decides whether it can be narrowed — the ADK refuses, so an offline layout is
526
- always the whole kit), and **Advanced Installer projects** (the `.aip` CLI, a fresh ProductCode per build
527
- changing the package identity every time, and the undocumented behaviour that relative paths resolve
528
- against the .aip's own location — moving a project between drives breaks it silently).
529
- ### 0.26.2 - 08.09.2026
530
- - **Fixed: a WHQL driver pack turned the pre-flight red.** `Get-DriverSignatureInfo.ps1` did not strip the
531
- inline comment that WHQL packs write after `CatalogFile=` (`; for WHQL certified`), so the catalog never
532
- resolved and validly signed drivers classified as `Unsigned` — 6 of 70 INF in a Dell WinPE set, all of
533
- whose catalogs were Authenticode-valid and Microsoft-signed. An unquoted `;` now ends the value.
534
- - **Fixed: `New-MsiPackage.ps1` was the sixth script with the `-File` binder trap.** `-ProcessesToClose
535
- 'a','b'` arrived as one element, so the scaffold closed no processes at all — silently, while reporting
536
- success. 0.25.1 had fixed the other five and missed the generator.
537
- - **Documented: seven findings from a three-package dependency chain** (Appendix G) and four new
538
- anti-patterns (Appendix B), including `msiexec /a` against bundled payload, the `-Include` trap, 8.3
539
- short names in path comparisons, and what a service's `%TEMP%` really is on Windows 11.
540
- ### 0.26.1 - 06.09.2026
541
- - **Fixed: a syntax error in the template silently killed the whole dossier script.** A `
542
- ` inside a JS
543
- string literal became a real newline; JavaScript then discards the entire `<script>` block, so the
544
- language toggle, all copy buttons and the condensing sticky header died at once — while all 419 tests
545
- stayed green, because they only assert on rendered HTML. `tests/Report-Template.Tests.ps1` now runs
546
- `node --check` on the script block plus a node-free quote-balance check, both proven against the
547
- reintroduced bug.
548
- - **Changed: every table copies by clicking the row**, replacing the hover icon. Key/value tables give the
549
- value, return codes the code, assignments the group name, the SYSTEM-test table the whole row. Clicks on
550
- a row's own controls (the foldable detection script) and text selections are left alone. The Markdown
551
- description keeps its own copy button.
552
-
553
- ### 0.26.0 - 06.09.2026
554
- - **Fixed: the dossier could name a return-code type Intune does not have.** Graph accepts exactly
555
- `success`, `softReboot`, `hardReboot`, `retry`, `failed` — there is no `ignored`, yet "Ignored" reached a
556
- real dossier because the report rendered a caller-supplied table without validating anything (`Label` was
557
- free text, `Cls` was interpolated raw into a `class` attribute). `Label` and `Cls` are now derived from
558
- `Type` through a closed switch and cannot be supplied at all; an invalid type throws.
559
- - **Return codes are sorted and copyable.** Sorted by type in Appendix F.4 order, then numerically — the
560
- order the table is verified against and typed into the portal. Each code cell has a copy icon and the
561
- section a "copy table" button.
562
- - **New `Get-PsadtReturnCodes.ps1`: one source of truth for the dossier AND the upload.** Previously two
563
- independent literals that agreed by coincidence. `Invoke-IntuneWin32Upload.ps1` gained `-ReturnCodes`, so
564
- installer-specific codes finally reach the app instead of only the document, and a new manifest key
565
- `research.returnCodes` records them once for both.
566
- - 419 tests; six new regression guards, each verified to fail on its reintroduced bug.
567
-
568
- ### 0.25.2 - 06.09.2026
569
- - **Fixed: a sandbox run left an error dialog on the desktop.** The guest shuts itself down at the end, the
570
- RDP-style client loses its session and shows a connection-lost box. The host now disposes of the viewer as
571
- soon as the run reports in. The tempting fix — let the host kill the VM instead of shutting the guest down
572
- — was measured and is wrong: it orphans the `vmmemWindowsSandbox` worker, which then holds the work folder
573
- open for minutes and cannot be killed. Guest shuts down, host closes the window, and the wait is on the VM
574
- worker rather than on the client processes.
575
-
576
- ### 0.25.1 - 06.09.2026
577
- - **Fixed: array parameters broke under the `-File` binder.** `pwsh script.ps1 -Intents a,b` passes the
578
- whole string as ONE element, so `Invoke-IntuneAppAssignment.ps1` rejected a perfectly valid comma-separated
579
- intent list with a ValidateSet error naming a value nobody typed. It now splits the list itself and still
580
- rejects a real typo by name. `Invoke-PsadtSandboxTest.ps1 -Paths*` had the same trap with a worse outcome —
581
- only the first path was asserted and the run still reported GREEN. Six regression tests; suite at 382.
582
-
583
- ### 0.25.0 - 05.09.2026
584
- - **New: `Get-PsadtMsiFacts.ps1` reads an MSI in one pass** — identity, signature, SHA256, features with
585
- component counts, *decoded* upgrade flags, shortcuts, directories, file versions, registry rows and the
586
- Icon table. For an MSI this probe **is** the research: it is what reveals an auto-updater sitting in its
587
- own feature (so `ADDLOCAL` replaces post-install cleanup), a `DesktopFeature` you must not install, and
588
- `MigrateFeatures` on the upgrade row — the flag that means `ADDLOCAL` alone is not enough. Five
589
- regression guards, each verified to fail on its reintroduced bug.
590
- - **Fixed: the sandbox test left litter and put the evidence in the wrong place.** It kept one work folder
591
- per app under the config home forever, and `result.json` plus the PSADT logs lived there with the
592
- manifest pointing into a profile directory. Evidence now lands in
593
- `<outputRoot>\<Stem>\SandboxTest\` beside the dossier — not in the package folder, which is what
594
- IntuneWinAppUtil packs — the logs are recorded in `artifacts.logs[]`, and the scratch is removed.
595
- `-KeepWorkFolder` keeps it, and a failed run keeps it automatically.
596
- - **Fixed: the test suite wrote into the real config home.** It now redirects `PSADT_DEPLOY_HOME` to a temp
597
- directory, with a test that asserts it.
598
- - **Measured:** PuTTY 0.85 packaged with these lessons applied took **13 min 45 s end to end** — versus 75
599
- minutes for the Notepad++ run that produced them. The largest single factor: the sandbox test costs zero
600
- wall-clock time when started as soon as pre-flight goes GREEN, because packaging and the dossier do not
601
- depend on its verdict.
602
-
603
- ### 0.24.0 - 05.09.2026
604
- - **New: `Invoke-PsadtSandboxTest.ps1` runs the whole Phase 6 loop in a throwaway Windows Sandbox** —
605
- Install, detection, Uninstall, detection, Reinstall, Repair, final Uninstall, every action as
606
- `NT AUTHORITY\SYSTEM` via a scheduled task. **No elevation on the host, and the host is never touched**,
607
- so the SYSTEM test stops being the step that gets deferred to "a DEV VM later". Every action starts from
608
- a machine that has never seen the app, so a pass cannot be an artefact of the previous run. The verdict
609
- is keyed on the detection script; package-specific facts go in via `-PathsPresentAfterInstall` /
610
- `-PathsAbsentAfterInstall` / `-PathsAbsentAfterUninstall`. Verified end to end on Notepad++ 8.9.8 x64:
611
- GREEN, all seven steps, 5 min 58 s.
612
- - **Why it is a script and not a snippet:** a hand-rolled version hit three bugs that each destroyed a full
613
- VM run and each present as a timeout or a null-reference minutes after launch — `echo %ERRORLEVEL%>file`
614
- silently becoming the `0>` stdin redirection (empty file, never a number), file existence read as
615
- completion, and `[string]$null` still being `$null` in Windows PowerShell 5.1 (an empty detection output
616
- is the *normal* result, so the harness crashed because the package was clean). All three now have
617
- regression guards that were each verified to fail on the reintroduced bug.
618
- - Phase 6 in SKILL.md and the guide now lead with the sandbox route; the per-action route stays for apps a
619
- VM cannot host. New anti-patterns cover hand-rolled harnesses, debugging through a long job instead of a
620
- two-second local check, and "speeding up" the test by disabling Defender or dropping Uninstall/Repair.
621
-
622
- ### 0.23.1 - 04.09.2026
623
- - **Fixed: two generators were broken since 0.21.0.** `New-BrowserExtensionPackage.ps1` and
624
- `New-WindowsFeaturePackage.ps1` died at run time — the per-run `LogName` change had wedged a statement
625
- into the template `Replace(...)` chain, which PowerShell parses as a property assignment with an empty
626
- name. Valid syntax, so neither the parse check nor the source-inspecting tests could see it; it surfaced
627
- the first time the whole chain ran against a real package. A guard in all four generator test suites now
628
- fails on a broken chain, and the guard itself was verified against a broken and a good sample.
629
-
630
- ### 0.23.0 - 04.09.2026
631
- - **`npx psadt-deploy-skill`.** One line installs or updates the skill into `~/.claude/skills/psadt-deploy`
632
- and runs the setup doctor. Flags `--dir`, `--project`, `--ref`, `--no-setup`. Cloning to exactly the right
633
- path by hand was the first thing a new user could get wrong.
634
- - **Zero dependencies** (Node 18's `fetch` + the `tar.exe` Windows ships). Three acquisition routes:
635
- update an existing clone, else `git clone --depth 1`, else the branch tarball — because plenty of managed
636
- machines have no git. The npm package ships only `bin/`; the skill comes from GitHub at install time, so
637
- a new skill version needs no republish.
638
- - **The installer never writes the config itself** — it spawns `Set-PsadtConfig.ps1` and
639
- `Initialize-PsadtSkill.ps1 -Fix`, because a second config-home implementation in JavaScript would drift
640
- from `Get-PsadtConfig`. `Update-PsadtSkill.ps1` now tracks `package.json` and `bin/` so the archive
641
- update route stops dropping them. Suite 307 → 326 tests.
642
-
643
- ### 0.22.0 - 04.09.2026
644
- - **Third-party drivers.** New `scripts/Get-DriverSignatureInfo.ps1` classifies a driver folder before
645
- anything is built — Microsoft-signed / vendor-signed / unsigned — by checking the **catalog** signature
646
- rather than the `.sys` (a dual-signed `.sys` reports only its primary signature). New
647
- `scripts/New-DriverPackage.ps1` builds the package: pnputil staging per INF, uninstall that resolves
648
- `oemNN.inf` by original name instead of a remembered index, `Get-WindowsDriver` detection.
649
- - **The rule that saves the most time:** a vendor-signed *kernel* driver is RED, not a warning. With Secure
650
- Boot on, only Microsoft Dev-Portal-signed kernel drivers load — importing the signer certificate removes
651
- the "install device software?" prompt but does nothing for Code Integrity, so the driver installs and
652
- then never loads. Unsigned drivers are refused outright, with three honest options and no testsigning.
653
- - Pre-flight gained a `DriverTrust` check that fires for **any** package shipping an `.inf` (a vendor
654
- installer staging a driver is the case nobody declares), the dossier has a driver-trust row, and
655
- `New-IntuneTrustedCertPolicy.ps1` is now fully self-contained like the firewall script.
656
- - New guide **Appendix Q** with the decision tree, pnputil exit codes and the installer-bundled-driver
657
- case. Suite 251 → 307 tests.
658
-
659
- ### 0.21.0 - 04.09.2026
660
- - **Every package gets a manifest** (`psadt-package.json`): identity, gate decisions, research findings,
661
- every phase result and the produced artifacts, in one file per app. The generators write it, every phase
662
- script reads and updates it — so an app's version, name and test evidence stop living in someone's head.
663
- - **The `.intunewin` is named after the app.** New: `<outputRoot>\<Vendor>_<App>_<Version>_<Arch>\<same
664
- stem>.intunewin`, produced by the new `scripts/Invoke-PsadtPackage.ps1`. Until now every package came out
665
- as `Invoke-AppDeployToolkit.intunewin` — that name reached Intune, and concurrent uploads collided in one
666
- shared temp folder. The new script also verifies the archive before calling it a deliverable and refuses
667
- an output folder inside the package (which made the tool pack its own previous output).
668
- - **One PSADT log per run** instead of one ever-growing file:
669
- `<Vendor>_<App>_<Version>_<Arch>_<Install|Uninstall|Repair>_<timestamp>.log`. PSADT appends to a fixed
670
- name by default, so by the third attempt a failed install was unreadable.
671
- - **Pre-flight gained two checks:** a missing or incomplete manifest is RED, a pre-0.21 launcher without a
672
- per-run log name is a WARN. Report and upload both take their identity from the manifest.
673
- - Guide: the missing `Phase 6 / 9 / 10` sections now exist, and Appendix E is numbered by phase.
674
- Suite 173 → 251 tests.
675
-
676
- ### 0.20.0 - 04.09.2026
677
- - **Intune access is state, not a 403.** New `scripts/Test-PsadtIntuneAccess.ps1` answers *before* Phase 9
678
- whether the configured app can actually upload, assign groups or create policies — and for how long the
679
- credential lives. `TokenOk` and every capability are three-valued: verified / refused / **unknown**,
680
- because Graph tokens are opaque by contract and "we could not tell" is not "not permitted". An offline run
681
- never overwrites what was verified earlier.
682
- - **The scripts assert the role they need before their first write** instead of discovering it from a 403
683
- mid-upload. Group assignment requires *both* group roles and names the missing half.
684
- - **`New-PsadtEntraApp.ps1` is re-runnable.** It finds the app by the recorded `clientId`, **merges**
685
- requested permissions instead of replacing them (a run without `-IncludeConfigurationManagement` used to
686
- silently revoke that role), never prompts, persists what it learned, and only sets `uploadEnabled` once
687
- consent is really in place. Older client secrets are counted, never deleted.
688
- - **Auth failures say what to do:** expired/invalid client secret, unknown app or tenant, Conditional Access
689
- block — and an undecryptable DPAPI secret now explains that DPAPI is bound to the Windows user profile.
690
- - `references/app-registration.md` is now the single permission matrix (app roles → capabilities → how to
691
- grant), referenced from the guide instead of duplicated. Suite 128 → 173 tests.
692
-
693
- ### 0.19.0 - 04.09.2026
694
- - **Setup doctor: `scripts/Initialize-PsadtSkill.ps1`.** One idempotent script replaces the Phase 0 prose
695
- wizard and reports GREEN/YELLOW/RED over 13 prerequisite checks, each with a concrete fix hint. `-Fix`
696
- installs the modules, downloads the content-prep tool, fills the EN/DE + tool-path defaults and migrates an
697
- old setup; `-Set @{...}` persists your values first; `-Json` / `-JsonPath` for other tooling. `.Missing`
698
- lists only the four values a human has to supply (`paths.packageRoot`, `paths.outputRoot`, `author.person`,
699
- `author.company`) — never a key the doctor could fill itself.
700
- - **Config, secret and tools moved to a per-user config home** (`%LOCALAPPDATA%\psadt-deploy\`, override
701
- `$env:PSADT_DEPLOY_HOME`) instead of the skill folder, so a `git pull`, re-clone or re-install no longer
702
- takes the whole setup with it — and scripts started from an output folder still find their config.
703
- `Get-PsadtConfig.ps1` is the single resolver and now returns `.Home` / `.DefaultHome` / `.LegacyInUse`. An
704
- old config beside `scripts/` keeps working read-only until `-Fix` migrates it (originals renamed
705
- `*.migrated`, nothing deleted).
706
- - **`Set-PsadtConfig.ps1 -Remove`** deletes dotted keys, so switching credential type can clean up the
707
- stale one. **Fixed:** `New-PsadtEntraApp.ps1` reported `<skill>\config.json` even when the config lived
708
- elsewhere. Suite 120 → 128 tests.
709
-
710
-
711
- <details>
712
- <summary><strong>Earlier releases (0.18.1 and older)</strong></summary>
713
-
714
- ### 0.18.1 - 03.09.2026
715
- - **Upload: `-MaxRunTimeMinutes`.** `Invoke-IntuneWin32Upload.ps1` can now set
716
- `installExperience.maxRunTimeInMinutes` (1–1440); `0` (default) omits the field and keeps the service default
717
- of 60 min. Raise it for long-running installs (OS in-place upgrades, large suites) so the IME does not kill them.
718
-
719
- ### 0.18.0 - 01.07.2026
720
- - **HanseMerkur corporate design + editorial report redesign.** The dossier/report template is re-themed to the
721
- HanseMerkur CD (green brand family on a light mint canvas; Metric font stack with Segoe fallback and no
722
- web-font fetch → no CORS console errors on a local `file://` open) and relaid out as an "editorial
723
- data-report": flat hairline sections, auto-numbered headings (`01…13`), an at-a-glance KPI band under the hero
724
- (version · pre-flight · min OS · arch), and a wider 1600px layout. The detection script is folded behind a
725
- collapsed `<details>` (the rule summary stays visible). German report text now uses real umlauts. Fixed the
726
- sticky-header flicker (Chrome/Edge scroll-anchoring vs. the condensing hero → `overflow-anchor: none`,
727
- Playwright-verified) and removed the redundant hero status pill.
728
-
729
- ### 0.17.0 - 01.07.2026
730
- - **install4j fingerprint + behavioral silent-switch verification.** Appendix L.1 now recognises install4j
731
- (Java) installers (`com/install4j/runtime`, `exe4j`, `i4jparams.conf`, bundled `jre\`) and records that `/S`
732
- is NOT its switch (it hangs on the language dialog) — the unattended switch is `-q`, run elevated. New BINDING
733
- rule: a single string match is a hint, not proof; confirm the engine by its definitive fingerprint AND run the
734
- silent switch once (timeout+kill, expect exit 0, no dialog) before packaging. Appendix L.3 adds the
735
- trademark-sign gotcha (`Name(R)` breaks `-match 'Name'` → tolerant regex); Appendix B adds anti-patterns 13–15.
736
- (Driven by an Aperio install4j installer misidentified as NSIS, where `/S` hung on the language dialog.)
737
-
738
- ### 0.16.0 - 29.06.2026
739
- - **Dossier auto-sync convention (BINDING)** + report header layout fix. Any change to the package scripts
740
- (launcher, Extensions, detection, version/changelog, return codes, re-packaging) now requires regenerating
741
- `Intune-Dossier.html` in the same pass; a stale dossier is a defect. The `.pill-lg` status badge caps at 230px
742
- and wraps so a long status no longer overlaps the hero title.
743
-
744
- ### 0.15.2 - 15.06.2026
745
- - **Follow-up doc fix.** A contradiction sweep after 0.15.1 caught one more stale "Phase 7.5" in
746
- `New-PsadtReport.ps1` help (upload is Phase 9); corrected. No other live stale references remain.
747
-
748
- ### 0.15.1 - 15.06.2026
749
- - **Generator hardening from a self-review (correctness + security).** All three generators now single-quote-escape
750
- values embedded in `$adtSession` literals, so an apostrophe in the App name/vendor/author (e.g. "Bob's App",
751
- "L'Oreal") no longer produces an unparseable package; the MSI `-AdditionalArgumentList` / `ProcessesToClose`
752
- literals are escaped too (also closing a SYSTEM code-injection path). Detection exit-code drift fixed:
753
- `New-MsiPackage.ps1` + the WinGet example now `exit 0` for "not installed" (a non-zero exit reads as a detection
754
- error), and a new **pre-flight Detection check** WARNs on a non-zero exit in `Detect*.ps1`. The WSUS bypass in
755
- `New-WindowsFeaturePackage.ps1` now saves all prior state before writing and runs inside the `try/finally`, so a
756
- partial failure can't leave `UseWUServer=0` permanently. Added input guards (`$Name` path-traversal, `__TOKEN__`
757
- leak), MSI `-Author` config fallback + `-InstallerPath` validation. Stale refs fixed (SKILL.md "A-M"->"A-P",
758
- `New-PsadtEntraApp.ps1` "Phase 7.5"->"Phase 9").
759
-
760
- ### 0.15.0 - 15.06.2026
761
- - **Windows-feature packages (optional features + capabilities / FoD).** New `scripts/New-WindowsFeaturePackage.ps1`
762
- — one-call generator that enables Windows **Optional Features** (`Enable-WindowsOptionalFeature`: NetFx3,
763
- Hyper-V, WSL, TelnetClient, …) and **Capabilities / Features on Demand** (`Add-WindowsCapability`: RSAT.*,
764
- OpenSSH, …) from one typed list, multiple per package. Uninstall reverts (disable/remove); Repair re-enables
765
- (idempotent). Reboot surfaces **3010** via `$adtSession.SetExitCode(3010)` (`-NoRestart`); detection treats
766
- `EnablePending` as not-yet-done. Content comes from a bundled `-Source` (offline SxS) else Windows Update
767
- behind a **temporary** WSUS bypass (`RepairContentServerSource=2`, `UseWUServer=0`) whose exact prior state is
768
- restored. Guide **Appendix P**, SKILL.md Gate-1/anti-patterns/ref-lookup. Pre-flight GREEN; helper logic
769
- verified against an in-memory registry sim; ASCII-clean.
770
-
771
- ### 0.14.0 - 15.06.2026
772
- - **Browser-extension force-install packages (Edge / Chrome / Firefox).** New
773
- `scripts/New-BrowserExtensionPackage.ps1` — one-call generator for force-installing browser extensions via
774
- enterprise **policy registry keys** (policy-only, no installer, ESP-safe). Multiple extensions per package.
775
- Chromium helper computes the **next free `ExtensionInstallForcelist` index** (never hard-codes `1`), dedupes by
776
- ID and removes only its own entry (coexistence); Firefox merges into the single `ExtensionSettings` JSON written
777
- as **`REG_MULTI_SZ`** (single-line `REG_SZ` is silently ignored — Mozilla bug 1750233). Guide **Appendix O**,
778
- SKILL.md Gate-1/anti-patterns/ref-lookup. Pre-flight GREEN; helpers validated against a scratch registry hive;
779
- ASCII-clean.
780
-
781
- ### 0.13.1 - 15.06.2026
782
- - **Firewall policy body fixed against the live template (verified 201).** `New-IntuneFirewallPolicy.ps1`
783
- produced a body Graph rejected (400). Corrected via the **msgraph skill** (not guessed): the group id needs
784
- the `{firewallrulename}` token, the program path is the direct child `..._app_filepath`, action values are
785
- `_action_type_1`/`_0`, and a template-based policy requires `settingInstanceTemplateReference` per instance +
786
- `settingValueTemplateReference` per simple/choice value (profiles collection: instance ref only — a per-value
787
- ref is a duplicate). Confirmed by a live **201 Create**; tests assert the references. Mirrored into the
788
- MxManagementCenter Output deliverable.
789
-
790
- ### 0.13.0 - 15.06.2026
791
- - **Self-contained firewall deliverable (copy-to-client safe).** `scripts/New-IntuneFirewallPolicy.ps1` is now
792
- fully self-contained — no dot-sourcing of `_GraphCommon`/`_GraphInteractive`, no skill path; WAM sign-in +
793
- policy body builder + console helpers are embedded. It runs on a test client that does **not** have the skill
794
- installed (`-Interactive` WAM, or `-GraphToken`). Fixes the "Skill script not found … -SkillRoot" failure when
795
- the deliverable was copied to another machine. New **binding SKILL.md convention "Self-contained deliverables"**
796
- + a Pester test that enforces it (no dot-source / no skill path / embeds WAM). Test-first per writing-skills.
797
-
798
- ### 0.12.0 - 15.06.2026
799
- - **Interactive WAM sign-in for the Intune policy scripts.** `New-IntuneFirewallPolicy.ps1` and
800
- `New-IntuneTrustedCertPolicy.ps1` gain `-Interactive` (+ `-TenantId`): delegated sign-in via **WAM**
801
- (Windows Web Account Manager) when there is no app registration — **no device code**. The WAM machinery
802
- (`Initialize-MsalBroker` / `Get-WamToken` / new `Get-InteractiveGraphToken`) was extracted from
803
- `New-PsadtEntraApp.ps1` into a shared `scripts/_GraphInteractive.ps1` (one implementation, no copy-paste
804
- drift); the bootstrap now consumes it. The MxManagementCenter firewall deliverable became a thin wrapper
805
- over the generic script.
806
-
807
- ### 0.11.0 - 15.06.2026
808
- - **Intune firewall-rules policy + app config-management permission.** New `scripts/New-IntuneFirewallPolicy.ps1`
809
- (Endpoint Security "Windows Firewall Rules" policy, one program-scoped rule; dry-run / `-Execute` / manual
810
- portal fallback) + Pester test. Suppresses the first-run Windows Firewall prompt for apps that listen inbound.
811
- New `New-PsadtEntraApp.ps1 -IncludeConfigurationManagement` consents `DeviceManagementConfiguration.ReadWrite.All`
812
- (needed by the firewall and trusted-cert policies for `-Execute`).
813
-
814
- ### 0.10.0 - 15.06.2026
815
- - **Certificate store deployment (driver-trust / TrustedPublisher).** New `scripts/New-IntuneTrustedCertPolicy.ps1` —
816
- a Custom OMA-URI profile that places a certificate into a Windows machine store via the
817
- `RootCATrustedCertificates` CSP (the policy-based way to suppress the Windows "install device software?"
818
- driver-trust prompt). Guide Appendix N, dossier "Treiber-Zertifikat" row, SKILL.md convention + Pester test.
819
-
820
- ### 0.9.2 - 12.06.2026
821
- - **Reconciled a diverged install copy back into the repo.** Fixed `Invoke-PsadtSystemTest.ps1` crashing at
822
- param binding under the WinPS 5.1 re-exec (`$SkillRoot` default is now fail-safe, so the SYSTEM Install/
823
- Uninstall gate works on a pwsh-7 host). Added per-field copy buttons + a `file://`-safe clipboard to the
824
- HTML dossier (`Report-Template.html`, token set unchanged). Added `scripts/New-MsiPackage.ps1` (reusable
825
- MSI package generator, `$PSScriptRoot`-relative, ASCII-only) + its Pester test.
826
-
827
- ### 0.9.1 - 12.06.2026
828
- - **Applicability/portability drift cleanup** (docs + instructions; no script logic changed). Removed the
829
- phantom `test.maxIterations` / `test.endState` config keys from SKILL.md Phase 6 (the cap is a hard count of
830
- 5 the orchestrator owns); fixed stale "Phase 7.5" -> **Phase 9** in SKILL.md + `app-registration.md`; fixed
831
- the README project tree (report **Phase 8**, added the 3 missing scripts `Invoke-PsadtPreflight`,
832
- `Invoke-IntuneAppAssignment`, `_GraphCommon`).
833
- - **`superpowers` downgraded from hard `REQUIRED` to optional/preferred.** The Researcher/Reviewer roles now
834
- prefer `superpowers:*` if installed and otherwise fall back to the native Agent tool / `/code-review`; the
835
- Requirements list documents it as an optional (recommended) enhancement. The workflow no longer depends on it.
836
-
837
- ### 0.9.0 - 11.06.2026
838
- - **Audit cleanup** (quality / content / applicability+compatibility / tests). Highlights: shared
839
- `scripts/_GraphCommon.ps1` (de-duplicates the 3 Graph scripts) + **28 new Pester tests** for the previously
840
- untested upload/assignment/Entra-app/token scripts (suite now 74); fixed an `Invoke-WithRetry` precedence
841
- bug (retried every error 6x) and PS7-fragile throttling reads; the HTML report no longer shows synthetic
842
- "passed" rows when no pre-flight/SYSTEM-test results are supplied (neutral "not run") and derives the PSADT
843
- version from the installed module; GUID `ValidatePattern` on the MSI codes.
844
- - **Phase numbering unified** across SKILL.md + guide into one integer scheme **0-12**; every Phase/Appendix
845
- cross-reference re-verified. Anti-pattern list trimmed; SYSTEM-test prerequisites, `/beta` drift caveat,
846
- rollback step, logging convention, and config help added. Guide `$adtSession` template `1.0.0`->`0.1` +
847
- author-from-config; intro appendix index -> A-M.
848
-
849
- ### 0.8.1 - 11.06.2026
850
- - **Docs consistency.** Fixed a stale cross-reference in SKILL.md (the guide range said **Appendix A-J** but
851
- the guide now runs through **M** — K/L were added in 0.7.0 and M in 0.8.0 without updating it). Restored the
852
- **0.5.3** entry that was missing from this README changelog mirror (it was present in `CHANGELOG.md`).
853
-
854
- ### 0.8.0 - 11.06.2026
855
- - **Opt-in Entra group assignment, wired end-to-end.** New Phase 10 + `Invoke-IntuneAppAssignment.ps1`:
856
- create/reuse Entra security groups by a configured naming scheme (`intune.groups`) and assign the uploaded
857
- app (Required / Available / Uninstall). Read-only dry-run → confirm → execute; idempotent; never deletes a
858
- group or another app's assignment; ambiguous/duplicate names skipped. Least-privilege roles
859
- (`Group.Create` + `GroupMember.Read.All`) via `New-PsadtEntraApp.ps1 -IncludeGroupManagement`. Full
860
- reference in **guide Appendix M** (config schema, naming tokens, version-independent default vs `%version%`
861
- opt-in, permission model).
862
- - **Upload min-OS fix.** `-MinWindowsRelease` is now a `ValidateSet` of backend-accepted release IDs
863
- (`1607..2004`) — `21H2`/`22H2` are server-rejected and used to kill the upload mid-flight with a Graph
864
- `BadRequest`. Fails fast at param binding instead; set a higher minimum in the portal. Guide **H.11**.
865
-
866
- ### 0.7.5 - 10.06.2026
867
- - **Honest exit codes + detection (correctness fix).** Removed the dangerous "always `exit 0`" guidance from
868
- guide Appendix K — a blanket `exit 0` (or a detection tag written in a `finally`) reports GREEN on failure.
869
- New **K.7**: the exit code reflects whether the fix could RUN (couldn't-run -> non-zero; the 64-bit relaunch
870
- propagates the child's exit code), detection reflects the real END-STATE (tag only on success), with a
871
- per-package decision table; "never block enrollment" is now an explicit ESP-assignment + return-code-mapping
872
- choice, not a masked exit code. SKILL.md anti-pattern added.
873
-
874
- ### 0.7.0 - 10.06.2026
875
- - **Value-adding extensions.** New `scripts/Invoke-PsadtPreflight.ps1` turns the Phase-5 Reviewer gate into one
876
- deterministic `GREEN/RED` tool (encoding/parse/v3-scan/top-level/structure/GUID-to-`-FilePath`), with a Pester
877
- suite. New guide **Appendix K** (script-only remediation / fix packages, ESP-safe — the debloat/Cisco pattern)
878
- and **Appendix L** (installer technologies + silent switches, a lookup consulted before web research). Expanded
879
- error-code catalogue (MSI 1603/1605/1619/1638/1639…, PSADT 60001/60008 + ranges) in guide Appendix A and the
880
- SKILL.md troubleshooting table. SKILL.md rewired to point at the pre-flight script and the new appendices.
881
-
882
- ### 0.6.2 - 10.06.2026
883
- - **Audit & harden.** Agent-based audit + source-level verification (discarded ~8 false positives). Fixes: the
884
- guide's broken `New-ADTTemplate` "Extended scaffold" (passing app metadata params that v4.1.x rejects);
885
- the Graph uploader leaving its extracted work dir (with the AES keys in `Detection.xml`) in `%TEMP%` (now
886
- `try/finally` cleanup); the report `Notes` default `&middot;` double-escape; the fallback initials-SVG logo
887
- now XML-escapes + base64-encodes (no markup injection, with a regression test). Robustness: `Invoke-Graph`
888
- 429/5xx retry with `Retry-After`; malformed-`config.json` safety in four scripts; WinGet 2-byte header read;
889
- symmetric temp cleanup in self-update. Graph request shapes left untouched.
890
-
891
- ### 0.6.1 - 10.06.2026
892
- - **Report header flicker fixed** (`references/Report-Template.html`): at widths where the content height met
893
- the viewport edge, the vertical scrollbar toggled on/off and the header's `vw`-based `clamp()` padding and
894
- `h1` font-size reflowed on every toggle — a wild flicker loop. Reserving the scrollbar gutter
895
- (`html { overflow-y: scroll; scrollbar-gutter: stable; }`) keeps the width constant and breaks the loop.
896
-
897
- ### 0.6.0 - 10.06.2026
898
- - **SKILL.md slimmed to a control plane** (733 → 244 lines, ~67% fewer tokens) via progressive disclosure:
899
- the long inline code moved into the reference guide (new **Appendix I** WinGet + **Appendix J** app-logo),
900
- loaded on demand. Intake restructured into **4 decision gates** (researchable facts become stated
901
- assumptions, not questions); explicit **sub-agent roles** (Researcher×3 / Builder / Reviewer) with GREEN
902
- handoff gates; a single **blockade protocol** for errors. No binding rule or behaviour dropped.
903
-
904
- ### 0.5.3 - 09.06.2026
905
- - **Guide code-fences are now English/ASCII.** Anglicized every German comment, string literal and placeholder
906
- living **inside** PowerShell/text code fences in the guide (and the inline-code placeholders in the Appendix
907
- F.1 table) — snippets get copied verbatim into deployment scripts, where the binding rule is English + 7-bit
908
- ASCII. German explanatory **prose** and the **F.2 Company-Portal dossier template** deliberately stay German
909
- (legitimate `language.dossier` end-user text). No script/tooling code changed.
910
-
911
- ### 0.5.2 - 08.06.2026
912
- - **HTML package report is now always generated** (upload or not) by `scripts/New-PsadtReport.ps1` from the
913
- fixed template `references/Report-Template.html`. One self-contained, **bilingual (DE/EN toggle)** document
914
- combining the Intune dossier + a technical package report; Fluent-2 styled, sticky shrink header, logo
915
- embedded as a data URI, description preview rendered from its Markdown source. New Pester test
916
- `tests/New-PsadtReport.Tests.ps1`.
917
-
918
- ### 0.5.1 - 06.06.2026
919
- - Self-update now decides by **commit** (git `HEAD` vs `origin/main`, or the GitHub commits-API sha vs a
920
- recorded `tooling.skillCommit`) instead of the CHANGELOG version — no more CDN lag / circular version reads.
921
- - README project-structure tree compacted so it renders without horizontal scroll.
922
-
923
- ### 0.5.0 - 06.06.2026
924
- - **Skill self-update** — `scripts/Update-PsadtSkill.ps1` checks GitHub for a newer version, shows what's new,
925
- and updates in place on confirmation (`git pull` for a clone, else branch-zip overwrite of tracked files
926
- only; `config.json` / `secret.dpapi` / `tools/` preserved). Triggers: *"update skill"*, *"/update-skill"*,
927
- *"psadt update"*.
928
-
929
- ### 0.4.0 - 06.06.2026
930
- - **WinGet packaging support** (strictly opt-in, never the default) + **certificate-based auth** for the Phase 9
931
- upload (no secret at rest) + MSI icon-table logo fallback + device-code first-poll fix. Contributed by
932
- **@joakim-i** (PR #4), reviewed and hardened before merge. See [CHANGELOG.md](CHANGELOG.md).
933
-
934
- ### 0.3.2 - 06.06.2026
935
- - **Test-before-upload is now a binding gate:** Install + Uninstall must pass the Phase 6 SYSTEM test before
936
- any Phase 9 upload. If it can't be run (no elevation / VM), stop before upload and hand back the command.
937
-
938
- ### 0.3.1 - 06.06.2026
939
- - `Invoke-IntuneWin32Upload.ps1` gains **`-DetectionScriptPath`** (PowerShell-script detection rule) for
940
- EXE / non-MSI installers without a ProductCode (e.g. Vivaldi). Verified live by uploading Vivaldi 8.0.4033.44.
941
- - Lesson: a *detection* script rule accepts only `ruleType,enforceSignatureCheck,runAs32Bit,scriptContent`
942
- (guide Appendix H.2).
943
-
944
- ### 0.3.0 - 06.06.2026
945
- - **Direct upload** (`scripts/Invoke-IntuneWin32Upload.ps1`, Phase 9): self-contained raw-Graph
946
- `win32LobApp` upload (parse `.intunewin` → token → probe → idempotency → create/update → content → SAS
947
- block-blob upload via HttpClient → commit → activate → categories → supersedence). Read-only dry-run by
948
- default; `-Execute` to write.
949
- - **WAM Entra-app bootstrap** (`scripts/New-PsadtEntraApp.ps1`): interactive Windows-broker sign-in (device
950
- code fallback), creates the app + admin consent + secret, DPAPI-stored.
951
- - **App-only token helper** (`scripts/Get-GraphToken.ps1`).
952
- - **Coexistence-safe versioning:** never deletes an older version; new versions coexist; optional
953
- supersedence wiring. **Logo guard:** refuses the PSADT default `AppIcon.png`. Fills the full
954
- App-information tab; never auto-assigns category/notes/groups.
955
- - Fixed the Repair `-FilePath`→`-ProductCode` example; reference guide gains **Appendix H**.
956
-
957
- ### 0.2.0 - 05.06.2026
958
- - **Automated SYSTEM test loop** (`scripts/Invoke-PsadtSystemTest.ps1`, Phase 6): install → uninstall →
959
- reinstall the package as the SYSTEM account via `Invoke-CommandAs`, with agent-driven auto-fix until
960
- green or a max-iteration cap. Opt-in; elevated session required.
961
- - Phase 8 now prefers `Invoke-CommandAs -AsSystem` for SYSTEM-context testing (PsExec kept as a fallback).
962
-
963
- ### 0.1.0 - 04.06.2026
964
- - Initial release: guided PSADT v4 → Intune Win32 lifecycle (intake, autonomous research, scaffolding, all
965
- three deployment types, pre-flight checks, packaging, dossier + logo, guided testing, troubleshooting).
966
- - First-run setup writing a machine-local `config.json` (paths, language, author).
967
- - Self-healing prerequisites: PSAppDeployToolkit module (PSGallery) and `IntuneWinAppUtil.exe`
968
- (auto-download + version check).
969
- - HTML dossier document with a Markdown app-description block (the Intune description field is
970
- Markdown-only).
971
- - English skill + reference guide; MIT licensed.
972
-
973
- </details>
428
+ The two most recent releases are below. **[CHANGELOG.md](CHANGELOG.md)** carries the complete history,
429
+ every release since 0.1.0, and nothing is ever removed from it - this section is a window onto it, not a
430
+ second copy to keep in sync.
431
+
432
+ ### 0.28.0 - 2026-09-11
433
+ - **Fixed: a space in the `.wsb` path silently disabled every custom mapped folder.** `Start-Process
434
+ -ArgumentList` does not quote its elements, so Windows Sandbox booted with the built-in shares only -
435
+ no error, no warning. A username with a space in it is enough, and it reads exactly like an upstream
436
+ Sandbox bug.
437
+ - **Fixed: `<LogonCommand>` never ran** on the affected Sandbox app version
438
+ ([#125](https://github.com/microsoft/Windows-Sandbox/issues/125)). The work folder is now mapped onto
439
+ the guest's Startup folder and a trigger `.cmd` starts the runner, which lives one level down because
440
+ Startup auto-executes only `.exe/.bat/.cmd/.lnk/.vbs`.
441
+ - **Fixed: a transient read reported a real result as empty** - a detection step captured `stdout: ""`
442
+ while the same file, re-read at the end of the run, held the right answer. The read now retries.
443
+ - **Changed: `schtasks` failures are checked** instead of surfacing as a 900-second timeout, and the
444
+ runner verifies it is elevated before the first action - a Startup item does not carry the full admin
445
+ token by construction the way `<LogonCommand>` did.
446
+ - **Added: NSIS MultiUser needs an explicit `/allusers` or `/currentuser`** (App. L.7). A bare `/S`
447
+ exits in under a second with no output and nothing installed. Now a Gate 2 decision.
448
+ - **Added: an unbundled runtime prerequisite is researched in Phase 2 and decided at Gate 1**, and a
449
+ manual interactive test is offered after a GREEN Phase 6 - the loop proves the package works, never
450
+ that the app does. Suite 484 -> 493.
451
+ ### 0.27.1 - 2026-09-10
452
+ - **Fixed: the dossier invented facts about the package and printed them as statements.** Found while
453
+ packaging two real apps to test 0.27.0. Without `-Metadata`, the report claimed `_Beschreibung folgt._`
454
+ as the Company-Portal text — which is copied into Intune verbatim and reads like a finished field —
455
+ plus `Start-ADTMsiProcess` and "Nutzerdaten bleiben erhalten" as the hook contents, for whatever package
456
+ happened to be reported on. A WinMerge package that only ever calls `Start-ADTProcess` with Inno Setup
457
+ switches was described four times as using a cmdlet it never touches. Nothing marked any of it as a guess.
458
+ - **Hooks and the cmdlet list are now read out of the real launcher by AST**, per `*-ADTDeployment`
459
+ function. No launcher to read means `nicht ermittelbar / not derivable` instead of a plausible list. An
460
+ explicit `-Metadata` value still wins.
461
+ - **A missing description warns and is marked visibly — and is refused outright when
462
+ `decisions.upload = true`**, in the same shape as the existing SYSTEM-test gate.
463
+ `-AllowMissingDescription` is the deliberate, visible opt-out.
464
+ - **The header status is derived from the evidence** instead of defaulting to `Upload-bereit - getestet`.
465
+ - **SKILL.md Phase 8** now says the description is mandatory; before, it only said how to format it.
466
+ Suite 474 → 484.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "psadt-deploy-skill",
3
- "version": "0.27.1",
3
+ "version": "0.28.0",
4
4
  "description": "Installer for the psadt-deploy Claude Code skill: build, test and deploy PSADT v4.x Intune Win32 packages.",
5
5
  "keywords": [
6
6
  "psadt",