Feat MVP #1

Merged
JMR-dev merged 13 commits from feat-mvp into main 2026-04-29 02:16:38 +00:00
JMR-dev commented 2026-04-29 01:31:21 +00:00 (Migrated from github.com)

Implementation of covenant-setup, a deterministic Windows installer engine.

Implementation of covenant-setup, a deterministic Windows installer engine.
copilot-pull-request-reviewer[bot] (Migrated from github.com) reviewed 2026-04-29 01:36:54 +00:00
copilot-pull-request-reviewer[bot] (Migrated from github.com) left a comment

Pull request overview

Implements the MVP for covenant-setup: a deterministic Windows installer/uninstaller engine in Rust with a bundled single-file packager, a C# WinForms UI over named-pipe IPC, and Windows VM harnesses/docs to validate real Win32 boundaries.

Changes:

  • Added core Win32 capabilities in Rust (path resolution, elevation/relaunch, filesystem + registry mutations, shortcut creation, Restart Manager + MoveFileEx fallback).
  • Added a C# WinForms UI sidecar/embedded helper (named-pipe server + JSONL protocol) plus xUnit coverage for protocol/helpers.
  • Added Vagrant-based smoke/coverage scripts, CI workflow, and extensive documentation/examples for packaging, install/uninstall, and debugging.

Reviewed changes

Copilot reviewed 31 out of 39 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
vm/self-test/install.toml Self-test manifest for VM smoke/coverage scenarios.
ui/Covenant.Setup.Ui/app.manifest WinForms app manifest (asInvoker).
ui/Covenant.Setup.Ui/Program.cs WinForms UI + named-pipe server and JSON message handling.
ui/Covenant.Setup.Ui/Covenant.Setup.Ui.csproj C# UI project definition (WinExe, WinForms, internals visible to tests).
ui/Covenant.Setup.Ui.Tests/UiMessageJsonTests.cs Tests for UI message/response JSON contract.
ui/Covenant.Setup.Ui.Tests/ProgramTests.cs Tests for CLI arg parsing (--pipe).
ui/Covenant.Setup.Ui.Tests/InstallerUiFormHelperTests.cs Tests for UI helper functions (errata JSON, mappings, safe summaries).
ui/Covenant.Setup.Ui.Tests/Covenant.Setup.Ui.Tests.csproj C# test project setup and dependencies.
src/win.rs Win32 boundary wrappers (known folders, elevation, registry, shortcuts, Restart Manager, file ops) + unit tests.
src/ui.rs Rust-side UI session management (extract/launch C# UI, pipe protocol, prompts, progress sink).
src/sys.rs Sys trait boundary abstraction and WinSys production implementation.
scripts/windows-vm/coverage/uac.ps1 Guest scenario: UAC/elevation coverage assertions.
scripts/windows-vm/coverage/self-test.ps1 Guest scenario: baseline install/uninstall + journal assertions.
scripts/windows-vm/coverage/reboot.ps1 Guest scenario: locked-file uninstall requiring reboot fallback signals.
scripts/windows-vm/coverage/hklm-registry.ps1 Guest scenario: HKLM registry writes forcing admin path.
scripts/windows-vm/coverage/bundled-exec.ps1 Guest scenario: packaged/bundled installer execution path.
scripts/windows-vm/Start-InteractiveSelfInstall.ps1 Guest interactive scheduled-task harness for install+uninstall verification + diagnostics.
scripts/windows-vm/Invoke-InteractiveInstaller.ps1 Guest wrapper that launches installer/uninstaller and captures diagnostics/trace.
scripts/windows-vm/Abort-SmokeDiagnostics.ps1 Guest abort/cleanup + trace bundle capture.
scripts/run-windows-vm-smoke.ps1 Host orchestrator for interactive VM smoke test (package, upload, run, collect traces).
scripts/run-windows-vm-coverage.ps1 Host orchestrator for multi-scenario VM coverage run.
project_mvp.md MVP requirements/reference document.
examples/payload/sample_app.cmd Example payload for demo installs.
examples/payload/post_install.ps1 Example post-install payload script.
examples/install.toml Example manifest covering directories/files/registry/shortcut/script/purge.
examples/README.md Example usage instructions for packaging/install/uninstall.
docs/vagrant-smoke-debugging-notes.md Deep-dive notes for diagnosing VM smoke failures/hangs.
docs/integration-tests-architecture.md Integration testing architecture and scenario coverage overview.
docs/implementation-notes.md Implementation/review notes capturing decisions and fixes.
build.rs Builds/embeds C# UI helper via dotnet publish + embeds application manifest.
Vagrantfile Windows 11 VM configuration for Hyper-V/WinRM based testing.
README.md Project overview, usage, UI modes, VM harness docs.
Cargo.toml Rust crate dependencies and build dependency configuration.
Cargo.lock Locked dependency graph for reproducible builds.
CLAUDE.md Repository build/run/conventions guidance.
.gitignore Ignore rules for build artifacts/VM artifacts.
.github/workflows/ci.yml Windows CI: build, Rust tests, C# UI tests, quality gate.
.cargo/config.toml Windows MSVC static CRT linking + optional GNU target runner configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

## Pull request overview Implements the MVP for `covenant-setup`: a deterministic Windows installer/uninstaller engine in Rust with a bundled single-file packager, a C# WinForms UI over named-pipe IPC, and Windows VM harnesses/docs to validate real Win32 boundaries. **Changes:** - Added core Win32 capabilities in Rust (path resolution, elevation/relaunch, filesystem + registry mutations, shortcut creation, Restart Manager + MoveFileEx fallback). - Added a C# WinForms UI sidecar/embedded helper (named-pipe server + JSONL protocol) plus xUnit coverage for protocol/helpers. - Added Vagrant-based smoke/coverage scripts, CI workflow, and extensive documentation/examples for packaging, install/uninstall, and debugging. ### Reviewed changes Copilot reviewed 31 out of 39 changed files in this pull request and generated 7 comments. <details> <summary>Show a summary per file</summary> | File | Description | | ---- | ----------- | | vm/self-test/install.toml | Self-test manifest for VM smoke/coverage scenarios. | | ui/Covenant.Setup.Ui/app.manifest | WinForms app manifest (asInvoker). | | ui/Covenant.Setup.Ui/Program.cs | WinForms UI + named-pipe server and JSON message handling. | | ui/Covenant.Setup.Ui/Covenant.Setup.Ui.csproj | C# UI project definition (WinExe, WinForms, internals visible to tests). | | ui/Covenant.Setup.Ui.Tests/UiMessageJsonTests.cs | Tests for UI message/response JSON contract. | | ui/Covenant.Setup.Ui.Tests/ProgramTests.cs | Tests for CLI arg parsing (`--pipe`). | | ui/Covenant.Setup.Ui.Tests/InstallerUiFormHelperTests.cs | Tests for UI helper functions (errata JSON, mappings, safe summaries). | | ui/Covenant.Setup.Ui.Tests/Covenant.Setup.Ui.Tests.csproj | C# test project setup and dependencies. | | src/win.rs | Win32 boundary wrappers (known folders, elevation, registry, shortcuts, Restart Manager, file ops) + unit tests. | | src/ui.rs | Rust-side UI session management (extract/launch C# UI, pipe protocol, prompts, progress sink). | | src/sys.rs | `Sys` trait boundary abstraction and `WinSys` production implementation. | | scripts/windows-vm/coverage/uac.ps1 | Guest scenario: UAC/elevation coverage assertions. | | scripts/windows-vm/coverage/self-test.ps1 | Guest scenario: baseline install/uninstall + journal assertions. | | scripts/windows-vm/coverage/reboot.ps1 | Guest scenario: locked-file uninstall requiring reboot fallback signals. | | scripts/windows-vm/coverage/hklm-registry.ps1 | Guest scenario: HKLM registry writes forcing admin path. | | scripts/windows-vm/coverage/bundled-exec.ps1 | Guest scenario: packaged/bundled installer execution path. | | scripts/windows-vm/Start-InteractiveSelfInstall.ps1 | Guest interactive scheduled-task harness for install+uninstall verification + diagnostics. | | scripts/windows-vm/Invoke-InteractiveInstaller.ps1 | Guest wrapper that launches installer/uninstaller and captures diagnostics/trace. | | scripts/windows-vm/Abort-SmokeDiagnostics.ps1 | Guest abort/cleanup + trace bundle capture. | | scripts/run-windows-vm-smoke.ps1 | Host orchestrator for interactive VM smoke test (package, upload, run, collect traces). | | scripts/run-windows-vm-coverage.ps1 | Host orchestrator for multi-scenario VM coverage run. | | project_mvp.md | MVP requirements/reference document. | | examples/payload/sample_app.cmd | Example payload for demo installs. | | examples/payload/post_install.ps1 | Example post-install payload script. | | examples/install.toml | Example manifest covering directories/files/registry/shortcut/script/purge. | | examples/README.md | Example usage instructions for packaging/install/uninstall. | | docs/vagrant-smoke-debugging-notes.md | Deep-dive notes for diagnosing VM smoke failures/hangs. | | docs/integration-tests-architecture.md | Integration testing architecture and scenario coverage overview. | | docs/implementation-notes.md | Implementation/review notes capturing decisions and fixes. | | build.rs | Builds/embeds C# UI helper via `dotnet publish` + embeds application manifest. | | Vagrantfile | Windows 11 VM configuration for Hyper-V/WinRM based testing. | | README.md | Project overview, usage, UI modes, VM harness docs. | | Cargo.toml | Rust crate dependencies and build dependency configuration. | | Cargo.lock | Locked dependency graph for reproducible builds. | | CLAUDE.md | Repository build/run/conventions guidance. | | .gitignore | Ignore rules for build artifacts/VM artifacts. | | .github/workflows/ci.yml | Windows CI: build, Rust tests, C# UI tests, quality gate. | | .cargo/config.toml | Windows MSVC static CRT linking + optional GNU target runner configuration. | </details> --- 💡 <a href="/JMR-dev/covenant-setup/new/main?filename=.github/instructions/*.instructions.md" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Add Copilot custom instructions</a> for smarter, more guided reviews. <a href="https://docs.github.com/en/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot" class="Link--inTextBlock" target="_blank" rel="noopener noreferrer">Learn how to get started</a>.
@@ -0,0 +1,212 @@
# covenant-setup
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:52 +00:00

The README links use absolute local Windows paths (e.g., C:\Users\...\workspace\...) which will be broken for other contributors and on GitHub. Replace these with repo-relative links (e.g., src/win.rs, src/main.rs, etc.) throughout the README so they render correctly in the repository.

- Uses Win32 APIs through the `windows` crate with unsafe isolated in [`src/win.rs`](src/win.rs)
The README links use absolute local Windows paths (e.g., `C:\Users\...\workspace\...`) which will be broken for other contributors and on GitHub. Replace these with repo-relative links (e.g., `src/win.rs`, `src/main.rs`, etc.) throughout the README so they render correctly in the repository. ```suggestion - Uses Win32 APIs through the `windows` crate with unsafe isolated in [`src/win.rs`](src/win.rs) ```
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:53 +00:00

The README’s early bullets say the engine is “observing” everything a program does during install and post-install scripts, but later sections (and the codebase) indicate journaling is currently based on declared actions (and script execution is logged, not its internal mutations). Consider rewording these bullets to match current behavior so users don’t overestimate what gets rolled back.

- Recording the install actions it applies (files, directories, registry entries, shortcuts, and script execution) into a `journal.json` written alongside the installed application. This journal is then used during uninstall to reverse those recorded actions and clean up associated state.
- Take a "leave the campground better than you found it" approach - this Eagle Scout practices Leave No Trace.
- Taking a "trust but verify model" to program installs and uninstalls by journaling engine-applied mutations and logging script execution in order to respect the user.
- Using the `journal.json` as a manifest of the actions the installer performed during install and post-install processing.
The README’s early bullets say the engine is “observing” everything a program does during install and post-install scripts, but later sections (and the codebase) indicate journaling is currently based on declared actions (and script execution is logged, not its internal mutations). Consider rewording these bullets to match current behavior so users don’t overestimate what gets rolled back. ```suggestion - Recording the install actions it applies (files, directories, registry entries, shortcuts, and script execution) into a `journal.json` written alongside the installed application. This journal is then used during uninstall to reverse those recorded actions and clean up associated state. - Take a "leave the campground better than you found it" approach - this Eagle Scout practices Leave No Trace. - Taking a "trust but verify model" to program installs and uninstalls by journaling engine-applied mutations and logging script execution in order to respect the user. - Using the `journal.json` as a manifest of the actions the installer performed during install and post-install processing. ```
@@ -0,0 +9,4 @@
### 1. The Rust CLI Interface & IPC Readiness
* **CLI Framework:** Utilize `clap` for robust argument parsing with standard subcommands (e.g., `covenant-setup install manifest.toml`, `covenant-setup uninstall journal.json`).
* **Structured Output Protocol:** The engine must accept a `--json` flag. When active, all standard text logs, progress percentages, and error stack traces must be suppressed and replaced with single-line serialized JSON objects emitted to `stdout`.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:53 +00:00

This doc uses glassbox as the example CLI name, but the actual tool/repo is covenant-setup (and the rest of the docs/examples use that). Consider updating the command examples here to match the real binary name to avoid confusion for readers.

This doc uses `glassbox` as the example CLI name, but the actual tool/repo is `covenant-setup` (and the rest of the docs/examples use that). Consider updating the command examples here to match the real binary name to avoid confusion for readers.
@@ -0,0 +1,949 @@
use crate::{AppError, Logger, RegistryRoot};
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:54 +00:00

is_elevated leaks the process token handle if GetTokenInformation returns an error, because the ? exits before close_handle(token, ...) runs. Consider wrapping HANDLE in a small RAII guard (or using a scope guard) so the handle is always closed on all paths, including error returns.

`is_elevated` leaks the process token handle if `GetTokenInformation` returns an error, because the `?` exits before `close_handle(token, ...)` runs. Consider wrapping `HANDLE` in a small RAII guard (or using a scope guard) so the handle is always closed on all paths, including error returns.
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:54 +00:00

known_folder only calls CoTaskMemFree after pwstr_to_path(raw, ...) succeeds. If UTF-16 decoding fails (or any future error is added before the free), the raw allocation from SHGetKnownFolderPath will be leaked. Consider freeing raw via a guard/defer so it is always released even when pwstr_to_path returns an error.

`known_folder` only calls `CoTaskMemFree` after `pwstr_to_path(raw, ...)` succeeds. If UTF-16 decoding fails (or any future error is added before the free), the `raw` allocation from `SHGetKnownFolderPath` will be leaked. Consider freeing `raw` via a guard/defer so it is always released even when `pwstr_to_path` returns an error.
@@ -0,0 +18,4 @@
{
var name = Program.ReadPipeName(new[] { "--pipe", @"\\.\pipe\foo" });
Assert.Equal("foo", name);
}
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:53 +00:00

This test uses a full pipe path (\\.\pipe\foo) as the value following --pipe, but the UI process currently passes that string directly into NamedPipeServerStream, which generally expects a pipe name (e.g., foo) rather than a full path. Consider changing the test input to a plain name, and (if you want to accept full paths) add a dedicated test that verifies the normalization/stripping behavior.

This test uses a full pipe path (`\\.\pipe\foo`) as the value following `--pipe`, but the UI process currently passes that string directly into `NamedPipeServerStream`, which generally expects a *pipe name* (e.g., `foo`) rather than a full path. Consider changing the test input to a plain name, and (if you want to accept full paths) add a dedicated test that verifies the normalization/stripping behavior.
@@ -0,0 +38,4 @@
value = value[pipePrefix.Length..];
}
return value;
}
copilot-pull-request-reviewer[bot] (Migrated from github.com) commented 2026-04-29 01:36:53 +00:00

ReadPipeName returns the raw token following --pipe with no validation/normalization. If the caller passes a full named-pipe path like \\.\pipe\foo, NamedPipeServerStream expects just the pipe name and will typically reject names containing path separators. Consider stripping the \\.\pipe\ prefix (or validating and showing a clear error) to make the UI process more robust to how the argument is passed.

`ReadPipeName` returns the raw token following `--pipe` with no validation/normalization. If the caller passes a full named-pipe path like `\\.\pipe\foo`, `NamedPipeServerStream` expects just the pipe name and will typically reject names containing path separators. Consider stripping the `\\.\pipe\` prefix (or validating and showing a clear error) to make the UI process more robust to how the argument is passed.
JMR-dev commented 2026-04-29 02:00:45 +00:00 (Migrated from github.com)

@copilot+claude-sonnet-4.6 apply changes based on the comments in this thread

@copilot+claude-sonnet-4.6 apply changes based on the comments in [this thread](https://github.com/JMR-dev/covenant-setup/pull/1#pullrequestreview-4193569389)
Copilot commented 2026-04-29 02:07:33 +00:00 (Migrated from github.com)

@copilot+claude-sonnet-4.6 apply changes based on the comments in this thread

Applied all changes from the review thread in commit 74e37e8:

  • README.md: Reworded the "observing" bullets to accurately describe declared-action journaling (not ETW observation); replaced the broken absolute Windows path with the repo-relative link src/win.rs.
  • project_mvp.md: Replaced glassbox CLI name with covenant-setup in the example subcommands.
  • Program.cs ReadPipeName: Now strips the \\.\pipe\ prefix if present, so NamedPipeServerStream always receives a plain pipe name regardless of how the argument is passed.
  • ProgramTests.cs: Updated the existing test to pass a plain name ("foo"); added a new ReadPipeName_strips_full_pipe_path_prefix test that verifies \\.\pipe\foo is normalized to foo.
  • src/win.rs is_elevated: Restructured to capture the GetTokenInformation result without ?, always call close_handle, then propagate the error — eliminating the token handle leak on error paths.
  • src/win.rs known_folder: Restructured to call pwstr_to_path and store the result, always call CoTaskMemFree, then return the stored result — eliminating the PWSTR memory leak when UTF-16 decoding fails.
> @copilot+claude-sonnet-4.6 apply changes based on the comments in [this thread](https://github.com/JMR-dev/covenant-setup/pull/1#pullrequestreview-4193569389) Applied all changes from the review thread in commit `74e37e8`: - **README.md**: Reworded the "observing" bullets to accurately describe declared-action journaling (not ETW observation); replaced the broken absolute Windows path with the repo-relative link `src/win.rs`. - **project_mvp.md**: Replaced `glassbox` CLI name with `covenant-setup` in the example subcommands. - **Program.cs `ReadPipeName`**: Now strips the `\\.\pipe\` prefix if present, so `NamedPipeServerStream` always receives a plain pipe name regardless of how the argument is passed. - **ProgramTests.cs**: Updated the existing test to pass a plain name (`"foo"`); added a new `ReadPipeName_strips_full_pipe_path_prefix` test that verifies `\\.\pipe\foo` is normalized to `foo`. - **`src/win.rs` `is_elevated`**: Restructured to capture the `GetTokenInformation` result without `?`, always call `close_handle`, then propagate the error — eliminating the token handle leak on error paths. - **`src/win.rs` `known_folder`**: Restructured to call `pwstr_to_path` and store the result, always call `CoTaskMemFree`, then return the stored result — eliminating the `PWSTR` memory leak when UTF-16 decoding fails.
Sign in to join this conversation.