The VM layer
Flare is a Windows desktop application and its plugin only means something inside it. The factory runs real Flare in Hyper-V VMs on the host, so a change can be used the way a writer would use it.
The VMs
Section titled “The VMs”Two VMs, HSFACTORY and HSFACTORY2, are built by scripts/factory/New-FactoryVM.ps1. It creates a generation 2 VM on the Default Switch with an unattended Windows install from an ISO, a local account called factory, remoting turned on, and a checkpoint named windows-ready. The VM account’s credential is generated once and kept in a DPAPI protected file that only this Windows account on this PC can read.
scripts/factory/Set-FactoryEgress.ps1 is an owner step, not part of a run. Inside the VM it sets the firewall’s default outbound action to block and allows only DNS and port 443 to the Anthropic API, GitHub, NuGet, the .NET download hosts and the owner’s AI gateway, for the factory account.
The host reaches a VM with PowerShell Direct, so no network path is needed.
vm-e2e.ps1, the runner
Section titled “vm-e2e.ps1, the runner”scripts/vm-e2e.ps1 runs E2E tests in an idle VM and is the workhorse behind everything else.
- Elevates. Hyper-V needs it, so the script starts itself as administrator, tails its log and relays the exit code.
- Picks an idle VM. For each VM it looks at a lock file holding the process id of the run that owns it. A dead process id is a stale lock and is ignored. It waits as long as it takes.
- Ships the working tree. It zips tracked and untracked files from the plugin checkout, minus docs, site, changes, the corpus and most of
tests, copies the zip over PowerShell Direct and expands it in a fresh work folder in the guest. Uncommitted changes count. - Runs in the interactive session. A scheduled task runs the guest driver as the logged in, un-elevated user, because Flare’s interface needs a real desktop session.
- Sets the screen to 1920 by 1080. If Windows chose something else, the driver changes it and says so, and fails if it cannot, so a run always matches what writers see.
- Runs the tests one at a time, installing the plugin before the first, and stops at the first failure. AI tests get a gateway key for this VM only. The others run with AI off.
- Brings results back (
results.json,summary.json, a folder per test with screenshots and state, the guest log) and removes the installed plugin and the work folder from the VM.
Exit codes are 0 when everything passed, 1 when a test failed and 2 when the VM could not be used. A failure the driver classifies as a VM failure is exit 2, never a pass.
Without a list of names it runs the E2E test scripts changed against origin/main. The parameter for the list is still called -Scenarios in the code. -Full runs all of them, which is what the release gate does.
vm-session.ps1 and vm-do.ps1, the interactive session
Section titled “vm-session.ps1 and vm-do.ps1, the interactive session”The acceptance agent needs to drive Flare step by step, not run a fixed script. scripts/vm-session.ps1 opens Flare in an idle VM and keeps it open.
- It asks for elevation once. The elevated process owns the VM until it is stopped.
- It runs
vm-e2e.ps1with the guestrepl.ps1as the only test, so it gets the same VM locking, install and clean up. - Once Flare is ready, it records proof of the build under test. It hashes the source on the host and in the VM and checks that the plugin DLLs were built fresh in this session and that the installed ones are the ones just built. A mismatch is reported as a factory bug and the session fails. It is never feedback for the implementer.
- It then listens on
127.0.0.1with a random token that is written, with the port, to.work/session.jsonwhere only the owner can read it. POST /dotakes PowerShell as the body. The session writes it into a command folder in the guest, waits for the output file and copies any screenshot taken while the command ran back to the host.POST /stopends the session.
scripts/vm-do.ps1 is the client. It sends one command or one file to the open session and prints the answer, or sends -Stop. It needs no elevation.
The guest repl.ps1
Section titled “The guest repl.ps1”scripts/guest/repl.ps1 runs inside the VM’s desktop session. It starts Flare with the test project, opens a topic so the pane has something to work on, and then loops. Every .ps1 file that appears in the command folder is run and its output written next to it, until a stop file appears.
It provides three helpers on top of the E2E harness.
| Helper | What it does |
|---|---|
Shot <name> |
Saves a screenshot of the whole virtual screen as a PNG. |
Tree |
Lists the UI Automation tree under the Flare window with names, control types and ids. |
UiClick <name> |
Invokes a control by its name. |
The harness adds Get-E2EState, Invoke-Ribbon, Invoke-RibbonMenu, Send-E2E and Wait-PaneDone. AI is off unless the copy of the file is named repl-ai.ps1, which vm-session.ps1 -Ai makes.
accept.ps1, the acceptance check
Section titled “accept.ps1, the acceptance check”scripts/accept.ps1 ties it together for one change.
- It reads the issue and fills
factory2/prompts/accept.mdwith the issue, its title, its body and what the implementer says it changed. - It takes a place in the VM line unless the caller already holds the slot.
- It opens a session with
vm-session.ps1and records the build proof line. - It clears the screenshot and command folders and runs the
acceptagent. The agent writes small step files and runs each one throughvm-do.ps1. Those are the only two things it can do. It reads only the screenshots it saved. Its timeout is 25 minutes and its limit is 60 turns. - It reads the structured result, writes
accept-result.jsonand prints each check with its evidence and any findings. - In a
finallyit releases the slot, stops the session and waits up to three minutes for the session file to disappear.
The exit code is 0 when no check failed and at least one passed, 1 when a check failed, and 2 when the VM or the agent could not be used. The factory tries a result of 2 again once. A second 2 stops the run.
The agent always adds a check named “A writer can use this without trouble” with a screenshot as evidence. It fails when the change leaves something hard to use, such as cut-off text, an unreachable control, a view that breaks at the pane’s default size, an unclear label or copy that is not plain. Problems that were already there go into findings, not checks.