From fcbbf31343b9d1e441d3ae24900386dc03c4706f Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sat, 8 Aug 2026 20:28:24 +0200 Subject: Add CLAUDE.md Document the parts that are not discoverable from a single file: the two alias maps and why they are separate per direction, the three installed version sources, and the verification commands (there is no test suite). Records the parsing traps that each produced silently wrong output, so they are not reintroduced: package filenames cannot be split by counting dashes, installed versions carry build metadata upstream lacks, and read with a tab IFS collapses empty fields. Co-Authored-By: Claude Opus 5 --- CLAUDE.md | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 CLAUDE.md (limited to 'CLAUDE.md') 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 `; 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 +`///.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. -- cgit v1.2.3