~ knotless Search/
↑↓ move↵ open⇧↵ all results
0
0
main
695 lines · 58.6 KB Raw History
plan: 0011-windows-bidder-hcs
paused: >
  PAUSED by the user on 2026-10-16: no Windows work until the user resumes it. The M0 spikes on the user's
  desktop (hat-9) were running when the machine crashed; the user stopped the work. The cause of the crash is
  not known and was not diagnosed. Done: M0-REVISIT only (docs/ARCHITECTURE.md section 15). Every other item is
  todo and nothing it produced was committed; its experiments (an HCS VM driver, an HNS NAT network, a port
  forwarder, a WSL distro running a Linux market network, temporary services and accounts for HCS identity
  probes) were scratch work and their results are lost. The next worker must not start from those results.
  Before resuming: the user decides whether the desktop (a dual-boot machine the user also works on) is the host
  for these spikes at all, or a separate Windows machine is used; the spikes must run in small, reversible steps,
  one at a time, each cleaned up before the next, with no HNS network, forwarding or firewall change left behind
  between steps, and the Windows host must never be rebooted or signed out by the worker. What may still be left
  on the desktop, to be listed and removed only when the user says so: C:\swc (bin, guest, vms, wsl, image,
  versions, kcp-run, bao, probe, socialweb-computer checkout), the WSL distro socialweb-ubuntu and its holder
  process, the HNS network socialweb-nat0, the firewall rules socialweb-hcsfwd-guest and socialweb-listen-test,
  IP forwarding enabled on vEthernet (WSL (Hyper-V firewall)) and vEthernet (socialweb-nat0), and possibly the
  temporary socialweb-hcsprobe-* services, user and scheduled task if a probe was cut short.
written: 2026-10-11
queued: >
  QUEUED. Starts only after plan 0006 (docs/plans/0006-socialweb-computer-unified-binary.yaml) and plan
  0007 (docs/plans/0007-compute-provider-kcp.yaml) are complete on Linux, every item done with evidence
  and their acceptance checks ACC6-* and ACC7-* recorded, and after the plan 0008
  (docs/plans/0008-embedded-deno-and-typescript-denopods.yaml) items M1-DENO, M1-TS-CLOSURE,
  M2-DENOKCP-FORK, M2-DENOKCP-INPROC, M2-SERVICE-TEMPLATE and M4-MARKET-NETWORK-PORT are done. Then M0-REVISIT
  runs first; no other item may move to doing before it is done. Every item is executed ON a Windows
  host (decided by the user 2026-10-11); the executor works on that host, not on hat-8.
source: >
  The user's decisions of 2026-10-09..11: macOS and Windows bidder work is deferred out of plans 0006,
  0007 and 0008 into their own plans (this one for Windows, 0012 for macOS). Plans 0006 and 0007 build the
  cross-OS seams on Linux: Go interfaces, GOOS build-tagged files, and per-OS embedded assets compiled in
  only for the target OS. Expected seam names (they may differ as landed; M0-REVISIT reconciles): a VM
  host/runtime interface (Start, Inspect returning a Verdict Running|Lost|Unknown, Stop with a grace,
  List by owner) with one backend per OS; a host-unit/service-manager interface; an OS-neutral
  ComputeVM status.runtime {backend, ref, instance, pid?, startTime?}; a content-addressed binary install
  versions/<sha256>/; asset dirs runtime/<sha256>/ garbage-collected only when nothing references them.
  AS LANDED (M0-REVISIT, ARCHITECTURE section 14 "The Windows bidder: the seams as they landed"): the VM
  seam is vmruntime.Runtime (Prepare, Reserve with a Placer, Await, Inspect -> Observation, Shutdown, Stop,
  List, Cleanup, Watch, Load, CollectImages); the verdict is hostsvc.Verdict; the service manager is
  hostsvc.Manager (unit/argv shaped); status.runtime is {backend, ref=unit, instance=InvocationID, pid,
  startTime=ticks, hostBootID, vmmID}; the core installs to bin/<sha>/ (bin/current, bin/previous) and assets
  to runtime/<sha256>/. Read every "versions/<sha256>/" below as bin/<sha>/.
  Headless servers only. A VM survives a crash or upgrade of the core binary and any logout; a host
  reboot makes every VM Lost, replaced through a new RFP. The mechanism is the native one per OS. The
  invariant: no socialweb-computer code is in a running VM's process tree; the OS service manager or VMM
  owns the VM directly. On Windows that is an HCS compute system created with
  ShouldTerminateOnLastHandleClosed=false and no helper process; our code only does one-shot preparation
  (disks, network endpoint, the create call) and later opens, inspects and stops the system by id. All
  code, docs, architecture and plan items are changeable; the best solution wins.
state_rules:
  - TypeScript lives in this repository (decided by the user 2026-10-12): the closure's ten repositories were vendored with their history under ts/<repository>/ and the originals are frozen; a path this plan names under ts/ is changed here, never in the original.
  - "AGENTS.md overrides this plan where they differ (amended by M0-REVISIT 2026-10-16): write no unit tests (ignore every test_first field; an offline or livegate test named in an item is a manual live proof or, at the end, an integration test in test/integration), flip an item's status only (the git history is the evidence), put decisions and readings in docs/ARCHITECTURE.md sections 14 and 15."
  - "The Windows machine is the user's own desktop and Linux workstation (decided by the user 2026-10-16): it is never rebooted, shut down, hibernated or signed out, not even on request, and nothing that needs a restart is enabled. A proof that needs a reboot is replaced by its closest no-reboot equivalent (the compute system's owner killed, the compute system terminated so Inspect answers Lost) and the reboot-only part is blocked."
  - This file is the only state. Every step updates status and evidence.
  - status is one of todo, doing, done, blocked.
  - An item is done only when its evidence is recorded (commit SHAs, test names watched to fail and
    then pass, readings from the Windows host with UTC timestamps, the host's Windows edition and build
    named in each reading).
  - Open items are mirrored in NEXT.yaml (P11-*).
  - A decision marked "decided by Claude, user may veto" was not confirmed by the user; one marked
    "decided by the user 2026-10-11" was.
  - An item names its dependencies in depends_on; an item may not start while one of them is not done.
    Cross-plan dependencies are written <plan>:<item id>.
  - >
    M0-REVISIT must run before any other item moves to doing, and must be redone (its status back to
    doing, every item that depends on it re-checked) whenever plan 0006, 0007 or 0008 changes a seam,
    the ComputeVM schema, the asset pipeline, the controller or the install/upgrade flow after
    M0-REVISIT's recorded commit SHA.
  - >
    RFP flow is the spine (org-root CLAUDE.md). Every test or spike that needs a live guest drives the
    requester (runComputeContract) against a real local bidder through RFP, bid, accept and cloud-init
    user_data. No test, spike or code boots a guest any other way: no hand-made compute system, no
    hand-attached VHD, no hand-written authorized_keys. Zero ssh sessions to any guest.
ownership:
  owned: >
    All code in socialweb-computer-kcp (including the Windows backends and build-tagged files this plan
    adds), the forked kcp, deno-kcp and kcp-libs copies plans 0006 and 0008 vendor, and the Windows host
    the user provides for this plan. hono-compute-provider and atproto-market may be changed by commit;
    never pushed.
  not_owned: >
    The market protocol records and lexicons, and the core architecture: nodes and guests only ever born
    through requester -> RFP -> bid -> accept -> cloud-init user_data, reached only through the relay.
    Windows itself (vmcompute, HNS, SCM behaviour) is measured, never patched.
research:
  measured_on: >
    2026-10-11 on hat-8 (Linux) from this repo at f0460fd (main) and from published documentation only.
    NOTHING in this block was observed on a Windows host; every Windows behaviour below is a claim the
    M0 items measure. The lifecycle notes this plan was to be written from (scratchpad
    lifecycle-windows.md, lifecycle-synthesis.md, lifecycle-linux.md) were not on disk when it was
    written; M0-REVISIT re-reads whatever 0006/0007 recorded in their place.
  in_repo_today:
    - "internal/hostos already has a Windows half: proc_windows.go:23-40 process identity from OpenProcess + GetProcessTimes creation time, :62 Alive, :100 Terminate; lock_windows.go:19 LockFileEx; portrange_windows.go:21 netsh dynamic port range; sysproc_windows.go:18 DieWithParent has no Pdeathsig equivalent"
    - "docs/ARCHITECTURE.md:2223-2280 (The host platform): the Windows column is design and cross-compilation only (GOOS=windows go build ./... and go vet clean), no Windows binary has ever run; the gateway-side node substrate on Windows is qemu with -machine q35,accel=whpx:tcg and slirp networking. That is the clusterprovider's own node substrate, not a market bidder, and this plan does not change it."
    - "internal/portscan/scan_windows.go:1-15 shells out to netstat -ano"
    - "cmd/socialweb-nodeboot/main.go:37-38 the user_data reaches a firecracker guest on a second read-only ext4 disk labelled cidata (cloud-init NoCloud); :457 seed dir /var/lib/cloud/seed/nocloud"
    - "cmd/socialweb-nodeinit/main.go:14-16 the guest's PID 1 loads the transport module and waits for /dev/vda; under Hyper-V the SCSI disks arrive through hv_storvsc as /dev/sd*, so the init's device wait is per hypervisor"
    - "cmd/socialweb-nodeimage/main.go:42-45 the image builder extracts THIS host's own kernel and module tree; a Windows host has neither, so a Windows bidder's guest kernel and rootfs must be built elsewhere and carried as assets"
    - "kcp-libs impl/execrunner/supervisor.go:141 SysProcAttr{Setpgid: true}: the DenoPod runner does not compile for GOOS=windows (syscall.SysProcAttr has no Setpgid there), so plan 0008's forked runner needs a Windows half before run bidder can exist on Windows"
    - "atproto-market lib/common/cloud-init-common/mod.ts:110 UserDataModule, :135-153 the registry, :1225-1231 the registered modules; the guest-side modules detect arch with uname -m (:487-494, :739-746, :1101-1108), so an amd64 Windows guest is the arch every module already serves"
    - "atproto-market lib/requester-xrpc/mod.ts:881 runComputeContract; ts/atproto-market/test/bidder_container_integration_test.ts:132 the canonical local harness (local dispatcher, fake PLC, bidder, requester)"
    - "atproto-market lexicons/com/publicdomainrelay/temp/compute/vm.json:19-45 compute.vm has cpus, mem, disk, network, location, role, user_data and NO arch field"
  hcs:
    - "HCS ComputeSystem document carries Owner and ShouldTerminateOnLastHandleClosed (hcsshim internal/hcs/schema2/compute_system.go; https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference). hcsshim's own utility VMs set ShouldTerminateOnLastHandleClosed: true (hcsshim internal/uvm/create.go:413) and default Owner to the executable name (:230-246), so the false setting this plan depends on is not the path hcsshim exercises."
    - "Chipset.LinuxKernelDirect {KernelFilePath, InitRdPath, KernelCmdLine}, schema 2.2 (https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#LinuxKernelDirect); WSL2 boots its kernel this way and attaches ComPorts 0 and 1 to named pipes (microsoft/WSL src/windows/service/exe/WslCoreVm.cpp, read 2026-10-10)"
    - "HcsOpenComputeSystem(id, requestedAccess, &handle) re-attaches to an existing system by id (https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsopencomputesystem); HcsEnumerateComputeSystems takes a query (hcsshim internal/vmcompute: vmcompute.HcsEnumerateComputeSystems), which is how a restarted bidder lists what it owns"
    - "HcsGrantVmAccess adds an ACL entry for the VM's per-system virtual account to a file (https://learn.microsoft.com/en-us/virtualization/api/hcs/reference/hcsgrantvmaccess): every VHD and the kernel/initrd must be granted, or the worker cannot open them"
    - "HcsCreateComputeSystem's securityDescriptor parameter is reserved and must be NULL (https://learn.microsoft.com/virtualization/api/hcs/reference/HcsCreateComputeSystem); who may call it is decided by the vmcompute service's own access check, which no document states for a Hyper-V Administrators member who is not an Administrator. Podman v6 runs Hyper-V machines for Hyper-V Administrators members after a one-time admin registry setup, but through WMI/VMMS (libhvee), not HCS (https://blog.podman.io/?p=21919, https://github.com/containers/libhvee). Unproven for HCS: M0-HCS-ACCOUNT."
    - "hcsshim ext4/tar2ext4 writes an ext4 image from a tar in pure Go and can append a VHD footer (https://github.com/microsoft/hcsshim/blob/main/ext4/tar2ext4/tar2ext4.go), so rootfs and cidata VHDs need no mkfs.ext4 on Windows"
  hns:
    - "HNS/HCN: networks, endpoints, namespaces and policies through a JSON-schema C API; the service checks the ACL against the caller's token (https://learn.microsoft.com/en-us/windows-server/networking/technologies/hcn/hcn-top, https://learn.microsoft.com/en-us/virtualization/api/hcn/hns_schema). Network types include NAT and ICS (hcsshim hcn/hcnnetwork.go); flag EnableNonPersistent (8) makes a network vanish at reboot."
    - "WinNAT allows a limited number of NAT internal prefixes per host, and WSL2 and Docker Desktop each create one; a collision with an existing prefix is the expected failure (measured in M0-HNS-NETWORK, not assumed)."
  windows_services:
    - "Virtual accounts NT SERVICE\\<service> need no password and are local managed accounts (https://learn.microsoft.com/en-us/windows/security/identity-protection/access-control/service-accounts); golang.org/x/sys/windows/svc implements a service; golang.org/x/sys/windows/svc/mgr installs one"
    - "Job objects with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE terminate every process in the job when the last handle closes (https://learn.microsoft.com/en-us/windows/win32/procthread/job-objects): the Windows equivalent of deno children dying with the bidder"
    - "A running .exe cannot be overwritten or deleted on Windows; it can be renamed. versions/<sha256>/socialweb-computer.exe plus an SCM ImagePath change avoids touching the running file."
items:
  - id: M0-REVISIT
    milestone: M0
    title: Re-read what 0006, 0007 and 0008 actually landed and amend this plan to match before anything else
    depends_on: []
    decision: >
      Decided by the user 2026-10-11: everything is changeable and the plan follows what landed, not what
      was expected. Before any execution, on the Windows host's checkout: re-read the status and evidence
      of every item of plans 0006, 0007 and 0008, docs/ARCHITECTURE.md (at least sections 5, 9 The host
      platform, 12, 13), NEXT.yaml, and the code as landed: the VM host/runtime seam (its interface,
      Verdict type, Stop grace, List by owner), the host-unit/service-manager seam, the GOOS build-tagged
      files and which ones are stubs for windows, the per-OS asset pipeline (internal/assets, how a
      windows-only asset is staged and pinned in versions.json), the ComputeVM schema (status.runtime and
      its field managers), the ComputeVM controller's re-population rule, the install and upgrade flow
      (versions/<sha256>/, GC of runtime/<sha256>/), the DenoPod runner, the market network command and
      plan 0006 M0-SIZE-BUDGET's platform matrix (it said darwin carries client modes only and named no
      windows target; both are superseded by the user's 2026-10-11 decisions). Then amend THIS file: item
      ids, decisions, depends_on (cross-plan ids to the real landed ids), files (real package paths) and
      the acceptance checks, so every later item names code that exists. Anything 0006/0007 left
      Linux-only that a Windows backend needs (an interface method, a schema field) is written as a new
      item here, not patched in silently.
    files: ["docs/plans/0011-windows-bidder-hcs.yaml (this file)"]
    test_first: >
      None (reconciliation). The reading is a diff of this file before and after, plus `GOOS=windows
      GOARCH=amd64 go build ./... && go vet ./...` on the landed tree (expected green with stubs) and a
      list of every symbol in the seams that has no windows implementation.
    done_when: >
      This file amended and committed; evidence names the socialweb-computer-kcp commit SHA (and the
      hono-compute-provider and atproto-market SHAs) it was checked against, the list of renamed or added
      items, and the windows stub list.
    risks:
      - "If 0006/0007 change after this ran, the state rule sends this item back to doing; the evidence keeps every SHA it was run against."
    status: done
    evidence: []
  - id: M0-HOST-BASELINE
    milestone: M0
    title: Record the Windows host and run the landed binary's non-VM modes on it
    depends_on: [M0-REVISIT]
    decision: >
      Read and record: Windows edition, version and build (winver, Get-ComputerInfo), CPU virtualization
      and SLAT, which optional features are enabled (Microsoft-Hyper-V-All, VirtualMachinePlatform,
      HypervisorPlatform, Containers), vmcompute, vmms and hns service states and file versions, whether
      WSL2 or Docker Desktop is installed and the NAT prefixes they hold (Get-NetNat), Defender state,
      the BitLocker state, and the group membership of the executor's account. Then build the landed tree
      for windows/amd64 on this host and run, as an unprivileged user: `version --json`, `check-host`,
      kcp (as landed: the release kcp binary behind kine on sqlite, not in-process; neither has a
      windows/amd64 asset, so the reading is whether kcp v0.33.0 and kine build for windows/amd64 from
      source and run, each on port 0), the embedded deno (0008:M1-DENO) running one fixture entry with
      --cached-only, the windows bao release, and one DenoPod through the forked runner (expected: refuses,
      runner_windows.go is a stub). Each that fails is an item added here by name. Fallback if kcp or kine
      do not build or run on Windows: record the error and add an item that decides between a source
      build asset per tool and running the bidder's bootstrap layer elsewhere; decided by Claude, user may veto.
    files: ["evidence only; new items appended to this file for every failure"]
    test_first: "None (measurement). The reading is each command's output and exit status with UTC timestamps."
    done_when: "Host record committed in evidence; each of the five non-VM modes recorded as works or fails-with-item."
    risks:
      - "Windows Home has no Hyper-V role but has VirtualMachinePlatform (what WSL2 uses); whether HCS VM creation works with only VirtualMachinePlatform is recorded here because it decides which editions the installer accepts."
      - "The DenoPod runner (kcp-libs supervisor.go:141 Setpgid) is expected to fail to build for windows; that becomes M1-DENOPOD-RUNNER-WINDOWS, not a blocker for the M0 spikes, which may run the bidder's deno as a plain child in the scratch branch."
    status: todo
    evidence: []
  - id: M0-SCRATCH-BACKEND
    milestone: M0
    title: A scratch-branch HCS backend behind the landed VM host seam, renting one guest through the RFP flow
    depends_on: [M0-HOST-BASELINE]
    decision: >
      The live spikes need a guest, and a guest is only ever born through the RFP flow (org-root
      CLAUDE.md), so this item builds the smallest HCS backend that lets `socialweb-computer run bidder`
      on this host win an RFP and boot the guest from its cloud-init user_data. Scratch branch only;
      nothing here is merged; M1 items rebuild it test-first. Content: a guest kernel (x86_64, Hyper-V
      drivers built in: CONFIG_HYPERV, CONFIG_HYPERV_STORAGE (hv_storvsc), CONFIG_HYPERV_NET (hv_netvsc),
      CONFIG_HYPERV_VSOCKETS, CONFIG_SERIAL_8250 console, ext4 built in) built on hat-8 from the kernel
      source the Linux image pipeline uses; the rootfs from the Linux image pipeline converted to an ext4
      VHD by hcsshim ext4/tar2ext4 on this host; the cidata seed (user-data, meta-data) written by the same
      pure-Go path; a compute system document with Owner socialweb-computer-<handle>,
      ShouldTerminateOnLastHandleClosed false, Chipset.LinuxKernelDirect, two SCSI attachments, one network
      adapter on an HNS endpoint, ComPorts 0 to a named pipe; HcsGrantVmAccess on every file. Driven by
      runComputeContract against the market network or, if the market network does not yet run on Windows (M0-HOST-BASELINE),
      against the canonical harness shape of ts/atproto-market/test/bidder_container_integration_test.ts with
      the bidder pointed at its local dispatcher. The guest reaches onNetwork and the requester's iroh
      session answers; zero ssh. As landed the guest image has no initramfs: the scratch guest kernel
      boots root=/dev/sda from the rootfs VHD (hv_storvsc built in), the seed is the NoCloud cidata disk of
      internal/guestseed, and the fixed guest network of internal/guestnet is its network-config.
    files: ["scratch branch only: internal/vmruntime (hcs scratch backend behind vmruntime.Runtime), deploy/guest-kernel/hyperv-x86_64.config, the disk writer; evidence names the branch and SHA"]
    test_first: >
      The live rent itself: the requester's runComputeContract onNetwork callback fires within the boot
      timeout, the guest's cloud-init log (read from the COM1 pipe) shows every module of the user_data
      finished, zero ssh sessions. Recorded red first (e.g. the kernel without hv_storvsc built in hangs
      waiting for its root) and then green.
    done_when: "One guest rented through the RFP flow on this host reached onNetwork and was released with vm.delete 200; boot time and the guest's console log recorded."
    risks:
      - "The landed guest boots root=/dev/vda with no init of ours; under Hyper-V the root arrives as /dev/sda, so the boot arguments of the hyperv-x86_64 target say root=/dev/sda (the rootfs is the only disk on SCSI controller 0 LUN 0)."
      - "If LinuxKernelDirect is refused on this build (schema 2.2), the fallback is UEFI boot from the rootfs VHD with the kernel inside it (WslCoreVm.cpp's other path); recorded and taken into M1-GUEST-IMAGE-HV."
    status: todo
    evidence: []
  - id: M0-HCS-ACCOUNT
    milestone: M0
    title: Can a non-admin member of Hyper-V Administrators create, open and stop HCS compute systems and HNS endpoints
    depends_on: [M0-SCRATCH-BACKEND]
    decision: >
      Measure, with the scratch bidder of M0-SCRATCH-BACKEND run as each candidate identity in turn:
      (a) a virtual account NT SERVICE\socialweb-computer-bidder-<handle> added to Hyper-V Administrators
      and nothing else, (b) a plain local user in Hyper-V Administrators, (c) LocalSystem. For each:
      HcsEnumerateComputeSystems, HcsCreateComputeSystem (through a real RFP rent), HcsOpenComputeSystem,
      HcsShutdownComputeSystem/HcsTerminateComputeSystem, HcsGrantVmAccess, HcnCreateEndpoint and
      HcnDeleteEndpoint on an existing network, HcnCreateNetwork. Record each HRESULT. The preferred
      outcome is (a) for everything except HcnCreateNetwork, which the installer does once as admin.
      Fallback decisions if (a) fails: first, LocalSystem for the bidder service with every deno child
      started under a restricted token (CreateRestrictedToken dropping Administrators and SeDebug, a low
      integrity level, inside the kill-on-close job) so TypeScript never runs as SYSTEM; second, if HCS is
      unusable without admin and LocalSystem is refused by the user, the WMI/VMMS path (Msvm_* through
      libhvee's shape) with persistent VMs that a startup sweep deletes after a reboot so reboot = Lost
      still holds. Which fallback was taken is recorded and asked of the user (questions_for_user).
    files: ["evidence only (HRESULT table per identity)"]
    test_first: "None (measurement). The table is the reading: identity x call -> HRESULT, with the UTC time and Windows build."
    done_when: "The table is complete for all three identities and the chosen identity is written into M2-INSTALLER-WINDOWS and M2-BIDDER-SERVICE-WINDOWS."
    risks:
      - "Membership changes need a new logon token: a service must be restarted, a user must log off and on, before a membership reading counts."
    status: todo
    evidence: []
  - id: M0-HCS-SURVIVAL
    milestone: M0
    title: A compute system with ShouldTerminateOnLastHandleClosed=false outlives its creator; what vmcompute restart does
    depends_on: [M0-SCRATCH-BACKEND]
    decision: >
      Safety first (the machine is the user's own): before (3) and (4) read whether any WSL distribution,
      Docker or other HCS system is running; restart vmcompute or hns only when none is, else record the
      refusal-to-risk as the reading and block that part. Never a reboot.
      With a guest rented through the RFP flow by the scratch bidder: (1) TerminateProcess the bidder
      (taskkill /F /PID) and read, every 5 s for 10 minutes, the compute system's State and RuntimeId from
      a fresh HcsOpenComputeSystem in another process, plus the guest's onNetwork heartbeat and the
      requester's tunnel; (2) repeat with the bidder stopped through SCM; (3) Restart-Service vmcompute
      while the guest runs and record whether the system, its RuntimeId and its vmwp.exe survive, and
      whether HcsOpenComputeSystem still works after; (4) Restart-Service hns and record the endpoint and
      the guest's network. Fallback if (1) fails (the system dies with its creator): the VM's owner becomes
      a dedicated, install-time SCM service per bidder whose only code is the create call and a handle
      held open (this breaks the 'no socialweb code in the VM's tree' invariant and is asked of the user
      before it is built); if (3) kills guests, a vmcompute restart is treated like a reboot (every
      ComputeVM Lost) and documented.
    files: ["evidence only"]
    test_first: "None (measurement). Readings: state/RuntimeId timeline across each event, UTC timestamps."
    done_when: "Each of the four events has a recorded outcome; the Verdict mapping for M1-VMHOST-HCS is written from them."
    risks: ["vmcompute restart may be refused while systems run; the refusal is the reading."]
    status: todo
    evidence: []
  - id: M0-HCS-IDENTITY
    milestone: M0
    title: Re-attach by id, list by Owner, and what makes a VM 'the same VM' across a bidder restart
    depends_on: [M0-SCRATCH-BACKEND]
    decision: >
      Measure: HcsEnumerateComputeSystems with a query naming Owners [socialweb-computer-<handle>] returns
      exactly this bidder's systems and none of WSL's or Docker's; HcsGetComputeSystemProperties returns
      RuntimeId, State and Owner; RuntimeId is stable across a bidder restart and changes if a system with
      the same Id is recreated. Decision this item writes (decided by Claude, user may veto):
      status.runtime = {backend: hcs, ref: <compute system Id, a GUID the controller chooses and records
      before the create call>, instance: <RuntimeId>}; pid and startTime are omitted on Windows because the
      worker process is not ours. Verdict: Running iff open succeeds AND Owner matches AND RuntimeId equals
      status.runtime.instance AND State is Running; Lost iff open returns 'not found' or RuntimeId differs
      or State is Stopped; Unknown on any other error (access denied, vmcompute unavailable), which the
      controller retries and never turns into a release.
    files: ["evidence only; the Verdict table is copied into M1-VMHOST-HCS's decision"]
    test_first: "None (measurement)."
    done_when: "Enumerate/open/properties outputs recorded before and after a bidder restart; Verdict table recorded."
    risks: []
    status: todo
    evidence: []
  - id: M0-HNS-NETWORK
    milestone: M0
    title: An HNS NAT network for guests, an endpoint per guest, tenant isolation, and coexistence with WSL and Docker
    depends_on: [M0-HCS-ACCOUNT]
    decision: >
      As landed every guest has the same fixed network (internal/guestnet: guest 172.30.0.2/30, gateway
      172.30.0.1, MAC 06:00:ac:1e:00:02, and it dials the constant host address 192.0.2.1); there is no
      network index allocator and user_data is final at create. So also measure whether one HNS network per
      guest can carry that identical plan (the host side 172.30.0.1 of each guest on its own isolated
      network, and 192.0.2.1 reaching the bidder's listeners), or whether the Windows guest needs a
      per-backend network-config (a seam change in guestseed/guestnet, to be written as a new item).
      Measure: HcnCreateNetwork of type NAT with a /24 the installer chooses (refusing a prefix any
      existing Get-NetNat or HNS network holds), with and without EnableNonPersistent; who may create it
      (M0-HCS-ACCOUNT's table); an endpoint per guest created at Start and deleted at Stop versus an
      install-time endpoint pool; whether two RFP-born guests on the same NAT network reach each other
      (tenant isolation) and whether an endpoint ACL policy (HNS ACL policy, Action Block, RemoteAddresses
      the network's prefix except the gateway) stops it; which host address a guest dials to reach the
      bidder's services (the NAT gateway address; the bidder's onNetwork, OIDC issuer and JSR listeners
      must bind it, and pointGuestAtHost from compute-provider-common must name it); and the Windows
      Firewall rule needed for guest -> gateway traffic. With WSL2 and Docker Desktop present and running,
      record whether our network coexists. Fallback if isolation by ACL fails: one NAT network per guest
      (prefix pool), or the ICS network type; recorded.
    files: ["evidence only"]
    test_first: "None (measurement): guest A -> guest B probe result (from cloud-init runcmd output of RFP-born guests, read on the COM pipe), guest -> gateway result, before and after the ACL."
    done_when: "Network creation privilege, isolation result, the guest's host address and the coexistence result recorded; the network decision written into M1-NETWORK-HNS."
    risks:
      - "A probe between two guests needs both born through the RFP flow, with the probe in their user_data; no ssh into either."
    status: todo
    evidence: []
  - id: M0-COM-CONSOLE
    milestone: M0
    title: The guest console on a COM-port named pipe, reconnected after the core binary restarts
    depends_on: [M0-HCS-SURVIVAL]
    decision: >
      Measure: ComPorts 0 bound to \\.\pipe\socialweb-vm-<id>-com1; the bidder connects as the pipe
      client and writes console lines to <workDir>/console.log; after TerminateProcess of the bidder and
      its restart, can a new client connect to the same pipe of the running VM, and is output written
      while nobody was connected lost or buffered? Decision if reconnect works: the controller reconnects
      at re-population. If it does not: console output after a core restart is lost until the guest's next
      reboot (which never happens; a Lost VM is replaced), and the console is diagnostics only; asked of the
      user (questions_for_user).
    files: ["evidence only"]
    test_first: "None (measurement)."
    done_when: "Reconnect outcome and the size of the gap recorded."
    risks: []
    status: todo
    evidence: []
  - id: M0-BINARY-SWAP
    milestone: M0
    title: Upgrading a running service whose exe is locked, through versions/<sha256>/ and the SCM ImagePath
    depends_on: [M0-HCS-SURVIVAL, M0-HCS-ACCOUNT]
    decision: >
      With the scratch bidder installed as an SCM service at bin/<sha A>/socialweb-computer.exe and a
      guest rented: write bin/<sha B>/socialweb-computer.exe, ChangeServiceConfig the ImagePath,
      restart the service; read: the guest's RuntimeId unchanged, the new process's version, the old exe
      deletable only after the service stopped using it. Also measure: Defender scanning a newly written
      exe (time to first start), and whether the virtual account may rewrite its own service's config
      (expected no: the upgrade runs elevated or through a service DACL granting SERVICE_CHANGE_CONFIG to
      the account; recorded). Fallback if the ImagePath change is refused for the chosen identity: the
      upgrade command runs elevated (one admin prompt per upgrade), recorded; a fixed-path launcher exe is
      not introduced, because it would be a second binary to upgrade.
    files: ["evidence only"]
    test_first: "None (measurement)."
    done_when: "Swap reading recorded: RuntimeId before/after, service downtime, Defender delay."
    risks: []
    status: todo
    evidence: []
  - id: M0-GATE
    milestone: M0
    title: Write every M0 outcome into the M1+ items and stop for the user where a fallback was taken
    depends_on: [M0-HCS-ACCOUNT, M0-HCS-SURVIVAL, M0-HCS-IDENTITY, M0-HNS-NETWORK, M0-COM-CONSOLE, M0-BINARY-SWAP]
    decision: >
      Amend M1+ decisions and files from the M0 readings; mark any item made unnecessary as blocked with
      the reason; if any M0 item took a fallback that changes an invariant (a helper owning the VM, WMI
      instead of HCS, LocalSystem for the bidder), set this item blocked until the user answers the
      matching question. The scratch branch is deleted after its SHA is recorded.
    files: ["docs/plans/0011-windows-bidder-hcs.yaml"]
    test_first: "None (decision record)."
    done_when: "Plan amended and committed; open user questions answered or none needed."
    risks: []
    status: todo
    evidence: []
  - id: M1-CONTROLLER-RUNTIME-OWNED
    milestone: M1
    title: The ComputeVM controller asks the VM runtime for liveness, orphans and events instead of the unit manager and HostProcess children
    depends_on: [M0-GATE]
    decision: >
      Added by M0-REVISIT. internal/computevm calls hostsvc.Manager.Inspect for liveness (live, placer,
      exits), lists HostProcess children for orphans, builds the VM slice name from vmruntime.SliceName
      and watches unit events, and Reserve hands a Placer that creates the HostProcess child. An HCS system
      has none of these. The seam becomes: Runtime gets the liveness and watch calls the controller needs
      (a runtime that places no HostProcess says so in Capabilities), the Linux runtime keeps its
      Placer path unchanged, and the controller branches on the capability, not on GOOS. Status.runtime
      stays the landed shape; on Windows backend=hcs, ref=compute system Id, instance=RuntimeId, pid and
      startTime empty, hostBootID empty.
    files: ["internal/computevm/computevm.go", "internal/vmruntime/vmruntime.go"]
    test_first: "None (AGENTS.md: no tests). Proof: the Linux market network still rents a guest."
    done_when: "The Linux path is unchanged in a live rent and a controller can run with a runtime that places no HostProcess."
    risks: []
    status: todo
    evidence: []
  - id: M1-KCP-WINDOWS
    milestone: M1
    title: kcp, kine and the other bootstrap executables for windows/amd64 as pinned assets
    depends_on: [M0-GATE, "0006:M1-ASSETS", "0006:M2-KCP-SLOTS"]
    decision: >
      Added by M0-REVISIT. The landed bidder runs the unpatched release kcp v0.33.0 behind kine v0.17.1 (the
      cgo sqlite build) and our front; the assets table has no windows/amd64 entry for kcp, kine, deno,
      bao or the plugins (versions.json platforms["windows/amd64"] is status seam with no assets), and
      the upstream kcp release carries no windows archive. M0-HOST-BASELINE reads whether kcp and kine
      build and run on Windows; this item then pins what it found: a source build per tool on the
      release builder (CGO_ENABLED=0 where possible, a pure-Go sqlite for kine), deno and bao from their
      windows/amd64 releases, each by sha256 in versions.json, staged by the same asset pipeline.
    files: ["internal/versions/versions.json (windows/amd64 assets)", "internal/assets", "scripts/build-socialweb-computer.sh"]
    test_first: "None (AGENTS.md: no tests)."
    done_when: "The windows/amd64 asset list is pinned and the Windows host runs a bootstrap layer (kine, kcp, front) from those assets."
    risks: ["kcp may not build for windows at all; if so the bidder's kcp runs elsewhere and this plan stops for the user."]
    status: todo
    evidence: []
  - id: M1-VMHOST-HCS
    milestone: M1
    title: The Windows backend of the VM host seam over HCS, with no helper process
    depends_on: [M0-GATE, M1-CONTROLLER-RUNTIME-OWNED, "0007:M2-CONTROLLER", "0007:M1-API"]
    decision: >
      hcs_windows.go in internal/vmruntime (build tag windows, ForHost returns it) implements the landed
      vmruntime.Runtime (Prepare, Reserve, Await, Inspect, Shutdown, Stop, List, Cleanup, Watch, Load,
      CollectImages; Reserve ignores the Placer because an HCS system has no HostProcess child; Watch polls
      HcsEnumerateComputeSystems): Start writes the compute system
      document (Owner socialweb-computer-<handle>, ShouldTerminateOnLastHandleClosed false, LinuxKernelDirect,
      SCSI rootfs + cidata, the HNS endpoint, ComPorts 0 pipe, memory and processor count from the
      ComputeVM spec), grants VM access to each file, creates, starts, then closes its handle; it records
      status.runtime {backend hcs, ref Id, instance RuntimeId}. Inspect returns the Verdict table of
      M0-HCS-IDENTITY. Stop(grace) calls HcsShutdownComputeSystem, waits up to grace for State Stopped,
      then HcsTerminateComputeSystem, then deletes the endpoint and the work dir. List(owner) is
      HcsEnumerateComputeSystems by Owner. Calls go through golang.org/x/sys/windows LazySystemDLL on
      computecore.dll (vmcompute.dll where the build lacks it) with a small owned wrapper, CGO_ENABLED=0;
      hcsshim is imported only for ext4/tar2ext4 (decided by Claude, user may veto: hcsshim's hcs package is
      container-oriented and pulls a large tree). An HCS system of this Owner with no ComputeVM is the
      controller's orphan rule (0007:M2-CONTROLLER), never touched if its Owner differs.
    files: ["internal/vmruntime/hcs_windows.go", "internal/hcsapi/ (windows-only wrapper)"]
    test_first: >
      Offline on any OS: TestTheHcsDocumentForAComputeVMSetsOwnerAndNeverTerminatesOnLastHandle (golden
      JSON) and TestTheHcsVerdictTable (fake API: not found -> Lost, RuntimeId differs -> Lost, access
      denied -> Unknown). Windows-only behind livegate: TestAnHcsSystemOutlivesItsStarterAndIsAdoptedByTheNext
      run through the RFP-flow fixture of 0007 (a rented guest, its bidder killed, a new bidder adopts by
      Id). Red first.
    done_when: "Offline tests green in check.sh on Linux; the livegate test green on the Windows host with readings."
    risks: ["computecore.dll exports differ by Windows build; the wrapper resolves each proc lazily and check-host names any missing one."]
    status: todo
    evidence: []
  - id: M1-NETWORK-HNS
    milestone: M1
    title: Guest networking on Windows through one HNS NAT network and an endpoint per guest
    depends_on: [M0-GATE, M1-VMHOST-HCS]
    decision: >
      From M0-HNS-NETWORK. As landed there is no network index: every guest has the fixed plan of
      internal/guestnet and dials 192.0.2.1. M0-HNS-NETWORK decides whether the Windows backend satisfies
      that plan with an HNS network per guest, or the guest's network-config becomes per backend (a new
      seam in guestseed/guestnet). The endpoint (MAC guestnet.GuestMAC) and its isolation policy are made at
      Start and deleted at Stop; the bidder's guest-facing listeners bind the address the guest dials. Linux
      pasta is untouched.
    files: ["internal/vmruntime/hns_windows.go", "internal/hcnapi/", "internal/guestnet (per-backend plan, if M0 says so)"]
    test_first: "Offline: TestTheEndpointDocumentCarriesTheIsolationAcl (golden). Livegate on Windows: TestTwoRfpGuestsOnOneBidderCannotReachEachOther (probe in each guest's user_data, result on onNetwork). Red first."
    done_when: "Both green; the isolation reading recorded."
    risks: []
    status: todo
    evidence: []
  - id: M1-GUEST-IMAGE-HV
    milestone: M1
    title: The guest kernel, initramfs and rootfs for Hyper-V, built on Linux, carried as windows-only assets, written to VHDs in pure Go
    depends_on: [M0-GATE, "0006:M1-ASSETS", "0007:M1-GUEST-IMAGE", "0007:M2-BACKPORT"]
    decision: >
      As landed (0007 M1-GUEST-IMAGE) a guest image is a guestImages target of versions.json built on any
      Linux host by scripts/build-guest-image.sh, pinned by the sha256 of each output (vmlinux, rootfs.ext4,
      image.json), never embedded, and fetched and verified by internal/guestimage; there is no initramfs
      and no nodeinit in the VM path. So this item adds the reserved target hyperv-x86_64: an x86_64
      kernel config deploy/guest-kernel/hyperv-x86_64.config with the Hyper-V drivers built in (the M0
      config) and boot arguments root=/dev/sda, the same Ubuntu base rootfs and preinstall manifest. On the
      host the rootfs ext4 is wrapped as a fixed VHD (a VHD footer appended, pure Go) once per image
      fingerprint under runtime/<sha256>/, the per-guest cidata disk is the landed guestseed image the same
      way, and a guest's rootfs is a copy (differencing VHDX through virtdisk.dll is a later optimisation).
      Whether this kernel is a separate hypervisor target or one multi-driver x86_64 kernel shared with
      firecracker is the user's question; until answered, a separate target (decided by Claude, user may veto).
    files: ["scripts/build-guest-image.sh (hyperv target)", "deploy/guest-kernel/hyperv-x86_64.config", "internal/guestdisk (vhd wrapper, windows copy)", "internal/guestimage", "internal/versions/versions.json (guestImages target and pins)"]
    test_first: >
      Offline: TestTheHypervKernelConfigHasEveryDriverBuiltIn (parses the shipped .config: HYPERV,
      HYPERV_STORAGE, HYPERV_NET, HYPERV_VSOCKETS, EXT4_FS =y), TestTar2Ext4ProducesAnImageCloudInitReadsAsCidata
      (label and files read back with a pure-Go ext4 reader), TestTheInitFindsItsRootByLabel. Red first.
    done_when: "Tests green; the windows/amd64 binary carries the image and `version --json` lists it with sha256."
    risks: ["The rootfs is the largest asset; its size is budgeted in M2-RELEASE-WINDOWS."]
    status: todo
    evidence: []
  - id: M1-DENOPOD-RUNNER-WINDOWS
    milestone: M1
    title: The forked DenoPod runner on Windows - a job object per run, identity by creation time
    depends_on: [M0-GATE, "0008:M2-DENOKCP-FORK"]
    decision: >
      The runner is internal/kcplibs/impl/execrunner, and the OS part is its osProcessHost seam (prepare,
      killGroup, termGroup, alive, startTicks), whose runner_windows.go refuses today. It gets a real
      body: each run is a process created
      suspended, assigned to its own job object (JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE), then resumed; stop
      is TerminateJobObject; recovery uses internal/hostos.ProcStart (GetProcessTimes creation time). Every
      per-run job is nested in the bidder service's own kill-on-close job (M2-BIDDER-SERVICE-WINDOWS), so a
      crash of the core binary kills every deno process and no VM (VMs are not in any job of ours).
    files: ["internal/kcplibs/impl/execrunner/runner_windows.go", "internal/hostos/job_windows.go"]
    test_first: "Windows livegate: TestAKilledRunnerParentTakesEveryDenoChildWithIt and TestARunIsRecoveredByPidAndCreationTime. Red first (today the package does not build for windows)."
    done_when: "GOOS=windows build of the runner green; both tests green on the Windows host."
    risks: ["Deno on Windows spawns its own subprocesses rarely; any it spawns inherit the job unless CREATE_BREAKAWAY_FROM_JOB is allowed, which the job forbids."]
    status: todo
    evidence: []
  - id: M1-HOSTUNIT-SCM
    milestone: M1
    title: The hostsvc.Manager backend on Windows - every non-VM host process is a child of the bidder service in a kill-on-close job
    depends_on: [M0-GATE, "0006:M2-HOST-UNITS", M1-KCP-WINDOWS]
    decision: >
      Amended by M0-REVISIT. As landed, hostsvc.Manager is argv and cgroup shaped, hostsvc.Open refuses on
      !linux, ParseBackend knows only systemd-user and proc, the proc backend (devsup) is Linux-only, and a
      bidder's bootstrap layer (kine, kcp slot, front) plus its OpenBao are units. Only VMs must outlive the
      core, and HCS owns them. So (decided by Claude, user may veto) the Windows backend, named job, starts
      each unit as a child process of the bidder service inside the kill-on-close job object, reports
      Inspect from the process handle, and a restart of the core re-creates its children (kcp state
      persists in kine's sqlite, OpenBao on disk, VMs in HCS). No SCM service per child, no
      SC_MANAGER_CREATE_SERVICE, no admin after install. Capabilities say EnforcesLimits false and no
      FDStore or PrivateHostname, so hostsvc.RefuseUnsupported refuses those specs by name.
    files: ["internal/hostsvc/job_windows.go", "internal/hostsvc/hostsvc.go (BackendJob)", "internal/hostos/job_windows.go"]
    test_first: "None (AGENTS.md: no tests)."
    done_when: "The bidder's bootstrap layer and OpenBao start as job children on Windows and a killed core takes every one of them with it."
    risks: []
    status: todo
    evidence: []
  - id: M2-BIDDER-SERVICE-WINDOWS
    milestone: M2
    title: run bidder as a Windows service, its deno children dying with it, its VMs not
    depends_on: [M1-VMHOST-HCS, M1-NETWORK-HNS, M1-GUEST-IMAGE-HV, M1-DENOPOD-RUNNER-WINDOWS, M1-HOSTUNIT-SCM, M1-KCP-WINDOWS, "0007:M5-RUN-BIDDER"]
    decision: >
      As landed the bidder core is Type=notify with a watchdog (internal/notify); under SCM the same Ready
      and Watchdog calls become service status reports.
      `socialweb-computer run bidder --handle <h>` detects it was started by SCM and runs under
      golang.org/x/sys/windows/svc (stop, shutdown and preshutdown handled with a bounded drain), service
      name socialweb-computer-bidder-<h>, account from M0-HCS-ACCOUNT (preferred NT SERVICE\socialweb-computer-bidder-<h>),
      state dir %ProgramData%\socialweb-computer\bidder\<h>, recovery actions restart after 5 s. At start
      the process creates a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE and assigns itself only its
      deno children (not itself, so SCM's kill of the service closes the last handle and the job takes the
      children). Everything else is plan 0007's run bidder unchanged: in-process kcp on port 0, the bidder
      set, the vault, token rotation, the ComputeVM controller with the hcs backend, the bidder DenoPod with
      --compute-provider-kcp.
    files: ["internal/stack/bidder (svc entry, windows file)", "cmd/socialweb-computer (service detection)"]
    test_first: "Windows livegate: TestTheBidderServiceKilledLeavesNoDenoAndEveryVm (rent through the RFP flow, taskkill /F the service process: no deno.exe of this bidder remains, the HCS system's RuntimeId unchanged). Red first."
    done_when: "Green; Get-Service and the HCS enumerate readings recorded."
    risks: []
    status: todo
    evidence: []
  - id: M2-INSTALLER-WINDOWS
    milestone: M2
    title: One elevated install step - features, account, group, HNS network, firewall, Defender, services
    depends_on: [M2-BIDDER-SERVICE-WINDOWS]
    decision: >
      `socialweb-computer install bidder --handle <h>` run once from an elevated prompt (the user's
      question asks whether a one-time admin step is acceptable; recommended yes), idempotent, every step
      observed before it is changed: (1) the minimum optional feature M0-HOST-BASELINE proved
      (Microsoft-Hyper-V-All or VirtualMachinePlatform) via DISM, printing 'reboot required' and stopping
      if one was enabled; (2) the service account and its Hyper-V Administrators membership; (3) the HNS
      NAT network with a prefix no existing NAT holds, refused with the conflicting owner named (WSL,
      Docker) rather than overriding it; (4) a Windows Firewall rule allowing the guest prefix to reach the
      bidder's guest-facing ports on the gateway address only; (5) Defender exclusions for the state dir's
      runtime\ and vms\ (VHD IO) and nothing else; (6) the binary into versions\<sha256>\, the bidder and
      vault services with their DACLs; (7) check-host. `uninstall bidder` reverses each, refusing while a
      ComputeVM is Running unless --drain.
    files: ["internal/install/system_windows.go (the install.System of the landed install flow; UserManager has no Windows meaning, the bidder service stands in for the user unit)", "internal/install/templates (Windows service definition)", "docs/bidder-host-windows.md"]
    test_first: "Offline: TestTheInstallPlanIsObservedBeforeEachChange (fake system: a second run changes nothing); Windows livegate: TestInstallTwiceThenUninstallLeavesTheHostAsFound (feature step skipped). Red first."
    done_when: "Both green; a clean-host install reading recorded end to end with timings."
    risks: ["Group Policy may block Defender exclusions or service DACL changes; each refusal is printed with the policy name, not ignored."]
    status: todo
    evidence: []
  - id: M2-BINARY-UPGRADE-WINDOWS
    milestone: M2
    title: socialweb-computer upgrade on Windows - a new versions/<sha256>/ exe and an ImagePath change, VMs untouched
    depends_on: [M2-INSTALLER-WINDOWS, "0006:M3-HOST-INSTALL"]
    decision: >
      The landed upgrade flow (0006:M3-HOST-INSTALL) gets its windows half from M0-BINARY-SWAP: verify
      the new binary's manifest and sha256, write versions\<sha256>\socialweb-computer.exe (never touching
      the running file), ChangeServiceConfig ImagePath for the bidder service, restart it; versions and
      runtime\<sha256>\ directories no service and no ComputeVM names are deleted by GC. A downgrade is
      the same command with an older verified binary.
    files: ["cmd/socialweb-computer upgrade (windows file)", "internal/install/windows/"]
    test_first: "Windows livegate: TestAnUpgradeKeepsEveryHcsRuntimeIdAndRemovesTheOldVersionOnce. Red first."
    done_when: "Green; RuntimeId before/after and service downtime recorded."
    risks: []
    status: todo
    evidence: []
  - id: M2-RELEASE-WINDOWS
    milestone: M2
    title: windows/amd64 in the release matrix, with its own asset list and size budget
    depends_on: [M1-GUEST-IMAGE-HV, "0006:M4-BUILD", "0006:M0-SIZE-BUDGET"]
    decision: >
      The reproducible build (0006:M4-BUILD) gains windows/amd64 carrying: the core binary, deno
      windows/amd64, bao windows/amd64, the Hyper-V guest image, the TypeScript closure (0008), and no
      k3s, kine, postgres, firecracker or pasta (the gateway modes stay linux-only on Windows unless a
      later plan says otherwise; `serve gateway kcp` refuses by name). Budget = measured sizes recorded in
      versions.json and enforced by check.sh. When the matrix entry is published (from the start as
      build-only, or only once ACC11-* pass) is the user's question; recommended: build and size-check
      from M2 on, publish after ACC11-* pass.
    files: ["internal/versions/versions.json (windows/amd64 entry)", "scripts/build-socialweb-computer.sh", "deploy/check.sh"]
    test_first: "check.sh --full: TestTheReleaseBinaryFitsItsSizeBudget for windows/amd64 red with a 1-byte budget, then green; two clean windows builds byte-identical."
    done_when: "Budget recorded and enforced; reproducibility reading recorded."
    risks: ["Cross-building on Linux works for CGO_ENABLED=0; anything that needs cgo on Windows is refused at review."]
    status: todo
    evidence: []
  - id: M3-LIVE-RENT
    milestone: M3
    title: A guest rented through the RFP flow from a Windows bidder, zero ssh
    depends_on: [M2-INSTALLER-WINDOWS, "0008:M4-MARKET-NETWORK-PORT"]
    decision: >
      On the Windows host, with the installed bidder service and the market network (0008's market network command,
      or the canonical TS harness if M0 recorded the market network cannot run on Windows): runComputeContract posts
      compute.vm and a signed market.rfp, the Windows bidder bids, the requester accepts, the guest is born
      from cloud-init user_data in an HCS system, reaches onNetwork, and the requester reaches it only
      through the relay tunnel. Read: ComputeVM Running with status.runtime {hcs, Id, RuntimeId}; the bid
      config, receipt and onNetwork records field-for-field the shapes the firecracker provider writes.
    files: ["test/integration (a Windows integration test, written only at the end per AGENTS.md; until then a manual live run)"]
    test_first: "The live test, red until M2 exists."
    done_when: "PASS with UTC timestamps on the Windows host; zero ssh sessions in every log."
    risks: []
    status: todo
    evidence: []
  - id: M3-LIVE-SURVIVE
    milestone: M3
    title: Bidder crash and bidder upgrade keep the same VM, then release deletes it
    depends_on: [M3-LIVE-RENT, M2-BINARY-UPGRADE-WINDOWS]
    decision: >
      After M3-LIVE-RENT's rent: TerminateProcess the bidder service, SCM restarts it, read the ComputeVM
      Running with the same RuntimeId, the same issuer URL, the guest's next onNetwork accepted; then
      `socialweb-computer upgrade --binary <a build with a different closure>`, same reads plus every
      DenoPod on the new closure; then releaseCompute's vm.delete answered 200, the ComputeVM gone,
      HcsEnumerateComputeSystems by Owner empty, the endpoint and the work dir's VHDs gone.
    files: ["test/integration (a Windows integration test, written only at the end per AGENTS.md; until then a manual live run)"]
    test_first: "That test, red until M2 exists."
    done_when: "PASS readings recorded with UTC timestamps; zero ssh sessions."
    risks: []
    status: todo
    evidence: []
  - id: M3-LIVE-REBOOT-LOST
    milestone: M3
    title: A host reboot makes every VM Lost, never booted again, and the receipt still releases
    depends_on: [M3-LIVE-SURVIVE]
    decision: >
      AMENDED 2026-10-16 (the user forbids any reboot of this machine): the reboot itself is never run and
      that part stays blocked. The equivalent that is run: with a guest rented through the RFP flow, stop
      the bidder service, terminate the guest's compute system (the HCS system is gone, as after a reboot),
      start the service; the controller reads Lost for that Id, creates no system, and the receipt's
      release answers vm.delete 200 and removes the Lost ComputeVM and its work dir. What a real reboot
      would add and stays unproven: the service's automatic start at boot, HNS network persistence across
      a boot, and the boot-id change. Original text: Restart-Computer. After boot the bidder service starts
      (automatic start), the controller re-populates: the ComputeVM is Lost (HCS has no system with that
      Id), no compute system is created for it, and the requester's releaseCompute of that receipt gets
      vm.delete 200 and the Lost ComputeVM and its work dir are deleted. The requester-side replacement
      through a new RFP is plan 0005 M3-NODE-LOSS's and is not re-proved here.
    files: ["test/integration (a Windows integration test, written only at the end per AGENTS.md)"]
    test_first: "That test, red until M2 exists."
    done_when: "PASS readings recorded: pre-reboot Running, post-reboot Lost, no new HCS system, release 200."
    risks: ["If the HNS network is non-persistent it must be recreated before the controller allocates; the installer's network is persistent unless M0 decided otherwise."]
    status: todo
    evidence: []
  - id: M4-DOCS
    milestone: M4
    title: The Windows bidder in ARCHITECTURE, measured, and the host guide
    depends_on: [M3-LIVE-REBOOT-LOST]
    decision: >
      docs/ARCHITECTURE.md's host platform section gains a measured Windows bidder column (HCS, HNS, SCM,
      job objects, VHD writer) replacing 'design and cross-compilation only' for the parts this plan
      measured, and states what stays unmeasured; docs/bidder-host-windows.md is the operator guide
      (supported editions and builds, the one elevated install, upgrade, uninstall, what a reboot does).
    files: ["docs/ARCHITECTURE.md", "docs/bidder-host-windows.md"]
    test_first: "None (documentation); every claim cites an evidence reading of this plan."
    done_when: "Committed; NEXT.yaml P11-* items closed and moved to ARCHITECTURE section 15."
    risks: []
    status: todo
    evidence: []
acceptance:
  where: "the Windows host the user provides; build steps also on hat-8"
  checks:
    - id: ACC11-RFP-ONLY
      text: Every guest booted in every run of this plan was born from cloud-init user_data through requester -> RFP -> bid -> accept, and zero ssh sessions to any guest appear in any log.
      status: todo
      evidence: []
    - id: ACC11-NO-OUR-CODE-IN-VM-TREE
      text: For every running guest, the process owning it is vmwp.exe under vmcompute; no socialweb-computer or deno process is its ancestor, and a TerminateProcess of every socialweb-computer and deno process leaves every guest Running with the same RuntimeId.
      status: todo
      evidence: []
    - id: ACC11-SURVIVE
      text: A bidder crash and a bidder upgrade each leave every ComputeVM Running with the same status.runtime.instance, and the bidder re-populates exactly the HCS systems of its Owner.
      status: todo
      evidence: []
    - id: ACC11-RELEASE
      text: vm.delete for a rented guest answers 200 and afterwards no HCS system of the bidder's Owner, no HNS endpoint and no VHD of that guest remain.
      status: todo
      evidence: []
    - id: ACC11-REBOOT-LOST
      text: After a host reboot every previously Running ComputeVM is Lost, none is booted again, and each still releases with vm.delete 200.
      status: todo
      evidence: []
    - id: ACC11-ISOLATION
      text: Two guests of different requesters on one Windows bidder cannot reach each other, and each reaches the bidder only on its guest-facing ports.
      status: todo
      evidence: []
    - id: ACC11-NO-PROTOCOL-CHANGE
      text: The bid config, receipt, onNetwork and vm.delete records a Windows bidder writes are field-for-field the shapes the firecracker provider writes.
      status: todo
      evidence: []
questions_for_user:
  - "If a virtual account in Hyper-V Administrators cannot drive HCS (M0-HCS-ACCOUNT), which fallback: (a) the bidder service as LocalSystem with every deno child under a restricted token in a kill-on-close job, or (b) WMI/VMMS with persistent VMs that a startup sweep deletes after a reboot? Recommended: (a); it keeps HCS's no-helper ownership and reboot = Lost by construction, and TypeScript never holds SYSTEM."
  - "When does windows/amd64 enter the release matrix? Recommended: built and size-checked from M2-RELEASE-WINDOWS on, published only after ACC11-* pass."
  - "Guest kernel: a separate Hyper-V x86_64 kernel target, or one multi-driver x86_64 kernel (virtio + Hyper-V built in) shared with firecracker and qemu? Recommended: one multi-driver kernel if its size stays within budget (one image to pin and test), else separate targets."
  - "Is a one-time elevated install step (features, account and group, HNS network, firewall, Defender exclusions, services) acceptable, with every later operation including upgrade running unelevated where M0 proves it can? Recommended: yes."
  - "If the COM-port pipe cannot be reconnected after a core restart (M0-COM-CONSOLE), is losing guest console output from that restart on acceptable, given the console is diagnostics only? Recommended: yes."
  - "If WSL2 or Docker Desktop already hold the host's NAT capacity, should install refuse (naming them) or create an ICS-type network instead? Recommended: refuse by name; a bidder host is a server, not a desktop."