Merge pull request 'fix(#60): checks that lied, and claims the machine stopped honouring' (#61) from fix/60-review-findings into main
This commit was merged in pull request #61.
This commit is contained in:
@@ -1,3 +1,13 @@
|
||||
# LFS routing for image types. Measured 2026-10-21: this repo tracks ZERO LFS files
|
||||
# (`git lfs ls-files` = 0) — the wallpaper library became live theme state and is gitignored
|
||||
# (omp-docs/tools/dotfiles-deploy.md), so these four rules match nothing today. They are kept
|
||||
# because tracked media is a plausible future and idle filters cost nothing; deleting the file
|
||||
# is a pruning call for a session with Frank, not a cleanup an agent runs alone.
|
||||
#
|
||||
# Do not cite this file as the reason `git-lfs` is installed on a box. The manifest carries its
|
||||
# own justification (packages/common.pkgs), and a tool justified by dead config is a tool nobody
|
||||
# can defend when the config goes.
|
||||
|
||||
*.jpg filter=lfs diff=lfs merge=lfs -text
|
||||
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
||||
*.png filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
+54
-11
@@ -34,24 +34,36 @@ placeholders='changeme|change[-_]|placeholder|redacted|example|sample|dummy|noke
|
||||
# how a gate gets permanently overridden.
|
||||
assign_exempt='(_FILE|_PATH|_DIR|_URL|_ENDPOINT|_SOCKET)[[:space:]]*[:=]|[:=][[:space:]]*["'"'"']?(/|\.\./)'
|
||||
# Fail closed: a gate that cannot read the staged diff must not report success.
|
||||
if ! files=$(git diff --cached --diff-filter=ACM --name-only 2>&1); then
|
||||
#
|
||||
# `-z`, and the list lives in a file. Two traps decide this: git quotes any path it cannot print
|
||||
# as UTF-8 (core.quotePath is on by default), and a quoted name handed back to
|
||||
# `git diff -- <path>` matches nothing — the file is skipped, and a skipped file used to be a
|
||||
# silent pass. Command substitution cannot hold the list either, because $() deletes NUL bytes,
|
||||
# so the names go to a temp file and the loop reads them with a NUL delimiter.
|
||||
TMPD=$(mktemp -d) || TMPD=""
|
||||
if [ -z "$TMPD" ]; then
|
||||
echo "SECRET GATE: cannot create a temp dir — refusing to commit." >&2
|
||||
[ -n "${ALLOW_SECRETS:-}" ] && exit 0
|
||||
exit 1
|
||||
fi
|
||||
trap 'rm -rf "$TMPD"' EXIT
|
||||
if ! git diff --cached --diff-filter=ACM --name-only -z > "$TMPD/files" 2> "$TMPD/err"; then
|
||||
{
|
||||
echo "SECRET GATE: cannot read the staged diff — refusing to commit."
|
||||
printf ' %s\n' "$files"
|
||||
sed 's/^/ /' "$TMPD/err"
|
||||
echo "Override deliberately: ALLOW_SECRETS=1 git commit ..."
|
||||
} >&2
|
||||
[ -n "${ALLOW_SECRETS:-}" ] && exit 0
|
||||
exit 1
|
||||
fi
|
||||
# stderr is captured so the failure branch can quote it; on success it must not
|
||||
# leak a `warning:` line into the file list and have it mistaken for a path.
|
||||
files=$(printf '%s\n' "$files" | grep -vE '^(warning|fatal|error):' || true)
|
||||
[ -z "${files//[[:space:]]/}" ] && exit 0
|
||||
# git's stderr stays out of the name list on purpose: a `warning:` line mistaken for a path
|
||||
# becomes a file that "cannot be read", which is how a gate starts skipping the real ones.
|
||||
[ -s "$TMPD/files" ] || exit 0
|
||||
|
||||
report=""
|
||||
exempt=""
|
||||
count=0
|
||||
while IFS= read -r f; do
|
||||
while IFS= read -r -d '' f; do
|
||||
[ -z "$f" ] && continue
|
||||
# Self-exemption, deliberately an exact filename list and not `tests/`: the
|
||||
# gate's fixtures must contain credential-shaped strings or there is nothing to
|
||||
@@ -67,17 +79,48 @@ while IFS= read -r f; do
|
||||
# gate refused the redaction of the password it exists to catch.
|
||||
added=$(printf '%s\n' "$diff" | grep -E '^\+' | grep -vE '^\+\+\+' | sed 's/^+//' || true)
|
||||
[ -z "$added" ] && continue
|
||||
hits=$(printf '%s\n' "$added" | grep -Ei "$shapes" || true)
|
||||
# Shape hits are judged on the MATCHED TOKEN, not on the whole line. Documentation about
|
||||
# credentials quotes vendor placeholders — AWS publishes an all-zeros access-key id that ends
|
||||
# in EXAMPLE for exactly this purpose — and
|
||||
# a gate that blocks prose over an inert example gets worked around until it means nothing:
|
||||
# the live-sync heartbeat failed three ticks on 2026-10-21 because a managed skill page quotes
|
||||
# exactly that id. A line is exempted only when EVERY shape on it is inert, so one live token
|
||||
# sharing the file with a documented fake still blocks.
|
||||
hits=$(printf '%s\n' "$added" | while IFS= read -r line; do
|
||||
live=0
|
||||
while IFS= read -r m; do
|
||||
# The shape regexes carry fixed quantifiers, so the match they return is TRUNCATED:
|
||||
# `AKIA` + exactly 16 characters stops inside the word EXAMPLE and the placeholder
|
||||
# disappears from what is judged. Judge the match plus what follows it instead.
|
||||
ctx=$(printf '%s' "$line" | awk -v m="$m" '{ i = index($0, m); print (i > 0 ? substr($0, i, length(m) + 8) : m) }')
|
||||
printf '%s' "$ctx" | grep -Eiq 'EXAMPLE|X{4,}|Y{4,}|Z{4,}|_{4,}|REDACTED|CHANGEME|PLACEHOLDER' || live=1
|
||||
done < <(printf '%s\n' "$line" | grep -Eio "$shapes" || true)
|
||||
[ "$live" = 1 ] && printf '%s\n' "$line"
|
||||
done)
|
||||
if [ "${f##*.}" != "md" ]; then
|
||||
assigns=$(printf '%s\n' "$added" | grep -Ei "$assign" | grep -Ev "$assign_exempt" | grep -Eiv "$placeholders" || true)
|
||||
# The placeholder filter reads the VALUE, never the whole line. A trailing comment that
|
||||
# says `# example` says nothing about the token the same line assigns; scoping the filter
|
||||
# to what follows the separator is what keeps `API_TOKEN=<real> # placeholder` a block.
|
||||
assigns=$(printf '%s\n' "$added" | grep -Ei "$assign" | grep -Ev "$assign_exempt" \
|
||||
| while IFS= read -r line; do
|
||||
# The judged value is the assigned token alone: it ends at whitespace or `#`, so a
|
||||
# trailing comment cannot vouch for it. Truncating a quoted value early only ever errs
|
||||
# toward blocking, which is the safe direction for a gate that can be overridden.
|
||||
value=${line#*[:=]}
|
||||
value=${value#"${value%%[![:space:]]*}"}
|
||||
value=${value%%[[:space:]]*}
|
||||
value=${value%%#*}
|
||||
printf '%s\n' "$value" | grep -Eiq "$placeholders" && continue
|
||||
printf '%s\n' "$line"
|
||||
done)
|
||||
hits=$(printf '%s\n%s\n' "$hits" "$assigns" | sed '/^[[:space:]]*$/d')
|
||||
fi
|
||||
if [ -n "$hits" ]; then
|
||||
n=$(printf '%s\n' "$hits" | wc -l)
|
||||
count=$((count + n))
|
||||
report+=" $f"$'\n'"$(printf '%s\n' "$hits" | cut -c1-100 | sed 's/^/ /')"
|
||||
report+=" $f"$'\n'"$(printf '%s\n' "$hits" | cut -c1-100 | sed 's/^/ /')"$'\n'
|
||||
fi
|
||||
done <<< "$files"
|
||||
done < "$TMPD/files"
|
||||
|
||||
# Say so out loud: an exemption that prints nothing is how a carve-out becomes
|
||||
# invisible, and invisible is the property a secret wants.
|
||||
|
||||
+16
-4
@@ -86,15 +86,27 @@ agent/memories/--home-blank-.omp-wt-*
|
||||
# 2026-10-20), so this path in the repo was a duplicate of it, not its source — and
|
||||
# note that untracking deletes the copy in every other checkout regardless of
|
||||
# `git rm --cached` on the branch. A fresh box starts with an empty library; every
|
||||
# filename carries its wallhaven ID, so `curl -L -o <name> https://wallhaven.cc/w/<id>`
|
||||
# puts it back. Law: omp-docs/tools/dotfiles-deploy.md
|
||||
# filename carries its wallhaven ID; `/w/<id>` answers `text/html`, so resolve the CDN URL at
|
||||
# `https://wallhaven.cc/api/v1/w/<id>` and download `data.path` — the recipe, with the reason it
|
||||
# is not a plain `curl -L`, is on omp-docs/tools/dotfiles-deploy.md
|
||||
dotfiles/noctalia/.local/share/noctalia/wallpapers/
|
||||
|
||||
# --- generated theme output: machine-written, never reviewable, never deployed ---
|
||||
# noctalia/matugen rewrite these in place; a tracked copy is a stale snapshot that
|
||||
# deploy.sh would push back over a live palette. Patterns match the exclusion list in
|
||||
# scripts/deploy.sh. Law: omp-docs/inventory/home-config-census.md, Refs #42
|
||||
# deploy.sh would push back over a live palette. These patterns are the ignore half of the
|
||||
# exclusion list in `deploy_file` (scripts/deploy.sh): change one, change the other, or a
|
||||
# generated file becomes tracked-but-never-deployed — the fiction class #41 removed. The
|
||||
# capital-N rule is noctalia's own palette-directory naming, and it was in deploy.sh only.
|
||||
# Law: omp-docs/inventory/home-config-census.md, Refs #42, #60
|
||||
dotfiles/**/themes/noctalia*
|
||||
dotfiles/**/colors/noctalia*
|
||||
dotfiles/**/noctalia.*
|
||||
dotfiles/**/noctalia-*
|
||||
dotfiles/**/Noctalia*
|
||||
|
||||
# --- harness natives: unpacked runtime payloads, never configuration ----------
|
||||
# 2.4 MB of tree-sitter grammars the harness unpacks into the repo root on first use (measured
|
||||
# on blankpad 2026-10-21). Regenerable, unreviewable, not config — every clause of AGENTS.md's
|
||||
# runtime-state doctrine applies, and it sat unnamed in the root, one `git add -A` away from
|
||||
# being committed as if it were configuration.
|
||||
natives/
|
||||
|
||||
@@ -94,19 +94,16 @@ knowledge base. Those pages own their facts; this is only the pointer.
|
||||
|
||||
### Issue tracker
|
||||
|
||||
Work items are Gitea issues on `dank/command-center` (Dankclaw,
|
||||
`192.168.0.143:3000`) — no `gh`, no `tea`. See
|
||||
[`omp-docs/skills/issue-tracker.md`](omp-docs/skills/issue-tracker.md).
|
||||
See [`omp-docs/skills/issue-tracker.md`](omp-docs/skills/issue-tracker.md).
|
||||
|
||||
### Triage labels
|
||||
|
||||
The five canonical roles, each label string equal to its name. See
|
||||
[`omp-docs/skills/triage-labels.md`](omp-docs/skills/triage-labels.md).
|
||||
See [`omp-docs/skills/triage-labels.md`](omp-docs/skills/triage-labels.md).
|
||||
|
||||
### Domain docs
|
||||
|
||||
Single-context, inside the existing knowledge base: `omp-docs/glossary.md` and
|
||||
`omp-docs/decisions/`. See [`omp-docs/skills/domain-docs.md`](omp-docs/skills/domain-docs.md).
|
||||
ADRs: `omp-docs/decisions/`. `omp-docs/glossary.md` is intentionally not created.
|
||||
See [`omp-docs/skills/domain-docs.md`](omp-docs/skills/domain-docs.md).
|
||||
|
||||
## Secret gate (HARD rule)
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ Documentation exists for ONE reason: **point of use**. A page earns its bytes on
|
||||
#### THE WORK LOG IS ONE FILE.
|
||||
The work log is a single page in the knowledge base: `omp-docs/log.md` in this repo, or whichever change-log page the repo's docs index names. Write the entry when the change lands — in the same commit as the change — and read that page to recover where things stand before starting. It follows its own entry law: a dated entry is what changed plus the durable lesson, a few lines.
|
||||
|
||||
- NEVER create a second log: not per directory, not per project, feature, branch or run, not `NOTES.md` / `PROGRESS.md` / `STATUS.md` / `worklog/`, not a dated diary. Logging in fifty places is not logging — the entry is never where the next reader looks, and each copy re-dates the same event on its own authority. In `dank/command-center` the KB linter reports any `log.md` that is not the bundle root's.
|
||||
- NEVER create a second log: not per directory, not per project, feature, branch or run, not `NOTES.md` / `PROGRESS.md` / `STATUS.md` / `worklog/`, not a dated diary. In `dank/command-center` the KB linter reports any `log.md` that is not the bundle root's; the rule and its reasoning are owned by `omp-docs/references/okf-spec.md`.
|
||||
- A log holds laws and incidents, never bookkeeping: no per-file inventories, no run-by-run numbers, no transcripts. A number in a log still has to pass the purpose test above.
|
||||
- If a knowledge base has no log page, add one at its root and list it in the index. That is the fix — never a log somewhere else.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[Unit]
|
||||
Description=swayidle idle lock (locks via noctalia-shell IPC, suspends on lid)
|
||||
Description=swayidle idle lock watcher (arms the idle lock via noctalia-shell IPC; suspend is covered by noctalia's sleep inhibitor)
|
||||
Documentation=https://github.com/swaywm/swayidle
|
||||
After=graphical-session.target
|
||||
# Restart together with the session, not merely at boot: this unit is wanted by
|
||||
|
||||
@@ -7,10 +7,14 @@
|
||||
// requires: Lenovo Group Limited 0x40A9 Unknown
|
||||
// optional: HDMI-A-1 (PM1601, dock-dependent)
|
||||
|
||||
// This panel's EDID tops out at 3440x1440@180 (read live from niri: no 240 Hz mode
|
||||
// exists). Ask for a mode the panel does not offer and niri does not fail — it logs
|
||||
// "could not be found, falling back to preferred" and drops to 60 Hz on every
|
||||
// reconnect. Keep 180 here unless a re-read EDID says otherwise.
|
||||
// The panel offers 3440x1440@239.998 as well as @180.000 (`niri msg outputs`, 2026-10-21), and
|
||||
// 180 is driven DELIBERATELY: the link trained at x8 32.0 GT/s against a max of x16 and the
|
||||
// display engine is throwing DMCUB errors with EINVAL page flips after s2idle, so the 240-class
|
||||
// mode stays undriven until a reboot clears that state. niri matches mode strings VERBATIM and
|
||||
// does not round: "@239.999" is not "@239.998", and a mode it cannot find does not fail — it
|
||||
// logs "could not be found, falling back to preferred" and drops the panel to 60 Hz on every
|
||||
// reconnect, which is how this desk ran for weeks. Copy the string from `niri msg outputs`.
|
||||
// Reasoning and triage: omp-docs/tools/niri/displays.md ("The config pattern", rules 4 and 5).
|
||||
output "Sceptre Tech Inc Sceptre M34 Unknown" {
|
||||
mode "3440x1440@180.000"
|
||||
scale 1
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Architecture decision records
|
||||
|
||||
Decisions this repo has made and will not silently revisit. One file per
|
||||
decision, named `NNN-<slug>.md`, listed here — an unlisted page fails `kb-lint`
|
||||
and does not exist as far as any agent is concerned.
|
||||
decision, named `NNN-<slug>.md`, listed here — an unlisted page fails
|
||||
`scripts/kb-coverage.py` and does not exist as far as any agent is concerned.
|
||||
|
||||
**No ADRs yet.** The first one lands the moment a decision stops being
|
||||
reversible.
|
||||
|
||||
+2
-2
@@ -57,8 +57,8 @@ search practices, open models, MCP.
|
||||
|
||||
## [Inventory](inventory/index.md)
|
||||
|
||||
Pre-pruning census of every userspace config on this box — the input for the
|
||||
pruning session.
|
||||
Per-file tracked-vs-box-only census of `~/.config` on blankpad — the input for the
|
||||
dotfiles pruning/adoption session.
|
||||
|
||||
## [Decisions](decisions/index.md)
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Inventory Index
|
||||
|
||||
Census of all userspace program configuration on the workstation.
|
||||
Tracked-vs-box-only census of userspace configuration on blankpad. Read the scope before
|
||||
trusting coverage: these pages measure `~/.config`, not `~/.local/share`, not system package
|
||||
state, not the servers.
|
||||
|
||||
- [home-config-census.md](home-config-census.md) — Complete inventory of top-level dotfiles, ~/.config, ~/.local/share state, and special directories (~/.pi, ~/.omp)
|
||||
- [home-config-census.md](home-config-census.md) — Per-file tracked-vs-box-only census of ~/.config on blankpad — the input for the dotfiles pruning/adoption session.
|
||||
|
||||
+59
-13
@@ -16,32 +16,78 @@ lesson, a few lines. Full incident narratives are recoverable from git
|
||||
(`git log --follow omp-docs/log.md`); knowledge that outlives an entry lives
|
||||
on its concept page, linked here. Never re-expand an entry in place.
|
||||
|
||||
This page is the bundle's **only** work log: one file, at the root. Never start a
|
||||
second one in a subdirectory or under another name — `scripts/kb-lint.sh` reports any
|
||||
`log.md` that is not this one.
|
||||
This is the bundle's only work log; [references/okf-spec.md](references/okf-spec.md) owns the
|
||||
one-log rule and `scripts/kb-lint.sh` enforces it.
|
||||
|
||||
## 2026-10-21 — reviewing a day of work, and finding checks that lie
|
||||
|
||||
Two-axis review (Standards / Spec) of everything landed 2026-10-20 → 2026-10-21: 51 commits,
|
||||
30 findings, worked on `fix/60-review-findings` (issue #60). Three findings were the same shape —
|
||||
a check that reports confidence it did not earn:
|
||||
|
||||
- **A guard that cannot see must refuse.** `pcie-probe.sh` dropped the `/dev/kfd` half of its
|
||||
busy check when `fuser` was absent, and `gpu_busy_percent` reads `0` for a resident-but-idle
|
||||
service: the tool measured beside a live endpoint while its own LAW said not to. Missing
|
||||
visibility is now a refusal (exit `3`), not a narrower check.
|
||||
- **A swallowed status is a false report.** `deploy.sh` printed `ENABLE` for units systemd
|
||||
refuses to enable (`live-sync.service` has no `[Install]` section; its timer arms it), and the
|
||||
loop walked `*.service` only, so a rebuilt box deployed the heartbeat and never armed its
|
||||
timer. Every outcome now reports what actually happened.
|
||||
- **A gate that reads a quoted path reads nothing.** git quotes non-ASCII paths in
|
||||
`--name-only` output; the quoted name came back as an empty diff and the secret gate
|
||||
`continue`d past it. Fail closed means `-z`, and it means the placeholder filter reads the
|
||||
assigned value — a comment saying `# example` cannot vouch for the token beside it.
|
||||
|
||||
The law that cost the most, from the probe: **a test that observes the tool's environment instead
|
||||
of what the tool passed on cannot see the bug.** The stub probe echoed `$HIP_VISIBLE_DEVICES`
|
||||
rather than its argument, so the suite asserted the mask was set and per-card bandwidth was
|
||||
unobtainable on the 2×R9700 box. Model the platform rule in the stub and the existing test goes
|
||||
red against the old code — that is the red-proof worth having.
|
||||
|
||||
Same shape in `remote-job.sh status`: it globbed its arguments, so a job named `x*` was reported
|
||||
as whatever happened to match in the caller's directory. A name is data — split it (`read -a`),
|
||||
never let the shell expand it — and a multiplexed `tail` with no jobs says "no jobs" instead of
|
||||
handing `*.out` to tail and reporting a missing file.
|
||||
|
||||
Incident, found while verifying the gate: the live-sync heartbeat had failed three hourly ticks,
|
||||
because a managed skill that documents how to write a secret gate quotes the vendor's all-zeros
|
||||
placeholder id and the gate's shape rule fired on the documentation. Prose about credentials is
|
||||
what the machine tier is made of; a gate that blocks it is worked around, and the workaround is
|
||||
the hole. Placeholder judgement reads the matched token — plus what follows it, because a fixed
|
||||
quantifier truncates `EXAMPLE` to `EXA` — never the whole line. `ALLOW_SECRETS` stays the only
|
||||
override, and the heartbeat's own payload was replayed through the fixed hook to prove it lands.
|
||||
|
||||
The rest of #60 was claims about the machine that the machine had stopped honouring:
|
||||
`.gitignore` and `deploy.sh`'s exclusion list had drifted apart (`Noctalia*` was excluded, never
|
||||
ignored), `.gitattributes` was the stated reason `git-lfs` was installed — for wallpapers that
|
||||
same week's diff untracked, and the one-log rule was restated in four owners. A coverage claim
|
||||
names what was measured; a tool justified by dead config is a tool nobody can defend when the
|
||||
config goes.
|
||||
|
||||
## 2026-10-21 — a documented mode the panel never had
|
||||
|
||||
The home ultrawide had been running at 60 Hz for weeks while `desk-1-home.kdl`
|
||||
asked for `3440x1440@239.999`. The page that owns that file documented the same
|
||||
phantom mode, so every reader — including tonight's inspection — trusted it.
|
||||
`niri msg outputs` says the Sceptre M34's EDID tops out at 180 Hz; there is no
|
||||
240 Hz mode to ask for.
|
||||
The panel does offer a 240-class mode: `niri msg outputs` lists `3440x1440@239.998`. The defect
|
||||
was one character — niri matches mode strings verbatim, and `239.999` is not `239.998`.
|
||||
|
||||
Laws, now on [tools/niri/displays](tools/niri/displays.md):
|
||||
|
||||
- **An impossible mode does not fail, it falls back.** niri logs
|
||||
`configured mode … could not be found, falling back to preferred` and drives
|
||||
the EDID preferred mode — 60 Hz here. A config typo therefore looks like a
|
||||
dying monitor, and every connector drop re-enacts it.
|
||||
- **A mode string must match the advertised string verbatim.** niri does not round:
|
||||
`3440x1440@239.999` does not select `3440x1440@239.998`. It logs
|
||||
`configured mode … could not be found, falling back to preferred` and drives the EDID
|
||||
preferred mode — 60 Hz here. A one-character typo therefore looks like a dying monitor, and
|
||||
every connector drop re-enacts it.
|
||||
- **Three layers, three signatures:** config fallback, DP link drops, and
|
||||
amdgpu `DMCUB` errors with `EINVAL` page flips after s2idle. Only the first
|
||||
yields to a file edit; only the last yields to a reboot. Triage table and
|
||||
probes are on the page.
|
||||
- **A number in a page is a claim, not a measurement.** Modes, refresh rates and
|
||||
tool availability were wrong on four pages at once — `wlr-randr` was listed as
|
||||
installed while `pacman -Qs wlr` matches nothing. Read the hardware before
|
||||
writing a mode into a runbook, and destroy the stale copies.
|
||||
- **A number in a page is a claim, not a measurement.** Mode strings, refresh rates and tool
|
||||
availability were wrong on several pages at once — `wlr-randr` was listed as installed while
|
||||
`pacman -Qs wlr` matches nothing. Read the hardware before writing a mode into a runbook, and
|
||||
destroy the stale copies. A correction is itself a measurement: the first pass at this entry
|
||||
published a second wrong hardware fact, with a `verified:` date stamped on it.
|
||||
|
||||
Fix: `desk-1-home.kdl` 239.999 → 180.000 on the identity block and both legacy
|
||||
connector blocks, applied live with `niri msg output <panel> mode <mode>` — the
|
||||
|
||||
@@ -21,12 +21,11 @@ Knowledge bundles rot. It is the nature of knowledge to attract cobwebs, much li
|
||||
|
||||
## Change Tracking with `log.md`
|
||||
|
||||
Every OKF bundle has a `log.md`, and it is the bundle's **only** work log: one file,
|
||||
at the bundle root. A subdirectory never gets its own — not `core/log.md`, not
|
||||
`notes.md`, not a per-project, per-branch or per-run diary. Sprawl is not caution:
|
||||
with fifty logs the entry is never where the next agent looks, and each copy re-dates
|
||||
the same event on its own authority. `scripts/kb-lint.sh` reports any `log.md` that is
|
||||
not the root's.
|
||||
Exactly one `log.md` per bundle, at the bundle root — [okf-spec.md](okf-spec.md) owns that rule
|
||||
and `scripts/kb-lint.sh` enforces it, reporting any `log.md` that is not the root's. The
|
||||
maintenance action is collapse, never coexistence: when a second log turns up (`core/log.md`,
|
||||
`notes.md`, a per-run diary), move its entries into the root log under their own dates, delete
|
||||
the file, and re-run the linter.
|
||||
|
||||
**Format:** one dated section per day, newest first; a few lines per change — what
|
||||
happened, and the durable lesson. It is not a file manifest.
|
||||
@@ -38,7 +37,7 @@ happened, and the durable lesson. It is not a file manifest.
|
||||
|
||||
A gate that gets an empty file list reports success. Law: a check MUST fail closed;
|
||||
a silently-passing gate is worse than none, because it buys the belief of safety.
|
||||
Details: [secret gate](../tools/index.md).
|
||||
Details: [secret gate](/tools/index.md).
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
@@ -58,8 +57,9 @@ Details: [secret gate](../tools/index.md).
|
||||
- ❌ Forgetting the entry — the change lands undocumented and the next agent rediscovers
|
||||
the failure the hard way.
|
||||
- ❌ Starting a second log in a subdirectory — the sprawl that makes logging useless.
|
||||
- ❌ Invented absolute paths in links — links resolve relative to the page, and the
|
||||
linter checks them.
|
||||
- ❌ Invented paths in links — the RECOMMENDED form is an absolute bundle path
|
||||
(`/tools/index.md`), resolved against the bundle root; the mistake is a path that
|
||||
names no file there, and that is what the linter reports.
|
||||
- ❌ Re-expanding an old entry in place — the entry is a pointer; git is the transcript.
|
||||
|
||||
---
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
type: reference
|
||||
title: Dotfiles deployment tier
|
||||
description: How dotfiles/ packages become live configuration — stow-equivalent symlinks, host override packages, and the two laws about theme media and systemd enable symlinks.
|
||||
description: How dotfiles/ packages become live configuration — deploy.sh modes, host override packages, and the tier's hard-won laws, from media and generated theme output to systemd enable/start semantics, dconf dumps, and idle-lock watcher triage.
|
||||
tags:
|
||||
- dotfiles
|
||||
- deployment
|
||||
- systemd
|
||||
- noctalia
|
||||
timestamp: 2026-10-20
|
||||
verified: 2026-10-20
|
||||
verified: 2026-10-21
|
||||
---
|
||||
|
||||
# Dotfiles deployment tier
|
||||
@@ -25,10 +25,15 @@ is stowed last and its files replace symlinks coming from base packages.
|
||||
| `deploy.sh` (sync) | adopt identicals, link new, quarantine divergents into `.quarantine/` and replace them |
|
||||
| `deploy.sh --adopt` | like sync, but divergents are reported and skipped rather than quarantined |
|
||||
|
||||
Two package shapes are not symlinks. `dconf/dump.txt` is loaded with `dconf load /`, and
|
||||
`systemd-user` units get `daemon-reload` plus `enable`; a unit is **started** only when
|
||||
systemd reports it as nothing-yet (`""`/`disabled`), and never when its `ExecStart` binary
|
||||
is absent here, so a unit belonging to another box stays inert instead of crash-looping.
|
||||
Two package shapes are not symlinks. `dconf/dump.txt` is loaded with `dconf load /`. The
|
||||
`systemd-user` tier gets `daemon-reload`, then **every `*.service` and `*.timer` whose start
|
||||
command resolves on this box** is enabled — a timer is the schedule that makes a oneshot service
|
||||
run at all, and it carries no `ExecStart` of its own, so the belonging test is applied to the
|
||||
service it activates. A unit is **started** (`--now`) only when systemd reports it as nothing-yet
|
||||
(`""`/`disabled`), and never when its binary is absent here, so a unit belonging to another box
|
||||
stays inert instead of crash-looping. Three outcomes are printed, and each says what actually
|
||||
happened: `ENABLE` only when `systemctl enable` returned success, `INERT` when it could not or did
|
||||
not, `SKIP` when the unit is foreign. Refs #44, #60.
|
||||
Starting something already installed is what makes `deploy.sh` unsafe to run beside a live
|
||||
session — see *Never start a unit a session may already be running*.
|
||||
|
||||
@@ -54,8 +59,17 @@ checkout, `git rm --cached` on the branch notwithstanding.
|
||||
|
||||
Restore on a fresh box: the library lives in `~/.local/share/noctalia/wallpapers/`, a path
|
||||
`deploy.sh` does not manage. Every filename is `wallhaven_<id>.<ext>` and the ID is the
|
||||
whole address — `curl -L -o <name> "https://wallhaven.cc/w/<id>"` into that directory puts
|
||||
the set back. Losing it is an acceptable outcome; 80 MB per clone forever is not.
|
||||
whole address — but `https://wallhaven.cc/w/<id>` answers `text/html`, the web page, so
|
||||
resolve the direct CDN URL through the API and download that:
|
||||
|
||||
```bash
|
||||
id=73g8xo # the wallhaven_<id> stem of any file in the set
|
||||
url=$(curl -s "https://wallhaven.cc/api/v1/w/$id" | grep -o '"path":"[^"]*' | cut -d'"' -f4)
|
||||
curl -L -o ~/.local/share/noctalia/wallpapers/"${url##*/}" "$url"
|
||||
```
|
||||
|
||||
`data.path` already ends in `wallhaven_<id>.<ext>`, so the filename keeps itself. Losing it
|
||||
is an acceptable outcome; 80 MB per clone forever is not.
|
||||
|
||||
## Law: never `systemctl --user enable` a unit from a worktree
|
||||
|
||||
@@ -158,6 +172,15 @@ fresh box — where everything reports `disabled` — still gets its units up. B
|
||||
are pinned in `tests/deploy.bats` over a stub `systemctl`, because the real one on this box
|
||||
is exactly what must not be invoked from a test. Refs #44.
|
||||
|
||||
**A third state exists, and it is not an error.** A unit file with no `[Install]` section cannot be
|
||||
enabled at all — `systemctl --user enable` answers with a non-zero exit and a paragraph about
|
||||
missing `WantedBy`. `live-sync.service` is one on purpose: `live-sync.timer` pulls it in, which is
|
||||
also why systemd reports it as `linked`. Until 2026-10-21 the deploy swallowed that failure and
|
||||
printed `ENABLE` anyway, so the line an operator reads to learn what a rebuild armed was false on
|
||||
every box, on the repo's own heartbeat. The section is readable from the file being deployed, so
|
||||
the tool checks it and prints `INERT …(no [Install] section — another unit arms it, enable cannot)`
|
||||
without making the call. Refs #60.
|
||||
|
||||
## Law: expand systemd specifiers before calling a unit foreign
|
||||
|
||||
`ExecStart=%h/.omp/scripts/live-sync.sh` is valid systemd and meaningless to a shell. The
|
||||
|
||||
@@ -9,7 +9,7 @@ Configuration guides and reference material for installed software. Each tool ha
|
||||
* [applications](applications.md) — Default userspace applications on CachyOS with Niri — browsers, editor, terminal, file manager, audio, screenshots, gaming.
|
||||
* [cachyos](cachyos.md) — Arch-based rolling release with performance-optimized kernels, repositories, and gaming stack. Running on the workstation.
|
||||
* [comfyui](comfyui/index.md) — Node-based AI image generation engine on dankclaw. FLUX.2 klein 4B, CPU backend, systemd managed.
|
||||
* [dotfiles-deploy](dotfiles-deploy.md) — How `dotfiles/` packages become live config: `deploy.sh` modes, host override packages, and why theme media and worktree-enabled units are traps.
|
||||
* [dotfiles-deploy](dotfiles-deploy.md) — How `dotfiles/` packages become live config: `deploy.sh` modes, host override packages, and the tier's hard-won laws, from media and generated theme output to systemd enable/start semantics, dconf dumps, and idle-lock watcher triage.
|
||||
* [ghostty](ghostty/index.md) — GPU-accelerated terminal emulator with extensive configuration, keybindings, and shaders.
|
||||
* [gitea](gitea/index.md) — Private Git hosting on dankclaw. Quadlet-managed containers: Gitea, PostgreSQL, Caddy.
|
||||
* [handy](handy.md) — Offline speech-to-text transcription with WebKitGTK backend and Tauri v2 UI.
|
||||
|
||||
@@ -41,17 +41,19 @@ list it as an installed tool are stale.
|
||||
│ 1200x1920 │ landscape, scale 1 │ scale 1 │
|
||||
│ rot 90 │ position 1581,-600 │ 5021,-240│
|
||||
└──────────────┴──────────────────────────────┴──────────┘
|
||||
HDMI-A-1 DP-2 (focus-at-startup) eDP-1 (fallback focus)
|
||||
HDMI-A-1 DP-1 (focus-at-startup) eDP-1 (fallback focus)
|
||||
Connector names are today's (`niri msg outputs`, 2026-10-21). They reshuffle with
|
||||
the dock and the cable, which is why the config keys on the EDID long name instead.
|
||||
```
|
||||
|
||||
| Panel | EDID long name | Keyed by | Notes |
|
||||
|-------|----------------|----------|-------|
|
||||
| Sceptre M34 ultrawide | `Sceptre Tech Inc Sceptre M34 Unknown` | identity | `mode "3440x1440@180.000"` — the EDID maximum; no 240 Hz mode exists — `focus-at-startup` |
|
||||
| Sceptre M34 ultrawide | `Sceptre Tech Inc Sceptre M34 Unknown` | identity | `mode "3440x1440@180.000"`, **deliberate** — the panel also offers `3440x1440@239.998`, `144.001` and `120.000`; 180 is driven while the link is degraded (see the law below) — `focus-at-startup` |
|
||||
| PM1601 portrait | *not captured* | connector `HDMI-A-1` | dock-dependent; run `desk.sh capture` when plugged in, then re-key |
|
||||
| eDP-1 laptop panel | `Lenovo Group Limited 0x40A9 Unknown` | identity | scale **1.0** (deliberate; Ghostty grid at 1.25 rejected 2026-10-09), second `focus-at-startup` |
|
||||
|
||||
VRR: DP-2 supports it; **left disabled by decision** (2026-10-09). Add
|
||||
`variable-refresh-rate` per output if that ever changes.
|
||||
VRR: the M34 supports it (its connector today is `DP-1`); **left disabled by
|
||||
decision** (2026-10-09). Add `variable-refresh-rate` per output if that ever changes.
|
||||
|
||||
`focus-at-startup` on both M34 and eDP-1: niri focuses in config order, so
|
||||
the ultrawide wins when present and the laptop panel is the fallback.
|
||||
@@ -113,12 +115,22 @@ Four rules learned the hard way:
|
||||
3. **Positions are absolute, not relational.** A disconnected panel leaves
|
||||
a hole; that is exactly why positions belong to the *desk profile*, not a
|
||||
global file.
|
||||
4. **Ask only for modes the panel reports** (`niri msg outputs` → `Mode:` list).
|
||||
A mode the panel never offered does **not** leave the output dark: niri logs
|
||||
`configured mode … could not be found, falling back to preferred` and quietly
|
||||
drives the panel at its EDID *preferred* mode — **60 Hz** on the M34. Nothing
|
||||
looks broken except the refresh rate, and every reconnect repeats the
|
||||
fallback. Check Hz, not just resolution, after any "monitor is acting up".
|
||||
4. **Ask only for modes the panel reports, and copy the string exactly.** The M34's
|
||||
`Available modes` (`niri msg outputs`, 2026-10-21) are `3440x1440@239.998`,
|
||||
`3440x1440@180.000`, `144.001`, `120.000`, … with `59.998` marked preferred. niri
|
||||
matches mode strings **verbatim** and does not round, so `239.999` — one digit from
|
||||
the real mode, which is what this file and the config both carried for weeks — is not
|
||||
`239.998`. A mode it cannot find does **not** leave the output dark: niri logs
|
||||
`configured mode … could not be found, falling back to preferred` and quietly drives
|
||||
the panel at its EDID *preferred* mode — **60 Hz**. Nothing looks broken except the
|
||||
refresh rate, and every reconnect repeats the fallback. Check Hz, not just
|
||||
resolution, after any "monitor is acting up".
|
||||
5. **180 Hz here is a decision, not a ceiling.** The panel offers 239.998 and is driven
|
||||
at 180.000 deliberately: the link trained at `x8 32.0 GT/s` against a max of `x16`,
|
||||
and the display engine is throwing `DMCUB` errors with `EINVAL` page flips after
|
||||
s2idle, so the 240-class mode is not driven until a reboot clears that state. Before
|
||||
changing the mode, re-read `niri msg outputs` **and** the link sysfs; treat "restore
|
||||
240" as a change to verify on a healthy link, never as the obvious fix. Refs #60.
|
||||
|
||||
## "The monitors are broken" — three layers, three signatures
|
||||
|
||||
|
||||
@@ -413,17 +413,21 @@ noctalia msg templates-apply
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Package | Purpose |
|
||||
|---------|---------|
|
||||
| `noctalia` (Qt) | Native desktop environment |
|
||||
| `qt6-base` | Qt6 runtime |
|
||||
| `qt6-multimedia` | Multimedia support |
|
||||
| `niri` | Wayland compositor |
|
||||
| `wlr-randr` | Display information |
|
||||
| `brightnessctl` | Brightness control |
|
||||
| `imagemagick` | Image processing |
|
||||
| `ffmpeg` | Media processing |
|
||||
| `polkit` | PolicyKit integration |
|
||||
| Package | Purpose | Version on blankpad (2026-10-21) |
|
||||
|---------|---------|----------------------------------|
|
||||
| `noctalia` (Qt) | the shell: bar, widgets, `noctalia msg` | 5.3.0-1.1 |
|
||||
| `qt6-base` | Qt6 runtime | 6.12.0-3.1 |
|
||||
| `niri` | Wayland compositor | 26.04-1.1 |
|
||||
| `polkit` | PolicyKit integration | 128-1.1 |
|
||||
| `imagemagick` | image processing for palette wallpapers | 7.1.2.34-2.1 |
|
||||
| `ffmpeg` | media processing | 2:9.0.2-2.1 |
|
||||
|
||||
**Three rows this page used to claim are not installed**, verified with `pacman -Q` and
|
||||
`command -v`: `wlr-randr` (display information is `niri msg outputs` — no package needed, and
|
||||
[displays](niri/displays.md) owns that procedure), `qt6-multimedia` (audio reaches PipeWire
|
||||
through noctalia's own dependency), and `brightnessctl` (`/sys/class/backlight/amdgpu_bl1`
|
||||
exists and nothing on this box drives it — laptop brightness is a gap to close, not a feature
|
||||
to document). A dependency table is a claim about the machine: check it before writing it.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+24
-11
@@ -8,7 +8,7 @@ tags:
|
||||
- provisioning
|
||||
- fleet
|
||||
- blankpad
|
||||
verified: 2026-10-20
|
||||
verified: 2026-10-21
|
||||
timestamp: 2026-10-20
|
||||
---
|
||||
|
||||
@@ -25,9 +25,13 @@ if both directions are checked.
|
||||
## Manifests
|
||||
|
||||
`packages/common.pkgs` (every host) + `packages/<host>.pkgs` (this host) + `packages/aur.pkgs`
|
||||
(AUR-only, installed through `yay`). The parser is `sed 's/#.*//'` then the first field, so `#`
|
||||
comments trail freely and one token per line is the whole grammar. An unknown `--host` gets the
|
||||
common manifest only: a wrong-host run is a no-op, not a hazard.
|
||||
(AUR-only, installed through `yay`). **One parser reads them for both tools**:
|
||||
`scripts/lib/manifest.sh` — `sed 's/#.*//'` then the first field, so `#` comments trail freely and
|
||||
one token per line is the whole grammar; an unreadable manifest is a loud failure rather than an
|
||||
empty desired list. `tests/manifest-parser.bats` owns that contract. The two scripts carried
|
||||
verbatim copies of the body until 2026-10-21, which is exactly how a manifest line can come to mean
|
||||
two things to the two halves of the same check. An unknown `--host` gets the common manifest only:
|
||||
a wrong-host run is a no-op, not a hazard.
|
||||
|
||||
## Rebuild a workstation
|
||||
|
||||
@@ -48,7 +52,7 @@ undeclared additions (declare these): N ← exit 1 when N > 0
|
||||
aur (needs a helper): <names> → belongs in packages/aur.pkgs
|
||||
installer-provided, undeclared: N + names → a reinstall supplies these; do not declare
|
||||
declared, not installed: <names> → install.sh installs these
|
||||
orphans ignored: <names> → pacman -Qtdq
|
||||
orphans ignored: <names> → pacman -Qtdq, never drift (see the law below)
|
||||
flatpak: <app ids> → reported only, install.sh installs no flatpaks
|
||||
```
|
||||
|
||||
@@ -78,15 +82,23 @@ how a manifest becomes a pile of source builds.
|
||||
Classify with `pacman -Sl` (world-readable). **`pacman -Sqq` requires root** — a tool that only
|
||||
reads the machine must not need sudo to answer a question about it.
|
||||
|
||||
**Known gap:** `aur.pkgs` is read for *every* host, so a desktop-only AUR package (a niri module, a
|
||||
GUI client) cannot be declared there without asking the headless servers to build it. Such a name is
|
||||
still reported as an AUR addition; leave it undeclared until `install.sh` grows a per-host AUR
|
||||
**Known gap:** `aur.pkgs` is read for *every* host, so a desktop-only AUR package — the niri
|
||||
module `nirimod-git`, say, or a GUI client — cannot be declared there without asking the
|
||||
headless servers to build it. Such a name is still reported as an AUR addition and stays
|
||||
undeclared (see the note in `packages/blankpad.pkgs`) until `install.sh` grows a per-host AUR
|
||||
manifest.
|
||||
|
||||
## Law: orphans never reach a manifest
|
||||
|
||||
`pacman -Qtdq` names are packages nothing depends on; emitting them would teach a fresh machine to
|
||||
install the same dead weight. They are printed as `orphans ignored` and excluded from `--emit`.
|
||||
Drift is `pacman -Qqe` — *explicitly* installed, therefore yours. `pacman -Qtdq` names packages
|
||||
installed **as a dependency** that nothing requires any more, and a package carries exactly one
|
||||
install reason, so the two sets cannot overlap (blankpad 2026-10-21: 206 explicit, 3 orphans, none
|
||||
in common). That is why nothing in `pkg-inventory.sh` subtracts the orphan list from the additions:
|
||||
the query is already the guard. Do not add such a subtraction — it cannot fire, and it would bury
|
||||
the invariant that matters, *an implicit install is never drift no matter when it arrived*, which
|
||||
`tests/pkg-inventory.bats` pins with a dependency installed after the baseline. What the
|
||||
`orphans ignored` line is for is the other half: dead weight on the machine that a manifest must
|
||||
never teach a fresh box to install.
|
||||
|
||||
## Two traps that produced this tool
|
||||
|
||||
@@ -102,5 +114,6 @@ install the same dead weight. They are printed as `orphans ignored` and excluded
|
||||
|
||||
- [tools/index](index.md) · [dotfiles-deploy](dotfiles-deploy.md) (the config tier that follows) ·
|
||||
[live-sync](live-sync.md) (the heartbeat that commits machine state)
|
||||
- Tests: `tests/pkg-inventory.bats` (fixtures + stubbed `pacman`, no real package database touched).
|
||||
- Tests: `tests/pkg-inventory.bats` (fixtures + stubbed `pacman`, no real package database touched)
|
||||
and `tests/manifest-parser.bats` (the grammar above, observed as names in / names out).
|
||||
- Refs #51.
|
||||
|
||||
@@ -8,7 +8,7 @@ tags:
|
||||
- rdna4
|
||||
- benchmarking
|
||||
- dankserver
|
||||
verified: 2026-10-20
|
||||
verified: 2026-10-21
|
||||
timestamp: 2026-10-20
|
||||
---
|
||||
|
||||
@@ -40,7 +40,10 @@ scripts/pcie-probe.sh --build-only # compile, never touch the cards
|
||||
|
||||
The guard runs **before anything is built or run** and refuses when the GPU is
|
||||
occupied — `gpu_busy_percent` above `PCIE_PROBE_MAX_BUSY` (default 5 %) or any
|
||||
process holding `/dev/kfd`. Refusal is exit `3`, and it stops nothing.
|
||||
process holding `/dev/kfd`. Refusal is exit `3`, and it stops nothing. If the holder
|
||||
probe is missing the guard refuses too rather than assuming idle — see
|
||||
[seams](#exit-status-and-seams); `gpu_busy_percent` reads `0` for a service that is
|
||||
resident and idle, so a silent fallback is exactly the benchmark this law forbids.
|
||||
|
||||
Auto-stopping a production endpoint is not this tool's decision to make, and a
|
||||
measurement taken as noise beside serving traffic is worthless anyway: it is
|
||||
@@ -65,8 +68,11 @@ throughput at *that* size is what matters, not the big-transfer figure:
|
||||
- Pinned versus pageable columns apart means pinning is load-bearing; together
|
||||
means the copies are already pinned by someone else.
|
||||
|
||||
Measure one device at a time (`--device`). A run that lets the runtime pick
|
||||
several devices cannot attribute bandwidth to a card at all.
|
||||
Measure one device at a time (`--device`, a HIP ordinal). A run that lets the runtime
|
||||
pick several devices cannot attribute bandwidth to a card at all. The ordinal goes to
|
||||
the probe as an argument and **the driver never sets `HIP_VISIBLE_DEVICES`**: masking
|
||||
the device *and* passing its real ordinal asks `hipSetDevice` for a card the runtime
|
||||
has hidden, so every card but ordinal 0 fails to measure at all. One selector only.
|
||||
|
||||
## The trap carried in the sources
|
||||
|
||||
@@ -79,14 +85,18 @@ when touching these files.
|
||||
|
||||
## Exit status and seams
|
||||
|
||||
`0` measured · `2` usage or no sources · `3` refused, GPU busy · `4` no HIP
|
||||
toolchain or build failed. A client box without the SDK exits `4` loudly rather
|
||||
than reporting an empty success; the probes belong on the box with the cards.
|
||||
`0` measured · `2` usage or no sources · `3` refused, GPU busy or holders unknowable ·
|
||||
`4` no HIP toolchain or build failed · `5` a probe ran and failed. A client box without
|
||||
the SDK exits `4` loudly rather than reporting an empty success; the probes belong on
|
||||
the box with the cards. A probe that dies (the segfault above, an unfundable
|
||||
allocation) exits `5` and names which probe on which device — a sweep that quietly
|
||||
swallowed the failure would report success with half its rows missing.
|
||||
|
||||
Test seams (all documented in the script header): `PCIE_PROBE_SRC`,
|
||||
`PCIE_PROBE_BUILD`, `PCIE_PROBE_SYSFS`, `PCIE_PROBE_KFD`, `PCIE_PROBE_HIPCC`,
|
||||
`PCIE_PROBE_MAX_BUSY` — the sysfs/kfd paths are what make a refusal testable on
|
||||
a machine with no cards, which is the only honest way to test a refusal.
|
||||
`PCIE_PROBE_FUSER`, `PCIE_PROBE_MAX_BUSY` — the sysfs/kfd paths are what make a
|
||||
refusal testable on a machine with no cards, which is the only honest way to test a
|
||||
refusal.
|
||||
|
||||
Measured campaign numbers are not kept here; they belong with the endpoint they
|
||||
were taken on, in the serverAppConfigs LLM runbook vault.
|
||||
|
||||
@@ -38,10 +38,10 @@ read afterwards by a short-lived connection. That is what this is.
|
||||
|
||||
```bash
|
||||
remote-job.sh start NAME [--] CMD... # launch detached, return at once
|
||||
remote-job.sh status [NAME] # one line per job; exit 0 only if all DONE
|
||||
remote-job.sh status [NAME] # one line per job; names literal, never globs
|
||||
remote-job.sh wait NAME [SEC] [N] # block until DONE or timeout, then tail N lines
|
||||
remote-job.sh out NAME [N] # tail a job's output
|
||||
remote-job.sh tail [NAME] # follow one job, or every job at once
|
||||
remote-job.sh tail [NAME] # follow one job, or all; exit 1 if nothing to follow
|
||||
remote-job.sh watch # refreshing status, for a spare pane
|
||||
```
|
||||
|
||||
@@ -77,6 +77,7 @@ can be made from a fresh session on a machine that rebooted underneath it.
|
||||
| Command name is wrong | `PENDING (no output yet)`, exit 1 — never a false DONE |
|
||||
| Job exits non-zero | `DONE rc=N`; the code is data, not the report's status |
|
||||
| Two jobs, same NAME | the second replaces the first's state files |
|
||||
| `tail` with no job, or a name that is a glob | exit 1 saying there is nothing to follow; names are matched literally, never expanded |
|
||||
| Job dir missing | created on first use |
|
||||
|
||||
Pinned by `tests/remote-job.bats` (black-box: output, exit codes, sentinel
|
||||
|
||||
@@ -18,8 +18,8 @@ btop
|
||||
|
||||
# system / fleet tooling
|
||||
yay # AUR helper bootstrap: ships in the cachyos repo; aur.pkgs installs need it
|
||||
git-lfs # LFS smudge/clean for tracked wallpapers (see .gitattributes)
|
||||
swayidle # idle lock + lid policy via swayidle-lock.service unit
|
||||
git-lfs # cloning LFS-backed upstreams (Hugging Face model/dataset repos); this repo tracks zero LFS files — .gitattributes is idle config
|
||||
swayidle # idle lock watcher via swayidle-lock.service; suspend locking is noctalia's sleep inhibitor
|
||||
|
||||
# desktop apps
|
||||
flameshot # screenshot tool; bound to CTRL+Shift+1/2/3 in niri keybinds
|
||||
|
||||
+32
-12
@@ -120,9 +120,20 @@ done
|
||||
|
||||
if [ "$MODE" != "check" ] && [ -d "$PKGS_DIR/systemd-user/.config/systemd/user" ]; then
|
||||
systemctl --user daemon-reload || true
|
||||
for unit in "$PKGS_DIR"/systemd-user/.config/systemd/user/*.service; do
|
||||
UNITS_DIR="$PKGS_DIR/systemd-user/.config/systemd/user"
|
||||
# Services AND timers. A timer is the schedule that makes a oneshot service run at all, and it
|
||||
# carries no ExecStart of its own — the binary that decides whether the unit belongs on this
|
||||
# box lives in the service it activates. Walking *.service only deployed live-sync.service on
|
||||
# every rebuild and never armed live-sync.timer: the heartbeat was present and never ticked.
|
||||
for unit in "$UNITS_DIR"/*.service "$UNITS_DIR"/*.timer; do
|
||||
[ -e "$unit" ] || continue
|
||||
bin="$(sed -n 's/^ExecStart=\([^ ]*\).*/\1/p' "$unit" | head -1)"
|
||||
name="$(basename "$unit")"
|
||||
probe="$unit"
|
||||
if [ "${name##*.}" = timer ]; then
|
||||
act="$(sed -n 's/^Unit=\(.*\)$/\1/p' "$unit" | head -1)"
|
||||
probe="$UNITS_DIR/${act:-"${name%.timer}.service"}"
|
||||
fi
|
||||
bin="$(sed -n 's/^ExecStart=\([^ ]*\).*/\1/p' "$probe" 2>/dev/null | head -1)"
|
||||
# systemd expands specifiers inside ExecStart; this shell does not. Matching the literal
|
||||
# `%h/…` makes a unit of THIS box look foreign and skips it in silence — which is exactly
|
||||
# how live-sync.service, the repo's own heartbeat, has been reported "absent on this
|
||||
@@ -132,22 +143,31 @@ if [ "$MODE" != "check" ] && [ -d "$PKGS_DIR/systemd-user/.config/systemd/user"
|
||||
bin="${bin//%u/$(id -un)}"
|
||||
bin="${bin//%U/$(id -u)}"
|
||||
if [ -n "$bin" ] && command -v "$bin" >/dev/null 2>&1; then
|
||||
name="$(basename "$unit")"
|
||||
# Read the state, not the exit status: `is-enabled` exits 1 for a `linked` unit exactly
|
||||
# as for a `disabled` one. Starting something already installed is how a deploy locks a
|
||||
# live session — swayidle-lock sits enabled/inactive on blankpad and `--now` arms it
|
||||
# mid-conversation. Only a unit nothing has installed yet gets started. Refs #44.
|
||||
state="$(systemctl --user is-enabled "$name" 2>/dev/null || true)"
|
||||
case "$state" in
|
||||
"" | disabled)
|
||||
systemctl --user enable --now "$name" || true
|
||||
echo "ENABLE $name (ExecStart resolves here; nothing running it yet)" ;;
|
||||
*)
|
||||
systemctl --user enable "$name" || true
|
||||
echo "ENABLE $name (already $state; not started — a live session decides what runs)" ;;
|
||||
esac
|
||||
if ! grep -q '^\[Install\]' "$unit"; then
|
||||
# systemd refuses to enable these: no [Install] section means nothing can want the unit
|
||||
# at login, and live-sync.service has none on purpose — its timer pulls it in. The old
|
||||
# branch swallowed that failure and printed ENABLE anyway, so the one line a rebuild is
|
||||
# checked by was false on every box. The section is readable right here, so do not make
|
||||
# the call at all. Refs #60.
|
||||
echo "INERT $name (state=${state:-none}; no [Install] section — another unit arms it, enable cannot)"
|
||||
elif [ -z "$state" ] || [ "$state" = disabled ]; then
|
||||
if systemctl --user enable --now "$name"; then
|
||||
echo "ENABLE $name (ExecStart resolves here; nothing running it yet)"
|
||||
else
|
||||
echo "INERT $name (enable --now failed — systemd's message above says why)"
|
||||
fi
|
||||
elif systemctl --user enable "$name"; then
|
||||
echo "ENABLE $name (already $state; not started — a live session decides what runs)"
|
||||
else
|
||||
echo "INERT $name (already $state; enable failed — systemd's message above says why)"
|
||||
fi
|
||||
else
|
||||
echo "SKIP $(basename "$unit") (ExecStart '$bin' absent on this host)"
|
||||
echo "SKIP $name (ExecStart '$bin' absent on this host)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
+3
-4
@@ -17,7 +17,8 @@
|
||||
# otherwise sudo prompts normally. The value is never stored in this repo.
|
||||
set -euo pipefail
|
||||
|
||||
REPO="$(git -C "$(dirname "$(realpath "$0")")" rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
HERE="$(dirname "$(realpath "$0")")"
|
||||
REPO="$(git -C "$HERE" rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
REPO="${INSTALL_REPO:-$REPO}"
|
||||
[ -n "$REPO" ] && [ -d "$REPO/packages" ] || { echo "install.sh: no packages/ under ${REPO:-<repo>}" >&2; exit 1; }
|
||||
|
||||
@@ -36,9 +37,7 @@ SUDO() {
|
||||
else sudo "$@"; fi
|
||||
}
|
||||
|
||||
read_manifest() { # $1 = manifest path; emits package names, strips comments/blanks
|
||||
sed 's/#.*//' "$1" | tr -d '\r' | awk '{if ($1) print $1}'
|
||||
}
|
||||
source "$HERE/lib/manifest.sh" # the same parser pkg-inventory.sh reads with
|
||||
# ---- desired-minus-installed delta ------------------------------------------
|
||||
PKGS=()
|
||||
for m in common.pkgs "$HOST.pkgs"; do
|
||||
|
||||
+9
-2
@@ -20,6 +20,10 @@ def pages():
|
||||
if ".AGENTTRASH" not in p.parts:
|
||||
yield p
|
||||
|
||||
# Names that a wikilink may point at: exactly the pages pages() reports. Deriving this
|
||||
# once keeps linting and link resolution from disagreeing about what exists.
|
||||
live = {p.stem for p in pages()}
|
||||
|
||||
for p in pages():
|
||||
rel = p.relative_to(kb)
|
||||
text = p.read_text(errors="replace")
|
||||
@@ -70,9 +74,12 @@ for p in pages():
|
||||
if not Path(target).is_file():
|
||||
violations.append(f"{rel}: broken relative link -> {m.group(1)}")
|
||||
for m in re.finditer(r"\[\[([^\]|#]+)", text):
|
||||
# wikilinks resolve by filename anywhere in the KB (Obsidian semantics)
|
||||
# wikilinks resolve by filename anywhere in the KB (Obsidian semantics) — but only
|
||||
# among pages that BELONG to the bundle. A raw rglob also walked .AGENTTRASH/, so a
|
||||
# link to a quarantined page stayed "valid" here while pointing at nothing on every
|
||||
# other machine (the trash is gitignored). Same exclusion as pages(), one source.
|
||||
name = m.group(1).strip().rsplit("/", 1)[-1]
|
||||
if not list(kb.rglob(name + ".md")):
|
||||
if name not in live:
|
||||
violations.append(f"{rel}: broken wikilink -> [[{m.group(1).strip()}]]")
|
||||
|
||||
for v in violations:
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
# scripts/lib/manifest.sh — the one parser for packages/*.pkgs
|
||||
#
|
||||
# install.sh walks desired → installed; pkg-inventory.sh walks installed → declared. Both read the
|
||||
# same hand-written manifests, so both must agree on what a line MEANS: if one forgives an inline
|
||||
# comment and the other does not, the same name is simultaneously "desired" and "drift", and the
|
||||
# pair stops being a check on the machine. Source it — do not paste the body into a caller again
|
||||
# (Refs #60: it was pasted twice, in install.sh and in pkg-inventory.sh).
|
||||
#
|
||||
# Contract, and tests/manifest-parser.bats is that contract:
|
||||
# name first field of a line, emitted as one bare token — callers hand it to pacman
|
||||
# comment `#` and everything after it is dropped, whole-line or trailing
|
||||
# CRLF stripped (the manifests are edited on more than one box)
|
||||
# blank empty and whitespace-only rows vanish
|
||||
# unreadable non-zero exit, no names: a silent empty list reads as "nothing desired", and a
|
||||
# rebuild would then install nothing while reporting success
|
||||
#
|
||||
# Sourced, never executed; callers own `set -euo pipefail`.
|
||||
|
||||
read_manifest() { # $1 = manifest path; prints one package name per line
|
||||
[[ -r "$1" ]] || { printf 'read_manifest: cannot read %s\n' "$1" >&2; return 2; }
|
||||
sed 's/#.*//' "$1" | tr -d '\r' | awk '{if ($1) print $1}'
|
||||
}
|
||||
+37
-8
@@ -15,8 +15,12 @@
|
||||
#
|
||||
# usage: pcie-probe.sh [--device N]... [--build-only] [--force]
|
||||
#
|
||||
# --device N measure this device; repeat for each card. Without it the probes run once with the
|
||||
# environment's own device selection — one shared run cannot attribute bandwidth.
|
||||
# --device N measure HIP device ordinal N; repeat for each card. Without it the probes run
|
||||
# once with the environment's own device selection — one shared run cannot
|
||||
# attribute bandwidth. The ordinal is passed to the probes as argv, which is the
|
||||
# only selector that survives contact with HIP: HIP_VISIBLE_DEVICES=1 makes
|
||||
# ordinal 0 the sole visible card, so masking AND passing N asks the probe for a
|
||||
# device that no longer exists. The mask is deliberately not set here.
|
||||
# --build-only compile and stop; never touches the cards.
|
||||
# --force measure even though the guard says the GPU is busy.
|
||||
#
|
||||
@@ -27,10 +31,11 @@
|
||||
# PCIE_PROBE_SYSFS drm sysfs root (/sys/class/drm)
|
||||
# PCIE_PROBE_KFD KFD device node (/dev/kfd)
|
||||
# PCIE_PROBE_HIPCC compiler (hipcc)
|
||||
# PCIE_PROBE_FUSER KFD holder probe (fuser)
|
||||
# PCIE_PROBE_MAX_BUSY refusal threshold, percent (5)
|
||||
#
|
||||
# Exit status: 0 measured · 2 usage or missing sources · 3 REFUSED, GPU busy · 4 no toolchain or
|
||||
# build failed.
|
||||
# Exit status: 0 measured · 2 usage or missing sources · 3 REFUSED, GPU busy or unknowable ·
|
||||
# 4 no toolchain or build failed · 5 a probe ran and failed.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
@@ -39,6 +44,7 @@ BUILD="${PCIE_PROBE_BUILD:-$HOME/.cache/pcie-probe}"
|
||||
SYSFS="${PCIE_PROBE_SYSFS:-/sys/class/drm}"
|
||||
KFD="${PCIE_PROBE_KFD:-/dev/kfd}"
|
||||
HIPCC="${PCIE_PROBE_HIPCC:-hipcc}"
|
||||
FUSER="${PCIE_PROBE_FUSER:-fuser}"
|
||||
MAX_BUSY="${PCIE_PROBE_MAX_BUSY:-5}"
|
||||
|
||||
force=0
|
||||
@@ -63,9 +69,19 @@ fi
|
||||
# ---- the guard, before anything touches the card --------------------------------
|
||||
if (( ! force )); then
|
||||
busy=""
|
||||
if [[ -e "$KFD" ]] && command -v fuser > /dev/null 2>&1; then
|
||||
holders="$(fuser "$KFD" 2>/dev/null | tr -s '[:space:]' ' ')"
|
||||
[[ -n "${holders// /}" ]] && busy="KFD in use by:${holders}"
|
||||
if [[ -e "$KFD" ]]; then
|
||||
if command -v "$FUSER" > /dev/null 2>&1; then
|
||||
holders="$("$FUSER" "$KFD" 2>/dev/null | tr -s '[:space:]' ' ')"
|
||||
[[ -n "${holders// /}" ]] && busy="KFD in use by:${holders}"
|
||||
else
|
||||
# No fuser (psmisc) means the KFD holders are unknowable — and gpu_busy_percent reads 0
|
||||
# for a service that is resident but idle. Guessing "idle" here is how you end up
|
||||
# benchmarking beside a live endpoint, so the guard fails closed instead.
|
||||
printf 'pcie-probe: REFUSED — cannot see who holds %s: `%s` not found (psmisc)\n' "$KFD" "$FUSER" >&2
|
||||
printf ' install psmisc, or pass --force if you have decided the contention is\n' >&2
|
||||
printf ' acceptable and read gpu_busy_percent yourself first.\n' >&2
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
for card in "$SYSFS"/card*; do
|
||||
[[ -f "$card/device/gpu_busy_percent" ]] || continue
|
||||
@@ -111,7 +127,11 @@ for card in "$SYSFS"/card*; do
|
||||
done
|
||||
|
||||
# ---- measure, per device --------------------------------------------------------
|
||||
# One selector only (see --device above), and every probe's exit status is honoured: a probe
|
||||
# that dies must not let the sweep report success. Failures are collected so the remaining
|
||||
# cards are still measured — losing the second card's numbers is how a campaign lies.
|
||||
(( ${#devices[@]} > 0 )) || devices=(default)
|
||||
failures=()
|
||||
for d in "${devices[@]}"; do
|
||||
if [[ $d == default ]]; then
|
||||
printf '\n########## engine device selection ##########\n'
|
||||
@@ -121,6 +141,15 @@ for d in "${devices[@]}"; do
|
||||
for name in hip_smoke pcie_bw pcie_probe; do
|
||||
bin="$BUILD/$name"
|
||||
[[ -x "$bin" ]] || continue
|
||||
if [[ $d == default ]]; then "$bin"; else HIP_VISIBLE_DEVICES="$d" "$bin" "$d"; fi
|
||||
if [[ $d == default ]]; then
|
||||
"$bin" || failures+=("$name (engine selection)")
|
||||
else
|
||||
"$bin" "$d" || failures+=("$name (device $d)")
|
||||
fi
|
||||
done
|
||||
done
|
||||
if (( ${#failures[@]} > 0 )); then
|
||||
printf 'pcie-probe: %d probe(s) failed:\n' "${#failures[@]}" >&2
|
||||
printf ' %s\n' "${failures[@]}" >&2
|
||||
exit 5
|
||||
fi
|
||||
|
||||
@@ -19,11 +19,19 @@
|
||||
# aur (needs a helper): <names> → belongs in packages/aur.pkgs (yay)
|
||||
# installer-provided, undeclared: N a reinstall supplies these; do not declare
|
||||
# declared, not installed: N install.sh installs these
|
||||
# orphans ignored: <names> pacman -Qtdq — never emitted
|
||||
# orphans ignored: <names> pacman -Qtdq — never drift, never emitted
|
||||
# flatpak: <app ids> reported only, install.sh installs no flatpaks
|
||||
#
|
||||
# --emit prints the additions as manifest lines, ready to fold into packages/. Names are bare
|
||||
# tokens so install.sh's `sed 's/#.*//'` + first-field parser reads them unchanged.
|
||||
|
||||
# Drift is `pacman -Qqe`: explicitly installed, therefore something you asked for and a rebuild
|
||||
# must declare. `pacman -Qtdq` orphans are packages installed AS A DEPENDENCY and no longer
|
||||
# required, and a package carries exactly one install reason, so the two sets are disjoint by
|
||||
# construction (blankpad, 2026-10-21: 206 explicit, 3 orphans, none in common). Nothing here
|
||||
# subtracts the orphan list from the additions and nothing should — the query is the guard. Do not
|
||||
# add such a subtraction: it cannot fire, and it would hide the real invariant (implicit installs
|
||||
# are never drift, whenever they arrived) behind a line that looks like protection.
|
||||
#
|
||||
# Exit status: 0 clean · 1 drift (undeclared additions exist) · 2 usage · 3 cannot classify
|
||||
# (pacman log unreadable or sync databases unavailable — worse to guess than to say so, because
|
||||
@@ -42,23 +50,22 @@ MODE="report"
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--emit) MODE="emit"; shift ;;
|
||||
-h | --help) sed -n '2,40p' "$0"; exit 0 ;;
|
||||
-h | --help) awk '/^set /{exit} NR>1' "$0"; exit 0 ;;
|
||||
*) echo "usage: pkg-inventory.sh [--emit]" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
HERE="$(dirname "$(realpath "$0")")"
|
||||
REPO="${PKGINV_REPO:-}"
|
||||
if [[ -z "$REPO" ]]; then
|
||||
REPO="$(git -C "$(dirname "$(realpath "$0")")" rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
REPO="$(git -C "$HERE" rev-parse --show-toplevel 2>/dev/null || true)"
|
||||
fi
|
||||
[[ -n "$REPO" && -d "$REPO/packages" ]] || { echo "pkg-inventory: no packages/ under ${REPO:-<repo>}" >&2; exit 2; }
|
||||
HOST="${PKGINV_HOST:-$(hostname)}"
|
||||
|
||||
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
|
||||
|
||||
read_manifest() { # same parser install.sh uses, so a name means the same thing to both
|
||||
sed 's/#.*//' "$1" | tr -d '\r' | awk '{if ($1) print $1}'
|
||||
}
|
||||
source "$HERE/lib/manifest.sh" # the same parser install.sh installs with
|
||||
# ---- installed state ---------------------------------------------------------
|
||||
# Four queries are the tool's whole interface to pacman. -Sl is the unprivileged sync listing:
|
||||
# -Sqq asks for root, and a read of the machine must not need sudo. Field 2 of -Sl is the package
|
||||
|
||||
+20
-6
@@ -51,13 +51,21 @@ case "${1:-}" in
|
||||
;;
|
||||
|
||||
status)
|
||||
# Job names are data, never patterns. `for n in $names` word-split AND globbed: a job named
|
||||
# `x*` was expanded against the caller's working directory, so the report described unrelated
|
||||
# files there as jobs and never mentioned the one that ran. Splitting stays (several names in
|
||||
# one argument is the documented call shape); expansion does not.
|
||||
names=()
|
||||
if [[ -n "${2:-}" ]]; then
|
||||
names="$2"
|
||||
read -r -a names <<< "$2"
|
||||
else
|
||||
names=$(cd "$JOBS" && ls *.out 2>/dev/null | sed 's/\.out$//' | sort)
|
||||
while IFS= read -r -d '' f; do
|
||||
names+=("$(basename "$f" .out)")
|
||||
done < <(find "$JOBS" -maxdepth 1 -name '*.out' -print0 2>/dev/null | sort -z)
|
||||
fi
|
||||
if [[ ${#names[@]} -eq 0 ]]; then echo " (no jobs)"; exit 0; fi
|
||||
pending=0
|
||||
for n in $names; do
|
||||
for n in "${names[@]}"; do
|
||||
if [[ -f "$JOBS/$n.done" ]]; then
|
||||
printf ' %-20s DONE rc=%s %s lines\n' "$n" \
|
||||
"$(cat "$JOBS/$n.rc" 2>/dev/null)" "$(wc -l < "$JOBS/$n.out" 2>/dev/null | tr -d ' ')"
|
||||
@@ -69,7 +77,6 @@ case "${1:-}" in
|
||||
pending=1
|
||||
fi
|
||||
done
|
||||
if [[ -z "$names" ]]; then echo " (no jobs)"; exit 0; fi
|
||||
exit "$pending"
|
||||
;;
|
||||
|
||||
@@ -95,8 +102,15 @@ case "${1:-}" in
|
||||
|
||||
tail)
|
||||
# Multiplexed follow: no NAME means every job at once, so one pane shows the whole harness.
|
||||
if [[ -n "${2:-}" ]]; then exec tail -f "$JOBS/$2.out"; fi
|
||||
exec tail -f "$JOBS"/*.out
|
||||
# Both branches check for something to follow first: an unexpanded `*.out` handed to tail
|
||||
# reports a missing *file*, which reads as a broken tool instead of an empty harness.
|
||||
if [[ -n "${2:-}" ]]; then
|
||||
[[ -f "$JOBS/$2.out" ]] || { echo "remote-job: no job '$2' (no $JOBS/$2.out)" >&2; exit 1; }
|
||||
exec tail -f "$JOBS/$2.out"
|
||||
fi
|
||||
mapfile -d '' -t follow < <(find "$JOBS" -maxdepth 1 -name '*.out' -print0 2>/dev/null | sort -z)
|
||||
(( ${#follow[@]} )) || { echo "remote-job: no jobs in $JOBS yet" >&2; exit 1; }
|
||||
exec tail -f "${follow[@]}"
|
||||
;;
|
||||
|
||||
watch)
|
||||
|
||||
+59
-1
@@ -37,7 +37,22 @@ if [ "$2" = is-enabled ]; then
|
||||
printf '%s\n' "$state"
|
||||
case "$state" in disabled | "") exit 1 ;; *) exit 0 ;; esac
|
||||
fi
|
||||
exit 0
|
||||
if [ "$2" = enable ]; then
|
||||
# Modelled, not stubbed flat: systemd refuses to enable a unit whose file has no [Install]
|
||||
# section, and the deployed file is what it reads. A stub that always says yes hides the
|
||||
# one case this repo actually ships (live-sync.service is driven by its timer).
|
||||
target=""
|
||||
for a in "$@"; do
|
||||
case "$a" in
|
||||
*.service | *.timer | *.socket) target="$a" ;;
|
||||
esac
|
||||
done
|
||||
file="$HOME/.config/systemd/user/${target##*/}"
|
||||
if [ -e "$file" ] && ! grep -q '^\[Install\]' "$file"; then
|
||||
printf 'The unit files have no installation config (WantedBy=, RequiredBy=, Also= settings in the [Install] section). This means they are not meant to be enabled using this command.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
STUB
|
||||
chmod +x "$TEST_TMP/bin/systemctl"
|
||||
export PATH="$TEST_TMP/bin:$PATH"
|
||||
@@ -57,6 +72,21 @@ systemd_unit() {
|
||||
> "$REPO/dotfiles/systemd-user/.config/systemd/user/$1"
|
||||
}
|
||||
|
||||
# systemd_unit_no_install <name> <ExecStart> — a unit nothing can enable (live-sync.service:
|
||||
# it is pulled in by live-sync.timer, so it carries no [Install] section on purpose).
|
||||
systemd_unit_no_install() {
|
||||
mkdir -p "$REPO/dotfiles/systemd-user/.config/systemd/user"
|
||||
printf '[Unit]\nDescription=%s\n[Service]\nType=oneshot\nExecStart=%s\n' "$1" "$2" \
|
||||
> "$REPO/dotfiles/systemd-user/.config/systemd/user/$1"
|
||||
}
|
||||
|
||||
# systemd_timer <name> <service-name> — a schedule; it has no ExecStart of its own.
|
||||
systemd_timer() {
|
||||
mkdir -p "$REPO/dotfiles/systemd-user/.config/systemd/user"
|
||||
printf '[Unit]\nDescription=%s\n[Timer]\nOnCalendar=hourly\nPersistent=true\n[Install]\nWantedBy=timers.target\n' "$1" \
|
||||
> "$REPO/dotfiles/systemd-user/.config/systemd/user/$1"
|
||||
}
|
||||
|
||||
@test "generated theme output is never adopted into a symlink" {
|
||||
repo_file "ghostty/.config/ghostty/themes/noctalia" 'background = "#f43841"'
|
||||
live_file "$HOME/.config/ghostty/themes/noctalia" 'background = "#f43841"'
|
||||
@@ -152,3 +182,31 @@ systemd_unit() {
|
||||
grep -q 'systemctl --user enable heartbeat.service' "$STUB_LOG"
|
||||
! grep -q -- '--now' "$STUB_LOG"
|
||||
}
|
||||
|
||||
@test "a unit nothing can enable is reported as what it is, never as ENABLE" {
|
||||
# `systemctl --user enable live-sync.service` fails on every deploy: the file has no
|
||||
# [Install] section (its timer arms it), and systemd says so with a non-zero exit. The
|
||||
# deploy swallowed that and printed ENABLE, so the one line the operator reads to learn
|
||||
# what a rebuild armed was false on every box, on the repo's own heartbeat.
|
||||
systemd_unit_no_install heartbeat.service /bin/sh
|
||||
printf 'linked\n' > "$UNIT_STATE"
|
||||
run "$REPO/scripts/deploy.sh"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"heartbeat.service"*"[Install]"* ]]
|
||||
[[ "$output" != *"ENABLE heartbeat.service"* ]]
|
||||
# No futile call: the section is readable from the file being deployed.
|
||||
! grep -q 'systemctl --user enable heartbeat.service' "$STUB_LOG"
|
||||
}
|
||||
|
||||
@test "a timer in the tier is armed, because a service on its own has no schedule" {
|
||||
# The loop walked *.service only. On a rebuilt box live-sync.service is deployed and the
|
||||
# timer is never enabled, so the heartbeat that commits machine state simply does not run
|
||||
# — and the deploy report says nothing about it.
|
||||
systemd_unit heartbeat.service /bin/sh
|
||||
systemd_timer heartbeat.timer heartbeat.service
|
||||
printf 'disabled\n' > "$UNIT_STATE"
|
||||
run "$REPO/scripts/deploy.sh"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" != *"SKIP heartbeat.timer"* ]]
|
||||
grep -q 'systemctl --user enable --now heartbeat.timer' "$STUB_LOG"
|
||||
}
|
||||
|
||||
@@ -120,6 +120,28 @@ EOF
|
||||
[[ "$output" == *"0 violation(s)"* ]]
|
||||
}
|
||||
|
||||
@test "a wikilink must not resolve into the quarantine" {
|
||||
# Obsidian resolves [[name]] by filename anywhere under the vault, and the resolver copied
|
||||
# that with a raw rglob — so [[log]] stayed "valid" by landing on the copy waiting in
|
||||
# .AGENTTRASH/. That is #57's failure wearing a green check: the page is out of circulation
|
||||
# until a pruning session with Frank, and it is not even tracked, so on another machine the
|
||||
# link points at nothing while the gate here says it is fine.
|
||||
mkdir -p "$KB/.AGENTTRASH"
|
||||
printf 'no frontmatter, quarantined\n' > "$KB/.AGENTTRASH/log.md"
|
||||
printf -- '---\ntype: reference\n---\nSee [[log]] for the history.\n' > "$KB/good.md"
|
||||
run bash "$LINT" "$KB"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"broken wikilink -> [[log]]"* ]]
|
||||
}
|
||||
|
||||
@test "a wikilink to a live page still resolves, in any directory" {
|
||||
# The narrowing above must cost nothing: filename-anywhere semantics is the point.
|
||||
printf -- '---\ntype: reference\n---\nSee [[good]].\n' > "$KB/sub/other.md"
|
||||
run bash "$LINT" "$KB"
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"0 violation(s)"* ]]
|
||||
}
|
||||
|
||||
@test "the shipped knowledge base itself is conformant" {
|
||||
# The tests above exercise the linter against fixtures; nothing else in the
|
||||
# suite ever points it at the real bundle, so a page added without frontmatter
|
||||
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bats
|
||||
# tests/manifest-parser.bats — the one manifest parser, shared by install.sh and pkg-inventory.sh
|
||||
#
|
||||
# `packages/*.pkgs` is hand-written, so the parser's whole job is to forgive the ways a hand-edited
|
||||
# list is wrong. The same function decides what install.sh tries to install and what pkg-inventory
|
||||
# calls drift, so a manifest line must mean the same thing to both — which is why it lives in one
|
||||
# file instead of being pasted into each caller (Refs #60: it was pasted twice).
|
||||
#
|
||||
# Observed through the function's exit status and stdout only:
|
||||
# names first field of a line, one bare token; `#` and everything after it is a comment
|
||||
# CRLF tolerated (the manifests get edited on more than one box)
|
||||
# blank lines vanish
|
||||
# unreadable non-zero with no names — an empty desired list must never be a silent success,
|
||||
# because "nothing desired" is exactly what a rebuild would install
|
||||
|
||||
setup() {
|
||||
TEST_TMP="$(mktemp -d)"
|
||||
LIB="$BATS_TEST_DIRNAME/../scripts/lib/manifest.sh"
|
||||
}
|
||||
|
||||
teardown() { rm -rf "$TEST_TMP"; }
|
||||
|
||||
parse() { # $1 = manifest contents, verbatim
|
||||
printf '%s' "$1" > "$TEST_TMP/m.pkgs"
|
||||
run bash -c 'source "$1"; read_manifest "$2"' _ "$LIB" "$TEST_TMP/m.pkgs"
|
||||
}
|
||||
|
||||
@test "inline notes, full-line comments, blank rows and CRLF come out as bare names" {
|
||||
parse $'# desired state\r\nghostty # terminal\r\n\r\n \r\nzoxide\r\n'
|
||||
[ "$status" -eq 0 ]
|
||||
[ "$(printf '%s\n' "$output" | tr '\n' ' ')" = "ghostty zoxide " ]
|
||||
}
|
||||
|
||||
@test "only the first field of a line is a package" {
|
||||
# install.sh hands every emitted token straight to pacman, so a stray second token on a line
|
||||
# must never become an install target; whatever it is, the manifest did not declare it.
|
||||
parse $'neovim extra tokens here\n'
|
||||
[ "$output" = "neovim" ]
|
||||
}
|
||||
|
||||
@test "a comment that names a package is not a package" {
|
||||
parse $'# vlc\n#typora\nbun\n'
|
||||
[ "$output" = "bun" ]
|
||||
}
|
||||
|
||||
@test "an unreadable manifest fails loudly instead of yielding an empty desired list" {
|
||||
run --separate-stderr bash -c 'source "$1"; read_manifest "$2"' _ "$LIB" "$TEST_TMP/nope.pkgs"
|
||||
[ "$status" -ne 0 ]
|
||||
[ -z "$output" ]
|
||||
[[ "$stderr" == *nope.pkgs* ]]
|
||||
}
|
||||
+41
-2
@@ -6,7 +6,8 @@
|
||||
# built or run, and the tool stops nothing and starts nothing (auto-stopping a production service is
|
||||
# the thing it must not do unasked).
|
||||
#
|
||||
# Exit status: 0 measured · 2 usage · 3 refused (GPU busy) · 4 no toolchain / build failed.
|
||||
# Exit status: 0 measured · 2 usage · 3 refused (GPU busy) · 4 no toolchain / build failed ·
|
||||
# 5 a probe ran and failed.
|
||||
#
|
||||
# hipcc and the compiled probes are stubbed: this box has no HIP SDK (it is a client laptop) and a
|
||||
# real measurement belongs on a machine with the cards, beside no running model.
|
||||
@@ -46,7 +47,16 @@ while [ $# -gt 0 ]; do [ "$1" = "-o" ] && out="$2"; shift; done
|
||||
: > "$PCIE_PROBE_BUILD/.built"
|
||||
{ echo '#!/usr/bin/env bash'
|
||||
echo ': > "$PCIE_PROBE_BUILD/.ran"'
|
||||
echo 'echo "PROBE RAN dev=${HIP_VISIBLE_DEVICES:-none} bin=$(basename "$0")"'
|
||||
# HIP rule, modelled because it is the trap: with HIP_VISIBLE_DEVICES set, the ONLY
|
||||
# valid ordinal is 0. A driver that masks the device and still passes its real ordinal
|
||||
# asks for a card the runtime cannot see — which is why the stub used to report the
|
||||
# env var instead of the argument, and could not see the collision.
|
||||
echo 'dev="${1:-0}"'
|
||||
echo 'if [ -n "${HIP_VISIBLE_DEVICES:-}" ] && [ "$dev" != 0 ]; then'
|
||||
echo ' printf "setDevice(%s) failed\n" "$dev" >&2; exit 1'
|
||||
echo 'fi'
|
||||
echo '[ "${STUB_PROBE_FAILS:-0}" = 1 ] && { echo "stub probe died mid-measurement" >&2; exit 1; }'
|
||||
echo 'echo "PROBE RAN dev=$dev bin=$(basename "$0")"'
|
||||
echo 'echo " slab 2.344 MiB: 31.4 GB/s"'
|
||||
} > "$out"
|
||||
chmod +x "$out"
|
||||
@@ -131,6 +141,35 @@ teardown() { rm -rf "$TEST_TMP"; }
|
||||
[[ "$output" == *"dev=1"* ]]
|
||||
}
|
||||
|
||||
@test "a device ordinal the environment mask would hide is still measured" {
|
||||
# Reproduction, not preference: HIP_VISIBLE_DEVICES=1 leaves ordinal 0 as the only
|
||||
# selectable card, so masking AND passing the ordinal asks hipSetDevice(1) for a
|
||||
# device the runtime cannot see. Per-card bandwidth was unobtainable that way.
|
||||
run "$SCRIPT" --device 0 --device 1
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"PROBE RAN dev=1"* ]]
|
||||
[[ "$output" != *"setDevice"* ]]
|
||||
}
|
||||
|
||||
@test "a probe that dies is reported as a failed run, not a green one" {
|
||||
# The header promises "0 measured". A probe that segfaults (the documented hazard on
|
||||
# this ROCm build) must not let the sweep exit 0.
|
||||
run env STUB_PROBE_FAILS=1 "$SCRIPT"
|
||||
[ "$status" -eq 5 ]
|
||||
[[ "$output" == *"failed"* ]]
|
||||
[[ "$output" == *"hip_smoke"* ]]
|
||||
}
|
||||
|
||||
@test "the guard refuses when it cannot see who holds the KFD, instead of assuming idle" {
|
||||
# Without fuser the /dev/kfd half of the guard cannot run, and gpu_busy_percent reads
|
||||
# 0 for a service that is resident but idle — a silent fallback would benchmark beside
|
||||
# a live endpoint, which is the one thing this tool's LAW forbids.
|
||||
run env PCIE_PROBE_FUSER="$TEST_TMP/nope-fuser" "$SCRIPT"
|
||||
[ "$status" -eq 3 ]
|
||||
[[ "$output" == *"fuser"* ]]
|
||||
[ ! -e "$BUILD/.built" ]
|
||||
}
|
||||
|
||||
@test "no probe sources is a usage-level error, not an empty success" {
|
||||
rm -f "$SRC"/*.hip
|
||||
run "$SCRIPT"
|
||||
|
||||
@@ -39,15 +39,18 @@ EOF
|
||||
[2026-03-04T09:00:00-0800] [ALPM] installed bun (1.2-1)
|
||||
[2026-03-04T09:30:00-0800] [ALPM] upgraded filesystem (2026.01.01-1 -> 2026.03.01-1)
|
||||
[2026-03-05T09:00:00-0800] [ALPM] installed nirimod-git (r7-1)
|
||||
[2026-03-07T09:00:00-0800] [ALPM] installed libstray (1.0-1)
|
||||
[2026-03-06T09:00:00-0800] [ALPM] removed base (3-1)
|
||||
EOF
|
||||
|
||||
# --- installed state --------------------------------------------------------
|
||||
# ghostty/zoxide/neovim/typora declared. libfoo: explicit, arrived with the installer.
|
||||
# bun/nirimod-git: installed by hand afterwards, undeclared. python-hatchling: orphan.
|
||||
printf '%s\n' base bun filesystem ghostty libfoo neovim nirimod-git typora zoxide > "$TEST_TMP/local-all"
|
||||
# bun/nirimod-git: installed by hand afterwards, undeclared. python-hatchling and libstray:
|
||||
# installed as dependencies and now unrequired (pacman -Qtdq). libstray arrived AFTER the
|
||||
# baseline, so the date filter alone cannot keep it out of drift — only the explicit query can.
|
||||
printf '%s\n' base bun filesystem ghostty libfoo libstray neovim nirimod-git typora zoxide > "$TEST_TMP/local-all"
|
||||
printf '%s\n' bun ghostty libfoo neovim nirimod-git typora zoxide > "$TEST_TMP/local-explicit"
|
||||
printf '%s\n' python-hatchling > "$TEST_TMP/orphans"
|
||||
printf '%s\n' python-hatchling libstray > "$TEST_TMP/orphans"
|
||||
# nirimod-git is deliberately ABSENT here: no sync db can serve it, which is the fact that
|
||||
# routes it to aur.pkgs in the test below.
|
||||
printf '%s\n' bun ghostty neovim typora zoxide libfoo vlc python-hatchling base filesystem > "$TEST_TMP/sync-all"
|
||||
@@ -131,11 +134,34 @@ ADDITIONS() { section "^undeclared additions" "^installer-provided"; }
|
||||
[[ "$(section "^declared," "^orphans")" == *vlc* ]]
|
||||
}
|
||||
|
||||
@test "orphans are reported as ignored and never emitted" {
|
||||
@test "orphans are named on their own line, not mixed into the drift answer" {
|
||||
run "$SCRIPT"
|
||||
[[ "$output" == *orphans*python-hatchling* ]]
|
||||
[[ "$(section "^orphans" "^flatpak")" == *python-hatchling* ]]
|
||||
[[ "$(ADDITIONS)" != *python-hatchling* ]]
|
||||
}
|
||||
|
||||
@test "a dependency installed after the baseline is never drift, orphan or not" {
|
||||
# Drift means `pacman -Qqe` — you asked for this one. libstray arrived as a dependency
|
||||
# after install day and is now unrequired, so the baseline filter does not exclude it:
|
||||
# only the explicit query does. Swap -Qqe for -Qq and every dependency on the machine,
|
||||
# dead ones included, becomes a manifest line install.sh will reinstall on a rebuild.
|
||||
run "$SCRIPT"
|
||||
[[ "$(ADDITIONS)" != *libstray* ]]
|
||||
[[ "$output" == *"undeclared additions (declare these): 2"* ]]
|
||||
[[ "$(section "^orphans" "^flatpak")" == *libstray* ]]
|
||||
run "$SCRIPT" --emit
|
||||
[[ "$output" != *python-hatchling* ]]
|
||||
[[ "$output" != *libstray* ]]
|
||||
}
|
||||
|
||||
@test "--help prints the header and stops before the shell" {
|
||||
# The header IS the contract (report shape, exit status, seams). A hardcoded line range
|
||||
# that runs past the comment block hands the reader `set -euo pipefail` as if it were
|
||||
# documentation.
|
||||
run "$SCRIPT" --help
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" == *"Exit status"* ]]
|
||||
[[ "$output" != *"pipefail"* ]]
|
||||
[[ "$output" != *"LC_ALL"* ]]
|
||||
}
|
||||
|
||||
@test "--baseline moves packages installed on that date into the installer set" {
|
||||
|
||||
@@ -122,6 +122,31 @@ until_done() {
|
||||
[[ "$output" == *38* ]] # the tail, not the head
|
||||
}
|
||||
|
||||
@test "status treats a job name as a name, not a pattern" {
|
||||
"$SCRIPT" start 'x*' -- bash -c 'echo star'
|
||||
until_done 'x*'
|
||||
touch "$TEST_TMP/anchor"
|
||||
# The report is read from a directory full of unrelated files on purpose: a name that is
|
||||
# expanded as a glob resolves against the CALLER's cwd, so the tool would describe `anchor`
|
||||
# as a job and never mention the one that really ran.
|
||||
run bash -c 'cd "$1" && "$2" status "$3"' _ "$TEST_TMP" "$SCRIPT" 'x*'
|
||||
[[ "$output" == " x*"*"DONE rc=0"* ]]
|
||||
[[ "$output" != *anchor* ]]
|
||||
}
|
||||
|
||||
@test "tail on an empty harness and on a missing job both say so" {
|
||||
# An unexpanded `*.out` reaching tail is reported as a missing FILE, which reads as a broken
|
||||
# script; the honest answer is that there is nothing to follow yet.
|
||||
run timeout 5 "$SCRIPT" tail
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"no jobs"* ]]
|
||||
[[ "$output" != *".out"* ]]
|
||||
run timeout 5 "$SCRIPT" tail nosuchjob
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"nosuchjob"* ]]
|
||||
[[ "$output" != *"No such file"* ]]
|
||||
}
|
||||
|
||||
@test "an unknown verb is a usage error" {
|
||||
run "$SCRIPT" frobnicate
|
||||
[ "$status" -eq 2 ]
|
||||
|
||||
@@ -157,3 +157,48 @@ export API_KEY_FILE=/run/secrets/api_key'
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"tests/other.bats"* ]]
|
||||
}
|
||||
|
||||
@test "a credential under a path git quotes is still read" {
|
||||
# core.quotePath is on by default, so `git diff --name-only` prints "caf\303\251/…" for a
|
||||
# non-ASCII directory. Feed that quoted string back to `git diff -- <path>` and the diff is
|
||||
# empty — and a gate that skips an unreadable file reports success. This is the fail-closed
|
||||
# promise breaking on its own terms, not a cosmetic quoting complaint.
|
||||
try_commit "café/postgres.env" 'POSTGRES_PASSWORD=hunter2corp2026'
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"postgres.env"* ]]
|
||||
}
|
||||
|
||||
@test "a placeholder word elsewhere on the line does not exempt a live value" {
|
||||
# The rule is about the VALUE. `# example` in a comment says nothing about the token the
|
||||
# line assigns, but a whole-line placeholder filter lets the comment vouch for the secret.
|
||||
try_commit "app.env" 'API_TOKEN=hunter2corp2026 # example, placeholder, changeme'
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"app.env"* ]]
|
||||
}
|
||||
|
||||
@test "each hit is attributed to the file it came from" {
|
||||
# The report is how the next agent finds the leak. If one file's last hit line runs into the
|
||||
# next file's name, the audit starts at a clean file and the leaker keeps its name.
|
||||
mkdir -p "$REPO/a" "$REPO/b"
|
||||
printf 'A_PASSWORD=hunter2corp2026\n' > "$REPO/a/one.env"
|
||||
printf 'B_PASSWORD=hunter2corp2026\n' > "$REPO/b/two.env"
|
||||
run git -C "$REPO" add -A
|
||||
run git -C "$REPO" commit -q -m "two leakers"
|
||||
[ "$status" -ne 0 ]
|
||||
printf '%s\n' "$output" | grep -qx ' a/one.env'
|
||||
printf '%s\n' "$output" | grep -qx ' b/two.env'
|
||||
! printf '%s\n' "$output" | grep -qE 'hunter2corp2026 +b/two\.env'
|
||||
}
|
||||
|
||||
@test "a documented fake id in prose is committable; a live-shaped id in the same file is not" {
|
||||
# Measured on blankpad 2026-10-21: the live-sync heartbeat failed three ticks in a row
|
||||
# because a managed-skill page documents how to write a secret gate and quotes AWS's own
|
||||
# placeholder id. A gate that blocks the machine tier over an inert example gets worked
|
||||
# around, and a workaround is the hole. The exemption reads the matched token, so a real
|
||||
# id in the same file still blocks.
|
||||
try_commit "docs.md" 'Inert fixtures look like AKIAIOSFODNN7EXAMPLE or AKIA0000000000000EXAMPLE.'
|
||||
[ "$status" -eq 0 ]
|
||||
try_commit "docs.md" 'Inert fixture AKIAIOSFODNN7EXAMPLE and a live one AKIAQZR4W7DBPCFG7V3Q.'
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" == *"docs.md"* ]]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user