Repository navigation
v1.4: correctness fixes, published guides, tests and CI - #2
Merged
Merged
Conversation
- Added deep research test outputs
This repository is maintained from Windows with core.autocrlf=true, which
rewrites shell scripts to CRLF on checkout. A CRLF shell script fails on
Linux with a confusing error ("bad interpreter", or "$'\r': command not
found"), which is exactly the kind of thing that wastes a beginner's evening.
The scripts are stored correctly as LF in git today, so this is a guard
against a future accidental commit from a Windows checkout, not a fix for
existing damage. Verified with `git check-attr` and `git add --renormalize`,
which found nothing to change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… user edits
The headline bug: --models-max was set to 0 on CPU-only machines on the
assumption that 0 was the restrictive value. It is the opposite. From
llama.cpp's own common/arg.cpp:
"maximum number of models to load simultaneously (default: 4, 0 = unlimited)"
and three sites in tools/server/server-models.cpp treat <= 0 as no limit,
disabling the capacity check, LRU eviction and the idle queue. Since -c applies
per model and each model is a separate child process, this meant unbounded
memory growth on exactly the machines least able to survive it. Now 1
everywhere.
Also in this change:
- Pin LLAMA_VERSION to b10313. Tracking master was not the safe default it
looked like: before tag b9702 (18 Jun 2026) the router parsed -ngl, -c and
--jinja and then silently discarded them (fixed by ggml-org/llama.cpp#24760),
so anyone who installed before that date had a GPU that was never used.
?reload=1 likewise only exists from b9023.
- Never destroy a hand-edited systemd unit. The manual guide tells people to
edit these files (Step 10 adds --api-key, Step 11 changes the bind address)
while this script promised re-running was safe. Both cannot be true. Units we
wrote are now checksummed, so we update our own and keep the user's,
with --replace-units to override.
- Odysseus: clone the canonical odysseus-dev/odysseus and pin a commit. The old
pewdiepie-archdaemon address is a GitHub rename redirect over a name slot that
account no longer uses; if it ever reuses the name, this script would clone
and execute whatever landed there. Upstream publishes no tags, so a commit is
the only thing we can hold still.
- Odysseus credentials: set ODYSSEUS_ADMIN_PASSWORD instead of grepping stdout.
Upstream branches on sys.stdin.isatty(), which is true under this script, so
it took the interactive getpass path and never printed the line we searched
for. The password now goes to a root-only file rather than the terminal.
- Read the real SSH port from sshd before enabling ufw. Allowing the OpenSSH
profile assumes port 22 and locked out anyone running sshd elsewhere, which is
the exact outcome the old comment promised to avoid.
- Check disk and VRAM before downloading. --full claimed to need a 24 GB card
and verified neither, then fetched 26 GB. -ngl is now sized from the card
rather than hardcoded to 999, so the 8 GB card in the Part 1 BOM no longer
runs out of memory on the --full tier. Override with NGL=<n>.
- Verify downloads landed and are a plausible size, and fall back to
huggingface-cli when hf is absent.
- LC_ALL=C around nvidia-smi. Driver health was classified by matching English
words, so a non-English system misread "mismatch" as "broken" and reinstalled
a driver that only needed a reboot.
- AMD Vulkan health checks: detect Resizable BAR being off (llama.cpp#27097
measures 37.09 -> 13.89 t/s on an RX 7900 XTX) and work around it, warn on
Mesa below 25.2, and warn when AMDVLK is in use instead of RADV.
- Honest error trap. It claimed "nothing else was changed" when packages,
drivers and services may already have been written.
Uninstaller, so install and remove stay symmetrical:
- FIX: a successful reset exited 1 and only a dry run exited 0, because the
last statement was a bare `[[ "${DRY}" == "yes" ]] && warn ...`. Anything
checking the exit code was told every successful reset had failed.
- Read the ports back out of the installed units instead of assuming 8020/7000,
which deleted rules that were never added and left the user's real ports open.
- Remove the render/video groups, but only the ones we recorded adding, so a
user who was already in 'video' keeps their graphics working.
- Clean up /var/lib/patteros, including the saved first-login file.
Tests added, both run in a disposable container:
tests/test_unit_preservation.sh 8 assertions, extracts the real function
tests/test_uninstall_reversal.sh 8 assertions, covers the exit-code and
group-safety regressions above
shellcheck 0.11.0 clean on all four files.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The published Part 2 video description links to github.com/Daniel-Parke/PatterOS/blob/main/scripts/install_local_ai.sh, so the FILE keeps its name and everything else is brought into line with it. This is the opposite of the earlier assumption that the header was right and the filename was wrong: renaming the file would break a link in a video that is already out. - 12 references to setup_local_ai.sh corrected across both scripts, including the two die() messages a user hits first if they forget sudo, which told them to run a file that does not exist. - Versions matched at v1.4. They had drifted to v1.3 and v1.2 despite being a pair, in both the header comment and the on-screen banner. - show_help() now reads to an "end of help" sentinel instead of `sed -n '2,51p'`. Counting lines meant any header edit silently truncated the help or spilled code into it, and this change adds 7 header lines, which would have done exactly that. - Removed the reference to a "full PatterOS install.sh" holding the heavy provisioning. No such file exists anywhere, public or private; it was an aspiration from the Part 1 guide that got carried into shipped code. - Documented --replace-units and the LLAMA_VERSION / CTX / NGL environment settings, which existed but were undocumented. - Second duplicated "==== 8 ==" section banner renumbered to 9, matching the visible 9/9 counter. - README: removed the link to the vision document. It is gitignored and has never been committed, so the link was broken for everyone except the maintainer. The document stays private by choice. Every remaining relative link in the README was checked and resolves. - House style: 44 em-dashes removed across the scripts, README, REBRANDING.md and branding/LICENSE.md. Verified in an Ubuntu 24.04 container: shellcheck 0.11.0 clean on all four shell files, both help outputs render from first line to sentinel, and both test suites still pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The guide the installer points at was not in the repository. The installer prints "see the guide, Step 10/11/12", and those numbers are correct, but they resolved against a Word file that lived only on the maintainer's machine. A novice cloning the repo had nothing to open. Published as Markdown (docs/part-2-manual-setup-guide.md and docs/local-ai-handbook.md) via tools/docx_to_md.py, which recovers structure from the Word markup rather than rewriting prose, so the author's wording carries across unchanged and every correction below is a separate visible edit. The Markdown is now the master copy. Corrections to the Part 2 guide, all of which matched bugs already fixed in the script: - --models-max 0 on the CPU path. Same bug, same reason: 0 means unlimited. Corrected in Step 6 and Step 8, and the explanation now says so explicitly. - The Odysseus clone URL, which pointed at a rename redirect over a name slot its old owner no longer uses. - "We deliberately use the latest llama.cpp" replaced with the actual reason to pin: before tag b9702 the router silently ignored -ngl, -c and --jinja. - setup_local_ai.sh renamed to install_local_ai.sh throughout. Part 1 BOM guide, patched in place by tools/patch_part1_docx.py because the Part 1 video links to that exact path and it cannot move. 10 paragraphs changed, all 210 paragraphs and 16 document parts intact, and the script is idempotent so it can run in CI as a staleness check: - Ports 8080 and 7860 corrected to 8020 and 7000, and Unsloth Studio corrected to Odysseus. Neither the ports nor the application were right. - ROCm corrected to Vulkan in both places. The installer builds Vulkan for AMD, and on gfx1100 Vulkan is also the faster of the two for token generation. - The multi-GPU and mixed-vendor claim removed. The installer supports one card. - github.com/Daniel-Parke/YouTube-Repo repointed to PatterOS, in the visible text and in the underlying hyperlink relationship. - install.sh renamed and "(coming soon)" dropped, since it now exists. Models switched to the QAT builds, in the script and the guide together. They are 0.6 to 1.6 GB smaller per model at the same UD-Q4_K_XL level, which matters on the 512 GB drive the Part 1 build specifies, and they are what this project's own Part 3 comparison tests actually measured. All four repos were checked against the Hugging Face API on 15 August 2026: each resolves, each is Apache 2.0 and ungated, and each contains exactly one file matching the glob. Note the casing differs from the non-QAT repos (12B, not 12b). No verified quality comparison between QAT and standard builds at the same quantisation level exists, so the size saving is measured and the quality claim is deliberately not made. README: links both new guides, documents the third-party software PatterOS installs with each licence, and drops the link to the vision document, which is gitignored and stays private. Verified: shellcheck clean, both test suites pass, every relative link in every Markdown file resolves, and the script and guide agree on the model list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The project's promise is "this will not break your machine", and until now nothing checked that. This adds the missing floor. CI (.github/workflows/ci.yml), five jobs: - shellcheck, run against TWO versions on purpose. They disagree: 0.9.0 is what Ubuntu 24.04 ships, so it is what a contributor gets from apt, and it flags `a && b || true` patterns and reports SC2317 where 0.11.0 reports SC2329. Pinning only the newer one would have meant CI failing on findings that never appear locally, which is exactly what happened while writing this. Four such findings are fixed in the scripts here. - bash -n on every script, plus a check that --help still renders and that the sentinel comment has not leaked into it. - The consistency and behaviour tests. - A CRLF check. The repo is maintained from Windows and a CRLF shell script fails on Linux with a confusing error. tests/test_docs_consistency.sh, 9 assertions, no root needed. Every one of them is a defect that actually shipped: - --models-max 0 must never reappear in the scripts or the guide - nothing may refer to setup_local_ai.sh, which is not the file's name - Odysseus must use the canonical address, checked excluding comments so the explanation of why the old one is dangerous can stay - installer and uninstaller versions must match - every "guide, Step N" the installer prints must resolve to a real heading - ports and models in the script must appear in the guide - no full-precision model may creep into the download list That test immediately found a real gap: the express tier downloads Gemma 4 E4B and the guide never named it. The guide now carries the full four-model table with sizes and a note about the capitalisation. Project files: CONTRIBUTING.md (with the seven non-negotiable rules and the "never claim something you have not checked" one, which is where several v1.4 bugs came from), SECURITY.md (which treats "breaks the user's desktop" as a security problem, because for this audience it is), CODE_OF_CONDUCT.md, CHANGELOG.md documenting v1.4 in terms of what it means for someone's machine, issue and pull request templates, and .editorconfig. NOTICE now attributes the third-party software PatterOS installs, with each licence, and states plainly that none of it is bundled or redistributed. The AGPL on Odysseus matters if that ever changes. SPDX headers added to both scripts, placed after the help sentinel so they do not appear in --help output. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Shown once, after detection and before any change is made. It does two things. First, it lets the user catch a wrong detection while that is still free. If PatterOS has the graphics card wrong, everything downstream (which backend to build, how many layers fit, whether ComfyUI could ever work) is wrong too, and right now the user has no way to notice until it fails. Second, it is straight about the risk. Installing graphics drivers is the one step here that can leave someone unable to boot into their desktop, and the audience for this project frequently has one computer. Saying so is both fairer and a reasonable liability position, and it is written to inform rather than to frighten: six sentences, no jargon, ending on the fact that everything is optional and nothing has happened yet. The wording adapts: with --skip-drivers it says the drivers will not be touched rather than warning about a step that will not run, and the graphics line reads "none found, so the processor will do the work" rather than the internal token "cpu". Verified in a container across three cases: --cpu, no GPU present, and --skip-drivers. shellcheck clean on 0.9.0 and 0.11.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two skills written from the video transcripts and the setup guide, then the raw transcripts deleted as agreed. They live in the user-level skills directory rather than this repository, so they are available across all the Patter* projects and are never published here. pattertech-voice written register: docs, README, changelog, web copy pattertech-script spoken register: video scripts, narration, descriptions Both record the measured patterns rather than an impression: 11-word average sentences, 'so' as the default opener at 10.9 per 1000 words, 'going to' rather than 'will', and the deliberate split between 'we' for the shared journey, 'you' for the viewer's own choices, and 'I' for opinion and admission. Both also carry a warning about the source. The captions are auto-generated and unreliable: 'cause' is transcribed as 'cuz', and ROCm, Vulkan, Odysseus, Ubuntu, llama.cpp and LACT are all mangled. Those are transcription errors rather than usage, and the skills say so explicitly so neither is ever reproduced. The .gitignore entry stays as a guard against publishing caption exports as if they were documentation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Docs-only, no code or licensing change. Aligns TRADEMARK.md and NOTICE with the sibling PatterStage repository.
Seven commits covering the installer and uninstaller fixes, the published Part 2 guide, the Part 1 corrections, the test suite and CI, and the pre-install hardware check. See CHANGELOG.md for what each one means on a user's machine. # Conflicts: # NOTICE
These nine files existed only on the dev branch, inside a parallel "Budget Build Guide" tree that also held byte-identical duplicates of both scripts and the BOM guide. Keeping two copies of a script in one repository is how they drift, so only the test artefacts come across and the duplicate paths do not. Uses the newer filenames, which prefix each result with size and quantisation and fix the "Qwn3.6" typo, so the files sort usefully and say what they are. The zip alongside them is a duplicate of the same nine files and is not committed. The README explains the method, reproduces the prompt, and adds three things a reader needs to interpret the results honestly: - The Nemotron runs deserve scepticism. llama.cpp support for Nemotron-3-Nano-30B-A3B was closed as not planned (#18064) and third-party GGUF conversions have hit load assertions (#20570), so a poor showing here may be the conversion rather than the model. - The licences differ. Gemma 4 and Qwen3.6 are Apache 2.0; the NVIDIA Nemotron Open Model License is permissive but bespoke, with an attribution requirement and an indemnity clause. - Only Gemma 4 ships in the installer. The rest were run for comparison. Scanned before committing: no email addresses, credentials, tokens or IP addresses in any of the nine files. The single /home/ match is a Siemens Energy URL inside a citation, not a filesystem path. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Brings together PR #1 (PatterStage trademark alignment) and the whole v1.4 correctness effort. See CHANGELOG.md for what each change means on a user's machine.
This tree held a second copy of every file the repository already had. Git
confirmed it: `scripts/install_local_ai.sh` and the copy under Part 2 were the
same object, as were both uninstallers and both copies of the BOM guide. Two
copies of a script in one repository is how they drift, and drift is what most
of v1.4 was spent fixing.
Nothing is lost. Verified before removal:
- All nine Part 3 result files are byte-identical to the copies now at
docs/model-tests/hydrogen-deep-research/, which use the clearer
<size>_<quant>-<model> names and fix the "Qwn3.6" typo.
- The research prompt is reproduced word for word in docs/model-tests/README.md,
alongside the method and the caveats needed to read the results honestly.
- The scripts and the BOM guide keep their original paths, which matters because
the Part 1 and Part 2 video descriptions link to them directly:
scripts/install_local_ai.sh
scripts/uninstall_local_ai.sh
docs/PatterOS_AI_Rig_BOM_and_Build_Guide.docx
The repository now has one place for each thing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
…pport The existing tests covered the uninstaller and the docs. The installer itself was never actually run, which is awkward for a script whose whole promise is "this will not break your machine". tests/harness/stubs.sh puts fake versions of the external commands on PATH. The installer is completely unmodified: real branching, real decisions, real service files. Only the outside world is pretend, and every stub logs what it was asked to do so a test can assert on the commands that WOULD have run. That makes the dangerous paths testable. These are the ones you cannot try for real without deliberately breaking something: - A mismatched NVIDIA driver. Must NOT reinstall, must ask for one reboot. - A genuinely missing driver. Must install. - An 8 GB card asked to hold the --full tier, whose largest model is 17.3 GB. Asserts it computes a partial offload rather than claiming 999 layers fit. It chose 23. - Resizable BAR switched off, which roughly halves throughput and looks like nothing is wrong. Asserts the workaround reaches the service file. - SSH on port 2222, asserting the rule is added BEFORE ufw is enabled. - A disk with 4 GB free, asserting it refuses before downloading anything. - Mesa 24.0.5 and AMDVLK, asserting both are called out. - CPU only, asserting --models-max is 1 and never 0. 57 assertions across ten scenarios, bringing the suite to 82. Also validates the generated units with systemd's own systemd-analyze, and checks the first-login file is mode 600 and that the password never reaches stdout. Three bugs in the tests themselves, found and fixed while writing them: - The has/hasnt helpers already pass -- to grep, so a second -- at the call site became the search pattern. Seven assertions were matching "--" instead of what they claimed. Several were passing for the wrong reason. - STUB_HAS_HF=0 made `hf` exit 127 rather than removing it. The installer detects with `command -v hf`, which tests for the file, so the fallback was never exercised. - STUB_NO_NVCC=1 had the same flaw, so the CUDA-to-CPU fallback was never taken either. Worth recording, because the first one is the reason to be suspicious of a test that has only ever passed. README rewritten. It was organised around "where are the files", which suits a maintainer, not somebody arriving from a video wanting to know whether this will work on their machine. Now leads with what you end up with, a diagram of how the pieces connect, a table answering "will it run on my computer" in about ten seconds, and three routes in: run the installer, do it by hand, or just read. Adds what to expect when it finishes and how to undo it. Every port, path and model size cross-checked against the script. shellcheck clean on 0.9.0 and 0.11.0, including the new harness. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Found by the new hardware-path tests, on CI rather than locally, which is the whole point of them. After each download the installer confirmed the file had arrived and was a plausible size. It searched ~/models for anything matching the quantisation glob and took the first result. But ~/models holds every model you have ever downloaded and they all match a glob like *UD-Q4_K_XL*.gguf, so on a directory with more than one model it could size-check a different file entirely. When the file it happened to pick was smaller than the one being fetched, the size check failed and the installer reported a perfectly good download as possibly incomplete, then told the user to re-run to fetch it again. Whether it happened depended on the order `find` returned entries, which is filesystem dependent. It passed on my machine and failed on GitHub's runner. That is worth stating plainly: a green local run proved nothing here. Hugging Face names the file after the repository, so the search is now anchored on the repository name, with the bare glob kept as a fallback for any repo that does not follow that convention. Regression test seeds ~/models with an unrelated larger model first, then asserts each confirmation names the file for the repo actually being fetched. Matching only on "did it report success" would have passed against the old code in the favourable ordering, so the assertion is on the filename. 61 assertions now, all passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… working Found by running the installer for real, twice, on an RTX 3090 Ti, and by driving every prompt, flag and uninstall combination that -y had been skipping. The workspace pinned commit raises NameError on a missing Any import, so Odysseus started, died, and was restarted every ten seconds while the summary printed a login URL. The installer now carries that one-line upstream fix, watches until the service answers or visibly fails, and prints NOT RUNNING instead of a URL for something that is down. Several other claims of success were false in the same way: --lact skipped its purge on a real package list because grep -q plus pipefail turned SIGPIPE into failure; the uninstaller announced firewall rules gone that were still there; an existing ~/llama.cpp ignored LLAMA_VERSION, so the GPU-offload pin never applied to people who had cloned by hand; changing a setting rewrote the unit and left the old process running; a busy port 8020 was blamed on the NVIDIA driver and then reported healthy because something else answered on the port. Safety fixes that go with that: CUDA compile jobs are capped by free RAM so a 16-thread / 31 GB box cannot OOM the desktop mid-build; the service is stopped before the build directory is deleted; --drivers and --packages only reverse what PatterOS recorded adding and never purge mesa-vulkan-drivers; a default uninstall keeps Odysseus notes in a dated backup; both scripts refuse a home of /. Tests now cover the prompts (72), the flags (167, including the post-upgrade NVIDIA watchdog), and every uninstall flag (145). CI runs them. 463 stubbed assertions, all passing. Real hardware: CUDA build, 232 tok/s on the 3090 Ti with llama-server holding 1788 MiB VRAM, live model swap, ?reload=1, and a clean uninstall/reinstall round trip under a throwaway user. Co-authored-by: Cursor <cursoragent@cursor.com>
NVIDIA, AMD, and --cpu remain the tested paths. The installer still takes the Vulkan path on Intel and now says so. NVIDIA Path A, GPU offload, and --cpu were retested on the 3090 Ti after AMD commit e472e01; no NVIDIA or CPU installer bugs turned up, so those paths were not rewritten. Co-authored-by: Cursor <cursoragent@cursor.com>
The reversal suite grepped dry-run output for 9100/9200. After the uninstaller only reported LAN rules that actually existed, GitHub's ubuntu container (no ufw) never printed those ports even though it had read them from the units. The plan now names the recovered ports. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Status (17 August 2026)
This PR is the whole of
devsincemain. The original v1.4 write-up below iskept as-is. Everything that landed after it is in the sections at the end:
NVIDIA real-hardware testing, the four AMD-rig installer bugs, Intel labelled
untested, a NVIDIA retest after those AMD fixes, and the CI reversal-test fix.
Hardware tested by us: NVIDIA RTX 3090 Ti, AMD RX 7900 XTX, CPU /
--cpu. Intel Arc / Intel iGPU: not tested by us.What this is
v1.4, the correctness release. 17 commits, 39 files, +13,740/-142.
Almost everything here is a fix to something already shipping. People are
downloading these scripts on the strength of the videos, and several of the bugs
below could cost them an evening.
Every external fact was checked against a first-party source on 15 August 2026:
the Hugging Face API, the upstream repository's own source, or the vendor's own
documentation. Where something could not be confirmed, it is marked unverified
rather than guessed.
The three that matter most
1.
--models-max 0meant unlimited, not noneThe CPU path set
--models-max 0believing it was the restrictive value. Fromllama.cpp's own
common/arg.cpp:Three sites in
tools/server/server-models.cpptreat<= 0as no limit,switching off the capacity check, LRU eviction and the idle queue. Since
-capplies per model and each model is a separate child process, this was unbounded
memory growth, on exactly the machines least able to survive it. Now
1everywhere. The manual guide taught the same error and is corrected too.
2. The GPU was doing nothing on installs before 18 June 2026
LLAMA_VERSIONdefaulted tomaster. Until upstream tagb9702the routerparsed
-ngl,-cand--jinjaand then silently discarded them(ggml-org/llama.cpp#24760), so every model ran on upstream defaults with the
card idle.
?reload=1likewise only exists fromb9023. Now pinned tob10313.3. The Odysseus clone address could have been taken over
pewdiepie-archdaemon/odysseusresolves only as a GitHub rename redirect over aname slot that account no longer uses; its only remaining public repo is
dionysus. Had that name been reused, the installer would have cloned and runpython setup.pyfrom whatever landed there, directly beneath a banner tellingusers never to blind-run code from the internet. Now clones
odysseus-dev/odysseusand pins commitcf4e240a, since upstream publishes notags.
Also fixed
edit them (Step 10 adds an API key, Step 11 changes the bind address) while the
installer promised re-running was safe. Both could not be true. Units are now
checksummed: ours get updated, yours get kept and backed up.
--replace-unitsoverrides.
sys.stdin.isatty(), which is true under the installer, so it took theinteractive
getpasspath and never printed the line being grepped for.PatterOS now sets the password itself and writes it to a root-only file
instead of printing it to the terminal.
definition) then force-enabled ufw. Anyone with sshd elsewhere lost access to
their own machine. The real port is now read from
sshd -T.English words in
nvidia-smioutput, so a translated "mismatch" fell throughto "broken" and triggered the reinstall the check exists to prevent. Now
LC_ALL=C.--fullcould run a card out of memory. It claimed to need 24 GB andchecked neither card nor disk, then downloaded ~26 GB. The Part 1 BOM sells an
8 GB card. Disk and VRAM are now checked first, and
-nglis computed from theactual card instead of hardcoded to 999. Override with
NGL=<n>.and plausible size, with a
huggingface-clifallback whenhfis absent.because its last statement was a bare
[[ "${DRY}" == "yes" ]] && warn.Confirmed pre-existing against unmodified
main.leaving the user's real ports open. It now reads them back from the installed
units.
and only the ones it recorded adding, so a pre-existing
videomembershipsurvives.
installer confirmed a file had arrived at a plausible size, but it searched
~/modelsfor anything matching the quantisation glob and took the firstresult. Every model matches that glob, so with more than one present it could
size-check a different file and report a perfectly good download as
incomplete. Found by the new tests, on CI rather than locally, because it
depends on the order
findreturns entries.time most errors fire.
Added
memory and free disk and asks for confirmation, so a wrong detection is caught
while that is still free. States plainly that the driver step is the one part
carrying real risk, and that nothing has changed yet. Wording adapts to
--skip-drivers. Skipped under-y.throughput and looks like nothing is wrong (Misc. bug: Vulkan - slow token generation with AMD DGPU Resizable BAR disabled ggml-org/llama.cpp#27097 measures
37.09 to 13.89 t/s on an RX 7900 XTX). PatterOS now detects it, works around it
with
GGML_VK_DISABLE_HOST_VISIBLE_VIDMEM=1, and names the real BIOS fix. Alsowarns below Mesa 25.2 and when AMDVLK is in use instead of RADV.
docs/. Theinstaller prints "see the guide, Step 10/11/12" and those numbers were correct,
but they resolved against a Word file that existed only on the maintainer's
machine. A novice cloning the repo had nothing to open.
docs/model-tests/, with the method,the prompt, and the caveats needed to read them honestly.
Changed
the same
UD-Q4_K_XLlevel, which matters on the 512 GB drive the Part 1 buildspecifies, and matching what the Part 3 tests actually measured. All four repos
verified: each resolves, each is Apache 2.0 and ungated, each has exactly one
file matching the glob. Note the casing differs (
12B, not12b). Noquality claim is made between QAT and standard at the same quantisation,
because no first-party comparison exists.
install_local_ai.sheverywhere. The header, banner, helpand both
die()messages called itsetup_local_ai.sh, which is not thefile's name. The file keeps its name because the Part 2 video description links
to it directly; everything else was aligned to the file.
show_help()reads to a sentinel rather thansed -n '2,51p'. Countinglines meant any header edit silently truncated the help or spilled code into
it, and this PR adds header lines.
tools/patch_part1_docx.py,because the Part 1 video links to that exact path. 10 paragraphs changed, all
210 paragraphs and 16 document parts intact, and the tool is idempotent so CI
uses it as a staleness check. Corrections: ports 8080/7860 to 8020/7000,
Unsloth Studio to Odysseus, ROCm to Vulkan twice, the multi-GPU claim removed,
install.shrenamed and "coming soon" dropped, and theYouTube-Repolinkrepointed in both the visible text and the underlying hyperlink relationship.
Budget Build Guide/tree removed. Git confirmed both scriptcopies were the same object. Nothing lost: all nine Part 3 files are
byte-identical at their new path and the prompt is reproduced verbatim.
Removed
install.sh" holding the heavy provisioning.No such file has ever existed; it was an aspiration from the Part 1 guide that
reached shipped code.
Why ROCm was not adopted for AMD
Worth recording, since it was actively considered. Vulkan stays, because on
gfx1100 it is faster for token generation: roughly 167 to 195 t/s against
ROCm's 122 to 167 t/s on llama-2-7b Q4_0, across three first-party llama.cpp
sources (discussions #10879 and #15021, issue #20934). ROCm also does not support
Linux Mint at all, wants ~100 GB free by AMD's own guidance, drops RDNA 2 consumer
cards entirely, and its
--usecase=rocm,graphicspath plusamdgpu-dkmscantake out the user's desktop graphics.
ROCm is still required for ComfyUI, which has no Vulkan backend, so it will
appear later as an opt-in for image and video generation only. The Part 2 video
chapter "Why Vulkan instead of ROCm" remains correct.
Testing
--severity=stylebash -n, all scriptstest_install_paths.sh, 10 hardware scenariostest_docs_consistency.shtest_unit_preservation.shtest_uninstall_reversal.shsystemd-analyze verify86 assertions, all running in CI on every push.
The installer is now actually executed, not just linted.
tests/harness/stubs.shputs fake external commands on PATH; the installer itself is unmodified, so the
branching, the decisions and the service files are all real. That makes the
paths nobody can safely test for real testable:
--fulltier, largest model 17.3 GB. Asserts apartial offload is computed rather than claiming 999 layers fit. It chose 23.
--models-maxis 1 and never 0.Three bugs in the tests themselves were found and fixed while writing them,
including one where the
has/hasnthelpers already pass--to grep, so asecond
--at the call site became the search pattern. Seven assertions werematching
"--"rather than what they claimed, and several were passing for thewrong reason.
Still not tested on real hardware. Everything above runs in Ubuntu 24.04
containers against stubbed hardware. No GPU has executed any of this. The
computed
-nglpath and the Intel paths in particular are unverified againstreal silicon, and the Intel rows ship marked untested.
Notes for review
branch and PatterStage is live in
TRADEMARK.mdandNOTICE; closing avoidedtwo routes to
mainfor the same commits.descriptions link to them directly:
scripts/install_local_ai.sh,scripts/uninstall_local_ai.sh,docs/PatterOS_AI_Rig_BOM_and_Build_Guide.docx..docxrenders correctly in Word. The XML isvalid and the paragraph count is unchanged, but the rendered formatting was not
visually checked.
🤖 Generated with Claude Code
After the original v1.4 write-up
The body above was written before the installer had been run on real silicon.
What follows is the rest of this PR: NVIDIA hardware, the AMD-rig fixes, Intel
honesty, a NVIDIA retest that did not rewrite AMD paths, and the CI failure that
was blocking merge.
NVIDIA, on an RTX 3090 Ti (
0727c95)Ran the real installer twice under a throwaway user (
patteros-test), neveragainst the live
danielaccount.--skip-upgrade --skip-firewall. Driver595.71.05 left alone. CUDA build, express models, services started.
GPU was actually used: llama-server held 1788 MiB VRAM; Gemma 4 E2B generated
at 232 tok/s (later retest 236). Live model swap,
?reload=1,--models-max 1.Host restored: units checksum-matched, ufw numbered ports matched, dpkg 2179/2179.
Bugs found by that run, all "the script said success while doing something else":
NameErroron a missingAnyimport);the installer now carries that one-line upstream fix and will not print a
login URL for a workspace that is not answering.
systemctl is-activewas treated as health; a crash-looping unit is "active".--lactskipped its purge on a real package list (grep -q+pipefail+SIGPIPE, exit 141).
~/llama.cppignoredLLAMA_VERSION, so the GPU-offload pin neverapplied to people who had cloned by hand.
because something else answered on the port.
build_jobs()), so a 16-thread/ 31 GB box cannot OOM the desktop mid-build; the service is stopped before
build/is deleted.--drivers/--packagesonly reverse what PatterOS recorded adding, andnever purge
mesa-vulkan-drivers./.Tests added for prompts (72), flags, and every uninstall flag. Reversal still
runs only in a disposable container.
AMD, on an RX 7900 XTX (
e472e01)Four real installer bugs, found on that card, plus stub coverage. These are
Vulkan/ReBAR/Mesa fixes. They do not change CUDA on a healthy NVIDIA box.
vram_gb()was blind unlessmesa-utilshappened to be installed. Itasked
nvidia-smi, thenglxinfo.glxinfois not in the base package list,so a machine that only has what this installer put on it has neither.
Resizable BAR detection and partial
-nglboth use that number, so an AMD(or Intel) card with ReBAR off, and an 8 GB card asked to hold a 19 GB model,
both took
-ngl 999and skipped the workaround. The function now reads/sys/class/drm/card*/device/mem_info_vram_totalwhen the two userland toolsare missing. NVIDIA still uses
nvidia-smifirst and never reaches sysfs.The graphics driver was reported as "Mesa Overlay".
vulkaninfo --summarylists instance layers first, including
Mesa Overlay layer. Matching/driverInfo|Mesa/took that line. The parser now readsdriverInfoonly.-y --replace-unitssaid it had kept the edited unit, then replaced it.install_unit()printed "your version is kept" whenever-ywas set, andonly afterwards noticed
--replace-units. Vendor-neutral.--cpu --skip-buildon an AMD Vulkan install stripped the ReBARworkaround from the live service.
--cpuskipped PCI detection, sovulkan_healthnever ran,VK_ENVstayed empty, and the unit rewrite droppedGGML_VK_DISABLE_HOST_VISIBLE_VIDMEMandRADV_PERFTESTwhile still runningthe vulkan binary (
-ngl 999). The card is still detected under--cpu; ifthe reused engine is vulkan, the workarounds are filled in before the unit is
written. On NVIDIA,
--cpustill setsVENDOR=cpuand builds the CPU engine.Stub cases added: Mesa Overlay in
vulkaninfo,glxinfostub, AMD--cpu --skip-buildkeeps ReBAR/RADV, AMD 8 GB VRAM withoutnvidia-smi,--replace-unitswording.Intel honesty, then NVIDIA retested (
f30b0b6)No Intel hardware in front of us, so we stopped implying it was supported.
--cputested; Intel Arc / iGPUuntested by us. No bare "Yes" on the Intel row.
warn()whenVENDOR=intel;--cpustill says "ignoring anygraphics card".
untested. Intel stub case prints the warning; NVIDIA and AMD cases do not.
.tools/shellcheck(15 MB binary) removed from git; CI useskoalaman/shellcheck-alpine.Then the same 3090 Ti was retested under
patteros-testaftere472e01,without rewriting AMD paths:
bash -n, shellcheck 0.9.0 and 0.11.0--severity=style, CRLF, docs, units, Part 1 docx-ngl 999, no Vulkan/ReBAR env on the unit?reload=1,--models-max 1--cpuon this NVIDIA cardubuntu-drivers, Engine built (cpu),-ngl 0,libggml-cpuonlypatteros-testdeletedtest_uninstall_reversal.shis not run on a live workstation (it deletesllama.service).CI (
4e29103)Job install and remove round trip was red on every push after the firewall
honesty change, including
e472e01. The reversal test grepped dry-run outputfor
9100/9200. After the uninstaller only reported LAN rules that actuallyexisted, GitHub's
ubuntu:24.04container (noufw) never printed those portseven though
read_porthad the right numbers.The uninstaller plan now names the ports recovered from the installed units
(
LAN ports from the installed units: 9100 and 9200). The reversal test assertsthat string. The stubbed uninstall suite asserts it too. CI also installs
sudo,python3andadduser, which the later steps in that job need oncereversal no longer fails first.
AMD paths were not rewritten for the NVIDIA retest or for this CI fix.