Skip to content

fix(installer): Windows memory recommendation can no longer suggest an unachievable range - #444

Merged
LukasWodka merged 5 commits into
developfrom
fix/417-memory-truth-ps
Jul 31, 2026
Merged

fix(installer): Windows memory recommendation can no longer suggest an unachievable range#444
LukasWodka merged 5 commits into
developfrom
fix/417-memory-truth-ps

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Windows half of #417. Three memory-reporting bugs seen live across all three test environments (7 GB / 6 GB / 15 GB readings, and a 15 GB laptop told to "give Docker ≥ 16 GB"):

  1. Flip-flop: the "Memory:" line preferred docker info MemTotal (the WSL2 VM's 50%-of-RAM default) over physical RAM — same machine, different numbers across re-runs depending on whether Docker happened to be up.
  2. Impossible advice: recommendations used the raw 16 GB constant regardless of what the machine has.
  3. Diverging copy: Step 1 and the Step-2 post-Docker re-check printed different warnings for the same condition, and could both fire in one run.

Change

  • Get-PfHostMemGb (CIM only) replaces the runtime-preferring Get-PfMemGb. Step 1 prints two labeled truths: Memory: 15 GB (machine) always, and Docker's memory budget: 7 GB … when a runtime is up.
  • Get-PfMemTargets clamps Docker-budget advice to host − 2 GB (floor 4) → the 15 GB laptop now gets [wsl2] memory=13GB. Hardware statements ("16 GB of RAM recommended to train") stay unclamped — they describe the machine, not a setting.
  • Write-PfRuntimeMemStatus is the single shared copy for the budget line (Step 1 + Step 2), with a latch so one run warns once.
  • Tests: clamp matrix (15/8/32/unknown GB hosts), two-line integration assertion, Step-2 dedupe latch, existing preflight tests rewired to the new readers; the old "prefers docker MemTotal" test is deleted — that preference was the bug.
  • manifest.sha256 regenerated.

Out of scope, noted: Get-PfCpu still prefers the runtime view (same class, milder — cores don't halve); macOS floor enforcement + VM sizing is #428; the bash half of this ticket follows separately (same labels + clamp in preflight.sh).

Stacked on #440#439#438#437 — merge in that order; this branch contains all of them.

Type

Bug fix

Test plan

Full Pester suite locally on pwsh 7.5 (CI invocation): 181 passed, 0 failed, 8 skipped (9 new). gen-manifest.sh --check clean.

🤖 Generated with Claude Code


Note

Low Risk
Changes are limited to installer preflight messaging, recommendation math, and tests; no runtime cluster or auth behavior.

Overview
Tightens Windows memory preflight and .wslconfig sizing so printed advice and written budgets stay consistent with the client minimum and with bash preflight.sh.

Get-PfMemRecommendation no longer floors capped values at 1 GB; it floors at Get-PfMinMemGb (default 5), avoiding impossible copy like “at least 5 GB (up to 4 GB)” and memory=4GB.

Show-MemoryStatus adds a hostTooSmall path when physical RAM minus the OS reserve cannot reach the client floor: it explains the ~2 GB reserve and practical minimum (~7 GB) instead of Docker resize hints, and applies the same rule in the “enough to run but training may OOM” branch so tiny hosts never get a concrete memory= training value.

Thresholds are centralized via Get-PfWarnMemGb and accessors for min/warn/rec/reserve; Pester coverage adds clamp invariants, single-source drift guards, and host-too-small scenarios. manifest.sha256 is updated for install-k8s.ps1.

Reviewed by Cursor Bugbot for commit fb6dde9. Bugbot is set up for automated code reviews on this repo. Configure here.

@LukasWodkaLukasWodka self-assigned this Jul 27, 2026
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/install-k8s.ps1 Outdated
Base automatically changed from fix/413-docker-wait to fix/412-create-deadlineJuly 27, 2026 15:06
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

Converted to draft to disable the merge button while the base is a feature branch (the #438-#440 mis-merge trap). Plan: once #447 (re-land of #412/#413) merges to develop, rebase this onto develop, retarget, regen manifest.sha256, re-run Pester, flip ready.

@LukasWodka
LukasWodkaforce-pushed the fix/417-memory-truth-ps branch from 5dc09fa to 3143e1cCompareJuly 27, 2026 15:34
@LukasWodka
LukasWodka changed the base branch from fix/412-create-deadline to developJuly 27, 2026 15:34
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

Rebased onto develop as a single clean commit (the tangled pre-squash history and the duplicate #440 copy-fix are gone — develop already carries that content byte-identically via #447). Manifest regenerated; full Pester green locally. bugbot run

@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/install-k8s.ps1 Outdated
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

LukasWodka added a commit that referenced this pull request Jul 27, 2026
…gbot #444)
Get-PfMemTargets used Max(4, host-2): on <=6 GB hosts it paired 'below
the 5 GB the client needs' with 'give Docker 4 GB' (advice that can
never clear the warning), and on <=4 GB hosts the floor exceeded the
machine. The floor is now PF_MIN_MEM_GB (5, env-overridable) — the
exact twin of the bash fix on #445.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LukasWodka added a commit that referenced this pull request Jul 31, 2026
…ad-end advice on a tiny host (Bugbot #445 r2)
Two findings from Bugbot on c50df6b, both reproduced and both real.
1) The recheck never actually used the "shared" copy, and graded differently.
`_pf_runtime_mem_status` was documented as the single copy for preflight AND
the post-Docker recheck, but the recheck still printed its own text — so the
COLD install path (Docker starts mid-run, the common case) got no colima
guidance on macOS and no hint at all on Linux. Worse, the two compared against
different thresholds: the helper against the clamped target, the recheck
against the raw PF_WARN_MEM_GB. Measured on an 8 GB host (clamped warn = 6)
with a 6 GB budget, one run printed both:
✔ Docker's memory budget: 6 GB
⚠ Docker is running with 6 GB — recommended ≥ 6 GB (6 GB to train)
Grading now lives only in the helper, and the recheck calls it. The helper
takes MiB so it uses the same PF_VM_MEM_GRACE_MIB tolerance as the recheck —
rounding to whole GB first misgraded a VM sized to exactly the documented
floor (4900 MiB guest) as sub-floor. A `quiet_ok` flag keeps the recheck
silent on a healthy budget, so no run prints the same ✔ twice.
2) A machine too small for the floor was still told to resize Docker.
On a 4 GB Mac the budget line advised "colima start --memory 5" — more than
the machine has, undercutting the honest "use a larger machine" stop the
recheck owns. The helper now detects host − PF_OS_RESERVE_GB < PF_MIN_MEM_GB
and points at the machine instead. This mirrors the same fix on the PowerShell
side (#444), so both installers now agree on the same hardware.
The sub-floor HARD-FAIL is untouched and still unconditional: the latch is tested
inside the warn branch only, and a test pins that a set latch plus a sub-floor VM
still exits non-zero.
Tests: preflight.bats 94/94 (5 new — host-too-small gets no resize, a host that
CAN reach the floor still does, the preflight-OK'd budget is never re-warned, the
recheck is silent when healthy, and the cold path carries the colima guidance).
Full suite 664/665 — the one failure is the pre-existing macOS-only
`validate_config` case fixed by #443. shellcheck --severity=error and --warning
both clean on preflight.sh; bash -n, check-style and check-drift clean; manifest
regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… too-small host says so (#417 residual)
This PR's ORIGINAL scope is superseded. #483 ("report host RAM consistently +
achievable memory advice (#417)") merged to develop on 2026-07-30 and closed#417, delivering the same Windows half by a different route: `Get-PfMemGb`
returns host RAM only, `Get-PfRuntimeMemGb` is shown as its own labelled line,
`Get-PfMemRecommendation` caps at host − 2 GB, and `Show-MemoryStatus` is the
single copy shared by Step 1 and the post-Docker recheck. Re-landing this
branch's `Get-PfHostMemGb` / `Get-PfMemTargets` / `Write-PfRuntimeMemStatus`
would only rename what already works.
What survives is the finding behind this branch's second commit, which is STILL
LIVE on develop: `Get-PfMemRecommendation` floors at 1 GB, so on a small host it
returns a number below the client's own minimum. Measured on develop:
Show-MemoryStatus -HostGb 6 -BudgetGb 3
⚠ Memory: 6 GB (Docker's current share: 3 GB) - below the 5 GB the client needs; it will OOM.
Give Docker at least 5 GB (up to 4 GB): ... [wsl2] memory=4GB ...
"at least 5 GB (up to 4 GB)" is an empty range, and the concrete value it tells
the operator to write is below the 5 GB the same sentence demands — the warning
cannot be cleared by following the advice. #483's Bugbot pass fixed this only for
the host-RAM-unreadable case; a KNOWN small host still hits it.
- `Get-PfMemRecommendation` now floors at PF_MIN_MEM_GB instead of 1, matching
bash's `_pf_clamp_mem_gb` exactly so both installers advise the same on the
same hardware.
- `Show-MemoryStatus` treats a host that cannot reach the floor even with the OS
reserve honoured (host − PF_OS_RESERVE_GB < PF_MIN_MEM_GB) as NOT a budget
bottleneck, so it gets the honest "use a larger machine" line rather than a
resize remedy it can never satisfy. This mirrors the bash recheck's
host-too-small branch, and the sibling fix in #445.
After:
Show-MemoryStatus -HostGb 6 -BudgetGb 3
⚠ Memory: 6 GB (Docker's current share: 3 GB) - below the 5 GB the client needs; it will OOM.
This machine has 6 GB of RAM total; the client needs at least 5 GB. Free up memory or use a larger machine.
Hosts that can reach the floor are unchanged (8 GB host still offers the resize;
15 GB → 13 and 16 GB → 14 recommendations are untouched).
Tests: 7 new/updated — the floor is the client minimum not 1, a 6 GB host never
yields a sub-floor number, PF_MIN_MEM_GB overrides the floor, an invariant sweep
over hosts 1..24 GB, the too-small host gets "larger machine" with no memory=1-4GB
value, and a host that CAN reach the floor still gets the resize hint. The former
"floors at 1 GB" assertion is replaced — it pinned the bug. Pester 374 passed /
0 failed / 9 skipped; PSScriptAnalyzer 0 errors; check-style, check-drift and
gen-manifest --check clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka
LukasWodkaforce-pushed the fix/417-memory-truth-ps branch from 7481c57 to 8e8615fCompareJuly 31, 2026 11:32
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

Rebased onto develop — and the original scope is superseded. Needs your call, @LukasWodka.

This PR's original scope is dead.#483 ("report host RAM consistently + achievable memory advice (#417)") merged to develop on 2026-07-30 and closed #417. It delivers the same Windows half by a different route:

#444 proposed#483 shipped (on develop now)
Get-PfHostMemGb (CIM only)Get-PfMemGb returns host RAM only
budget as its own labelled lineMemory: 15 GB (Docker's current share: 7 GB)
Get-PfMemTargets clampGet-PfMemRecommendation caps at host − 2 GB
Write-PfRuntimeMemStatus shared copyShow-MemoryStatus, shared by Step 1 + recheck

Re-landing this branch as written would just rename working code, so I did not re-apply it.

What did survive is the finding behind this branch's second commit (the Bugbot "clamp floor" thread), which is still live on develop. Get-PfMemRecommendation floors at 1 GB, so a known-small host gets advice below the client's own minimum. Measured against develop before my change:

Show-MemoryStatus -HostGb 6 -BudgetGb 3
⚠ Memory: 6 GB (Docker's current share: 3 GB) - below the 5 GB the client needs; it will OOM.
Give Docker at least 5 GB (up to 4 GB): WSL2 backend - [wsl2] memory=4GB ...

at least 5 GB (up to 4 GB) is an empty range, and memory=4GB is below the 5 GB the same sentence demands — following the advice cannot clear the warning. #483's own Bugbot pass fixed this only for the host-RAM-unreadable case.

So this PR is now narrowed to that residual fix:

Show-MemoryStatus -HostGb 6 -BudgetGb 3 # after
⚠ Memory: 6 GB (Docker's current share: 3 GB) - below the 5 GB the client needs; it will OOM.
This machine has 6 GB of RAM total; the client needs at least 5 GB. Free up memory or use a larger machine.

Hosts that can reach the floor are untouched (8 GB still offers the resize; 15 GB → 13, 16 GB → 14 unchanged).

Verification: Pester 374 passed / 0 failed / 9 skipped · PSScriptAnalyzer 0 errors · gen-manifest.sh --check, check-style.sh, check-drift.sh all clean · both .ps1 files parse.

Your call: keep this as the narrow residual fix, or close it and let me file the residual as its own ticket. I have not closed anything.

@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/install-k8s.ps1
…#444)
The previous commit routed 5-6 GB hosts into `Show-MemoryStatus`'s generic
too-small hint, which compares total RAM to the floor and stops there:
host=6 GB, budget=3
⚠ Memory: 6 GB (Docker's current share: 3 GB) - below the 5 GB the client needs; it will OOM.
This machine has 6 GB of RAM total; the client needs at least 5 GB. Free up memory or use a larger machine.
6 >= 5, so as written the operator is told they have enough and still need a
bigger machine. The shortfall only adds up once the ~2 GB the OS needs is named —
which is exactly what bash's `_pf_recheck_runtime_mem` already says. That hint
predates this PR, but this PR is what made 5-6 GB hosts reach it, so it fixes it.
host=6 GB, budget=3 # after
⚠ Memory: 6 GB (Docker's current share: 3 GB) - below the 5 GB the client needs; it will OOM.
This machine has 6 GB of RAM total - too little for tracebloc: the client needs a 5 GB Docker budget and the OS needs ~2 GB, so about 7 GB physical is the practical minimum. Use a larger machine.
Hosts that can reach the floor are untouched (8 GB still gets the resize remedy;
16 GB still gets the training recommendation), and a host below the floor outright
keeps the plain copy.
Tests: 3 new — the reserve and the practical minimum are both named, and the
arithmetic is explained for every too-small host (4-6 GB). Pester 376 passed /
0 failed / 9 skipped; PSScriptAnalyzer 0 errors; parse, check-style, check-drift
and gen-manifest --check all clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadscripts/install-k8s.ps1 Outdated
…nch (Bugbot #444 r2)
`hostTooSmall` was consulted only inside the below-floor branch, so a 5-6 GB host
with Docker DOWN graded as "enough to run" and fell into the TRAINING branch,
which printed a concrete budget to write:
host=6 GB, budget=<none>
⚠ Memory: 6 GB - enough to run the client, but training (~8 GB/job) may OOM; 5 GB recommended to train locally.
For local training, give Docker up to 5 GB: WSL2 backend - [wsl2] memory=5GB ...
memory=5GB on a 6 GB machine leaves the OS 1 GB — a budget this same function
calls unachievable two branches up. The previous commit's floor change is what
lifted that number from 4 to 5 and made it reachable, so this closes the hole it
opened rather than trading one inconsistency for another.
Such a machine cannot be tuned into a training box at all, so the branch now says
that instead of printing a number:
host=6 GB, budget=<none> # after
⚠ Memory: 6 GB - enough to run the client, but too little to train locally (~8 GB/job).
This machine has 6 GB of RAM total and the OS needs ~2 GB, so it cannot give Docker a training-sized budget. Run the client here and train on a larger machine.
Machines that CAN reach the floor keep the actionable number (16 GB host with a
6 GB budget still gets "give Docker up to 14 GB" / memory=14GB), and the healthy
paths are untouched.
Tests: 4 new, including the invariant that closes this class for good — across
every branch and every budget shape (host 1-6 GB x budget none/1-6), no branch may
emit a concrete memory= value for a host that cannot reach the floor while keeping
the OS reserve. Pester 379 passed / 0 failed / 9 skipped; PSScriptAnalyzer
0 errors; parse, check-style, check-drift, gen-manifest --check clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit aabe3ec. Configure here.

LukasWodka added a commit that referenced this pull request Jul 31, 2026
…floor (#516)
Get-WslConfigMemoryGb writes the WSL2 memory budget into the daily user's
.wslconfig during elevated provisioning. It did its own arithmetic -- physical
RAM minus a PRIVATE 4 GB reserve, floored at 1 GB -- so it could persist a
budget the client cannot run in, and it contradicted the installer's own advice
in the same run.
Measured before ($env:TB_PESTER="1"; . scripts/install-k8s.ps1):
host writes advises (run / train)
8 4 6 / 6 <- below the 5 GB floor: guaranteed OOM crashloop
16 12 8 / 14
32 28 8 / 16 <- over-committed: Windows left 4 GB
6 2 4 / 4 <- doomed budget persisted as if intentional
4 1 2 / 2
8 GB is the important case: a perfectly viable host (8 - 2 GB OS reserve = 6,
clear of the 5 GB floor) got memory=4GB while Show-MemoryStatus in the same run
said "give Docker up to 6 GB".
Rather than re-derive a floor here, the function now DELEGATES to
Get-PfMemRecommendation -- the existing single-source helper the advice path
uses. The written budget is therefore the advised budget by construction, not
by two calculations agreeing:
host writes advises (train)
7 memory=5GB 5 (= the floor; 5+2 is the stated practical minimum)
8 memory=6GB 6 (was 4 -> OOM)
16 memory=14GB 14 (was 12)
32 memory=16GB 16 (was 28; Windows keeps 16, and the client cannot
64 memory=16GB 16 use more than the recommended training budget)
6 skipped -
4 skipped -
Reserve reconciled: the private ReserveGb=4 is gone and the PARAMETER is gone
with it, so no caller can reintroduce the drift. $script:PfOsReserveGb (2) is
now the only reserve -- which is what its own comment already claimed ("used to
cap recommendations AND to reason about the achievable budget in one place, so
the two can't drift", #417 reviewer). Three accessors (Get-PfOsReserveGb /
Get-PfMinMemGb / Get-PfRecMemGb) give the printing and writing paths one read
path for the same numbers; the reserve accessor fails closed (never 0, which
would hand WSL2 the entire host).
Host genuinely too small (physical - reserve < floor): the function returns 0
and the caller SKIPS the memory setting instead of persisting a budget known to
OOM -- which would also bake the failure into the daily account for every later
run and read as intentional to whoever debugs it next. It says so plainly
instead ("about 7 GB physical is the practical minimum ... use a larger
machine"), the same honest framing the macOS path landed in #513. Still
warn-only: Set-DailyUserProvisioning is documented never to fail the install,
and Windows memory preflight is warn-only throughout.
No headroom fudge is needed on Windows, unlike colima in #513: only a 7 GB host
lands exactly on the floor, 7 GB is precisely the practical minimum this code
reports, and the Windows recheck warns rather than hard-failing -- so there is
no catch-22 where the installer hard-fails on the size it just chose.
Scope: Show-MemoryStatus and Get-PfMemRecommendation are deliberately untouched
so this does not collide with the open #444 (which rewrites both). Verified
forward-compatible -- with #444's floored recommender simulated, every host
above returns an identical result, because the too-small gate tests the
achievable ceiling rather than the recommender's output.
Tests: the two assertions that pinned the old cap/floor are replaced by the new
contract -- never below the floor, 8 GB -> 6 not 4, 0 (don't write) when the
host can't reach the floor, never over-commits, caps at the training budget,
PF_MIN_MEM_GB override honoured, no ReserveGb parameter, and a lock asserting
written == advised across seven host sizes. Plus source guards that the caller
gates the write on 0 and prints the honest too-small message.
Gates: Invoke-Pester scripts/tests/ -> 377 passed / 0 failed;
Invoke-ScriptAnalyzer (as CI scopes it) -> 0 errors; check-style.sh clean;
check-drift.sh no drift; manifest.sha256 regenerated.
Refs #418
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…or each
#516 merged the WRITE half of this story (the daily-user .wslconfig could be
sized below the client's own floor) and, to fix it, added accessors next to
$script:PfOsReserveGb: Get-PfOsReserveGb / Get-PfMinMemGb / Get-PfRecMemGb.
This branch had independently added its own inline reads of the same numbers,
so the merge left three duplicate read paths for values whose whole problem was
drifting apart. Only scripts/manifest.sha256 conflicted textually (both sides
changed the install-k8s.ps1 hash); the duplication was the real conflict.
Consolidated, so each threshold now has exactly ONE read site:
- Get-PfMemRecommendation: inline $env:PF_MIN_MEM_GB read and the raw
$script:PfOsReserveGb -> Get-PfMinMemGb / Get-PfOsReserveGb. The accessor also
fails closed on the reserve (never 0, which would advise handing Docker the
whole host).
- Show-MemoryStatus: its three inline threshold reads -> accessors, plus one
local $reserveGb for the hostTooSmall arithmetic and the two hints that name
the reserve.
- Added Get-PfWarnMemGb so all four thresholds are uniform. PF_WARN_MEM_GB was
the only one #516 didn't need an accessor for, and leaving it inline is
exactly the asymmetry that invites the next drift. Say the word if you'd
rather keep this branch minimal and drop it.
Verified the two halves compose, on the real merged code rather than by
simulation. #444 floors Get-PfMemRecommendation at the client minimum, and
#516's Get-WslConfigMemoryGb DELEGATES to that function -- so the floor now
feeds the path that writes real config. On a 6 GB host the recommender returns 5
(its floor), and writing that would leave Windows 1 GB; the write path refuses
anyway because it gates on the achievable ceiling (physical - reserve) rather
than on the recommender's output. Combined result:
host .wslconfig advice(train) agree
7 memory=5GB 5 yes
8 memory=6GB 6 yes
16 memory=14GB 14 yes
32 memory=16GB 16 yes
2/4/6 skipped - n/a (and no branch prints a memory= value)
New no-drift guard (Describe "Memory thresholds are single-sourced"): each PF_*
threshold is read on exactly one LINE (counting lines, not occurrences -- an
accessor names its var twice on one line, in the truthiness test and the [int]
cast), the reserve is read only by its accessor, the accessors match the
documented defaults, and the advice path and the write path agree for every host
that can reach the floor while writing nothing for every host that can't.
Mutation-tested the guard rather than trusting it: reintroducing the inline
$env:PF_MIN_MEM_GB read and the raw $script:PfOsReserveGb fails both drift
assertions, and restoring passes all four.
Gates: Invoke-Pester scripts/tests/ -> 392 passed / 0 failed (this branch's
tests and #516's together); Invoke-ScriptAnalyzer as CI scopes it -> 0 errors;
check-style.sh clean; check-drift.sh no drift; manifest.sha256 regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

Refreshed onto develop (now includes #516) — and consolidated the overlap

#516 merged the write half of this story (the daily-user .wslconfig could be sized below the client's own floor) and added accessors next to $script:PfOsReserveGb: Get-PfOsReserveGb / Get-PfMinMemGb / Get-PfRecMemGb. This branch had independently added its own inline reads of the same numbers, so the merge left three duplicate read paths for values whose whole problem was drifting apart. Only scripts/manifest.sha256 conflicted textually; the duplication was the real conflict.

Each threshold now has exactly one read site:

  • Get-PfMemRecommendation — inline $env:PF_MIN_MEM_GB and raw $script:PfOsReserveGb → the accessors (which also fail closed on the reserve, never 0)
  • Show-MemoryStatus — its three inline reads → accessors, plus one local $reserveGb for the hostTooSmall arithmetic and the two hints that name the reserve
  • Added Get-PfWarnMemGb for uniformity. PF_WARN_MEM_GB was the only threshold fix(#418): .wslconfig memory can't be written below the client's own floor #516 didn't need, and leaving it inline is the asymmetry that invites the next drift — happy to drop it if you'd rather keep this branch minimal.

The two halves compose — verified on the merged code, not by simulation

This PR floors Get-PfMemRecommendation at the client minimum, and #516's Get-WslConfigMemoryGbdelegates to that function — so this PR's floor now feeds the path that writes real config. On a 6 GB host the recommender returns 5 (its floor), and writing that would leave Windows 1 GB; the write path refuses anyway because it gates on the achievable ceiling (physical − reserve), not on the recommender's output.

host.wslconfigadvice (train)agree
7 GBmemory=5GB5
8 GBmemory=6GB6
16 GBmemory=14GB14
32 GBmemory=16GB16
2/4/6 GBskippedno branch prints a memory= value

New no-drift guard

Describe "Memory thresholds are single-sourced" asserts each PF_* threshold is read on exactly one line (lines, not occurrences — an accessor names its var twice on one line, in the truthiness test and the [int] cast), the reserve is read only by its accessor, the accessors match the documented defaults, and the advice and write paths agree for every host that can reach the floor while writing nothing for every host that can't.

Mutation-tested rather than trusted: reintroducing the inline read and the raw reserve fails both drift assertions; restoring passes all four.

Gates

Invoke-Pester scripts/tests/392 passed / 0 failed (this branch's tests and #516's together) · Invoke-ScriptAnalyzer (CI scope) → 0 errors · check-style.sh clean · check-drift.sh no drift · manifest.sha256 regenerated.

Still blocked only on the required approving review.

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 69cdf93. Configure here.

@LukasWodkaLukasWodka changed the title fix(installer): memory truth — machine RAM vs Docker's budget, advice clamped to the machine (Windows half of #417)fix(installer): Windows memory recommendation can no longer suggest an unachievable rangeJul 31, 2026
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

Scope narrowed — retitled to match, so this isn't reviewed against a dead brief.

The original scope (report host RAM consistently + achievable memory advice) shipped in #483, merged 2026-07-30, which also closed #417. Re-landing this PR as written would have renamed working code, so it doesn't.

What remains is one residual defect that is still live on develop: Get-PfMemRecommendation floored its upper bound at 1, so a 6 GB host was told

at least 5 GB (up to 4 GB) -> memory=4GB

an empty range whose ceiling sits below its own stated minimum, and a written .wslconfig that contradicts the same installer's advice.

Fixed, plus two further Bugbot rounds: reserve-aware copy, then the training branch — where my own floor change had made memory=5GB reachable on a 6 GB host. Added an invariant sweep (host 1–6 GB × budget none/1–6) so no branch can emit an unachievable memory= again.

Decision (Lukas): keep this PR rather than close it. The residual is real, already fixed, and green — closing would mean re-filing identical work. Pester 379/0, 35 checks SUCCESS, Bugbot clean, zero unresolved threads.

Not a duplicate of, and not stacked on, #445: different merge-bases, neither an ancestor of the other, disjoint files (this is PowerShell + Pester; #445 is bash + bats). No no-stacked-PRs concern.

develop moved again while this branch sat waiting for review: #517 (drop the dead
Get-WslConfigContent + its test) and #434 (RFC-CLIENT-0003 docs). Both touch the
same install-k8s.ps1 hash line in scripts/manifest.sha256, which is the only
textual conflict; the PowerShell and test files auto-merged.
Verified the merge rather than trusting it:
- #517's removal came through cleanly -- Get-WslConfigContent is gone from both
the installer and the test file (0 references in each).
- This branch's + #516's accessor work is intact (15 accessor references).
- Pester 391 passed / 0 failed. One fewer than the 392 before this merge, which
is exactly the Get-WslConfigContent test #517 deleted -- not a lost assertion.
Gates: Invoke-ScriptAnalyzer as CI scopes it -> 0 errors; check-style.sh clean;
check-drift.sh no drift; manifest.sha256 regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit fb6dde9. Configure here.

@aptraceblocaptracebloc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving on the merits — floors advice at the client minimum (not 1), consults hostTooSmall in both the below-floor and warn paths so no unachievable memory= is printed, single-sources thresholds through accessors mirroring bash's _pf_clamp_mem_gb, and the no-drift/invariant-sweep Pester coverage is excellent. Note: Bugbot/Unit tests/Prereqs are still pending on the latest commit — merge stays gated on those going green.

@LukasWodka
LukasWodka merged commit 37f3151 into developJul 31, 2026
38 checks passed
LukasWodka added a commit that referenced this pull request Jul 31, 2026
… proceed (#520)
* fix(installer): Windows was the one OS that let a sub-floor Docker VM proceed
#513 decided a Docker VM below the client's memory floor must STOP the install
rather than proceed and OOM-crashloop -- "proceeding is worse than the jarring
stop the WARN path used to avoid" -- and implemented that in bash for every OS
(_pf_recheck_runtime_mem -> error -> exit 1). The Windows installer never got it.
Test-PreflightRuntimeMem just called Show-MemoryStatus, which is warn-only, so
Windows printed "it will OOM" and then carried on and OOM-crashlooped. The
platform this whole memory story (#417/#418/#428/#444/#516) is about was the one
platform still shipping the crash.
Enforcement now lives in Test-PreflightRuntimeMem, mirroring bash's split:
Show-MemoryStatus stays purely presentational (its documented job -- and the
function two PRs just contended over), the recheck grades then enforces. It runs
as New-K3dCluster's FIRST statement, so exiting leaves no half-built cluster.
The subtlety that makes this safe: Get-PfRuntimeMemGb floors to whole GB, and a
guest reports a few hundred MiB BELOW its configured size, so a VM set to exactly
the documented 5 GB floor reports ~4.8 and floors to 4. A bare `-lt 5` would have
hard-failed a correctly configured machine -- the same trap #513's reviewer caught
in bash, but worse here because flooring to whole GB discards up to a GB. So the
gate compares MiB against floor - grace:
- New Get-PfRuntimeMemMib: the same `docker info` value at MiB precision.
- New Get-PfVmMemGraceMib (512, PF_VM_MEM_GRACE_MIB) -- the same constant and the
same comparison bash uses, so both installers put the floor in the same place.
- The recheck now reads the budget ONCE, in MiB, and derives GB from it, so the
number printed and the number enforced on cannot disagree. Flooring (not
rounding) is kept deliberately: Step-1 floors too, and #417 exists so the
reported figure doesn't flip-flop between the two reads.
Remedies stay honest and achievable, matching the copy the advice path already
prints: a host that CAN reach the floor gets a resize target clamped to it
(min(warn, physical - reserve) -- bash's clamped warn target); a host that cannot
(physical - reserve < floor) gets the practical minimum and "run the client on a
larger machine", never a resize that repeats an impossible size. A
between-floor-and-warn budget still only warns -- it can run, just tightly.
TRACEBLOC_SKIP_PREFLIGHT still overrides, and Err names it.
Tests: the Describe that asserted warn-only is replaced by the new contract --
sub-floor 4 GB hard-fails; a floor-sized VM reporting 4800 MiB passes; the grace
band is bounded on both sides (4607 fails, 4608 passes); daemon-silent is a
no-op; between-floor-and-warn only warns; the rec is still capped at host RAM;
big-host vs host-too-small remedies; host RAM unreadable still fails; the skip
env overrides; and the budget is read exactly once. Plus a parity Describe that
reads BOTH sources and asserts bash still hard-fails, Windows hard-fails too and
is no longer warn-only, and both name the same grace constant -- so the next
divergence fails a test instead of shipping.
The old tests mocked Get-PfRuntimeMemGb, which this no longer calls; left as-is
they would have passed while testing nothing, so they now mock the MiB reader.
Mutation-tested rather than trusted: neutering the gate back to warn-only fails 4
enforcement tests; restoring passes all 11.
Gates: Invoke-Pester scripts/tests/ -> 401 passed / 0 failed;
Invoke-ScriptAnalyzer as CI scopes it -> 0 errors; check-style.sh clean;
check-drift.sh no drift; manifest.sha256 regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(installer): make the grade and the gate share one boundary (Bugbot)
Bugbot caught a self-contradiction I had reasoned about and wrongly accepted: the
grade was computed from floor($mib / 1024), so a VM configured at exactly the 5 GB
floor (reporting ~4800 MiB) became budget 4, and Show-MemoryStatus printed
hard-floor "it will OOM" copy plus a resize hint -- for a machine the grace-aware
gate immediately ACCEPTED. We told a correctly configured box it would crash and
then carried on. That is precisely the "installer contradicts itself in the same
run" pattern #418/#516 existed to remove; bash classifies that band warn-only.
I had rejected rounding because Step-1 floors and #417 exists so the reported
figure doesn't flip-flop. The fix avoids that trade-off entirely: fold the SAME
grace in before flooring.
$budget = floor(($mib + $grace) / 1024)
- 4800 + 512 -> 5: reports the CONFIGURED size (what the user set and can change),
grades in the warn band, gate passes. Consistent.
- 4096 + 512 -> 4: still sub-floor, still "it will OOM", gate still fails.
Consistent.
Because the grade and the gate now pivot on the same constant, their boundaries
are the same boundary -- (floor * 1024 - grace) MiB. There is no band that warns
"will OOM" yet proceeds, and none that passes while being called sub-floor. The
contradiction is impossible by construction, not merely absent at the values I
happened to test.
Tests: the floor-sized VM is asserted NOT to be told it will OOM and to report
its configured 5 GB; plus a boundary-coincidence test sweeping 4096/4607/4608/
4800/5120 that asserts at EVERY point the copy and the gate agree.
Mutation-tested: reverting the grade to floor($mib / 1024) fails both new tests;
restoring passes all 13.
Gates: Invoke-Pester scripts/tests/ -> 403 passed / 0 failed; Invoke-ScriptAnalyzer
as CI scopes it -> 0 errors; check-style.sh clean; check-drift.sh no drift;
manifest.sha256 regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
LukasWodka added a commit that referenced this pull request Jul 31, 2026
develop moved a long way while this sat open (#513 macOS memory floor, #518
storage remedy, #516/#444/#517/#520 on the Windows side, #434 RFC docs). Only
scripts/manifest.sha256 conflicted textually — the same single install-k8s.ps1
hash line that has now collided four times today. preflight.sh and
preflight.bats auto-merged.
Resolution: regenerate the manifest (it is a DERIVED artifact — 18 digests, no
secrets; authenticity comes from the release workflow's cosign signature, not
from git), so regenerating is the only correct resolution. Taking either side
would leave a wrong digest, which the R8 gate then rejects.
Verified the auto-merge rather than trusting it. The real hazard here was not the
conflict but the clean-looking merge: this branch DELETES _pf_total_mem_kb (the
"prefer the runtime" memory selector whose conflation of host RAM and VM budget
is the bug it fixes), so any caller that landed on develop meanwhile would have
merged into a call to a function that no longer exists — a silent break git
reports as success.
- _pf_total_mem_kb: undefined and unreferenced after the merge; the only mentions
are this branch's own guard test asserting its absence, and a comment.
- #518's _pf_storage_type network-FS remedy survived intact.
- This branch's _pf_runtime_mem_status is present and still wired into both
_pf_memory and _pf_recheck_runtime_mem.
Gates: bats scripts/tests/*.bats -> 683 ok / 0 not ok (full TAP plan reported,
not a truncated read); shellcheck --severity=error over the CI file set -> rc=0;
bash -n clean; Pester -> 403 passed / 0 failed (install-k8s.ps1 arrived via this
merge); check-style clean; check-drift no drift; gen-manifest.sh --check current.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

/fr-pass

LukasWodka added a commit that referenced this pull request Aug 3, 2026
… clamped (bash half of #417) (#445)
* fix(installer): memory truth — machine RAM vs Docker's budget as two lines (#417, bash half)
Rebuilt on top of #513, which landed the CLAMP half of this work while this PR
sat open. #513 already gives every SHOWN figure `_pf_clamp_mem_gb` (physical −
PF_OS_RESERVE_GB, floored at PF_MIN_MEM_GB) and hard-fails a sub-floor Docker VM
in the post-Docker recheck. This PR's own `_pf_mem_targets` clamp helper is
therefore dropped as redundant — it reuses #513's helper instead.
What #513 did NOT fix, and this does: the flip-flop. `_pf_total_mem_kb` preferred
the runtime view over the host, so the SAME machine reported "16 GB (host)" on a
cold run and "6 GB (Docker VM)" on a warm one, purely on whether Docker happened
to be running. Two of its tests asserted that behaviour — one was literally named
"the Mac trap".
- the `_pf_total_mem_kb` memory selector is deleted. Memory has two distinct
truths and each caller now names the one it means: `_pf_host_mem_kb` for a
hardware fact, `_pf_runtime_mem_kb` for the budget the pods actually get.
(CPU keeps its fallback selector — there is no equivalent advice split.)
- `_pf_memory` gates on the MACHINE and prints `Memory: N GB (machine)`. The
Linux hard-fail gate, the 64 MiB grace and the MemAvailable check are all
unchanged. On a machine below the floor the macOS branch no longer offers a
Docker resize remedy — no Docker setting fixes too little physical RAM.
- Docker's budget becomes its OWN second line via `_pf_runtime_mem_status`, shown
only when a runtime is up AND its budget is meaningfully smaller than the
machine (the VM case). Native Linux, where the daemon sees all host RAM, no
longer repeats the same number twice.
- `_pf_hw_summary_line` reports host RAM — it had the same flip-flop in miniature
("7 GB memory" on a 15 GB WSL2 box).
- Linux budget hints drop the Docker Desktop dead end (Bugbot #445): a headless
box has no Desktop UI, so the remedy names the VM/cgroup limit instead.
- `PF_RUNTIME_MEM_WARNED` latches the budget warning so one run never warns twice
about the identical condition. It is tested INSIDE the warn branch, never at the
top of `_pf_recheck_runtime_mem`, so it can never gate #513's sub-floor
hard-fail; a test pins that (latch set + sub-floor VM still exits non-zero).
#513's reviewed recheck copy is left exactly as-is.
Tests: preflight.bats 89/89 (11 new — the (machine) label, the two-line output,
the Linux no-duplicate case, host-unreadable fallback, the clamped/floored advice,
both OS hint shapes, the latch, and the latch-can't-gate-the-hard-fail guard; the
two Mac-trap selector tests are replaced by a guard that the selector stays gone).
Full suite 659/660 — the one failure is `validate_config: valid config passes`,
pre-existing on clean develop and macOS-only (the /var symlink; fixed by #443).
bash -n + shellcheck --severity=error + check-style clean; manifest regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(installer): one threshold + one copy for the Docker budget; no dead-end advice on a tiny host (Bugbot #445 r2)
Two findings from Bugbot on c50df6b, both reproduced and both real.
1) The recheck never actually used the "shared" copy, and graded differently.
`_pf_runtime_mem_status` was documented as the single copy for preflight AND
the post-Docker recheck, but the recheck still printed its own text — so the
COLD install path (Docker starts mid-run, the common case) got no colima
guidance on macOS and no hint at all on Linux. Worse, the two compared against
different thresholds: the helper against the clamped target, the recheck
against the raw PF_WARN_MEM_GB. Measured on an 8 GB host (clamped warn = 6)
with a 6 GB budget, one run printed both:
✔ Docker's memory budget: 6 GB
⚠ Docker is running with 6 GB — recommended ≥ 6 GB (6 GB to train)
Grading now lives only in the helper, and the recheck calls it. The helper
takes MiB so it uses the same PF_VM_MEM_GRACE_MIB tolerance as the recheck —
rounding to whole GB first misgraded a VM sized to exactly the documented
floor (4900 MiB guest) as sub-floor. A `quiet_ok` flag keeps the recheck
silent on a healthy budget, so no run prints the same ✔ twice.
2) A machine too small for the floor was still told to resize Docker.
On a 4 GB Mac the budget line advised "colima start --memory 5" — more than
the machine has, undercutting the honest "use a larger machine" stop the
recheck owns. The helper now detects host − PF_OS_RESERVE_GB < PF_MIN_MEM_GB
and points at the machine instead. This mirrors the same fix on the PowerShell
side (#444), so both installers now agree on the same hardware.
The sub-floor HARD-FAIL is untouched and still unconditional: the latch is tested
inside the warn branch only, and a test pins that a set latch plus a sub-floor VM
still exits non-zero.
Tests: preflight.bats 94/94 (5 new — host-too-small gets no resize, a host that
CAN reach the floor still does, the preflight-OK'd budget is never re-warned, the
recheck is silent when healthy, and the cold path carries the colima guidance).
Full suite 664/665 — the one failure is the pre-existing macOS-only
`validate_config` case fixed by #443. shellcheck --severity=error and --warning
both clean on preflight.sh; bash -n, check-style and check-drift clean; manifest
regenerated.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(review): three diverging-copy findings, all closed via one shared predicate
Bugbot r3 on #445. All three were the SAME shape as the bug this PR exists
to remove — two copies of one judgement disagreeing — surviving on paths
the first pass missed.
1. Sub-floor remedy contradicted the hard-fail. On a warm run with a
sub-floor budget, _pf_runtime_mem_status hinted "Give Docker <rec>"
while _pf_recheck_runtime_mem hard-failed with "raise to <warn>" —
the latch suppresses a duplicate warning, deliberately never the
hard-fail, so both printed. The remedy now quotes the size that
failure quotes; the between-floor-and-warn branch, where no hard-fail
follows, still aims at the train figure.
2. Budget GB omitted the VM grace. rt_mib/1024 showed a VM configured at
exactly the documented floor as one GB BELOW it — graded correctly by
the grace-aware thresholds, displayed as a contradiction. Now
(mib + grace)/1024, matching the PowerShell peer.
3. "Enough to run" contradicted "use a larger machine". _pf_memory
compared host RAM straight against the Docker floor, ignoring the OS
reserve, so a 5-6 GB Mac was graded enough-to-run on one line and told
to use a larger machine two lines later. Both now read ONE predicate,
_pf_host_too_small_for_floor, which fails safe on unknown input. Native
Linux keeps its original wording: the daemon sees host RAM, so the
reserve arithmetic does not apply.
696 bats pass (4 new: sub-floor remedy agreement, floor-sized VM display,
the machine-line verdict, and the predicate incl. junk input).
shellcheck clean; manifest regenerated.
* fix(preflight): grace the WARN threshold too, and make the r3 tests enforce
Two gaps in 718af66, which otherwise stands as-is — the shared
_pf_host_too_small_for_floor predicate is the right shape and is kept.
1. The display became grace-aware but the WARN threshold did not (only the floor
one was), so the same self-contradiction reopened one boundary up. Measured on
718af66 with a 32 GB host (warn_eff 8):
rt_mib=7680 -> ⚠ budget: 8 GB — recommended ≥ 8 GB
rt_mib=8000 -> ⚠ budget: 8 GB — recommended ≥ 8 GB
rt_mib=8191 -> ⚠ budget: 8 GB — recommended ≥ 8 GB
rt_mib=8192 -> ✔ budget: 8 GB
A ~512 MiB band telling the operator to raise a budget to the size it already
reports — and Docker Desktop's own defaults land in it. The warn threshold now
carries the same grace, so shown == target implies the ✔ branch at BOTH
boundaries rather than just the floor.
2. The four r3 tests were only partially enforcing. Under Bats 1.13 a failing bare
`[[ ]]` that is not the LAST command in a test body does not fail the test, so
`budget: 5 GB`, `!= enough to run`, `Give Docker <warn_eff>` and every line but
the last of the predicate test were advisory — they would have passed against
broken code. All 16 assertions in those tests now carry `|| return 1`.
Verified by mutation, not by inspection:
- reverting the warn-threshold grace (i.e. 718af66's shipped state) fails the new
boundary test — so this is a real gap, not a hypothetical one;
- reverting the display grace fails 2 tests;
- neutering _pf_host_too_small_for_floor fails 3, including the r3 predicate test
that only became capable of failing once hardened.
Baseline and restored are clean in every case.
Scope note: only the r3 tests are hardened here. 170 of ~698 tests in this suite
share the un-hardened pattern; that sweep needs its own PR because hardening will
surface previously-vacuous failures that each need triage (real installer bug vs
stale assertion), and burying that in this PR would hide it.
Gates: bats scripts/tests/*.bats -> plan 697, ok 697, not ok 0 (complete TAP run);
shellcheck --severity=error over the CI file set -> rc=0; check-style clean;
check-drift no drift; gen-manifest.sh --check current.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(review): make _pf_memory's thresholds match the grace on its own display
Bugbot r5 (High), and correcting my own assumption: the grace on
_pf_memory's shown GB came in from develop via the merge, not from my r4
edit. Either way the halves disagreed — the display added
PF_VM_MEM_GRACE_MIB while the floor gate kept a 64 MiB tolerance, so a
5 GB VM reporting ~4900 MiB printed "Memory: 5 GB — below the 5 GB the
client needs" and, on Linux, hard-failed on it.
- floor_mib and warn_mib now use PF_VM_MEM_GRACE_MIB, the same tolerance
the display uses and the same one _pf_runtime_mem_status already used
for its floor and warn tests. All three now agree on the boundaries.
- _pf_host_too_small_for_floor is now fed $(_pf_host_mem_gb) instead of
$gb. A shared predicate only prevents divergence if both call sites
pass the same input; _pf_memory was passing a grace-adjusted
VM-or-host figure while the status path passed raw host GB.
MemAvailable is deliberately left ungraced: it is a live measurement, not
a configured size, so adjusting it would mask a real shortage.
713 bats pass (2 new: the floor-sized-VM message, and a guard that both
call sites feed the predicate the same figure). shellcheck clean; manifest
regenerated.
* fix(review): render every memory GB through one converter
Bugbot r6 found a FOURTH site: _pf_hw_summary_line computed its own
memory GB, so the collapsed summary could print a different size from the
memory line in the same preflight.
Investigating it turned up something worse, and it corrects the record on
r4/r5: my r4 fix for _pf_recheck_runtime_mem anchored on a two-line
pattern that also existed in _pf_memory, and the replace took the FIRST
match — so the fix landed in _pf_memory and the recheck never got it. The
grace on _pf_memory's display, which I attributed on the PR to develop
via the merge, was actually that misapplied edit. The r5 High finding was
a direct consequence.
Structural fix rather than a fifth patch: _pf_display_gb_from_mib is now
the single definition, used by _pf_memory, _pf_runtime_mem_status,
_pf_recheck_runtime_mem and _pf_hw_summary_line. rt_gb goes through it too
— it feeds the 'is the VM meaningfully smaller than the machine'
comparison, and grading one grace-adjusted side against a raw other side
is precisely the mistake these six rounds keep rediscovering.
Deliberately still raw: MemAvailable (a live measurement — inflating it
would hide a real shortage), disk, and _pf_host_mem_gb (physical RAM needs
no compensation, and it is the input _pf_host_too_small_for_floor grades).
Every replacement in this commit asserted its anchor matched EXACTLY once
and refused otherwise — the guard that would have caught r4's error.
715 bats pass (2 new: a source-level invariant that no site renders its own
memory GB, and summary-vs-memory-line agreement). shellcheck clean;
manifest regenerated.
* fix(review): apply the too-small predicate on every OS in the recheck
Bugbot r7 (High). Two divergences in one branch: the reserve arithmetic
was inlined instead of calling _pf_host_too_small_for_floor, and the branch
was gated OS != Linux while _pf_runtime_mem_status applies it everywhere.
On a warm Linux install with a sub-floor cgroup/VM budget, preflight said
'use a larger machine' and this hard-fail then advised raising Docker to a
size that machine cannot give.
Now calls the shared predicate on every OS, with an OS-appropriate noun so
the Mac wording is preserved. Verified both messages agree on a 6 GB Linux
host with a 3 GB budget: both say 'use a larger machine'.
Same class as r1-r6 on a new axis: not two values disagreeing but two
OS GATES disagreeing about when one judgement applies.
717 bats pass (2 new: the Linux path, and an invariant that the reserve
arithmetic exists in exactly one place). shellcheck clean; manifest
regenerated.
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@LukasWodka
LukasWodka deleted the fix/417-memory-truth-ps branch August 14, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@LukasWodka@aptracebloc