Files
winutil/AGENTS.md
T
Phuc ToandGitHub 6de45a38b9 Fix UI helper errors during headless -Preset and -Config runs (#4941)
* Make UI helpers no-op when no window exists

The -Preset and -Config paths run Invoke-WinUtilAutoRun before the XAML
form is created and before PresentationCore is loaded, so every call into
Invoke-WPFUIThread failed with InvokeMethodOnNull and every call into
Set-WinUtilTweaksProgressIndicator failed to resolve [Windows.Visibility].
The errors were non-terminating, so tweaks still applied, but each run
filled the log with noise.

Loading presentationframework earlier does not help: Visibility lives in
PresentationCore, which only loads once a WPF object is instantiated, and
the XAML-named progress controls do not exist in these paths either.
Guarding the two helpers covers Invoke-WPFtweaksbutton, Invoke-WPFundoall
and Invoke-WPFFeatureInstall, which the existing per-call-site $hasUI
convention never reached.

* Suppress stray console output from automation runs

Invoke-WPFRunspace returns an IAsyncResult that no caller uses, and
Set-WinUtilRegistry was the only New-PSDrive call site that did not
suppress its output. A GUI click handler discards both, but the -Preset
and -Config paths call the workflows directly, so an async handle dump
and a PSDrive table landed in the user's log.

The handle itself is kept because pester/runspace.Tests.ps1 asserts that
Invoke-WPFRunspace returns a single IAsyncResult, so the suppression goes
at the four call sites reachable from Invoke-WinUtilAutoRun.

* Add project learning for window-free UI helpers
2026-08-09 13:06:17 -05:00

12 KiB

AGENTS.md

Drop-in operating instructions for coding agents. Read this file before every task.

Working code only. Finish the job. Plausibility is not correctness.

SPEC.md in the repository root is the project contract — read it for what WinUtil is and how it's architected. This file covers how to work on it.

0. Non-Negotiables

These rules override everything else in this file when in conflict:

  1. Do not edit winutil.ps1 directly. It is generated build output (see SPEC.md's Build Model). Change source files and compile.
  2. Do not commit winutil.ps1. It is ignored locally and generated by GitHub Actions for releases.
  3. Never touch docs/src/content/docs/code-reference/tweaks/ or docs/src/content/docs/code-reference/features/. Both are auto-generated (see SPEC.md's Docs Site). Edit the source JSON (config/tweaks.json, config/feature.json) or the relevant PowerShell function file instead. Other hand-written pages under code-reference/ (e.g. architecture.mdx) are not touched by the generator and may be edited directly.
  4. Never fabricate. Do not invent file paths, function names, command output, test results, commit hashes, or API behavior. Read the file or run the command.
  5. Disagree when the premise is wrong. Say what is wrong before acting on it.
  6. Stop when genuinely ambiguous. If two interpretations would produce materially different diffs, ask before editing.
  7. Touch only what the task requires. No drive-by refactors, formatting sweeps, or unrelated cleanup.
  8. Verify before saying done. A plausible-looking diff is not proof.

1. Key Commands

  • Compile:
    .\Compile.ps1
    
  • Compile and run GUI:
    .\Compile.ps1 -Run
    
  • Install the supported Pester version (one-time). -SkipPublisherCheck is required because Windows ships an inbox Pester 3.4.0 that is catalog-signed, and PowerShell Gallery's Pester 5.8.0 is Authenticode-signed — Install-Module refuses the upgrade without it. This does not skip download integrity (still HTTPS + NuGet package hash verification); -Repository PSGallery pins the trusted source explicitly rather than relying on whatever repositories happen to be registered:
    Install-Module -Name Pester -RequiredVersion 5.8.0 -Repository PSGallery -Scope CurrentUser -Force -SkipPublisherCheck
    
  • Run tests:
    Import-Module Pester -RequiredVersion 5.8.0 -Force
    Invoke-Pester -Path 'pester/*.Tests.ps1' -Output Detailed -CI
    
  • Run Script Analyzer with project settings when available. If a locally compiled winutil.ps1 exists, delete it first — lint/PSScriptAnalyser.ps1 only excludes rules, not files, so -Recurse would also lint the generated script and produce noise against line numbers that don't map to any source file:
    Invoke-ScriptAnalyzer -Path . -Settings .\lint\PSScriptAnalyser.ps1 -Recurse
    
  • Docs site dev server (run from docs/):
    npm install
    npm run dev
    
  • Docs site production build (run from docs/):
    npm run build
    

Prefer the narrowest useful verification while iterating. Use the full relevant check before finishing.

2. Source Of Truth

For changes that affect the compiled WinUtil script, make them only in the source files described in SPEC.md's Repository Layout — never in winutil.ps1 itself. If behavior changes require the compiled script to change, update the source files and run .\Compile.ps1 only to verify generation.

This scoping applies to compiled-script behavior only. Repository metadata — AGENTS.md, SPEC.md, CLAUDE.md/GEMINI.md/.github/copilot-instructions.md, .github/workflows/, and the root .gitignore — is edited directly when a task requires it, per the other sections of this file.

3. Before Editing

  • State the plan in one or two sentences before editing. For non-trivial work, include the verification you intend to run.
  • Read the files you will touch and the files that call them.
  • Match existing patterns even when a different greenfield design would be cleaner.
  • Surface assumptions when they affect behavior, compatibility, or user data.
  • If two approaches have meaningful tradeoffs, name them before choosing. Trivial tasks can proceed directly.

4. Coding Guidelines

  • Prefer the minimum code that solves the stated problem.
  • Keep PowerShell functions in one function file when practical, with the file name matching the primary function name.
  • Use approved PowerShell verb-noun names and follow the existing WPF / WinUtil naming conventions; keep UI event handler names aligned with XAML element names per SPEC.md's UI And Event Contract.
  • Use $sync for shared state and UI references, consistent with SPEC.md's Runtime Model.
  • Update WPF controls through the UI dispatcher when running work in a background runspace.
  • Keep config-driven features in JSON when they fit the existing schema instead of hard-coding lists in PowerShell; follow SPEC.md's Configuration Contract for required fields and key-renaming rules.
  • Preserve undo/original-state data for tweaks so users can reverse changes.
  • Do not add abstractions, configurability, hooks, or "future extensibility" unless the task needs them now.
  • Clean up orphans created by your own changes, such as unused variables or functions made obsolete by the edit.
  • Avoid broad formatting-only edits, especially in JSON config files, XAML, docs, and generated output.

5. Runtime And Safety Rules

  • WinUtil performs system-level Windows changes; treat registry, services, AppX removal, package manager, Windows Update, ISO, and unattended setup changes as high-risk (see SPEC.md's Safety Requirements).
  • Prefer existing helper functions for WinGet, Chocolatey, registry, services, progress, and UI updates.
  • Keep tweaks reversible where the schema supports it by including original values or original states.
  • Never modify a user's original ISO in-place; follow existing copy/mount/export patterns.
  • Avoid storing credentials, secrets, or machine-specific paths in repo files.
  • Preserve logging and user feedback patterns for long-running or destructive operations.

6. Surgical Changes

  • Do not improve adjacent code, comments, formatting, imports, or docs unless required.
  • Do not refactor working code because you are already in the file.
  • Do not delete pre-existing dead code unless asked; mention it in the summary if relevant.
  • Keep diffs reviewable. Every changed line should trace to the user's request.
  • If a change starts spreading across unrelated areas, pause and reassess the plan.

7. Verification

Define success in terms that can be checked, then check it.

  • For compile/build changes, run .\Compile.ps1.
  • For GUI behavior changes, run .\Compile.ps1 -Run when practical and verify the affected path manually.
  • For config changes, run the compile check and relevant Pester tests.
  • For function changes, run the relevant Pester tests or add/update focused tests when practical.
  • For docs-only changes, proofread the changed files and skip runtime tests unless docs generation is affected.
  • Read command output. Do not report tests as passing unless they actually passed.
  • If verification fails, fix the cause rather than weakening the test.

If a check cannot be run, say exactly why and what residual risk remains. See SPEC.md's Testing And CI for what GitHub Actions runs on every push.

8. Generated Files And Git Hygiene

  • Treat local winutil.ps1 changes as disposable compile output.
  • Never stage or commit winutil.ps1, binary/, or anything else ignored by the root .gitignore or docs/.gitignore — read those files rather than assuming. docs/public/ is tracked source for static assets, not generated output.
  • Do not remove .gitignore rules that keep generated artifacts out of Git.
  • Before finishing, check git status --short and separate your changes from pre-existing user changes.
  • Do not revert user changes unless explicitly asked.
  • Commit messages, when requested, should be descriptive: short subject under 72 characters, body explaining why when needed.
  • When committing, split changes into small, logical commits rather than one large commit, so each commit's diff is reviewable as a single group of related changes.

9. Documentation Expectations

  • Update docs/src/content/docs/guides/ when user-facing behavior changes.
  • Update docs/src/content/docs/code-reference/architecture.mdx and other hand-written developer docs when architecture, build flow, config schema, or contribution workflow changes — but never hand-edit the auto-generated code-reference/tweaks/ or code-reference/features/ subfolders (see Non-Negotiables).
  • Keep sidebar entries in docs/astro.config.mjs in sync with page slugs (see SPEC.md's Docs Site).
  • Keep README changes brief and high-level.
  • Put detailed user and developer documentation under docs/.
  • Keep SPEC.md aligned with project/architecture changes, and this file aligned with process changes.

10. Communication Style

  • Be direct and concise. Start with the answer or action.
  • No flattery, filler, ceremonial closings, or fake certainty.
  • Use bullets only when they improve scanning.
  • Report what changed, how it was verified, and anything not done.
  • If the user asks for a review, lead with findings and file/line references.

11. When To Ask

Ask before proceeding when:

  • The request has two plausible interpretations and the choice materially changes behavior or files touched.
  • The change affects release generation, generated artifacts, migrations, or high-risk Windows behavior in a way the user did not specify.
  • You need credentials, secrets, production resources, or access you do not have.
  • The user's stated goal conflicts with the literal request.

Proceed without asking when:

  • The task is trivial and reversible.
  • Ambiguity can be resolved by reading the code or running a local command.
  • The user already answered the question in this session.

12. Project Learnings

When the user corrects an agent approach, add or tighten one concrete rule here before ending the session. Keep this section short and prune rules that no longer matter.

  • Keep winutil.ps1 generated-only: change source files, compile to verify, and never stage the generated script.
  • Keep WinUtil runtime logging in the existing timestamped %LocalAppData%\winutil\logs\winutil_*.log session file; do not create a separate root winutil.log.
  • Import Pester 5.8.0 before running tests so Invoke-Pester -Output Detailed -CI does not resolve to Windows' inbox Pester 3.4.0.
  • Keep package install/uninstall process launches simple unless explicitly requested; do not add a separate stdout/stderr process logging helper for winget or Chocolatey.
  • When the active log file is owned by Start-Transcript, do not call Add-Content against that file; write to host output so the transcript captures the line in the same log file without recording a terminating-error diagnostic.
  • Keep UI helpers such as Invoke-WPFUIThread and Set-WinUtilTweaksProgressIndicator safe to call without a window; the -Preset and -Config paths run the workflows before the form is created and before PresentationCore is loaded.
  • Log install/uninstall package names and package-manager IDs before queuing background runspace work; do not rely on runspace host output for the package identity.
  • For Win11 Creator, start each new ISO modification in a fresh WinUtil_Win11ISO_* temp directory; existing-work detection is only for resuming/exporting already modified media.
  • For Win11 Creator driver injection, keep offline WIM servicing to one mount, one /Add-Driver, and one commit; do not export editions or run unrelated WIM cleanup, and reject damaged metadata before ISO export.
  • For Script Analyzer cleanup, fix actionable source warnings first and do not globally suppress accepted convention warnings such as plural names, ShouldProcess on UI helpers, $global:sync, or compile-time cross-file false positives.
  • For DNS DHCP reset, keep the cmdlet reset and explicitly set IPv4 and IPv6 DNS source to DHCP.