aboutsummaryrefslogtreecommitdiffstats
path: root/CLAUDE.md
diff options
context:
space:
mode:
Diffstat (limited to 'CLAUDE.md')
-rw-r--r--CLAUDE.md136
1 files changed, 136 insertions, 0 deletions
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..0b76b62
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,136 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What this is
+
+Two deliverables: `nvchecker.toml` (a merged nvchecker config) and `nvtable` (a
+Bash script that joins nvchecker's upstream results against the version
+installed on this host and the version shipped by the SlackBuild repo that
+packages it). No build step, no test framework, no dependencies to install.
+
+## Verifying changes
+
+There is no test suite. These are the checks that actually catch regressions:
+
+```bash
+bash -n nvtable # syntax only
+./nvtable -n -C # default view, no state writes, no color
+./nvtable -n -C -a # every tracked package
+```
+
+**Always pass `-n` while developing.** Without it `nvtable` runs `nvtake`,
+which rewrites `~/.config/nvchecker/old_ver.json` as a side effect of merely
+displaying the table.
+
+To validate the config without touching live state, rewrite `[__config__]` to
+point at scratch paths and run there:
+
+```bash
+sed 's|^oldver.*|oldver = "/tmp/o.json"|; s|^newver.*|newver = "/tmp/n.json"|' \
+ nvchecker.toml > /tmp/run.toml
+echo '{"version":2,"data":{}}' > /tmp/o.json
+nvchecker -c /tmp/run.toml 2>&1 | grep -oE '\] [^:]+: no-result'
+```
+
+A stanza that resolves prints `updated to <version>`; a broken one prints
+`no-result`. The strongest check on a stanza is that the version it returns
+equals the `VERSION=` in that package's `.info` — that caught a stanza pointing
+at an entirely wrong upstream project (`mutagen-io/mutagen` vs
+`quodlibet/mutagen`) that still resolved cleanly.
+
+`nvtable` reads `new_ver.json` and never refreshes it. After renaming stanzas
+the table reports the old names as untracked until `nvchecker` runs again.
+
+## Live state this touches
+
+The repo is self-contained, but the installed tool reaches outside it:
+
+| Path | Role |
+|------|------|
+| `~/.config/nvchecker/nvchecker.toml` | the deployed copy of this config |
+| `~/.config/nvchecker/{old,new}_ver.json` | nvchecker state; `nvtake` writes `old_ver` |
+| `~/bin/nvtable` | symlink to the script in this repo |
+| `~/.profile.d/qar.sh` | login script; calls `nvtable` where `nvcmp` used to run |
+
+Because `qar.sh` runs on every interactive shell, a bug in `nvtable` shows up
+in every new terminal. Test with `-n -C` before touching the deployed copy.
+
+`NVTABLE_CONF` and `NVTABLE_NEWVER` override the config and state paths, which
+is how to exercise the real script against scratch data.
+
+## Architecture
+
+`nvtable` is one pass with three joins. The subtlety is entirely in the joins,
+not the control flow.
+
+**Name resolution.** The design rule is that a stanza name equals the Slackware
+package name, so most rows join with no mapping at all. Two alias maps cover
+the rest, and they are separate because the mappings differ per direction:
+`ALIAS` maps a stanza to installed package name(s) (`kvantum` →
+`kvantum-qt5 kvantum-qt6`, one-to-many), while `REPO_ALIAS` maps a stanza to a
+SlackBuild directory (`kitty` → `kitty-bin`). Adding one map for both breaks
+the other direction.
+
+**Installed version** resolves through three sources in order:
+`/var/log/packages`, then the appimage-updater state file, then `flatpak list`.
+
+**Repo version** is the `VERSION=` line from
+`<repo>/<category>/<pkg>/<pkg>.info`. Note the category level: `.info` files sit
+at depth 3, not 2. The repositories scanned are the `REPOS` array at the top of
+the script.
+
+### Traps that produced real bugs here
+
+These are non-obvious and each one silently produced wrong output:
+
+- **A Slackware package filename cannot be parsed by counting dashes.** It is
+ `PRGNAM-VERSION-ARCH-BUILD`, but VERSION may itself contain a dash
+ (`solvespace-3.1-659215d`). Lookups anchor on the package name being searched
+ for, and require the following character to be a digit so `kvantum` does not
+ match `kvantum-qt5`.
+- **Installed versions carry build metadata upstream never has** (`r8125` is
+ `9.018.00_6.18.41`, version plus kernel). `vers_equal` treats installed as
+ equal when it merely extends upstream at a component boundary; strict equality
+ marks such packages permanently behind.
+- **`read` with `IFS=$'\t'` collapses consecutive tabs**, so an empty field
+ shifts every later field left. Row records are packed with `|` for this
+ reason. Do not "simplify" it back to tabs.
+- **Installed-behind and repo-behind are independent conditions.** A row can be
+ both (🔴🟡). Collapsing the marker to a single worst-case state hides the
+ packaging work.
+- **Column padding must happen before color codes are added**, or `printf`
+ counts escape bytes as width.
+
+### nvtake
+
+When a package is caught up, its name is collected and one `nvtake` call
+records them all, moving `new_ver` → `old_ver` so nvchecker's state tracks this
+system rather than only upstream history. `-n` disables it.
+
+The call is wrapped in `flock -n` because `nvtake` does read-modify-write on
+`old_ver.json` with no locking of its own, and `qar.sh` runs on every shell — so
+several terminals opening at once means concurrent writers. On lock contention
+the take is skipped rather than queued; the next run recomputes the same set.
+
+## nvchecker.toml conventions
+
+- **Stanza name = Slackware package name** wherever a package exists. Renaming a
+ stanza silently orphans its history in `old_ver.json` (the old key stays,
+ nothing matches the new one) and breaks any external consumer that looks
+ entries up by name — `appimage-updater` reads this config and requires the
+ entries `Feishin` and `cherrytree` verbatim.
+- **`# repo=` comments** mark which repository ships each package (`sbo`, `my`,
+ `sps`); entries with none are tracked-only.
+- **A name containing a dot must be quoted.** `[llama.cpp-vulkan]` parses as a
+ nested `llama` table and fails with `no source specified`.
+- **`use_latest_release` on GitHub needs `prefix = "v"`** when the tags carry
+ one, otherwise the version comes back as `v8.30.1` and never matches a `.info`
+ VERSION.
+- Prefer `use_latest_release` over `use_max_tag`: `use_max_tag` hits a tightly
+ rate-limited endpoint.
+- Per-repo `.extras/nvchecker.toml` files are deliberately kept in their own
+ repositories for standalone sweeps. This config is the superset; they drift.
+
+The GitHub token lives in `~/.config/nvchecker/keys.toml`, which `.gitignore`
+excludes so it cannot be committed alongside the config.