chore(merge): land origin/main (PR #61, PR #63) in the deployment checkout

Two conflicts, both resolved to trunk, both proven content-free for the running
machine before the merge was committed:

- `agent/APPEND_SYSTEM.md` — the checkout's dirty copy and trunk differ in one line
  only: #61's trim of the duplicated one-log reasoning, replaced by the owner pointer.
  The doctrine text itself is byte-identical on both sides.
- `desk-1-home.kdl` — non-comment lines are byte-identical (`diff` on the stripped
  file: no output), so the deployed modes are unchanged: `3440x1440@180.000` ×3 and
  `1920x1080@60.001`. What changes is the comment: trunk's carries the measured mode
  list (`3440x1440@239.998` exists), the reason 180 is deliberate, and the verbatim
  mode-string law — the live copy still asserted "no 240 Hz mode exists", which the
  panel contradicts. `niri validate` on the assembled config: valid.

Deployment state after this: the checkout is main, and `agent/memories/**` +
`agent/managed-skills/**` were committed by the heartbeat commit underneath. Refs #60 #62
This commit is contained in:
Frank committed 2026-10-21 01:45:33 -07:00
commit 22d2f3bcd8
44 files changed
+1047 -211

No files matched your search

+10
View File
@@ -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 *.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text *.jpeg filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text *.png filter=lfs diff=lfs merge=lfs -text
+54 -11
View File
@@ -34,24 +34,36 @@ placeholders='changeme|change[-_]|placeholder|redacted|example|sample|dummy|noke
# how a gate gets permanently overridden. # how a gate gets permanently overridden.
assign_exempt='(_FILE|_PATH|_DIR|_URL|_ENDPOINT|_SOCKET)[[:space:]]*[:=]|[:=][[:space:]]*["'"'"']?(/|\.\./)' assign_exempt='(_FILE|_PATH|_DIR|_URL|_ENDPOINT|_SOCKET)[[:space:]]*[:=]|[:=][[:space:]]*["'"'"']?(/|\.\./)'
# Fail closed: a gate that cannot read the staged diff must not report success. # 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." 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 ..." echo "Override deliberately: ALLOW_SECRETS=1 git commit ..."
} >&2 } >&2
[ -n "${ALLOW_SECRETS:-}" ] && exit 0 [ -n "${ALLOW_SECRETS:-}" ] && exit 0
exit 1 exit 1
fi fi
# stderr is captured so the failure branch can quote it; on success it must not # git's stderr stays out of the name list on purpose: a `warning:` line mistaken for a path
# leak a `warning:` line into the file list and have it mistaken for a path. # becomes a file that "cannot be read", which is how a gate starts skipping the real ones.
files=$(printf '%s\n' "$files" | grep -vE '^(warning|fatal|error):' || true) [ -s "$TMPD/files" ] || exit 0
[ -z "${files//[[:space:]]/}" ] && exit 0
report="" report=""
exempt="" exempt=""
count=0 count=0
while IFS= read -r f; do while IFS= read -r -d '' f; do
[ -z "$f" ] && continue [ -z "$f" ] && continue
# Self-exemption, deliberately an exact filename list and not `tests/`: the # Self-exemption, deliberately an exact filename list and not `tests/`: the
# gate's fixtures must contain credential-shaped strings or there is nothing to # 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. # gate refused the redaction of the password it exists to catch.
added=$(printf '%s\n' "$diff" | grep -E '^\+' | grep -vE '^\+\+\+' | sed 's/^+//' || true) added=$(printf '%s\n' "$diff" | grep -E '^\+' | grep -vE '^\+\+\+' | sed 's/^+//' || true)
[ -z "$added" ] && continue [ -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 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') hits=$(printf '%s\n%s\n' "$hits" "$assigns" | sed '/^[[:space:]]*$/d')
fi fi
if [ -n "$hits" ]; then if [ -n "$hits" ]; then
n=$(printf '%s\n' "$hits" | wc -l) n=$(printf '%s\n' "$hits" | wc -l)
count=$((count + n)) 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 fi
done <<< "$files" done < "$TMPD/files"
# Say so out loud: an exemption that prints nothing is how a carve-out becomes # Say so out loud: an exemption that prints nothing is how a carve-out becomes
# invisible, and invisible is the property a secret wants. # invisible, and invisible is the property a secret wants.
+16 -4
View File
@@ -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 # 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 # 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 # `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>` # filename carries its wallhaven ID; `/w/<id>` answers `text/html`, so resolve the CDN URL at
# puts it back. Law: omp-docs/tools/dotfiles-deploy.md # `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/ dotfiles/noctalia/.local/share/noctalia/wallpapers/
# --- generated theme output: machine-written, never reviewable, never deployed --- # --- generated theme output: machine-written, never reviewable, never deployed ---
# noctalia/matugen rewrite these in place; a tracked copy is a stale snapshot that # 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 # deploy.sh would push back over a live palette. These patterns are the ignore half of the
# scripts/deploy.sh. Law: omp-docs/inventory/home-config-census.md, Refs #42 # 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/**/themes/noctalia*
dotfiles/**/colors/noctalia* dotfiles/**/colors/noctalia*
dotfiles/**/noctalia.* dotfiles/**/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/
+4 -7
View File
@@ -94,19 +94,16 @@ knowledge base. Those pages own their facts; this is only the pointer.
### Issue tracker ### Issue tracker
Work items are Gitea issues on `dank/command-center` (Dankclaw, See [`omp-docs/skills/issue-tracker.md`](omp-docs/skills/issue-tracker.md).
`192.168.0.143:3000`) — no `gh`, no `tea`. See
[`omp-docs/skills/issue-tracker.md`](omp-docs/skills/issue-tracker.md).
### Triage labels ### Triage labels
The five canonical roles, each label string equal to its name. See See [`omp-docs/skills/triage-labels.md`](omp-docs/skills/triage-labels.md).
[`omp-docs/skills/triage-labels.md`](omp-docs/skills/triage-labels.md).
### Domain docs ### Domain docs
Single-context, inside the existing knowledge base: `omp-docs/glossary.md` and ADRs: `omp-docs/decisions/`. `omp-docs/glossary.md` is intentionally not created.
`omp-docs/decisions/`. See [`omp-docs/skills/domain-docs.md`](omp-docs/skills/domain-docs.md). See [`omp-docs/skills/domain-docs.md`](omp-docs/skills/domain-docs.md).
## Secret gate (HARD rule) ## Secret gate (HARD rule)
+1 -1
View File
@@ -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 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. 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. - 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. - 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.
+68 -17
View File
@@ -16,7 +16,7 @@ providers:
auth: none auth: none
models: models:
- id: frank-archon - id: frank-archon
# Model id renamed from frank-archon on 2026-10-08 (official # Model id renamed from frank-medium on 2026-10-08 (official
# unit-name scheme; the systemd service, scripts and runbooks keep # unit-name scheme; the systemd service, scripts and runbooks keep
# the frank-archon name — service names are unchanged). # the frank-archon name — service names are unchanged).
# Server-side compaction routes to compaction.remoteEndpoint in # Server-side compaction routes to compaction.remoteEndpoint in
@@ -82,9 +82,11 @@ providers:
auth: none auth: none
models: models:
- id: frank-dragoon - id: frank-dragoon
# Model id renamed from frank-dragoon on 2026-10-08 (official # Model id renamed from frank-large on 2026-10-08 (official
# unit-name scheme; the service/unit/scripts keep the frank-dragoon # unit-name scheme; the service/unit/scripts keep the frank-dragoon
# name — service names are unchanged). # name — service names are unchanged). The pre-rename id is
# `frank-large`, recorded here because the callsign rename overwrote
# its own explanation and left "renamed from frank-dragoon".
name: "frank-dragoon (dankclaw-gpu)" name: "frank-dragoon (dankclaw-gpu)"
# Server-side compaction runs on the compaction.remoteEndpoint in # Server-side compaction runs on the compaction.remoteEndpoint in
# config.yml (192.168.0.143:7771 — frank-zealot's 3090); this steers the # config.yml (192.168.0.143:7771 — frank-zealot's 3090); this steers the
@@ -111,31 +113,80 @@ providers:
maxTokens: 45056 maxTokens: 45056
reasoning: true reasoning: true
# VISION ON as of 2026-10-06: the v2 vision tower loads beside the # VISION ON as of 2026-10-06: the v2 vision tower loads beside the
# checkpoint (HALOGEN_VISION_TOWER; red-PNG probe verified on the # checkpoint (HALOGEN_VISION_TOWER; priced at 0.8 GiB in the boot
# card). image_url content parts only — the legacy top-level # ledger, and re-probed 2026-10-21 — a 512 px solid-red PNG comes back
# images field is the frank-zealot trap, do not repeat it here. # "Red" at 282 prompt tokens). image_url content parts only — the
# legacy top-level images field is the frank-zealot trap, do not
# repeat it here.
input: [text, image] input: [text, image]
compat: compat:
# flash-server emits thinking as `reasoning_content` — same # flash-server emits thinking as `reasoning_content` — same
# field as every engine this endpoint has run, no harness # field as every engine this endpoint has run, no harness
# change needed. # change needed.
reasoningContentField: "reasoning_content" reasoningContentField: "reasoning_content"
# Same Qwen3.8 template family (it ships inside the tokenizer # flash-server validates reasoning_effort against its own list and
# bundle): any reasoning_effort outside {xhigh, medium, low} is # 400s anything else, naming it: "reasoning_effort 'maximum'
# rejected, not clamped — the fold stays mandatory. # unsupported; use minimal|low|medium|high|xhigh|max|none" (measured
# 2026-10-21). Every omp level is already spelled by the engine, so
# the map is identity — it exists to record that, not to fold. An
# earlier version rewrote high->xhigh and minimal->low to stop
# rejections that do not exist, and the rewrite asked the engine for
# a different thinking budget than the caller named. Levels really do
# differ here: thinking length on one prompt at max_tokens 2048
# measured low 766 tokens, xhigh 614, max 1049 with the block closed
# at 1024 (reasoning_closed_at). The harness passes an unmapped level
# through verbatim (`compat.reasoningEffortMap?.[effort] ?? effort`),
# so removing an entry is a pass-through, never a 400.
#
# `max` still does not reach the engine, and the map is not why: the
# harness clamps the requested effort to the model's own effort ladder
# before the map applies, and its ladder for this model tops out at
# xhigh — captured on the wire 2026-10-21, `:max` left as
# `reasoning_effort: "xhigh"` with this identity map in place. The
# `max` row below is the engine-side half of the contract; it takes
# effect the moment the harness ladder gains `max`, and nothing here
# can force it sooner.
#
# Thinking OFF needs an explicit mode. The default disable mode sends
# the LOWEST effort rather than a switch — `:off` left the wire as
# `reasoning_effort: "minimal"` (captured), and minimal still thinks:
# a canned 26-token block on every "off" request. `none-effort` makes
# the harness send `reasoning_effort: "none"`, which this engine
# honours as a real switch: `reasoning_tokens: 0`, answer only, no
# thinking block in the rendered output (both verified 2026-10-21).
#
# Re-measure before changing any of it. The allow-list, in the
# engine's own words:
# curl -s :7770/v1/chat/completions -H 'content-type: application/json' \
# -d '{"model":"frank-dragoon","messages":[{"role":"user","content":"x"}],
# "reasoning_effort":"maximum"}'
# -> 400 "reasoning_effort 'maximum' unsupported; use
# minimal|low|medium|high|xhigh|max|none"
# and read usage.completion_tokens_details.reasoning_tokens at
# max_tokens 2048 to see the levels separate. The wire itself is
# settled by pointing a scratch HOME at a logging proxy on :7799 that
# forwards to :7770 and running `omp --model …:off` — config claims
# are worth exactly what their last capture says.
reasoningDisableMode: none-effort
reasoningEffortMap: reasoningEffortMap:
minimal: low minimal: minimal
low: low low: low
medium: medium medium: medium
high: xhigh high: high
max: xhigh xhigh: xhigh
# The card's thinking recipe, on the wire. The flash-server max: max
# engine HAS a server-side sampler (HALOGEN_TEMPERATURE/TOP_P/ # The card's thinking recipe, on the wire. The flash-server engine
# TOP_K = 1.0 / 0.95 / 20 in frank-dragoon.env) — these match it, # HAS a server-side sampler (HALOGEN_TEMPERATURE/TOP_P/TOP_K =
# so per-request values are consistent, not load-bearing. # 1.0 / 0.95 / 20 in frank-dragoon.env), but an omitted parameter is
# the engine's choice, not a pinned one: top_k is sent explicitly so
# the recipe survives someone editing HALOGEN_TOP_K. The engine
# accepts top_k, min_p, presence_penalty and repetition_penalty
# (measured 2026-10-21, all 200) — only the values this endpoint is
# meant to run are pinned here.
extraBody: extraBody:
temperature: 1.0 temperature: 1.0
top_p: 0.95 top_p: 0.95
top_k: 20
"llamacpp (dankclaw-3090)": "llamacpp (dankclaw-3090)":
baseUrl: http://192.168.0.143:7771/v1 baseUrl: http://192.168.0.143:7771/v1
@@ -143,7 +194,7 @@ providers:
auth: none auth: none
models: models:
- id: frank-zealot - id: frank-zealot
# Model id renamed from frank-zealot on 2026-10-08 (official # Model id renamed from frank-small on 2026-10-08 (official
# unit-name scheme; the service/unit/scripts keep the # unit-name scheme; the service/unit/scripts keep the
# frank-zealot name — service names are unchanged). # frank-zealot name — service names are unchanged).
name: "frank-zealot (dankclaw-3090)" name: "frank-zealot (dankclaw-3090)"
@@ -1,5 +1,5 @@
[Unit] [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 Documentation=https://github.com/swaywm/swayidle
After=graphical-session.target After=graphical-session.target
# Restart together with the session, not merely at boot: this unit is wanted by # Restart together with the session, not merely at boot: this unit is wanted by
@@ -7,10 +7,14 @@
// requires: Lenovo Group Limited 0x40A9 Unknown // requires: Lenovo Group Limited 0x40A9 Unknown
// optional: HDMI-A-1 (PM1601, dock-dependent) // optional: HDMI-A-1 (PM1601, dock-dependent)
// This panel's EDID tops out at 3440x1440@180 (read live from niri: no 240 Hz mode // The panel offers 3440x1440@239.998 as well as @180.000 (`niri msg outputs`, 2026-10-21), and
// exists). Ask for a mode the panel does not offer and niri does not fail — it logs // 180 is driven DELIBERATELY: the link trained at x8 32.0 GT/s against a max of x16 and the
// "could not be found, falling back to preferred" and drops to 60 Hz on every // display engine is throwing DMCUB errors with EINVAL page flips after s2idle, so the 240-class
// reconnect. Keep 180 here unless a re-read EDID says otherwise. // 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" { output "Sceptre Tech Inc Sceptre M34 Unknown" {
mode "3440x1440@180.000" mode "3440x1440@180.000"
scale 1 scale 1
+1 -3
View File
@@ -16,6 +16,4 @@ These files define the structural conventions that govern the agent system itsel
* [OMP Status Line Customization](statusline.md) — How presets, composer shapes, and segment arrays actually interact; the fleet's tuned bar. (moved from /pi/, 2026-10-19) * [OMP Status Line Customization](statusline.md) — How presets, composer shapes, and segment arrays actually interact; the fleet's tuned bar. (moved from /pi/, 2026-10-19)
* [OMP Subagent Limits & Fan-out Policy](subagent-limits.md) — Live caps: concurrency 2, no nested spawns (the `maxRecursionDepth: 0` trap), per-endpoint in-flight ceilings, isolation policy. (moved from /pi/, 2026-10-19) * [OMP Subagent Limits & Fan-out Policy](subagent-limits.md) — Live caps: concurrency 2, no nested spawns (the `maxRecursionDepth: 0` trap), per-endpoint in-flight ceilings, isolation policy. (moved from /pi/, 2026-10-19)
## Change Tracking **Change log:** the bundle keeps exactly one, one level up — [Bundle Change Log](../log.md). A directory here never has its own.
* [Bundle Change Log](log.md) — Chronological update history.
-21
View File
@@ -1,21 +0,0 @@
---
type: changelog
title: Core Bundle Change Log
description: Change log for the core documentation directory — tracks migrations and structural changes.
tags:
- core
- changelog
- okf
timestamp: 2026-07-18
---
# Core Bundle Change Log
## 2026-07-18
- **Migration**: Converted `index.md` (root) to `core/index-doc.md` — moved root bundle index into core domain
- **Migration**: Converted `agents-md-format.md` (root) to `core/agents-md-format.md` — agents-md format belongs in core
- **Creation**: Established `core/` directory with `index.md` as the domain entry point
- **Creation**: This change log for tracking core directory modifications
## 2026-07-15
- **Initialization**: OKF v0.1 conversion of the entire docs bundle — frontmatter applied, structure established, first `log.md` entries created
+2 -2
View File
@@ -1,8 +1,8 @@
# Architecture decision records # Architecture decision records
Decisions this repo has made and will not silently revisit. One file per 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` decision, named `NNN-<slug>.md`, listed here — an unlisted page fails
and does not exist as far as any agent is concerned. `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 **No ADRs yet.** The first one lands the moment a decision stops being
reversible. reversible.
+2 -2
View File
@@ -57,8 +57,8 @@ search practices, open models, MCP.
## [Inventory](inventory/index.md) ## [Inventory](inventory/index.md)
Pre-pruning census of every userspace config on this box — the input for the Per-file tracked-vs-box-only census of `~/.config` on blankpad — the input for the
pruning session. dotfiles pruning/adoption session.
## [Decisions](decisions/index.md) ## [Decisions](decisions/index.md)
+4 -2
View File
@@ -1,5 +1,7 @@
# Inventory Index # 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.
+110 -1
View File
@@ -6,7 +6,7 @@ tags:
- okf - okf
- maintenance - maintenance
- changelog - changelog
timestamp: 2026-10-19 timestamp: 2026-10-21
--- ---
# Bundle Change Log # Bundle Change Log
@@ -16,6 +16,115 @@ lesson, a few lines. Full incident narratives are recoverable from git
(`git log --follow omp-docs/log.md`); knowledge that outlives an entry lives (`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. on its concept page, linked here. Never re-expand an entry in place.
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 — an effort map folding against rejections that never existed
`agent/models.yml` rewrote `minimal→low`, `high→xhigh` and `max→xhigh` for
frank-dragoon to dodge 400s the engine does not send: flash-server's own error names
its allow-list (`minimal|low|medium|high|xhigh|max|none`) and the harness passes an
unmapped level through verbatim, so the fold only asked for a different thinking
budget than the caller named. The map is identity now, and
`reasoningDisableMode: none-effort` was added because the default disable mode does
not disable — `:off` left as `reasoning_effort: "minimal"` and paid for a canned
thinking block, where `none` returns `reasoning_tokens: 0`. Both settled by capturing
the wire through a logging proxy rather than reading the config: a claim about what
the harness sends is worth exactly its last capture. The same pass restored the three
pre-callsign model ids (`frank-medium` / `frank-large` / `frank-small`) that the
2026-10-08 rename had overwritten inside its own explanatory comments. Refs #62
## 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.
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):
- **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.** 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
temporary per-output path, so no config reload and no logged-out session.
## 2026-10-20 — one work log per bundle
The bundle had two: this page and `core/log.md`, created "for tracking core
directory modifications" — two entries of pure migration bookkeeping (file moved,
directory created, log created), and a date for the OKF conversion that neither this
page nor git agrees with: 2026-07-15 there, 2026-01-15 here, while `git log` dates the
OKF rebuild 2026-08-28. `core/index.md` was still pointing readers at it, which is how
a stale page stays alive. Trashed to `omp-docs/core/.AGENTTRASH/log.md` — its content
is recoverable from git, which is the entire argument against a second log.
Laws, now written down in [references/okf-spec.md](references/okf-spec.md) and
[references/okf-maintenance.md](references/okf-maintenance.md), and gated: a bundle has
ONE work log, `log.md`, at its root, and `scripts/kb-lint.sh` reports any `log.md` that
is not the root's — so the sprawl that made logging useless once cannot restart
quietly. The agent-side duty is in `agent/APPEND_SYSTEM.md` § 1: log here, read here,
never start another.
## 2026-10-20 — two instruments rescued from `$HOME` script sediment ## 2026-10-20 — two instruments rescued from `$HOME` script sediment
`$HOME` had accumulated 123 loose scripts at its root (archived to `$HOME` had accumulated 123 loose scripts at its root (archived to
+37 -15
View File
@@ -21,32 +21,46 @@ Knowledge bundles rot. It is the nature of knowledge to attract cobwebs, much li
## Change Tracking with `log.md` ## Change Tracking with `log.md`
Every OKF bundle has a `log.md` file. It is not optional. It is the bundle's memory. 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:** Date-grouped entries, newest first. One line per change with timestamp, file path, and what changed. **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.
```markdown ```markdown
# Bundle Change Log # Bundle Change Log
## 2026-01-15 ## 2026-01-15 — the gate was reading the wrong files
- `/docs/ghostty.md` — Updated keybind table after v1.2 release
- `/docs/pi-permission-system/` — Added `policy.md` and `audit.md` A gate that gets an empty file list reports success. Law: a check MUST fail closed;
- `/docs/index.md` — Synced with directory contents a silently-passing gate is worse than none, because it buys the belief of safety.
Details: [secret gate](/tools/index.md).
``` ```
**Rules:** **Rules:**
- **Newest entries first.** Putting old entries at the top inverts the timeline and makes it impossible to see what just happened. - **Newest entries first.** Old entries at the top invert the timeline.
- **One line per change.** A single line is sufficient. The git history is there if someone wants the gory details. - **What changed + the durable lesson, a few lines.** Full narratives are recoverable
- **Group by date.** Batch all changes from the same day together. from git (`git log --follow log.md`); never re-expand one in place.
- **Always date-stamp the section.** Use `## YYYY-MM-DD`. - **Laws and incidents, never bookkeeping.** No per-file inventories, no run-by-run
- **Mention `index.md` whenever you change a bundle.** If you added or removed a file, update the index and note it in the log. numbers, no transcripts. Measured numbers still have to pass the purpose test.
- **Link, do not duplicate.** Knowledge that outlives the entry belongs on its concept
page; the entry points there.
- **One dated section per day** (`## YYYY-MM-DD`), and mention `index.md` whenever a
file is added or removed.
### Common Mistakes ### Common Mistakes
- ❌ Writing `"Updated ghostty.md"` — Updated it with what? Be specific. - ❌ `Updated ghostty.md` — updated it with what, and what did it teach?
- ❌ Forgetting to write to `log.md` — This is the equivalent of doing surgery without keeping a record. - ❌ Forgetting the entry — the change lands undocumented and the next agent rediscovers
- ❌ Using relative paths — Use absolute paths (`/docs/ghostty.md`). the failure the hard way.
- ❌ Leaving stale entries from manual edits — The log is the single source of truth. - ❌ Starting a second log in a subdirectory — the sprawl that makes logging useless.
- ❌ 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.
--- ---
@@ -84,6 +98,14 @@ OKF is built on the principle that a file is one concept.
- ❌ Don't create a file called `misc.md` — `misc` is a graveyard, not a category. - ❌ Don't create a file called `misc.md` — `misc` is a graveyard, not a category.
- ❌ Don't use `# Other` or `# Additional Notes` sections — these are taping things to the wall. - ❌ Don't use `# Other` or `# Additional Notes` sections — these are taping things to the wall.
## Quarantine, Not Deletion
A page is never deleted outright: it moves to `.AGENTTRASH/` beside where it lived and
waits for a pruning session with Frank present. The quarantine is not part of the
bundle — `scripts/kb-lint.sh` and `scripts/kb-coverage.py` skip any path containing
`.AGENTTRASH`. That exemption is deliberate: a gate that goes green only once the
quarantined copy is gone is a gate that deletes history.
--- ---
## Auto-Generation Strategies ## Auto-Generation Strategies
+8 -2
View File
@@ -42,7 +42,7 @@ An OKF knowledge bundle is a directory tree. Every concept is one file. The file
``` ```
my-bundle/ my-bundle/
├── index.md # Optional directory listing ├── index.md # Optional directory listing
├── log.md # Optional update history ├── log.md # The bundle's ONE update history — root only
├── concept-one.md # A concept at the root level ├── concept-one.md # A concept at the root level
└── subdirectory/ └── subdirectory/
├── index.md # Optional subdirectory listing ├── index.md # Optional subdirectory listing
@@ -55,7 +55,13 @@ my-bundle/
| Filename | Purpose | | Filename | Purpose |
|----------|---------| |----------|---------|
| `index.md` | Directory listing for progressive disclosure | | `index.md` | Directory listing for progressive disclosure |
| `log.md` | Chronological update history | | `log.md` | Chronological update history — **exactly one, at the bundle root** |
**One work log per bundle.** `log.md` is reserved, and it is reserved for a single
file: the bundle root's. A subdirectory never keeps its own change log — a bundle
that logs in fifty places has no log, because the entry is never where the next
reader looks and every copy re-dates the same event. `scripts/kb-lint.sh` reports
any `log.md` that is not the root's.
--- ---
-1
View File
@@ -33,7 +33,6 @@ Applications that ship with or are configured on CachyOS with Niri on the workst
| **Noctalia Shell** | 4.7.7 | Desktop shell (launcher, wallpaper, session menu) | | **Noctalia Shell** | 4.7.7 | Desktop shell (launcher, wallpaper, session menu) |
| **Shelly Notifications** | — | Notification daemon | | **Shelly Notifications** | — | Notification daemon |
| **pavucontrol** | — | Audio mixer | | **pavucontrol** | — | Audio mixer |
| **wlr-randr** | — | Display management |
## Screenshot and Recording ## Screenshot and Recording
-1
View File
@@ -95,7 +95,6 @@ CachyOS with Niri ships with a curated set of applications:
- **wf-recorder** — Screen recording - **wf-recorder** — Screen recording
- **hyprpicker** — Color picker - **hyprpicker** — Color picker
- **wl-clipboard** — Clipboard management - **wl-clipboard** — Clipboard management
- **wlr-randr** — Display management
- **NetworkManager** — Network management - **NetworkManager** — Network management
- **BlueZ** — Bluetooth - **BlueZ** — Bluetooth
- **pavucontrol** — Audio mixer - **pavucontrol** — Audio mixer
+31 -8
View File
@@ -1,14 +1,14 @@
--- ---
type: reference type: reference
title: Dotfiles deployment tier 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: tags:
- dotfiles - dotfiles
- deployment - deployment
- systemd - systemd
- noctalia - noctalia
timestamp: 2026-10-20 timestamp: 2026-10-20
verified: 2026-10-20 verified: 2026-10-21
--- ---
# Dotfiles deployment tier # 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` (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 | | `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 Two package shapes are not symlinks. `dconf/dump.txt` is loaded with `dconf load /`. The
`systemd-user` units get `daemon-reload` plus `enable`; a unit is **started** only when `systemd-user` tier gets `daemon-reload`, then **every `*.service` and `*.timer` whose start
systemd reports it as nothing-yet (`""`/`disabled`), and never when its `ExecStart` binary command resolves on this box** is enabled — a timer is the schedule that makes a oneshot service
is absent here, so a unit belonging to another box stays inert instead of crash-looping. 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 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*. 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 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 `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 whole address — but `https://wallhaven.cc/w/<id>` answers `text/html`, the web page, so
the set back. Losing it is an acceptable outcome; 80 MB per clone forever is not. 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 ## 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 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. 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 ## 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 `ExecStart=%h/.omp/scripts/live-sync.sh` is valid systemd and meaningless to a shell. The
+1 -1
View File
@@ -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. * [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. * [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. * [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. * [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. * [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. * [handy](handy.md) — Offline speech-to-text transcription with WebKitGTK backend and Tauri v2 UI.
+71 -12
View File
@@ -1,14 +1,14 @@
--- ---
type: reference type: reference
title: Display Configuration title: Display Configuration
description: Niri output configuration on blankpad — EDID-identity-keyed desk profiles, hotplug auto-switching, portrait transforms, position pinning, and GPU notes (AMD Cezanne now; Intel Arc render-node pin is dankware-legacy). description: Niri output configuration on blankpad — EDID-identity-keyed desk profiles, hotplug auto-switching, portrait transforms, position pinning, the phantom-mode fallback trap, and GPU notes (AMD Cezanne now; Intel Arc render-node pin is dankware-legacy).
tags: tags:
- niri - niri
- display - display
- monitors - monitors
- rotation - rotation
- desk-profiles - desk-profiles
verified: 2026-10-09 verified: 2026-10-21
--- ---
# Display Configuration # Display Configuration
@@ -24,7 +24,11 @@ active profile (`desk-1-home.kdl`, `desk-2-portable.kdl`, …).
`config.kdl` includes it with `include optional=true` so a missing link is a `config.kdl` includes it with `include optional=true` so a missing link is a
warning, not a parse failure. warning, not a parse failure.
**Probe:** `niri msg outputs` is authoritative (connector, full EDID long **Probe:** `niri msg outputs` is authoritative (connector, full EDID long
name, modes, transform, scale, position, VRR). `wlr-randr` remains available. name, modes, transform, scale, position, VRR). Per-output changes go live with
`niri msg output <name> <mode|scale|transform|position|off|on>` and are
temporary — never written to the config file. `wlr-randr` is **not installed**
on blankpad (`pacman -Qs wlr` matches nothing, verified 2026-10-21); pages that
list it as an installed tool are stale.
--- ---
@@ -33,21 +37,23 @@ name, modes, transform, scale, position, VRR). `wlr-randr` remains available.
``` ```
┌──────────────┬──────────────────────────────┬──────────┐ ┌──────────────┬──────────────────────────────┬──────────┐
│ PM1601 │ Sceptre M34 ultrawide │ eDP-1 │ │ PM1601 │ Sceptre M34 ultrawide │ eDP-1 │
│ portrait │ 3440x1440 @ 239.999 │ 1920x1080│ │ portrait │ 3440x1440 @ 180.000 │ 1920x1080│
│ 1200x1920 │ landscape, scale 1 │ scale 1 │ │ 1200x1920 │ landscape, scale 1 │ scale 1 │
│ rot 90 │ position 1581,-600 │ 5021,-240│ │ 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 | | Panel | EDID long name | Keyed by | Notes |
|-------|----------------|----------|-------| |-------|----------------|----------|-------|
| Sceptre M34 ultrawide | `Sceptre Tech Inc Sceptre M34 Unknown` | identity | `mode "3440x1440@239.999"`, `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 | | 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` | | 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 VRR: the M34 supports it (its connector today is `DP-1`); **left disabled by
`variable-refresh-rate` per output if that ever changes. 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 `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. the ultrawide wins when present and the laptop panel is the fallback.
@@ -84,7 +90,7 @@ graph LR
```kdl ```kdl
// desk-1-home.kdl — one block per panel, keyed by identity // desk-1-home.kdl — one block per panel, keyed by identity
output "Sceptre Tech Inc Sceptre M34 Unknown" { output "Sceptre Tech Inc Sceptre M34 Unknown" {
mode "3440x1440@239.999" mode "3440x1440@180.000"
scale 1 scale 1
transform "normal" transform "normal"
position x=1581 y=-600 position x=1581 y=-600
@@ -98,6 +104,10 @@ Four rules learned the hard way:
name *and* long name) fight over properties — transform reverts are the name *and* long name) fight over properties — transform reverts are the
classic symptom. Desk profiles are self-contained: a panel's identity, classic symptom. Desk profiles are self-contained: a panel's identity,
mode, transform, **and** position live in exactly one block per file. mode, transform, **and** position live in exactly one block per file.
`desk-1-home.kdl` still carries legacy connector-keyed `DP-1` / `DP-2`
blocks next to the identity block. Their modes were aligned to 180 Hz on
2026-10-21 so a connector-named reconnect cannot drop the panel to 120, but
they belong folded into the identity block.
2. **Key by EDID identity, not connector.** Connector names swap between 2. **Key by EDID identity, not connector.** Connector names swap between
docks; the EDID long name travels with the panel. Serials on these panels docks; the EDID long name travels with the panel. Serials on these panels
read `Unknown`, so the full long name (manufacturer model serial) is the read `Unknown`, so the full long name (manufacturer model serial) is the
@@ -105,8 +115,57 @@ Four rules learned the hard way:
3. **Positions are absolute, not relational.** A disconnected panel leaves 3. **Positions are absolute, not relational.** A disconnected panel leaves
a hole; that is exactly why positions belong to the *desk profile*, not a a hole; that is exactly why positions belong to the *desk profile*, not a
global file. global file.
4. **Modes must match what the panel reports** (`niri msg outputs` → 4. **Ask only for modes the panel reports, and copy the string exactly.** The M34's
`Mode: current …`). A mode the panel cannot drive leaves the output dark. `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
A display complaint belongs to one of three layers. Sort it before editing
config, because the config is usually innocent — and when it is guilty, the
guilt may have been copied from this page.
| Layer | Log signature | Reality | Move |
|-------|---------------|---------|------|
| Config | `configured mode … could not be found, falling back to preferred` | a mode/scale the panel never offered | re-read the mode list with `niri msg outputs` and correct the file; do **not** trust a documented mode |
| Link | `disconnecting connector: "DP-1"` followed seconds later by `new connector: DP-1`, recurring | the DP link itself dropping — cable, dock, the monitor's DP-version/DSC setting, or panel power saving | every drop re-runs mode selection, so a config error is re-enacted in public; swap the cable before blaming the compositor |
| Driver | `Page flip commit failed … (Invalid argument)` plus kernel `*ERROR* … DMCUB error` | amdgpu's display engine wedged, typically after an s2idle resume | survives until a reboot; niri keeps running while the panel keeps dropping. No config edit clears it |
Probes, in this order:
```bash
niri msg outputs # connector, EDID long name, real mode list, current mode
journalctl --user -b | grep -E 'disconnecting connector|Page flip commit failed|falling back to preferred'
journalctl -k -b | grep -iE 'amdgpu.*(ERROR|DMCUB)|PM: suspend exit'
```
Correlate the drop timestamps against `PM: suspend exit` first. A resume a
minute before the first drop is the cause; the config file is not.
To fix a refresh rate without disturbing the session, use the temporary
per-output path — it touches one output, applies no config, reloads nothing:
```bash
niri msg output "Sceptre Tech Inc Sceptre M34 Unknown" mode "3440x1440@180.000"
```
It is temporary by design, so correct the profile too and let the next
reconnect or `load-config-file` make it permanent. With the display engine
already throwing `EINVAL`, prefer the per-output path over a full config
reload; on a healthy machine either is fine.
## GPU assignment ## GPU assignment
@@ -132,4 +191,4 @@ symlink matrix]]).
- [Niri](/tools/niri/index.md) — compositor overview and config layout - [Niri](/tools/niri/index.md) — compositor overview and config layout
- [[tips|tips]] — reload path, `niri msg layers/casts`, includes - [[tips|tips]] — reload path, `niri msg layers/casts`, includes
- [[noctalia-shell|noctalia-shell]] — notification shown on desk switch - [[noctalia-shell|noctalia-shell]] — notification shown on desk switch
- [[cachyos|cachyos]] — operating system, including `wlr-randr` - [[cachyos|cachyos]] — operating system
+15 -11
View File
@@ -413,17 +413,21 @@ noctalia msg templates-apply
## Dependencies ## Dependencies
| Package | Purpose | | Package | Purpose | Version on blankpad (2026-10-21) |
|---------|---------| |---------|---------|----------------------------------|
| `noctalia` (Qt) | Native desktop environment | | `noctalia` (Qt) | the shell: bar, widgets, `noctalia msg` | 5.3.0-1.1 |
| `qt6-base` | Qt6 runtime | | `qt6-base` | Qt6 runtime | 6.12.0-3.1 |
| `qt6-multimedia` | Multimedia support | | `niri` | Wayland compositor | 26.04-1.1 |
| `niri` | Wayland compositor | | `polkit` | PolicyKit integration | 128-1.1 |
| `wlr-randr` | Display information | | `imagemagick` | image processing for palette wallpapers | 7.1.2.34-2.1 |
| `brightnessctl` | Brightness control | | `ffmpeg` | media processing | 2:9.0.2-2.1 |
| `imagemagick` | Image processing |
| `ffmpeg` | Media processing | **Three rows this page used to claim are not installed**, verified with `pacman -Q` and
| `polkit` | PolicyKit integration | `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
View File
@@ -8,7 +8,7 @@ tags:
- provisioning - provisioning
- fleet - fleet
- blankpad - blankpad
verified: 2026-10-20 verified: 2026-10-21
timestamp: 2026-10-20 timestamp: 2026-10-20
--- ---
@@ -25,9 +25,13 @@ if both directions are checked.
## Manifests ## Manifests
`packages/common.pkgs` (every host) + `packages/<host>.pkgs` (this host) + `packages/aur.pkgs` `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 `#` (AUR-only, installed through `yay`). **One parser reads them for both tools**:
comments trail freely and one token per line is the whole grammar. An unknown `--host` gets the `scripts/lib/manifest.sh` — `sed 's/#.*//'` then the first field, so `#` comments trail freely and
common manifest only: a wrong-host run is a no-op, not a hazard. 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 ## 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 aur (needs a helper): <names> → belongs in packages/aur.pkgs
installer-provided, undeclared: N + names → a reinstall supplies these; do not declare installer-provided, undeclared: N + names → a reinstall supplies these; do not declare
declared, not installed: <names> → install.sh installs these 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 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 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. 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 **Known gap:** `aur.pkgs` is read for *every* host, so a desktop-only AUR package — the niri
GUI client) cannot be declared there without asking the headless servers to build it. Such a name is module `nirimod-git`, say, or a GUI client — cannot be declared there without asking the
still reported as an AUR addition; leave it undeclared until `install.sh` grows a per-host AUR 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. manifest.
## Law: orphans never reach a manifest ## Law: orphans never reach a manifest
`pacman -Qtdq` names are packages nothing depends on; emitting them would teach a fresh machine to Drift is `pacman -Qqe` — *explicitly* installed, therefore yours. `pacman -Qtdq` names packages
install the same dead weight. They are printed as `orphans ignored` and excluded from `--emit`. 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 ## 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) · - [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) [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. - Refs #51.
+19 -9
View File
@@ -8,7 +8,7 @@ tags:
- rdna4 - rdna4
- benchmarking - benchmarking
- dankserver - dankserver
verified: 2026-10-20 verified: 2026-10-21
timestamp: 2026-10-20 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 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 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 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 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 - Pinned versus pageable columns apart means pinning is load-bearing; together
means the copies are already pinned by someone else. means the copies are already pinned by someone else.
Measure one device at a time (`--device`). A run that lets the runtime pick Measure one device at a time (`--device`, a HIP ordinal). A run that lets the runtime
several devices cannot attribute bandwidth to a card at all. 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 ## The trap carried in the sources
@@ -79,14 +85,18 @@ when touching these files.
## Exit status and seams ## Exit status and seams
`0` measured · `2` usage or no sources · `3` refused, GPU busy · `4` no HIP `0` measured · `2` usage or no sources · `3` refused, GPU busy or holders unknowable ·
toolchain or build failed. A client box without the SDK exits `4` loudly rather `4` no HIP toolchain or build failed · `5` a probe ran and failed. A client box without
than reporting an empty success; the probes belong on the box with the cards. 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`, 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_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 `PCIE_PROBE_FUSER`, `PCIE_PROBE_MAX_BUSY` — the sysfs/kfd paths are what make a
a machine with no cards, which is the only honest way to test a refusal. 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 Measured campaign numbers are not kept here; they belong with the endpoint they
were taken on, in the serverAppConfigs LLM runbook vault. were taken on, in the serverAppConfigs LLM runbook vault.
+3 -2
View File
@@ -38,10 +38,10 @@ read afterwards by a short-lived connection. That is what this is.
```bash ```bash
remote-job.sh start NAME [--] CMD... # launch detached, return at once 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 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 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 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 | | 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 | | 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 | | 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 | | Job dir missing | created on first use |
Pinned by `tests/remote-job.bats` (black-box: output, exit codes, sentinel Pinned by `tests/remote-job.bats` (black-box: output, exit codes, sentinel
+2 -2
View File
@@ -18,8 +18,8 @@ btop
# system / fleet tooling # system / fleet tooling
yay # AUR helper bootstrap: ships in the cachyos repo; aur.pkgs installs need it yay # AUR helper bootstrap: ships in the cachyos repo; aur.pkgs installs need it
git-lfs # LFS smudge/clean for tracked wallpapers (see .gitattributes) git-lfs # cloning LFS-backed upstreams (Hugging Face model/dataset repos); this repo tracks zero LFS files — .gitattributes is idle config
swayidle # idle lock + lid policy via swayidle-lock.service unit swayidle # idle lock watcher via swayidle-lock.service; suspend locking is noctalia's sleep inhibitor
# desktop apps # desktop apps
flameshot # screenshot tool; bound to CTRL+Shift+1/2/3 in niri keybinds flameshot # screenshot tool; bound to CTRL+Shift+1/2/3 in niri keybinds
+32 -12
View File
@@ -120,9 +120,20 @@ done
if [ "$MODE" != "check" ] && [ -d "$PKGS_DIR/systemd-user/.config/systemd/user" ]; then if [ "$MODE" != "check" ] && [ -d "$PKGS_DIR/systemd-user/.config/systemd/user" ]; then
systemctl --user daemon-reload || true 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 [ -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 # 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 # `%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 # 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 -un)}"
bin="${bin//%U/$(id -u)}" bin="${bin//%U/$(id -u)}"
if [ -n "$bin" ] && command -v "$bin" >/dev/null 2>&1; then 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 # 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 # 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 # 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. # mid-conversation. Only a unit nothing has installed yet gets started. Refs #44.
state="$(systemctl --user is-enabled "$name" 2>/dev/null || true)" state="$(systemctl --user is-enabled "$name" 2>/dev/null || true)"
case "$state" in if ! grep -q '^\[Install\]' "$unit"; then
"" | disabled) # systemd refuses to enable these: no [Install] section means nothing can want the unit
systemctl --user enable --now "$name" || true # at login, and live-sync.service has none on purpose — its timer pulls it in. The old
echo "ENABLE $name (ExecStart resolves here; nothing running it yet)" ;; # 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
systemctl --user enable "$name" || true # the call at all. Refs #60.
echo "ENABLE $name (already $state; not started — a live session decides what runs)" ;; echo "INERT $name (state=${state:-none}; no [Install] section — another unit arms it, enable cannot)"
esac 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 else
echo "SKIP $(basename "$unit") (ExecStart '$bin' absent on this host)" echo "SKIP $name (ExecStart '$bin' absent on this host)"
fi fi
done done
fi fi
+20 -10
View File
@@ -42,17 +42,27 @@ gen() {
echo "> GENERATED by \`scripts/gen-keybinds.sh\` from \`$(basename "$KBD")\` — do not edit by hand." echo "> GENERATED by \`scripts/gen-keybinds.sh\` from \`$(basename "$KBD")\` — do not edit by hand."
echo echo
awk ' awk '
# section header: // ─── Name ─── # Section marker: a comment decorated with box-drawing dashes on both sides,
/^[[:space:]]*\/\/[[:space:]]*─+ (.+) ─+[[:space:]]*$/ { # `// ─── Name ───`. The dash is matched as a LITERAL and removed with gsub — never
# with a quantifier on the multibyte character. Under LC_ALL=C (cron, systemd units,
# agent shells) gawk works byte-wise, `─+` then means "the last byte of ─, repeated",
# and every header vanishes while the script still exits 0. Plain comments stay
# comments: only a dash-decorated one opens a section.
/^[[:space:]]*\/\/[[:space:]]*─/ {
s = $0 s = $0
sub(/^[[:space:]]*\/\/[[:space:]]*─+ /, "", s) sub(/^[[:space:]]*\/\/[[:space:]]*/, "", s)
sub(/ ─+[[:space:]]*$/, "", s) sub(/[[:space:]]+$/, "", s)
if (in_table) print "" if (s ~ /─ .*─/) {
print "## " s gsub(/─/, "", s)
print "" gsub(/[[:space:]]+/, " ", s)
print "| Key | Action |" sub(/^ /, "", s); sub(/ $/, "", s)
print "|-----|--------|" if (in_table) print ""
in_table = 1 print "## " s
print ""
print "| Key | Action |"
print "|-----|--------|"
in_table = 1
}
next next
} }
# bind line: KEY [flags] [hotkey-overlay-title="..."] { action; } # bind line: KEY [flags] [hotkey-overlay-title="..."] { action; }
+3 -4
View File
@@ -17,7 +17,8 @@
# otherwise sudo prompts normally. The value is never stored in this repo. # otherwise sudo prompts normally. The value is never stored in this repo.
set -euo pipefail 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}" REPO="${INSTALL_REPO:-$REPO}"
[ -n "$REPO" ] && [ -d "$REPO/packages" ] || { echo "install.sh: no packages/ under ${REPO:-<repo>}" >&2; exit 1; } [ -n "$REPO" ] && [ -d "$REPO/packages" ] || { echo "install.sh: no packages/ under ${REPO:-<repo>}" >&2; exit 1; }
@@ -36,9 +37,7 @@ SUDO() {
else sudo "$@"; fi else sudo "$@"; fi
} }
read_manifest() { # $1 = manifest path; emits package names, strips comments/blanks source "$HERE/lib/manifest.sh" # the same parser pkg-inventory.sh reads with
sed 's/#.*//' "$1" | tr -d '\r' | awk '{if ($1) print $1}'
}
# ---- desired-minus-installed delta ------------------------------------------ # ---- desired-minus-installed delta ------------------------------------------
PKGS=() PKGS=()
for m in common.pkgs "$HOST.pkgs"; do for m in common.pkgs "$HOST.pkgs"; do
+13 -1
View File
@@ -4,7 +4,15 @@ import re, sys
from pathlib import Path from pathlib import Path
kb = Path(sys.argv[1]) kb = Path(sys.argv[1])
issues = [] issues = []
dirs = sorted({p.parent for p in kb.rglob("*.md")})
def kept(p):
# `.AGENTTRASH/` is quarantine, not bundle content: a gate that flags it pushes an
# agent to delete a page that is only waiting for a pruning session with the operator.
return ".AGENTTRASH" not in p.parts
dirs = sorted({p.parent for p in kb.rglob("*.md") if kept(p)})
for d in dirs: for d in dirs:
idx = d / "index.md" idx = d / "index.md"
if not idx.is_file() and d != kb: if not idx.is_file() and d != kb:
@@ -21,12 +29,16 @@ for d in dirs:
# wikilinks too # wikilinks too
wl_names = {m.group(1).strip().rsplit("/", 1)[-1] for m in re.finditer(r"\[\[([^\]|#]+)", text)} wl_names = {m.group(1).strip().rsplit("/", 1)[-1] for m in re.finditer(r"\[\[([^\]|#]+)", text)}
for sib in sorted(d.iterdir()): for sib in sorted(d.iterdir()):
if sib.name == ".AGENTTRASH":
continue
if sib.suffix != ".md" or sib.name == "index.md": if sib.suffix != ".md" or sib.name == "index.md":
continue continue
if sib.resolve() not in linked and sib.stem not in wl_names: if sib.resolve() not in linked and sib.stem not in wl_names:
issues.append(f"{d.relative_to(kb)}/index.md: does not link {sib.name}") issues.append(f"{d.relative_to(kb)}/index.md: does not link {sib.name}")
# subdirs of d linked as their index? # subdirs of d linked as their index?
for sub in sorted(d.iterdir()): for sub in sorted(d.iterdir()):
if sub.name == ".AGENTTRASH":
continue
if sub.is_dir() and (sub / "index.md").is_file(): if sub.is_dir() and (sub / "index.md").is_file():
subidx = (sub / "index.md").resolve() subidx = (sub / "index.md").resolve()
dirref = sub.resolve() dirref = sub.resolve()
+31 -4
View File
@@ -12,7 +12,19 @@ from pathlib import Path
kb = Path(sys.argv[1]) kb = Path(sys.argv[1])
violations = [] violations = []
for p in sorted(kb.rglob("*.md")): def pages():
"""Markdown pages that belong to the bundle. `.AGENTTRASH/` is a quarantine, not
content: linting a deleted-but-not-yet-pruned page would demand its destruction to
make the gate go green, which is the wrong incentive."""
for p in kb.rglob("*.md"):
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) rel = p.relative_to(kb)
text = p.read_text(errors="replace") text = p.read_text(errors="replace")
lines = text.splitlines() lines = text.splitlines()
@@ -38,9 +50,21 @@ for p in sorted(kb.rglob("*.md")):
if not isinstance(fm, dict) or not str(fm.get("type", "")).strip(): if not isinstance(fm, dict) or not str(fm.get("type", "")).strip():
violations.append(f"{rel}: frontmatter lacks required 'type:'") violations.append(f"{rel}: frontmatter lacks required 'type:'")
# One work log per bundle, at the bundle root. A `log.md` is a reserved name, but
# reserved does not mean "allowed in every directory": per-directory logs are how a
# bundle ends up with fifty of them, the entry never where the next agent looks, and
# each copy dating the same event differently. A bundle with no log at all is left to
# the maintenance page to nag about; this gate's job is sprawl.
logs = sorted(str(p.relative_to(kb)) for p in pages() if p.name == "log.md")
if len(logs) > 1 or (logs and logs[0] != "log.md"):
violations.append(
"a bundle has one work log, log.md, at the bundle root; found: " + ", ".join(logs)
)
# link integrity: fenced code blocks hold example paths, not live links — strip first. # link integrity: fenced code blocks hold example paths, not live links — strip first.
# Vendored spec is exempt: its example paths are illustrations, not live links. # Vendored spec is exempt: its example paths are illustrations, not live links.
for p in sorted(kb.rglob("*.md")): for p in pages():
if str(p.relative_to(kb)) == "references/okf-spec.md": if str(p.relative_to(kb)) == "references/okf-spec.md":
continue continue
text = re.sub(r"^```[^\n]*$^```[^\n]*$", "", p.read_text(errors="replace"), flags=re.M) text = re.sub(r"^```[^\n]*$^```[^\n]*$", "", p.read_text(errors="replace"), flags=re.M)
@@ -50,9 +74,12 @@ for p in sorted(kb.rglob("*.md")):
if not Path(target).is_file(): if not Path(target).is_file():
violations.append(f"{rel}: broken relative link -> {m.group(1)}") violations.append(f"{rel}: broken relative link -> {m.group(1)}")
for m in re.finditer(r"\[\[([^\]|#]+)", text): 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] 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()}]]") violations.append(f"{rel}: broken wikilink -> [[{m.group(1).strip()}]]")
for v in violations: for v in violations:
+23
View File
@@ -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
View File
@@ -15,8 +15,12 @@
# #
# usage: pcie-probe.sh [--device N]... [--build-only] [--force] # 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 # --device N measure HIP device ordinal N; repeat for each card. Without it the probes run
# environment's own device selection — one shared run cannot attribute bandwidth. # 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. # --build-only compile and stop; never touches the cards.
# --force measure even though the guard says the GPU is busy. # --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_SYSFS drm sysfs root (/sys/class/drm)
# PCIE_PROBE_KFD KFD device node (/dev/kfd) # PCIE_PROBE_KFD KFD device node (/dev/kfd)
# PCIE_PROBE_HIPCC compiler (hipcc) # PCIE_PROBE_HIPCC compiler (hipcc)
# PCIE_PROBE_FUSER KFD holder probe (fuser)
# PCIE_PROBE_MAX_BUSY refusal threshold, percent (5) # 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 # Exit status: 0 measured · 2 usage or missing sources · 3 REFUSED, GPU busy or unknowable ·
# build failed. # 4 no toolchain or build failed · 5 a probe ran and failed.
set -uo pipefail set -uo pipefail
@@ -39,6 +44,7 @@ BUILD="${PCIE_PROBE_BUILD:-$HOME/.cache/pcie-probe}"
SYSFS="${PCIE_PROBE_SYSFS:-/sys/class/drm}" SYSFS="${PCIE_PROBE_SYSFS:-/sys/class/drm}"
KFD="${PCIE_PROBE_KFD:-/dev/kfd}" KFD="${PCIE_PROBE_KFD:-/dev/kfd}"
HIPCC="${PCIE_PROBE_HIPCC:-hipcc}" HIPCC="${PCIE_PROBE_HIPCC:-hipcc}"
FUSER="${PCIE_PROBE_FUSER:-fuser}"
MAX_BUSY="${PCIE_PROBE_MAX_BUSY:-5}" MAX_BUSY="${PCIE_PROBE_MAX_BUSY:-5}"
force=0 force=0
@@ -63,9 +69,19 @@ fi
# ---- the guard, before anything touches the card -------------------------------- # ---- the guard, before anything touches the card --------------------------------
if (( ! force )); then if (( ! force )); then
busy="" busy=""
if [[ -e "$KFD" ]] && command -v fuser > /dev/null 2>&1; then if [[ -e "$KFD" ]]; then
holders="$(fuser "$KFD" 2>/dev/null | tr -s '[:space:]' ' ')" if command -v "$FUSER" > /dev/null 2>&1; then
[[ -n "${holders// /}" ]] && busy="KFD in use by:${holders}" 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 fi
for card in "$SYSFS"/card*; do for card in "$SYSFS"/card*; do
[[ -f "$card/device/gpu_busy_percent" ]] || continue [[ -f "$card/device/gpu_busy_percent" ]] || continue
@@ -111,7 +127,11 @@ for card in "$SYSFS"/card*; do
done done
# ---- measure, per device -------------------------------------------------------- # ---- 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) (( ${#devices[@]} > 0 )) || devices=(default)
failures=()
for d in "${devices[@]}"; do for d in "${devices[@]}"; do
if [[ $d == default ]]; then if [[ $d == default ]]; then
printf '\n########## engine device selection ##########\n' 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 for name in hip_smoke pcie_bw pcie_probe; do
bin="$BUILD/$name" bin="$BUILD/$name"
[[ -x "$bin" ]] || continue [[ -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
done done
if (( ${#failures[@]} > 0 )); then
printf 'pcie-probe: %d probe(s) failed:\n' "${#failures[@]}" >&2
printf ' %s\n' "${failures[@]}" >&2
exit 5
fi
+13 -6
View File
@@ -19,11 +19,19 @@
# aur (needs a helper): <names> → belongs in packages/aur.pkgs (yay) # aur (needs a helper): <names> → belongs in packages/aur.pkgs (yay)
# installer-provided, undeclared: N a reinstall supplies these; do not declare # installer-provided, undeclared: N a reinstall supplies these; do not declare
# declared, not installed: N install.sh installs these # 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 # 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 # --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. # 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 # 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 # (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 while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--emit) MODE="emit"; shift ;; --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 ;; *) echo "usage: pkg-inventory.sh [--emit]" >&2; exit 2 ;;
esac esac
done done
HERE="$(dirname "$(realpath "$0")")"
REPO="${PKGINV_REPO:-}" REPO="${PKGINV_REPO:-}"
if [[ -z "$REPO" ]]; then 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 fi
[[ -n "$REPO" && -d "$REPO/packages" ]] || { echo "pkg-inventory: no packages/ under ${REPO:-<repo>}" >&2; exit 2; } [[ -n "$REPO" && -d "$REPO/packages" ]] || { echo "pkg-inventory: no packages/ under ${REPO:-<repo>}" >&2; exit 2; }
HOST="${PKGINV_HOST:-$(hostname)}" HOST="${PKGINV_HOST:-$(hostname)}"
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
read_manifest() { # same parser install.sh uses, so a name means the same thing to both source "$HERE/lib/manifest.sh" # the same parser install.sh installs with
sed 's/#.*//' "$1" | tr -d '\r' | awk '{if ($1) print $1}'
}
# ---- installed state --------------------------------------------------------- # ---- installed state ---------------------------------------------------------
# Four queries are the tool's whole interface to pacman. -Sl is the unprivileged sync listing: # 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 # -Sqq asks for root, and a read of the machine must not need sudo. Field 2 of -Sl is the package
+20 -6
View File
@@ -51,13 +51,21 @@ case "${1:-}" in
;; ;;
status) 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 if [[ -n "${2:-}" ]]; then
names="$2" read -r -a names <<< "$2"
else 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 fi
if [[ ${#names[@]} -eq 0 ]]; then echo " (no jobs)"; exit 0; fi
pending=0 pending=0
for n in $names; do for n in "${names[@]}"; do
if [[ -f "$JOBS/$n.done" ]]; then if [[ -f "$JOBS/$n.done" ]]; then
printf ' %-20s DONE rc=%s %s lines\n' "$n" \ 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 ' ')" "$(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 pending=1
fi fi
done done
if [[ -z "$names" ]]; then echo " (no jobs)"; exit 0; fi
exit "$pending" exit "$pending"
;; ;;
@@ -95,8 +102,15 @@ case "${1:-}" in
tail) tail)
# Multiplexed follow: no NAME means every job at once, so one pane shows the whole harness. # 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 # Both branches check for something to follow first: an unexpanded `*.out` handed to tail
exec tail -f "$JOBS"/*.out # 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) watch)
+59 -1
View File
@@ -37,7 +37,22 @@ if [ "$2" = is-enabled ]; then
printf '%s\n' "$state" printf '%s\n' "$state"
case "$state" in disabled | "") exit 1 ;; *) exit 0 ;; esac case "$state" in disabled | "") exit 1 ;; *) exit 0 ;; esac
fi 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 STUB
chmod +x "$TEST_TMP/bin/systemctl" chmod +x "$TEST_TMP/bin/systemctl"
export PATH="$TEST_TMP/bin:$PATH" export PATH="$TEST_TMP/bin:$PATH"
@@ -57,6 +72,21 @@ systemd_unit() {
> "$REPO/dotfiles/systemd-user/.config/systemd/user/$1" > "$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" { @test "generated theme output is never adopted into a symlink" {
repo_file "ghostty/.config/ghostty/themes/noctalia" 'background = "#f43841"' repo_file "ghostty/.config/ghostty/themes/noctalia" 'background = "#f43841"'
live_file "$HOME/.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 'systemctl --user enable heartbeat.service' "$STUB_LOG"
! grep -q -- '--now' "$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"
}
+10
View File
@@ -48,6 +48,16 @@ teardown() {
[[ "$output" != *"a plain comment"* ]] [[ "$output" != *"a plain comment"* ]]
} }
@test "section headers survive a C locale, where awk reads bytes instead of characters" {
# Cron, systemd units and agent shells get LC_ALL=C while Frank's terminal does not.
# A matcher that puts a quantifier on a multibyte character then loses EVERY section
# header and prints a sheet with no sections at all — silently, exit status 0.
run env LC_ALL=C "$GEN" "$TMP/keybinds.kdl"
[[ "$output" == *"## Applications"* ]]
[[ "$output" == *"## Screenshots"* ]]
[[ "$output" != *"a plain comment"* ]]
}
@test "row count matches bind count" { @test "row count matches bind count" {
run "$GEN" "$TMP/keybinds.kdl" run "$GEN" "$TMP/keybinds.kdl"
local rows local rows
+49
View File
@@ -0,0 +1,49 @@
#!/usr/bin/env bats
# tests/kb-coverage.bats — black-box tests for scripts/kb-coverage.py
#
# Contract: exit 0 when every directory of a knowledge bundle has an index.md and each
# index links its siblings. Observable surface: stdout lines and exit status.
setup() {
CHECK="$BATS_TEST_DIRNAME/../scripts/kb-coverage.py"
KB="$(mktemp -d)/kb"
mkdir -p "$KB/sub"
printf '# Index\n\n* [Good](good.md)\n* [Sub](sub/)\n' > "$KB/index.md"
printf '# Sub\n\n* [Deep](deep.md)\n' > "$KB/sub/index.md"
printf 'body\n' > "$KB/good.md"
printf 'body\n' > "$KB/sub/deep.md"
}
teardown() { rm -rf "$(dirname "$KB")"; }
@test "a bundle whose directories index their contents passes" {
run python3 "$CHECK" "$KB"
[ "$status" -eq 0 ]
[[ "$output" == *"0 issue(s)"* ]]
}
@test "a directory nobody indexes is reported" {
mkdir "$KB/stray"
printf 'body\n' > "$KB/stray/thing.md"
run python3 "$CHECK" "$KB"
[ "$status" -ne 0 ]
[[ "$output" == *"stray: no index.md"* ]]
}
@test "an index that forgets a sibling page is reported" {
printf 'body\n' > "$KB/orphan.md"
run python3 "$CHECK" "$KB"
[ "$status" -ne 0 ]
[[ "$output" == *"does not link orphan.md"* ]]
}
@test "the quarantine trash directory is not part of the bundle" {
# Pages deleted from the KB wait in .AGENTTRASH/ for a pruning session with Frank.
# Requiring an index there, or listing its contents, would push an agent to delete
# the quarantined copy just to make the gate green.
mkdir -p "$KB/.AGENTTRASH"
printf 'retired page\n' > "$KB/.AGENTTRASH/old-page.md"
run python3 "$CHECK" "$KB"
[ "$status" -eq 0 ]
[[ "$output" == *"0 issue(s)"* ]]
}
+51
View File
@@ -91,6 +91,57 @@ EOF
[ "$status" -eq 2 ] [ "$status" -eq 2 ]
} }
@test "a second work log in a subdirectory is reported" {
# One bundle, one work log. Per-directory logs are how a bundle ends up with fifty
# of them: the entry is never where the next agent looks, and each copy re-dates the
# same event on its own.
printf -- '---\ntype: changelog\n---\n## 2026-01-01\n- created a log\n' > "$KB/log.md"
printf -- '---\ntype: changelog\n---\n## 2026-01-02\n- also a log\n' > "$KB/sub/log.md"
run bash "$LINT" "$KB"
[ "$status" -ne 0 ]
[[ "$output" == *"one work log"*"at the bundle root"* ]]
[[ "$output" == *"sub/log.md"* ]]
}
@test "a work log that is not at the bundle root is reported" {
printf -- '---\ntype: changelog\n---\n## 2026-01-02\n- misplaced log\n' > "$KB/sub/log.md"
run bash "$LINT" "$KB"
[ "$status" -ne 0 ]
[[ "$output" == *"one work log"*"at the bundle root"* ]]
}
@test "the quarantine trash directory is not part of the bundle" {
# Deleted pages wait in .AGENTTRASH/ for a pruning session with Frank. Holding them
# to frontmatter and one-log rules would push an agent to delete history to go green.
mkdir -p "$KB/.AGENTTRASH"
printf 'no frontmatter, and a second log besides\n' > "$KB/.AGENTTRASH/log.md"
run bash "$LINT" "$KB"
[ "$status" -eq 0 ]
[[ "$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" { @test "the shipped knowledge base itself is conformant" {
# The tests above exercise the linter against fixtures; nothing else in the # 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 # suite ever points it at the real bundle, so a page added without frontmatter
+51
View File
@@ -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
View File
@@ -6,7 +6,8 @@
# built or run, and the tool stops nothing and starts nothing (auto-stopping a production service is # built or run, and the tool stops nothing and starts nothing (auto-stopping a production service is
# the thing it must not do unasked). # 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 # 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. # 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" : > "$PCIE_PROBE_BUILD/.built"
{ echo '#!/usr/bin/env bash' { echo '#!/usr/bin/env bash'
echo ': > "$PCIE_PROBE_BUILD/.ran"' 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"' echo 'echo " slab 2.344 MiB: 31.4 GB/s"'
} > "$out" } > "$out"
chmod +x "$out" chmod +x "$out"
@@ -131,6 +141,35 @@ teardown() { rm -rf "$TEST_TMP"; }
[[ "$output" == *"dev=1"* ]] [[ "$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" { @test "no probe sources is a usage-level error, not an empty success" {
rm -f "$SRC"/*.hip rm -f "$SRC"/*.hip
run "$SCRIPT" run "$SCRIPT"
+32 -6
View File
@@ -39,15 +39,18 @@ EOF
[2026-03-04T09:00:00-0800] [ALPM] installed bun (1.2-1) [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-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-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) [2026-03-06T09:00:00-0800] [ALPM] removed base (3-1)
EOF EOF
# --- installed state -------------------------------------------------------- # --- installed state --------------------------------------------------------
# ghostty/zoxide/neovim/typora declared. libfoo: explicit, arrived with the installer. # ghostty/zoxide/neovim/typora declared. libfoo: explicit, arrived with the installer.
# bun/nirimod-git: installed by hand afterwards, undeclared. python-hatchling: orphan. # bun/nirimod-git: installed by hand afterwards, undeclared. python-hatchling and libstray:
printf '%s\n' base bun filesystem ghostty libfoo neovim nirimod-git typora zoxide > "$TEST_TMP/local-all" # 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' 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 # 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. # 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" 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* ]] [[ "$(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" 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 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" { @test "--baseline moves packages installed on that date into the installer set" {
+25
View File
@@ -122,6 +122,31 @@ until_done() {
[[ "$output" == *38* ]] # the tail, not the head [[ "$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" { @test "an unknown verb is a usage error" {
run "$SCRIPT" frobnicate run "$SCRIPT" frobnicate
[ "$status" -eq 2 ] [ "$status" -eq 2 ]
+45
View File
@@ -157,3 +157,48 @@ export API_KEY_FILE=/run/secrets/api_key'
[ "$status" -ne 0 ] [ "$status" -ne 0 ]
[[ "$output" == *"tests/other.bats"* ]] [[ "$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"* ]]
}