The one-work-log rule was written near-verbatim in four places — okf-spec,
okf-maintenance, log.md's preamble, and APPEND_SYSTEM.md. Four copies is four
authorities: each can be edited alone, and the next reader cannot tell which one the
linter implements. okf-spec.md owns it; maintenance now carries the action (collapse
a second log into the root, then re-run the linter) instead of re-arguing it; the log
page points at the rule rather than restating it. APPEND_SYSTEM keeps its imperative —
a system prompt cannot fetch a link at the moment of writing — but no longer carries
the duplicated reasoning.
`inventory/index.md` advertised "all userspace program configuration on the
workstation". The one page in that directory measures `~/.config` on blankpad: not
`~/.local/share`, not system package state, not the servers. A coverage claim that
names what was measured is the difference between a census and an alibi.
Refs #60
`deploy_file` excludes `*/Noctalia*` (noctalia's capital-N palette naming) but
`.gitignore` had only the lowercase classes, so a generated palette file could become
tracked-but-never-deployed — the fiction class #41 removed, reintroduced by the
ignore list falling out of step with the deployer. The patterns are one contract in
two files; both halves are now stated and the comment says so.
`natives/` (2.4 MB of tree-sitter grammars the harness unpacks into the repo root on
first use, measured on blankpad) sat unnamed in the root, one `git add -A` away from
being committed as configuration. It is runtime state by every clause of AGENTS.md's
doctrine, so it is ignored. The wallpaper re-fetch note pointed at
`https://wallhaven.cc/w/<id>`, which answers text/html — it now names the API
resolution the deploy page carries.
`git-lfs` was installed because `.gitattributes` routes image types through the LFS
filter — for wallpapers this week's diff untracked: `git lfs ls-files` is zero, so
those four rules match nothing. The manifest now states what the tool is actually for
(cloning LFS-backed upstreams such as Hugging Face model/dataset repos) and the file
records its own inert state. Deleting `.gitattributes` is a pruning call for a session
with Frank, not a cleanup an agent runs alone.
Verified with `git check-ignore -v` for both new rules.
Refs #60
`status` built its job list with `for n in $names`, which word-splits AND globs. The
second half is the bug: 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 — on a harness whose whole purpose is a report you can trust after
the session that started the job is gone. Splitting stays (several names in one
argument is the documented call shape); expansion goes.
`tail` was honest-by-accident in the wrong direction: with no jobs it handed a
literal `$JOBS/*.out` to tail, which reports a missing *file* — indistinguishable
from a broken tool — and a missing name produced tail's errno rather than "no job
called that". Both now say what is missing and exit 1.
Red first: the name test reads the report from a directory containing an unrelated
file, so the old glob is visible as a phantom job; both new tests fail against the
previous code and pass against this one.
Refs #60
Three rows claimed `wlr-randr`, `qt6-multimedia` and `brightnessctl` were
dependencies. `pacman -Q` on blankpad says none is installed, and the two features
they were cited for do not come from them at all: display information comes from
`niri msg outputs` (the niri page already said so), and brightness is driven through
the kernel — `/sys/class/backlight/amdgpu_bl1` exists with no helper installed — so
no package is doing that work.
A dependency table is a claim about a machine, and every row of it is now measured
against `pacman -Q` on blankpad (2026-10-21). Verified against the package database:
check `pacman -Q <name>` before adding a row.
Refs #60
The page asserted the Sceptre M34's EDID tops out at 180 Hz. Measured on blankpad
after the merge, `niri msg outputs` lists 3440x1440@239.998 — the claim was false
in main, and it was load-bearing: it is the reason nobody questioned the profile
while the panel ran at 60 Hz for weeks. The real defect was one character: the
profile asked for 239.999, the panel offers 239.998, and niri matches mode strings
verbatim, falling back to *preferred* without failing.
Corrected to the measured mode list, and the deployed 180 is now documented as a
deliberate choice (the link is trained at x8 32.0 GT/s against a max of x16 and the
display engine throws EINVAL), so the next reader neither "restores 240" blind nor
re-pins 180 as a hardware ceiling. The kdl profile comment carries the same facts at
the point of edit, because that is where the next change to this mode gets typed.
Refs #60
The resolver copied Obsidian's filename-anywhere semantics with a raw rglob, which
walked .AGENTTRASH/ as well. [[log]] therefore stayed "valid" by landing on the
quarantined core/log.md — a page that is out of circulation until a pruning session
and, being gitignored, does not exist on any other machine. The gate reported a
green bundle while pointing readers at a file git does not carry: exactly the #57
failure, hidden by the check that exists to catch it.
Resolution now uses the same filtered set linting uses (pages()), so the two can
never disagree about what exists. A companion test pins that filename-anywhere
resolution for live pages still works, so the narrowing costs nothing; mutating
the resolver back to rglob turns the new test red.
Refs #60
The live-sync heartbeat failed three hourly ticks (22:25, 23:27, 00:28) because a
managed skill that documents how to write a secret gate quotes the vendor's
all-zeros placeholder access-key id, and the shape rule fired on the
documentation. The machine tier is prose ABOUT credentials — the gate's own header
says a gate that fires there gets overridden until it means nothing — and a
heartbeat that cannot commit is a fleet that silently stops syncing config.
- shape hits are judged on the matched token, not the line: a documented fake is
inert, one live token sharing the file still blocks. The match is judged with
its following context, because a fixed quantifier truncates `EXAMPLE` to `EXA`
and the placeholder would vanish from what is examined.
- assignment values keep the stricter rule from the previous commit: a comment on
the same line cannot vouch for the token beside it.
- `ALLOW_SECRETS` remains the only override; nothing here widens what is scanned,
it only stops flagging text that was never a credential.
Verified against the payload that is stuck right now: the skill page that the
heartbeat cannot commit passes the fixed hook and was refused by the live one.
Refs #60
Two lies in the unit loop, both reported as success:
- `systemctl --user enable live-sync.service` fails on every deploy — the file has
no [Install] section, because live-sync.timer is what arms it. The result was
swallowed by `|| true` and the tool printed ENABLE anyway, so the line an
operator reads after a rebuild was false on every box, on the repo's own
heartbeat. The [Install] section is readable from the file being deployed, so
the tool now checks it and prints INERT with the reason instead of making a
call that cannot succeed; a real enable failure prints INERT too.
- The loop walked *.service only. live-sync.timer was never enabled by anything,
so a rebuilt box got the heartbeat and no schedule for it. Timers are walked
now; a timer has no ExecStart, so the "does this unit belong on this box" test
runs against the service it activates.
The systemctl stub models systemd's refusal (no [Install] section -> non-zero),
which is what makes the new tests red against the old loop; both new tests were
run red first, and the five existing unit tests still pin #44's semantics.
Refs #60
Three ways the fail-closed gate failed open, all reproduced by new tests first:
- `git diff --name-only` quotes any path it cannot print as UTF-8
(core.quotePath, on by default). The quoted string was handed back to
`git diff -- <path>`, which matches nothing, and an empty diff is `continue`d —
so a credential under `café/` committed cleanly. The list is now NUL-delimited
and read from a temp file, because command substitution deletes NUL bytes and
the names must survive to the loop that reads them.
- The placeholder filter matched the whole added line, so `API_TOKEN=<live> #
example, placeholder, changeme` was exempt on the strength of its own comment.
The filter now reads the assigned value only — the token up to whitespace or
`#`. Truncation errs toward blocking; ALLOW_SECRETS remains the way out.
- `report+=` ended on a command substitution, so the next file's name was glued
onto the previous file's last hit line and the report blamed the wrong file.
Three tests, all red before the change: a quoted path, a placeholder comment
beside a live value, and two leaking files each named on their own line.
Refs #60
install.sh and pkg-inventory.sh carried byte-identical copies of read_manifest.
That is the duplicated-code half of the problem; the worse half is untested —
neither script had any test for the grammar that decides what gets installed.
- scripts/lib/manifest.sh is the parser; both scripts source it
- tests/manifest-parser.bats writes the contract first (comments, CRLF, first
field only, unreadable file fails loudly instead of yielding "nothing desired")
- an unreadable manifest now fails inside the helper rather than depending on
whichever `set` options the caller happens to run under
- install.sh check mode verified end to end against a stubbed pacman: a CRLF
manifest with trailing comments resolves to bare names, installed names drop
Refs #60
Three things from the review (#60), one of them a finding of my own that did not
survive measurement:
- The reported defect — additions never subtract `pacman -Qtdq`, so an orphan can
be emitted as a manifest line — is wrong. A package carries one install reason:
`-Qtdq` selects installed-as-dependency, additions come from `-Qqe`, so the sets
are disjoint (blankpad: 206 explicit, 3 orphans, none in common). A subtraction
would be dead code that looks like a guard, so the header now says why not to
add one. The packages page law was rewritten to the same effect.
- What the old test should have pinned: an implicit dependency installed AFTER
the baseline. Only `-Qqe` keeps it out of drift — mutate that query to `-Qq` and
the new test goes red (verified), where the old assertion passed no matter what.
- `--help` printed `set -euo pipefail` and the LC_ALL line as documentation: the
hardcoded sed range ran past the comment block. It now stops at the first line
of code, so the header can keep growing.
Refs #60
Measured fixes, one line each, from the review of everything that landed on
2026-10-20 (issue #60). Each page keeps its own facts; nothing was moved.
- inventory/index, index: the census blurb says what the page actually covers
(tracked-vs-box-only per file), not "all userspace configuration"
- dotfiles-deploy: the wallpaper re-fetch recipe resolves the CDN URL through
the wallhaven API — /w/<id> answers text/html, so curl -L never yields a
JPEG; description widened to the whole page, tools/index blurb matched
- okf-maintenance: the link example and the mistake bullet now agree with
okf-spec, which RECOMMENDS absolute bundle paths (they said the opposite)
- decisions/index: the unlisted-page gate is kb-coverage.py, not kb-lint
- AGENTS.md: the three skill-config subsections are pointers again; the pages
own issue tracker, labels and domain docs
- swayidle-lock.service: Description names what the unit does (arms the idle
lock over noctalia-shell IPC); suspend belongs to noctalia's inhibitor
- common.pkgs: git-lfs re-justified by the tracked .gitattributes filter
contract; the swayidle comment drops its lid-policy claim
Per-card measurement was unreachable for every card but ordinal 0: the driver
set HIP_VISIBLE_DEVICES=N and still passed N as the device argument, so the
probe called hipSetDevice(N) on a runtime where the mask left only ordinal 0.
The bats stub reported the env var rather than the argument it received, so
the suite asserted the mask was set and never noticed the collision.
- one selector: the ordinal is passed as argv, the mask is never set
- a probe that fails is now exit 5 and names probe + device; the sweep still
measures the remaining cards instead of swallowing the failure
- the guard fails closed when `fuser` is missing (gpu_busy_percent reads 0 for
a resident-but-idle service), with PCIE_PROBE_FUSER as the test seam
- stub probes now model the HIP mask rule; the pre-existing per-device test
goes red against the old driver, which is the regression proof
Refs #60