nordlo.windows.gporeport 0.1.0-rc.3
No packages depend on nordlo.windows.gporeport.
Added
- Unified Settings tab in the HTML report, replacing the three separate settings tabs (All
settings / Settings by GPO / Other areas): a grain toggle (Values | Settings) plus a checkbox
"Include settings not configured in any GPO", backed by three .sql files (
all-settings.sql, reused unchanged, plus newsettings-per-gpo.sqlandsettings-all-known.sql). The checked state additionally lists ADMX policies and Security Options catalog entries nothing configures anywhere in the scan, with the catalog-boundary caveat stated insettings-all-known.sql's header: only Administrative Templates and Security Options have a full catalog to check absence against. - ADMX files hub tab, replacing the
admx-to-gpo/gpo-to-admxtabs: one row per template (admx-files.sql), expanding in place to show referencing GPOs, configured policies and<using>dependencies in both directions. - Findings tab grouping the five finding views (Conflicts, Unreferenced ADMX, Unlinked GPOs, Orphaned settings, Links with no effect) as chips under one landing statement that findings are supporting evidence, never a removal list or recommendation. Each chip keeps its own query, prose, badge and row behaviour unchanged.
- Overview stat cards are now clickable, navigating to the tab (and, where one exists, Findings chip) each number is about.
- Report renamed from "ADMX Reference Report" to "GPO Report" everywhere it is user-visible: the
masthead
<h1>, the browser tab title (both the initial<title>Export-NordloGpoReportToHtmlwrites and the onerenderMastheadsets once the snapshot has loaded), and the-Titleparameter's default. No module function, file or CLI surface renamed — display text only. - Masthead layout: the domain/scan-date/DC subtext now sits inline with the title (baseline-aligned, wrapping onto its own line only on a narrow viewport) instead of stacked beneath it, so the header claims less vertical room.
- New bundled reports for the CLI:
settings-per-gpo,settings-all-known,admx-files(seeGet-NordloGpoReportView -Name).all-settings,gpo-settings,other-settings,admx-to-gpoandgpo-to-admxremain available under their existing names for scripts and old bookmarks (#all-settingsetc. redirect into the new tabs in the HTML report).
Changed
- HTML report tab bar reduced from eleven tabs to six:
Overview | GPOs | Settings | ADMX files | Findings | SQL. The three settings tabs folded into Settings' two grains;admx-to-gpo/gpo-to-admxfolded into ADMX files; the five finding tabs folded into Findings. Every folded view's query is unchanged and still runs identically viaGet-NordloGpoReportView. - Full ADMX catalog in every snapshot.
admx_policies,admx_policy_elementsandadmx_enum_itemspreviously described only policies some GPO configures (a size trade-off: ~600 configured policies vs. a reference store's 7,786). They now describe every policy defined by every ADMX in the Central Store, unconditionally —Invoke-NordloGpoReportScanno longer passesConfiguredPolicyIdtoResolve-AdmxDisplayName, which still accepts the parameter for callers that want a restricted catalogue. A row inadmx_policiesno longer implies the policy is configured anywhere — join againstgpo_settingsto find out. Existing consumers (all-settings.sql,conflicting-settings.sql,gpo-settings.sql, the JS detail-modal queries in35-detail.js/30-views.js) all join from the configured side (gpo_settings/gpo_setting_valuesoutward via LEFT JOIN), so none of them read row-existence inadmx_policiesas "configured" and none needed changes. No test assertedadmx_policiesrow counts equal configured counts. Existing snapshots need a fresh scan to pick up the full catalogue.
Added
Invoke-NordloGpoReportScan— read-only scan of a single Active Directory domain producing a self-contained SQLite snapshot of which ADMX files in the Central Store are referenced by which GPOs. Never modifies AD, SYSVOL, or any GPO.Get-NordloGpoReportView— runs one of six bundled SQL reports (unreferenced-admx,admx-to-gpo,gpo-to-admx,unlinked-gpos,orphaned-settings,dead-links) against a snapshot, or arbitrary SQL via-Query.Attribution from
registry.polmatched against an ADMX registry-target index, with exactkey|valueNamematching and a key-only fallback restricted to<list>-defined keys. Unmatched values are recorded as orphans rather than guessed at.Setting counts at ADMX-policy level (
setting_count) alongside raw value counts (value_count), so numbers agree with GPMC. Delete/disable markers count as references and are flagged viadisabled_policy_count/has_delete_markers.Central Store hygiene findings: inter-ADMX
<using>dependency graph, per-culture ADML coverage gaps, and registry-target collisions between templates (all claimants retained).GPO context from AD directly — links with order/enabled/enforced, per-hive enabled state, WMI filter definitions (raw WQL, never evaluated), and object counts under each distinct link target.
AD-vs-SYSVOL consistency check comparing
versionNumberagainstGPT.INI.Unreadable GPOs are recorded with
is_accessible = 0and mark the whole scan's visibility as incomplete, so an unreferenced-ADMX result cannot be misread as complete.tests/Parsing.Tests.ps1— ADMX/PReg parsing fixtures covering all four observed ADMX root namespaces, the<using>-vs-<target>attribution trap, marker handling, collision detection, and the guarded key-only fallback.Export-NordloGpoReportToHtml— renders a snapshot into one self-contained HTML file containing the whole SQLite database, a SQLite engine compiled to WebAssembly (vendored sql.js, MIT), and a browser interface over all six reports. Opens fromfile://with zero network requests and needs no PowerShell, PSSQLite or RSAT to read. Sortable and filterable grids, CSV export of the filtered view, an ad-hoc SQL console, and a button that hands back the embedded.sqlite.-Htmlswitch onInvoke-NordloGpoReportScan, which renders the report it just wrote and returns both paths.Each HTML tab's explanatory text is extracted from the leading comment block of the corresponding bundled
.sqlfile, and the browser executes that same SQL, so the caveats and the query cannot drift from whatGet-NordloGpoReportViewruns..gitattributes, marking*.wasmand*.sqlitebinary.Configured settings detail. The scan now stores every configured registry value (
gpo_setting_values, decoded per registry type) and one row per configured policy (gpo_settings) with a derived Enabled/Disabled state and astate_basiscolumn stating, in words, how the verdict was reached — some verdicts are inferences and the basis makes each row checkable. Display names, category paths, element labels and explain text are resolved from the ADML files intoadmx_policies/admx_policy_elements/admx_enum_items, restricted to configured policies to keep the snapshot small.Three new reports in both
Get-NordloGpoReportViewand the HTML:all-settings(search every configured value),gpo-settings(what does this GPO actually do, with state and reasoning), andconflicting-settings(the same value set by several GPOs, values side by side, explicitly not RSOP).HTML report: rows in the settings views open a detail modal showing every registry value the policy wrote, the state with its basis, where else the policy is configured, and the template's own explain text.
GPOs tab with rows that expand in place to show where the GPO is linked (with subtree object counts, NULL rendered as "unknown" rather than 0) and every setting it configures; those settings rows open the policy modal.
dead-linksandunlinked-gposrows drill through to it.Editor-to-registry mapping in the policy modal: each editor field with the registry value it writes, its allowed range or enum options, and the default the editor pre-fills — plus the policy's own Enabled/Disabled sentinel values, marked when the ADMX declared neither and the schema default was assumed. Written enum values are annotated with their meaning (
2 (High blocking level)). Editor defaults come from the ADML presentation and are stored inadmx_policy_elements.default_value;admx_policiesgainsenabled_value,disabled_valueandstate_values_implied.Conflicts modal listing every GPO writing a contested registry value side by side, with the value each writes, its state, hive status and active link count.
The GPOs tab filter searches inside each GPO — every registry key, value name and written value, every setting name, category and template, and every link path — so "print" or "lsass" finds the GPOs that touch those things, not just ones whose name matches. Built from one grouped query, cached for the session (~480 KB, sub-millisecond filtering for a 158-GPO domain), so it is unconditional rather than hidden behind a toggle. The row count says when matches came from content not visible in the row.
Everything outside Administrative Templates is now collected, always — security settings, Preferences, scripts, Windows Firewall, AppLocker, wireless/wired policies, certificate services. 4,522 settings across the reference domain. Read from
Get-GPOReport -ReportType Xml, which is unusable for ADMX attribution (no namespace or policy id) but is the best source for every other area and resolves display names that exist nowhere in SYSVOL. New tablesgpo_other_settings,gpo_other_setting_props,gpo_security_filtering,gpo_policy_areas; newother-settingsreport; new "Other areas" tab with a detail modal.GroupPolicy(RSAT/GPMC) is now required. It is checked by importing it rather than byGet-Module -ListAvailable, which does not list it under PowerShell 7 even when installed.A GPO configuring only non-ADMX areas no longer reads as empty:
gposcarriesother_areasandother_setting_count, the GPOs tab tints a zero ADMX count when other areas exist, anddead-linkssplits the oldno settings configuredintono settings at all(9 GPOs here) andno ADMX settings (has other policy areas)(34).Item-level targeting is recorded per item as
has_filtersand surfaced prominently — recorded, never evaluated.Security filtering (who a GPO applies to) captured from the same report.
Search matches are highlighted wherever they appear, including inside an expanded GPO's settings, links and other-areas lists.
Typed parsing for the Security extension (
ConvertFrom-GpoSecuritySetting.ps1): SecurityOptions, Account, UserRightsAssignment, RestrictedGroups, SystemServices, Audit and EventLog records each decode to exactly one settings row, replacing the generic XML walker for this extension. Every DisplayFields checkbox state (e.g. Kerberos encryption types allowed) is now preserved as its own true/false property instead of being dropped.gpo_other_settings.recognized— 1 when a row came from a shape-aware handler (today, Security only), 0 when it came from the generic walker. Flagged in the "Other areas" grid, the GPO expansion table and the setting detail modal (a caveat banner, worded as a statement about this tool's coverage rather than about the GPO); aggregated as an Overview card only when the count is non-zero.gpo_other_settings.source_xml— the setting's own report XML fragment, verbatim, stored for every other-areas row (typed and generic alike) and shown in a collapsed "Source XML" disclosure in the detail modal, always present regardless ofrecognized.Unrecognised security element shapes (a future Windows security area, or a SecurityOptions Display variant this tool does not decode) fall back to the generic walker and raise a scan warning rather than mis-rendering silently.
Fixture and regression tests in
tests/OtherSettings.Tests.ps1for every typed Security shape, the two real-domain SecurityOptions fixtures this normalizer was built against (one collapses 3 rows to 1, the other 9 rows to 3), and the unrecognised-shape fallback.
Changed
- The page no longer caps at 1600px and grid cells have room (mono 44ch, text 56ch); modals widen to 1400px and no longer truncate inside. Widening the browser now actually shows more data.
- Report prose is collapsed behind an "About this report" summary. The scan-visibility banner stays outside it — a caveat that gates interpretation must not be collapsible.
- Console result cells truncate with a tooltip like the main grids, so one MULTI_SZ row cannot blow out the layout.
- The "Source XML" disclosure in the other-setting detail modal is now pretty-printed and
syntax-coloured in the browser (indentation,
.xml-tag/.xml-attr/.xml-val/.xml-text/.xml-comment), instead of renderingOuterXmlas one long unindented line. The storedsource_xmlvalue itself is unchanged — still the byte-faithful "as reported" artifact — only its rendering was reformatted.
Fixed
- SecurityOptions records (e.g. "Network security: Configure encryption types allowed for Kerberos")
fragmented into up to three settings rows — the real setting plus two phantom rows named
KeyNameandSettingNumberfrom the record's own leaf elements — inflating other-areas counts roughly threefold for any GPO configuring security settings. Fixed by the typed Security normalizer above.
Notes
gpo_other_settingsgained two columns (recognized,source_xml). Snapshot databases from a scan run before this change do not have them;Get-NordloGpoReportViewand the HTML report both assume the current schema, so old.sqlitefiles must be regenerated by a freshInvoke-NordloGpoReportScanrun rather than reused. This is consistent with the snapshot-per-file model — the database is a point-in-time export, not a store this tool migrates in place.ADML streaming reader skipped every other element (
ReadElementContentAsStringalready advances, and the loop advanced again) — 300 of 615 configured policies silently lost their display names. A regression test pins all-consecutive-strings parsing.Policies whose element shares the policy's own
valueName(TPM lockout, Defender enum policies) were reported Undetermined; the key index now merges roles instead of dropping the second registration.Policies writing the same registry value from both
enabledListanddisabledList(fDenyTSConnections) are now decided by comparing the written data against each list item's declared value — presence-based reasoning got them backwards half the time.Two state-derivation errors, found by cross-checking every derived state against GPMC's own reported state (agreement went from 99.6% to 99.93%, 1441 of 1442):
- A policy that expresses Disabled by deleting its element values (
**del.DCSettingIndex, no policy value of its own) was read as Enabled, because delete markers were only checked on the policy's own value and then counted as "element values present". A marker is evidence of the opposite of presence. - An implied enabled/disabled sentinel (the schema's 1/0 default, used when the ADMX declares neither) outranked real element evidence, so an enum choice of 0 read as Disabled. Declared sentinels still win; implied ones are now consulted only after element evidence.
- A policy that expresses Disabled by deleting its element values (
A stray
U+0001control character inside a string literal in20-grid.js. Harmless at runtime and invisible in every editor and diff, which is exactly why a test now rejects control characters in any bundled asset.The GPOs tab's "not actually empty" flag never fired: the columns it reads to decide were looked up before the grid switched to the new view's columns, so on the normal path (arriving from Overview) the lookup always ran against an empty column list.
Dead-links tooltip text was keyed to
no settings configured, a string the SQL never emits (it emitsno settings at all); the tooltip for that reason, and forno ADMX settings (has other policy areas), never appeared. Both keys are fixed, and a test now checks every non-conclusive reason the SQL can produce has a matching tooltip key.The SQL console's prose named "the eleven tables" by hand; the schema has since grown to twenty and the sentence had not been updated. The table list is now rendered at boot from
sqlite_master.highlightMatchmatched the search term against already-HTML-escaped text, so a needle likeamp,ltorquotcould land inside an entity produced by escaping a raw&,<or"in the value, corrupting its display. Matching now runs on the raw text before escaping.The ADMX setting detail modal did not show security filtering ("This GPO applies to"), unlike the other-areas modal — a policy configured in a GPO nobody is filtered to looked identical to one that actually applies. Both modals now share the same section.
The conflict-detail policy lookup filtered on registry key and value name but not hive, so a Computer-side and User-side conflict sharing those names could name the wrong policy in the modal.
CSV export wrote raw column names as headers and left every value un-formatted (booleans as
1/0, timestamps as ISO strings); it now exports the same labels and yes/no/formatted-date values the grid shows, while still writing numeric columns raw rather than with locale thousands separators.
Added
dead-linksreasonapplies to no one: a GPO whose security filtering grants Apply to no principal, checked only for GPOs the scan could actually read. Group nesting and membership are not evaluated.dead-linksreasonempty subtree for configured hive: a GPO that configures only one hive (via ADMX or any other policy area) whose subtree has zero objects right now, even when the other hive's count is nonzero or unknown. Not conclusive, same caveat asempty subtree.conflicting-settingsgainslive_writers: how many of the writing GPOs currently have at least one enabled link and the relevant hive switched on. Not precedence — it separates writers that could currently apply anywhere from ones that could not.all-settingsincludesstate_basisalongsidestate.- WMI filter presence (
wmi_filter_name) is now flagged wherever it appears — recorded, never evaluated, same treatment as item-level targeting. unlinked-gpos' header notes that an oldmodified_timeon an unlinked, configured GPO is the strongest staleness signal this data can offer.- Every grid row is now clickable: views with none of the richer row behaviours (
orphaned-settings,admx-to-gpo,gpo-to-admx,unreferenced-admx) fall through to a generic "every column, full value" modal, so a long mono value is reachable on touch and keyboard, not only via a hover tooltip. - The detail modal traps Tab/Shift+Tab within its panel while open.
- The tab strip is a real keyboard tablist: roving tabindex, and ArrowLeft/ArrowRight/Home/End move focus and select.
- URL hash deep links:
#<viewId>on tab selection,#gpos/<guid>when a GPO row is expanded from the GPOs tab. Restored on load and on browser back/forward; filter text and sort order are not serialised, and an unrecognised hash falls back to Overview silently.
Changed
- Header cells in the detail modal's tables and a GPO row's inline expansion tables are now plain
text instead of
<button disabled>— those tables never sorted, and the disabled button put an inert, unreachable-by-purpose control in the tab order and screen reader flow for no benefit.
Notes
- ADMX XML parsing is namespace-agnostic (
local-name()): 36 of 257 templates in a real store use a non-canonical root namespace or none at all, and a namespace-bound parser silently drops them. - The HTML writer streams the document through a
StreamWriter, base64-encoding payloads in chunks of 196 608 bytes (a multiple of three, so padding only ever lands at the end). The obvious alternative —ReadAllBytes,ToBase64String, substitute into a template — peaks at roughly 8-12x the database size in large-object-heap strings. A round-trip test pins the chunk arithmetic. - sql.js is initialised with
wasmBinary, neverlocateFile: the latter resolves a URL and goes throughfetch/XHR, which afile://page is not permitted to do, so it cannot work standalone. - All collections are
List[psobject], neverList[object]:@($list)throws "Argument types do not match" from PowerShell's own enumerable binder for that element type, on both 5.1 and 7. Two tests enforce this.
Added
- Typed parsing for nine Group Policy Preferences areas (
ConvertFrom-GpoPreferenceSetting.ps1): Drive Maps, Shortcuts, Folders, Files, Ini Files, Windows Registry, Environment and Local Users and Groups get full treatment (headline summary plus every attribute as a property); Scheduled Tasks gets a deliberately headline-only extraction (command line,runAs/RunLevel, task variant,Enabled, and trigger TYPE names only) — modelling the full Task schema (repetition, boundaries, idle settings, the 15+ Settings booleans) is out of scope this round by explicit agreement; the detail modal for a Scheduled Tasks row carries aNoteprop pointing at Source XML and the Group Policy editor as the authoritative view for everything past the headline. gpo_other_settings.targeting_text— item-level targeting (<Filters>) spelled out in words, e.g.member of CROSS\Domain Users OR member of CROSS\it-res-ou-Cross external users manager. Built by walkingFilterGroup("member of X"), genericFilter*types ("Type: attr=value, ..."), andFilterCollection(recursed and parenthesized), chaining clauses with each filter's ownboolattribute and honouringnot="1". Shown under the existing "item has item-level targeting" banner in the detail modal. Still recorded, never evaluated — this makes the recorded condition readable, not resolved.gpo_other_settings.item_disabled— 1 when a GPP item's owndisabledattribute is1. Previouslydisabledsat on the generic walker's noise-attribute skip list with nothing reading it, so a disabled preference item rendered identically to an active one. Surfaced as aDisabledprop, a(item disabled)summary suffix, a warn-flagged grid column, and a new banner in the detail modal.disabledstays on the noise list for prop emission — the column is the intended signal now, not a bare attribute dump.tools/Update-SecurityOptionCatalog.ps1, a maintainer-run script that regeneratessource/files/security-option-catalog.jsonfromHKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SeCEdit\Reg Values, resolving@wsecedit.dll/@scecli.dlldisplay strings viaLoadLibraryExW+LoadStringW. 101 Security Options on the generating build, with value types, display types, units and full choice/flag label lists (the metadata the security-settings editor itself renders from). Deliberately not part of the scan — the data is OS-shipped and stable, so the catalog is a bundled artifact refreshed by a maintainer after Windows feature updates; output is sorted so regenerations diff cleanly. Explain text is not included: an exhaustive dump of both DLLs' string tables found no mechanical mapping from display-name resource IDs to explain-text paragraphs, and shipping a fuzzy match was ruled out. Validated bytests/SecurityCatalog.Tests.ps1.- Real-domain fixtures in
tests/OtherSettings.Tests.ps1(trimmed of GPO-level SecurityDescriptor boilerplate, byte-faithful ExtensionData) for Scheduled Tasks (oneImmediateTaskV2running ConfigMgrClientHealth.ps1), a mixed Preferences GPO (Folders/Ini Files/Shortcuts sharing one item-level-targeting filter chain), and GPP Registry alongside NRPT and the literal Administrative-TemplatesRegistryextension (the regression fixture for the skip below). Plus synthetic fixtures for Drive Maps, Files, Environment, the Local Users and Groups<User>variant, and a non-immediateTaskV2withCalendarTrigger/LogonTriggerto prove trigger-type listing and the legacy/modern variant distinction. - The bundled Security Options catalog now travels INTO the snapshot at scan time instead of staying a
build-time-only asset:
Get-SecurityOptionCatalog.ps1readssecurity-option-catalog.json(the same$PSScriptRoot/files\resolutionGet-GpoReportHtmlAsset.ps1uses) and its rows are written to two new tables,security_option_catalogandsecurity_option_choices, plus three newscan_infoprovenance columns (catalog_generated_on,catalog_os_build,catalog_ui_culture). A snapshot now renders its security options identically forever, independent of which module version later opens it. A missing or unparsable catalog records a scan warning and leaves both tables empty rather than failing the scan. - Security option detail modal gained an "Editor metadata" section (
securityOptionMetadataSectionin35-detail.js) forSecurity-area rows with aKeyNameprop: the catalog's display name (when it differs from what is already shown), type plus units, the full choice list forchoice-type options with the configured value marked, and a decoded flag table forflags-type options (one row per flag, set/not-set against the configured DWORD). When the report XML's own decoded value and the catalog's choice label disagree — possible when the domain was scanned against a different OS generation than the catalog's build — both are shown side by side rather than one being silently preferred. No catalog row (including when the catalog was missing at scan time) renders the modal exactly as before: no empty section, no fabricated placeholder. - Overview facts gained a "Security option catalog" line (
securityOptionCatalogFactin10-database.js), e.g. "101 options, build 26100 (en-US)" — omitted entirely when the snapshot carries no catalog rows. tests/CatalogIntegration.Tests.ps1—Get-SecurityOptionCatalogagainst the real bundled JSON (row count, the KerberosSupportedEncryptionTypessix-choice ordering, theNoLMHashnull-display-name case) and against a missing/unparsable file (warning recorded, empty tables, no throw); the new schema (both tables plus thescan_infoprovenance columns); andWrite-GpoReportDataactually populating both tables from a loaded catalog.
Changed
- GPP summaries now lead with the operative fact instead of "first three attributes in document
order": a Shortcut's summary previously showed
targetType/shortcutKey/iconIndexwhile omitting both paths; it now reads-> \\cts03\cross at %APPDATA%\...\target. Folders, Ini Files, Files, Drive Maps, Windows Registry, Environment and Local Users and Groups summaries were rewritten the same way.
Notes
- The previously-recorded risk that the ADMX-only
Registryskip inConvertFrom-GpoReportXmlmight also be swallowing GPP Registry items has been verified against real report XML and does not apply: Administrative Templates reports as<Name>Registry</Name>in the.../Settings/Registrynamespace, while GPP Registry preferences report as<Name>Windows Registry</Name>in.../Settings/Windows/Registry— different names, so only the former is skipped. The code comment recording this was updated in the prior round; this round adds a regression fixture (gpo-gpp-registry.xml) pinning both extensions coexisting in one GPO. gpo_other_settingsgained two more columns (targeting_text,item_disabled), on top ofrecognized/source_xmlfrom the previous round. As before, snapshot databases from an earlier scan do not have them; regenerate with a freshInvoke-NordloGpoReportScanrun rather than reusing an old.sqlitefile.- Snapshots need to be regenerated to pick up
security_option_catalog/security_option_choicesand thescan_infocatalog columns; older.sqlitefiles simply render security options without the editor-metadata section — the same graceful-fallback path a catalog-load failure takes, not an error. - The catalog's own
display_type(boolean/number/string/choice/stringlist/flags) andvalue_type(REG_DWORD/REG_SZ/...) are already readable strings insecurity-option-catalog.json, not the numeric 0–5 / enum codes an earlier planning pass assumed —security_option_catalog.display_typeand.value_typestore them as-is;display_type_nameonly re-spacesstringlisttostring listfor display.
Fixed
- A
SecurityOptionsrecord with no<Display>block (hand-imported baseline templates write options the reporting machine's editor has no metadata for) threw one non-terminating "InvokeMethodOnNull" error per record during real scans. The local-name lookup helpers are now null-tolerant, and a Display-less record is treated as a typed row (its KeyName and setting value are fully understood; only display decoration is absent) rather than falling to the generic walker with an unrecognised-shape banner. Regression-tested against a Display-less fixture. - Restricted Groups reported "0 member(s)" for every group. Real Get-GPOReport output puts
and directly under the RestrictedGroups record; the handler was matching a wrapper invented by an early synthetic fixture. Members and member-of entries are now read from the real shape (either Memberof capitalisation), an unresolvable principal falls back to its SID, and the fixture was corrected to the verified real-world form. - The grid filter could not be typed into: the hash-routing loop guard was timing-based (setTimeout(0)), and the browser does not guarantee that timer fires after the queued hashchange task. When the view's query was slow enough the guard was already clear when hashchange arrived, and the handler re-ran the entire view load, wiping the filter input mid-typing. The guard now compares the hash VALUE this code itself wrote, which cannot lose a race.
- Searching for a principal that a Restricted Groups setting names as a member returned nothing. The GPOs tab's deep-search index covered non-ADMX settings only through area/item/summary, and those summaries deliberately truncate -- a restricted group's reads "3 member(s)" while the member names live only in gpo_other_setting_props. The index now folds in every property of every non-ADMX setting (plus targeting_text), and the Settings-grain and Other-areas grids gained an equivalent per-setting property index, so a search for a member name, a task command line or a mapped drive path finds the row that carries it. The row-count line now names which hidden content matched rather than always claiming "inside each GPO's settings and links".
- Publishing a tag failed on a clean CI runner:
Publish-PSResourcerunsTest-ModuleManifest, which resolves everyRequiredModulesentry againstPSModulePathand calls the entry invalid when it cannot find it.PSSQLiteis the module's runtime dependency but was never installed bybuild.ps1, so the publish job failed while every other task passed -- on a developer machine PSSQLite is already present and the publish path is never exercised. It is now installed with the other prerequisites. - Publishing then failed a second check:
Publish-PSResourcealso refuses to publish a module whoseRequiredModulesare absent from the TARGET repository, and PSSQLite is not mirrored to the feed this module publishes to. The publish call now passes-SkipDependenciesCheck. The manifest still declares the dependency; PSSQLite is installed from the PowerShell Gallery, which the README now states explicitly.
Any 0.0
| Version | Downloads | Last updated |
|---|---|---|
| 0.2.0-rc.1 | 3 | 08/18/2026 |
| 0.1.0 | 3 | 07/31/2026 |
| 0.1.0-rc.3 | 18 | 07/31/2026 |