Skip to content

v1.4: correctness fixes, published guides, tests and CI - #2

Merged
Daniel-Parke merged 21 commits into
mainfrom
dev
Aug 17, 2026
Merged

Daniel-Parke merged 21 commits into
mainfrom
dev

Conversation

@Daniel-Parke

@Daniel-Parke Daniel-Parke commented Aug 15, 2026 •

Copy link
Copy Markdown
Owner

Status (17 August 2026)

This PR is the whole of dev since main. The original v1.4 write-up below is
kept 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 0 meant unlimited, not none

The CPU path set --models-max 0 believing it was the restrictive value. From
llama.cpp's own common/arg.cpp:

maximum number of models to load simultaneously (default: 4, 0 = unlimited)

Three sites in tools/server/server-models.cpp treat <= 0 as no limit,
switching off the capacity check, LRU eviction and the idle queue. Since -c
applies 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 1
everywhere. The manual guide taught the same error and is corrected too.

2. The GPU was doing nothing on installs before 18 June 2026

LLAMA_VERSION defaulted to master. Until upstream tag b9702 the router
parsed -ngl, -c and --jinja and then silently discarded them
(ggml-org/llama.cpp#24760), so every model ran on upstream defaults with the
card idle. ?reload=1 likewise only exists from b9023. Now pinned to b10313.

3. The Odysseus clone address could have been taken over

pewdiepie-archdaemon/odysseus resolves only as a GitHub rename redirect over a
name slot that account no longer uses; its only remaining public repo is
dionysus. Had that name been reused, the installer would have cloned and run
python setup.py from whatever landed there, directly beneath a banner telling
users never to blind-run code from the internet. Now clones
odysseus-dev/odysseus and pins commit cf4e240a, since upstream publishes no
tags.


Also fixed

  • Re-running destroyed hand-edited service files. The guide tells users to
    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-units
    overrides.
  • The Odysseus password was never captured. Upstream branches on
    sys.stdin.isatty(), which is true under the installer, so it took the
    interactive getpass path 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.
  • The firewall could lock you out. It allowed the OpenSSH profile (port 22 by
    definition) then force-enabled ufw. Anyone with sshd elsewhere lost access to
    their own machine. The real port is now read from sshd -T.
  • Driver health was misread on non-English systems. Classification greps
    English words in nvidia-smi output, so a translated "mismatch" fell through
    to "broken" and triggered the reinstall the check exists to prevent. Now
    LC_ALL=C.
  • --full could run a card out of memory. It claimed to need 24 GB and
    checked 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 -ngl is computed from the
    actual card instead of hardcoded to 999. Override with NGL=<n>.
  • Downloads reported success when nothing arrived. Now verified for existence
    and plausible size, with a huggingface-cli fallback when hf is absent.
  • The uninstaller exited 1 on success and 0 on a dry run, exactly backwards,
    because its last statement was a bare [[ "${DRY}" == "yes" ]] && warn.
    Confirmed pre-existing against unmodified main.
  • The uninstaller assumed default ports, deleting rules never added and
    leaving the user's real ports open. It now reads them back from the installed
    units.
  • The uninstaller left users in groups PatterOS added. It now removes them,
    and only the ones it recorded adding, so a pre-existing video membership
    survives.
  • Download verification could check the wrong file. After each download the
    installer confirmed a file had arrived at a plausible size, but it searched
    ~/models for anything matching the quantisation glob and took the first
    result. 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 find returns entries.
  • The error trap claimed "nothing else was changed", which was untrue by the
    time most errors fire.

Added

  • A pre-install hardware check. Shows the detected processor, graphics card,
    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.
  • AMD Vulkan health checks. Resizable BAR disabled in BIOS roughly halves
    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. Also
    warns below Mesa 25.2 and when AMDVLK is in use instead of RADV.
  • The Part 2 manual setup guide and the Local AI Handbook, in docs/. The
    installer 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.
  • The Part 3 model comparison tests, at docs/model-tests/, with the method,
    the prompt, and the caveats needed to read them honestly.
  • A test suite: 25 assertions, and CI.

Changed

  • Models switched to the Gemma 4 QAT builds, 0.6 to 1.6 GB smaller each at
    the same UD-Q4_K_XL level, which matters on the 512 GB drive the Part 1 build
    specifies, 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, not 12b). No
    quality claim is made
    between QAT and standard at the same quantisation,
    because no first-party comparison exists.
  • The installer is install_local_ai.sh everywhere. The header, banner, help
    and both die() messages called it setup_local_ai.sh, which is not the
    file'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 than sed -n '2,51p'. Counting
    lines meant any header edit silently truncated the help or spilled code into
    it, and this PR adds header lines.
  • The Part 1 BOM guide corrected in place by 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.sh renamed and "coming soon" dropped, and the YouTube-Repo link
    repointed in both the visible text and the underlying hyperlink relationship.
  • The duplicate Budget Build Guide/ tree removed. Git confirmed both script
    copies were the same object. Nothing lost: all nine Part 3 files are
    byte-identical at their new path and the prompt is reproduced verbatim.
  • LACT to v0.10.0. Versions matched at v1.4. 44 em-dashes removed for house style.

Removed

  • The reference to a "full PatterOS 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,graphics path plus amdgpu-dkms can
take 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

Check Result
shellcheck 0.9.0 and 0.11.0, --severity=style clean
bash -n, all scripts pass
test_install_paths.sh, 10 hardware scenarios 61/61
test_docs_consistency.sh 9/9
test_unit_preservation.sh 8/8
test_uninstall_reversal.sh 8/8
Generated units, via systemd-analyze verify valid
Part 1 docx staleness not stale
Secrets scan, every blob in history clean

86 assertions, all running in CI on every push.

The installer is now actually executed, not just linted. tests/harness/stubs.sh
puts 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:

  • A mismatched NVIDIA driver. Asserts it does NOT reinstall and asks for one reboot.
  • A genuinely absent driver. Asserts it does install.
  • An 8 GB card asked to hold the --full tier, largest model 17.3 GB. Asserts a
    partial offload is computed rather than claiming 999 layers fit. It chose 23.
  • Resizable BAR off. Asserts the workaround reaches the service file.
  • SSH on 2222. Asserts the rule is added BEFORE ufw is enabled.
  • 4 GB free disk. Asserts it refuses before downloading anything.
  • Mesa 24.0.5 and AMDVLK. Asserts both are called out.
  • CPU only. Asserts --models-max is 1 and never 0.
  • A second run. Asserts an identical service file.

Three bugs in the tests themselves were found and fixed while writing them,
including one where the has/hasnt helpers already pass -- to grep, so a
second -- at the call site became the search pattern. Seven assertions were
matching "--" rather than what they claimed, and several were passing for the
wrong 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 -ngl path and the Intel paths in particular are unverified against
real silicon, and the Intel rows ship marked untested.

Notes for review

  • PR docs(trademark): add PatterStage to the protected Patter* family #1 was closed rather than merged. Its two commits are ancestors of this
    branch and PatterStage is live in TRADEMARK.md and NOTICE; closing avoided
    two routes to main for the same commits.
  • Three paths are load-bearing and must never move, because the 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.
  • Worth a human eye: the Part 1 .docx renders correctly in Word. The XML is
    valid 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), never
against the live daniel account. --skip-upgrade --skip-firewall. Driver
595.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":

  • Odysseus pinned commit crash-looped (NameError on a missing Any import);
    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-active was treated as health; a crash-looping unit is "active".
  • --lact skipped its purge on a real package list (grep -q + pipefail +
    SIGPIPE, exit 141).
  • Uninstaller announced firewall rules gone that were still there.
  • 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, then reported healthy
    because something else answered on the port.
  • CUDA compile jobs are now capped by free RAM (build_jobs()), so a 16-thread
    / 31 GB box cannot OOM the desktop mid-build; the service is stopped before
    build/ is deleted.
  • --drivers / --packages only reverse what PatterOS recorded adding, and
    never purge mesa-vulkan-drivers.
  • Default uninstall keeps Odysseus notes in a dated backup.
  • Both scripts refuse a home of /.

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.

  1. vram_gb() was blind unless mesa-utils happened to be installed. It
    asked nvidia-smi, then glxinfo. glxinfo is 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 -ngl both 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 999 and skipped the workaround. The function now reads
    /sys/class/drm/card*/device/mem_info_vram_total when the two userland tools
    are missing. NVIDIA still uses nvidia-smi first and never reaches sysfs.

  2. The graphics driver was reported as "Mesa Overlay". vulkaninfo --summary
    lists instance layers first, including Mesa Overlay layer. Matching
    /driverInfo|Mesa/ took that line. The parser now reads driverInfo only.

  3. -y --replace-units said it had kept the edited unit, then replaced it.
    install_unit() printed "your version is kept" whenever -y was set, and
    only afterwards noticed --replace-units. Vendor-neutral.

  4. --cpu --skip-build on an AMD Vulkan install stripped the ReBAR
    workaround from the live service.
    --cpu skipped PCI detection, so
    vulkan_health never ran, VK_ENV stayed empty, and the unit rewrite dropped
    GGML_VK_DISABLE_HOST_VISIBLE_VIDMEM and RADV_PERFTEST while still running
    the vulkan binary (-ngl 999). The card is still detected under --cpu; if
    the reused engine is vulkan, the workarounds are filled in before the unit is
    written. On NVIDIA, --cpu still sets VENDOR=cpu and builds the CPU engine.

Stub cases added: Mesa Overlay in vulkaninfo, glxinfo stub, AMD --cpu --skip-build keeps ReBAR/RADV, AMD 8 GB VRAM without nvidia-smi,
--replace-units wording.

Intel honesty, then NVIDIA retested (f30b0b6)

No Intel hardware in front of us, so we stopped implying it was supported.

  • README hardware table: NVIDIA / AMD / --cpu tested; Intel Arc / iGPU
    untested by us. No bare "Yes" on the Intel row.
  • Part 2 Path B no longer lumps "AMD or Intel" as "works on anything".
  • Installer warn() when VENDOR=intel; --cpu still says "ignoring any
    graphics card".
  • Docs-consistency asserts README, the guide, and the installer all say
    untested. Intel stub case prints the warning; NVIDIA and AMD cases do not.
  • .tools/shellcheck (15 MB binary) removed from git; CI uses
    koalaman/shellcheck-alpine.

Then the same 3090 Ti was retested under patteros-test after e472e01,
without rewriting AMD paths:

Check Result
Layer 0: bash -n, shellcheck 0.9.0 and 0.11.0 --severity=style, CRLF, docs, units, Part 1 docx 0 failures. Docs 14/14.
Stub suites, 180s each paths 77/77, flags 169/169, interactive 72/72, uninstall_paths 146/146
Real CUDA install Path A, driver 595.71.05 left alone, Engine built (cuda), -ngl 999, no Vulkan/ReBAR env on the unit
GPU after the AMD commit llama-server 1788 MiB, 236 tok/s on E2B, live swap, ?reload=1, --models-max 1
--cpu on this NVIDIA card Path C, no ubuntu-drivers, Engine built (cpu), -ngl 0, libggml-cpu only
Host restore units checksum-matched, ufw matched snapshot, dpkg 2179/2179, patteros-test deleted

test_uninstall_reversal.sh is not run on a live workstation (it deletes
llama.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 output
for 9100/9200. After the uninstaller only reported LAN rules that actually
existed, GitHub's ubuntu:24.04 container (no ufw) never printed those ports
even though read_port had 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 asserts
that string. The stubbed uninstall suite asserts it too. CI also installs
sudo, python3 and adduser, which the later steps in that job need once
reversal no longer fails first.

AMD paths were not rewritten for the NVIDIA retest or for this CI fix.

Daniel-Parke and others added 15 commits June 18, 2026 22:20
- 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>
@cursor

cursor Bot commented Aug 15, 2026

Copy link
Copy Markdown

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.

Daniel-Parke and others added 6 commits August 15, 2026 15:35
…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>
@Daniel-Parke
Daniel-Parke merged commit 620001d into main Aug 17, 2026
12 checks passed
Sign up for free to 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.

1 participant