# Keeping up with upstream

The Pascal/P100 llama.cpp scene moves fast: forks get new branches,
speculative-decoding modes change flags, and models get re-quantized. Hardware
support, on the other hand, is frozen: **driver R580 and CUDA 12.x are the end
of the line for Pascal.** Tersler handles this with three pieces:

1. **Pin everything.** `install/tersler.conf` pins the fork, branch, commit,
   driver package, CUDA toolkit and launch flags. The installer holds the
   driver package (`apt-mark hold`) and pins NVIDIA-repo drivers out, so
   nothing changes behind your back.
2. **Detect changes.** `scripts/check-updates.sh` (installed as
   `tersler-check-updates`) reports:
   - new commits on the fork beyond the pin, with the commit log
   - new branches on the fork
   - changes in the other repos in `install/upstreams.list`
   - new GitHub repos matching P100/Pascal searches
   - driver and CUDA package updates (on the rig)
   - model repo updates on Hugging Face (`MODEL_HF_REPO`)

   It exits with code `10` when something is new, so it works in cron or a
   systemd timer.
3. **Try, measure, then switch.** Builds are versioned side by side under
   `/opt/tersler/builds/<commit>`. `current` points to the active build, and
   `previous` keeps the last one.

## Workflow

```bash
# 1. what's new?
tersler-check-updates -o ~/tersler-updates/$(date +%F).md

# 2. build the candidate next to the current one (doesn't touch the running server)
sudo systemctl stop tersler-llama           # two models don't fit in VRAM
LLAMA_COMMIT=<new-commit> sudo -E ./install/install.sh build
#    the build stage switches `current` to it, so remember the old one:
ls -l /opt/tersler/previous
#    a full build takes ~25 min and ~1.3 GB (fork's BUILD.md)

# 2b. correctness first: the fork ships a gate (decode bench, perplexity, FA op tests).
#     Check its usage in the source tree, it may expect its own build dir:
less /opt/tersler/src/llama.cpp/p100-docs/BUILD.md     # section "Verify"
#     expected per the fork: perplexity ≈ 2.610 (band 2.6209 ± 0.0199); tg256 ≈ 32 t/s cold
#     a change can pass all op tests and still produce NaN in real inference
#     (perplexity catches that), so never skip this step.

# 3. compare: same model, prompts and settings
tersler-bench /opt/tersler/previous
tersler-bench /opt/tersler/current
column -s, -t < /opt/tersler/bench/results.csv | tail -12

# 4a. keep it: pin the new commit in install/tersler.conf, commit to git
# 4b. roll back:
sudo ln -sfn "$(readlink -f /opt/tersler/previous)" /opt/tersler/current

sudo systemctl start tersler-llama
```

- **New launch flags:** read the fork's docs (`p100-docs/` in the fork) and
  diff its README. Try them by appending flags to `tersler-server` before
  changing `LLAMA_ARGS`.
- **New fork or branch:** change `LLAMA_REPO`/`LLAMA_BRANCH` in
  `install/tersler.local.conf`, then build and bench as above.

## Automating the check

On the rig, a weekly user timer:

```bash
mkdir -p ~/.config/systemd/user
cat > ~/.config/systemd/user/tersler-updates.service <<'EOF'
[Service]
Type=oneshot
ExecStart=/bin/sh -c '/opt/tersler/bin/tersler-check-updates -o %h/tersler-updates/latest.md || [ $? -eq 10 ]'
EOF
cat > ~/.config/systemd/user/tersler-updates.timer <<'EOF'
[Timer]
OnCalendar=weekly
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl --user enable --now tersler-updates.timer
```

The script can't judge whether a change is *good*. For that, read the commit
log, benchmark, and record the result in [changelog.md](changelog.md).

## Hard limits (don't "upgrade" past these)

| Component | Limit | Why |
|---|---|---|
| NVIDIA driver | R580 branch | the last branch with Pascal support |
| Kernel modules | proprietary, not `-open` | the open modules don't support Pascal |
| CUDA | 12.x | CUDA 13 dropped Pascal |
| llama.cpp build | `CMAKE_CUDA_ARCHITECTURES=60` only | a second architecture disables the fork's Pascal tuning |
| Ubuntu | 24.04 LTS | R580 + CUDA 12.9 are packaged for it; check before moving to a newer release |

## Notes from the fork's BUILD.md (read 2026-11-03)

- The CMake flags match `install/tersler.conf` exactly.
- `GGML_CUDA_FA_QUANTS=all` compiles flash attention for every K/V type pair.
  This makes `libggml-cuda.so` bigger, and the library is loaded into **VRAM
  on each card**. Growing it from 374 to 530 MB once broke a 262144-token
  context. If the 128K configuration stops starting after an update, check the
  library size first. Upstream's default set (`q4_0-q4_0;q8_0-q8_0;f16-f16;bf16-bf16`)
  covers our q4_0 KV cache and builds a smaller library.
- `--version` reports the commit at *configure* time and can lag. Tersler
  writes the real commit to `build-info.json` in every build dir.
