From 4735702965226f6b13caee31e55a0050da375f56 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:10:37 -0700 Subject: [PATCH 01/16] fix(kb-lint): a markdown link may not resolve into the quarantine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The link-integrity pass ended in `is_file()`, and `.AGENTTRASH/` is a real directory on this machine: a page pointing at `[x](.AGENTTRASH/gone.md)` passed the gate here and was a dead link everywhere else, because the quarantine is gitignored. This is incident #57 — the wikilink resolver resolving `[[log]]` onto a quarantined copy — reproduced in markdown clothing, which is to say: fixing one resolver while leaving the identical assumption in the other one is not a fix. A link that lands inside the bundle must land on a page that belongs to the bundle. A link that leaves the bundle is still honoured (`tools/live-sync.md` -> `../../AGENTS.md` points at the operating contract, which is not bundle content and is real on disk). Red test added first, both link spellings covered; nothing previously green changed. Refs #64 --- scripts/kb-lint.sh | 13 +++++++++++-- tests/kb-lint.bats | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/kb-lint.sh b/scripts/kb-lint.sh index 4b8b933..72dd330 100755 --- a/scripts/kb-lint.sh +++ b/scripts/kb-lint.sh @@ -23,6 +23,7 @@ def pages(): # 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()} +live_paths = {p.resolve() for p in pages()} for p in pages(): rel = p.relative_to(kb) @@ -70,8 +71,16 @@ for p in pages(): text = re.sub(r"^```[^\n]*$^```[^\n]*$", "", p.read_text(errors="replace"), flags=re.M) rel = p.relative_to(kb) for m in re.finditer(r"\]\((?!https?:|#)([^)#\s]+\.md)", text): - target = (p.parent / m.group(1)).resolve() if not m.group(1).startswith("/") else (kb / m.group(1).lstrip("/")) - if not Path(target).is_file(): + target = (p.parent / m.group(1)).resolve() if not m.group(1).startswith("/") else (kb / m.group(1).lstrip("/")).resolve() + # A link INSIDE the bundle must land on a page that belongs to the bundle: `is_file()` + # alone also accepted a target parked in .AGENTTRASH/, which is gitignored — green here, + # a dead link on every machine that pulls (#57 again, in markdown clothing). A link that + # leaves the bundle is allowed: ../../AGENTS.md is the operating contract, not bundle + # content, and it is real on disk. + if kb in target.parents: + if target not in live_paths: + violations.append(f"{rel}: broken relative link -> {m.group(1)}") + elif not target.is_file(): violations.append(f"{rel}: broken relative link -> {m.group(1)}") for m in re.finditer(r"\[\[([^\]|#]+)", text): # wikilinks resolve by filename anywhere in the KB (Obsidian semantics) — but only diff --git a/tests/kb-lint.bats b/tests/kb-lint.bats index cc8dfc8..9f87af6 100755 --- a/tests/kb-lint.bats +++ b/tests/kb-lint.bats @@ -134,6 +134,21 @@ EOF [[ "$output" == *"broken wikilink -> [[log]]"* ]] } +@test "a markdown link into the quarantine is broken" { + # #57's failure mode wearing markdown instead of brackets: the relative-link check was a + # bare is_file(), so a link that lands on a page waiting in .AGENTTRASH/ passes this gate + # and points at nothing on every machine that pulls — the quarantine is gitignored. Both + # spellings count: relative, and bundle-absolute with a leading slash. + mkdir -p "$KB/.AGENTTRASH" + printf 'no frontmatter, quarantined\n' > "$KB/.AGENTTRASH/gone.md" + printf -- '---\ntype: reference\n---\nRelative: [gone](.AGENTTRASH/gone.md)\n' > "$KB/rel.md" + printf -- '---\ntype: reference\n---\nAbsolute: [gone](/.AGENTTRASH/gone.md)\n' > "$KB/abs.md" + run bash "$LINT" "$KB" + [ "$status" -ne 0 ] + [[ "$output" == *"rel.md: broken relative link -> .AGENTTRASH/gone.md"* ]] + [[ "$output" == *"abs.md: broken relative link -> /.AGENTTRASH/gone.md"* ]] +} + @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" From 62a70caebb9a12381f706973059f21974ec98545 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:11:56 -0700 Subject: [PATCH 02/16] docs(okf): 53 Obsidian wikilinks become OKF links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `[[page|Label]]` is Obsidian syntax, not OKF. `kb-lint` resolves it Obsidian-style — by filename, anywhere in the bundle — so it has never tripped the link gate, while every other consumer of the bundle (an OKF renderer, a Gitea wiki export, `grep` for reachability) sees bracketed text where a link should be. That is how two generated pages ended up with no inbound link at all and nobody noticed: the my-first link census of the bundle reported them as orphans because their only inbound edges were wikilinks. Mechanical conversion, 52 links across 17 pages, each resolved against the same page set `kb-lint` uses: an explicit `|Label` is kept verbatim, an unlabelled link takes the target's frontmatter `title`. Targets are emitted bundle-absolute (`/tools/niri/tips.md`), the form the spec recommends and the form 99 existing links in this bundle already use. Two links were `[[index]]`, which resolves by filename to 21 pages — the ambiguity that filename-resolution invites. Both meant their own directory's index, so both are now written out in full: `/tools/gitea/index.md`, `/tools/niri/index.md`. The linter still tolerates wikilinks; whether it should reject them outright is an open question on #64, because that would replace the two tests pinning today's tolerance. Refs #64 --- omp-docs/core/settings.md | 6 +++--- omp-docs/pi/models.md | 10 +++++----- omp-docs/pi/packages/pi-subagents.md | 4 ++-- omp-docs/system/dankclaw.md | 4 ++-- omp-docs/system/dankserver.md | 4 ++-- omp-docs/tools/applications.md | 4 ++-- omp-docs/tools/cachyos.md | 4 ++-- omp-docs/tools/gitea/architecture.md | 4 ++-- omp-docs/tools/gitea/client-access.md | 8 ++++---- omp-docs/tools/gitea/data-persistence.md | 4 ++-- omp-docs/tools/gitea/index.md | 4 ++-- omp-docs/tools/gitea/management.md | 8 ++++---- omp-docs/tools/handy.md | 4 ++-- omp-docs/tools/niri/displays.md | 12 ++++++------ omp-docs/tools/niri/index.md | 20 ++++++++++---------- omp-docs/tools/niri/screenshots.md | 6 +++--- omp-docs/tools/noctalia-shell.md | 4 ++-- 17 files changed, 55 insertions(+), 55 deletions(-) diff --git a/omp-docs/core/settings.md b/omp-docs/core/settings.md index 2fbadfa..a93101d 100644 --- a/omp-docs/core/settings.md +++ b/omp-docs/core/settings.md @@ -187,7 +187,7 @@ Currently configured providers: | `llamacpp (dankserver)` | `http://192.168.0.235:7770/v1` | `frank-default-small` (Qwen3.6-35B-A3B FP8, moe-cache 2048) | RTX 3090 | | `llamacpp (dankclaw)` | `http://192.168.0.143:7770/v1` | `frank-default-large` (Qwen3.8-Flash-Next Q4_0 ROCmFP4 Strix) | RTX 4070 Max-Q | -See [[models|models]] for the complete `models.json` reference. +See [models](/pi/models.md) for the complete `models.json` reference. --- @@ -235,6 +235,6 @@ When `lastChangelogVersion` is behind pi's internal version, pi prompts for migr ## See Also - [pi](../pi/index.md) — Pi configuration overview -- [[models|models]] — Provider and model definitions +- [models](/pi/models.md) — Provider and model definitions - [Pi Packages](/pi/packages/index.md) — Installed package documentation -- [[skills|skills]] — Installed skill documentation +- [skills](/pi/skills.md) — Installed skill documentation diff --git a/omp-docs/pi/models.md b/omp-docs/pi/models.md index 6039aaf..9564493 100644 --- a/omp-docs/pi/models.md +++ b/omp-docs/pi/models.md @@ -31,7 +31,7 @@ Current providers in `models.json` (verified 2026-08-27, after the | **Base URL** | `http://192.168.0.235:7770/v1` | | **API** | `openai-completions` | | **Auth** | None (EMPTY) | -| **Host** | [[dankserver|dankserver]] | +| **Host** | [dankserver](/system/dankserver.md) | #### Models @@ -52,7 +52,7 @@ value succeed. | **Base URL** | `http://192.168.0.143:7770/v1` | | **API** | `openai-completions` | | **Auth** | None (EMPTY) | -| **Host** | [[dankclaw|dankclaw]] | +| **Host** | [dankclaw](/system/dankclaw.md) | > 2026-08-22: `frank-default-large` moved from `:52625` to `:7770` on > Dankclaw (operator request: the primary inference server sits on the @@ -142,6 +142,6 @@ The subagent model IDs (`frank-default-small`, `frank-default-large`) resolve ag ## See Also -- [[settings|settings]] — Main configuration file -- [[dankserver|dankserver]] — Primary llama.cpp server -- [[dankclaw|dankclaw]] — Secondary llama.cpp server +- [settings](/core/settings.md) — Main configuration file +- [dankserver](/system/dankserver.md) — Primary llama.cpp server +- [dankclaw](/system/dankclaw.md) — Secondary llama.cpp server diff --git a/omp-docs/pi/packages/pi-subagents.md b/omp-docs/pi/packages/pi-subagents.md index 7914a54..91f0b5f 100644 --- a/omp-docs/pi/packages/pi-subagents.md +++ b/omp-docs/pi/packages/pi-subagents.md @@ -64,5 +64,5 @@ No per-model limit exists. All caps are global to a run or a session. They do no ## See Also -- [[settings|settings]] — Subagent configuration -- [[models|models]] — Model definitions +- [settings](/core/settings.md) — Subagent configuration +- [models](/pi/models.md) — Model definitions diff --git a/omp-docs/system/dankclaw.md b/omp-docs/system/dankclaw.md index 18bf01c..64b2c6f 100644 --- a/omp-docs/system/dankclaw.md +++ b/omp-docs/system/dankclaw.md @@ -88,6 +88,6 @@ live `free` reading. ## See Also -- [[dankserver|dankserver]] — the main-model box (frank-archon) +- [dankserver](/system/dankserver.md) — the main-model box (frank-archon) - [/tools/gitea/index.md](/tools/gitea/index.md) — Gitea stack on this host -- [[network|network]] — Network topology +- [network](/system/network.md) — Network topology diff --git a/omp-docs/system/dankserver.md b/omp-docs/system/dankserver.md index e6b67c2..a96588c 100644 --- a/omp-docs/system/dankserver.md +++ b/omp-docs/system/dankserver.md @@ -84,6 +84,6 @@ The old page (verified 2026-08-28) listed an RTX 3090, 60 GiB RAM, and a ## See Also -- [[dankclaw|dankclaw]] — the other inference box (dragoon + zealot) -- [[network|network]] — Network topology +- [dankclaw](/system/dankclaw.md) — the other inference box (dragoon + zealot) +- [network](/system/network.md) — Network topology - [/core/statusline.md](/core/statusline.md) — where these models surface in the TUI diff --git a/omp-docs/tools/applications.md b/omp-docs/tools/applications.md index 3802392..0b5eaef 100644 --- a/omp-docs/tools/applications.md +++ b/omp-docs/tools/applications.md @@ -70,7 +70,7 @@ graph LR ## See Also -- [[cachyos|cachyos]] — Operating system documentation -- [[noctalia-shell|noctalia-shell]] — Desktop shell +- [cachyos](/tools/cachyos.md) — Operating system documentation +- [noctalia-shell](/tools/noctalia-shell.md) — Desktop shell - [niri](niri/index.md) — Compositor - ](ghostty/index.md) — Terminal configuration diff --git a/omp-docs/tools/cachyos.md b/omp-docs/tools/cachyos.md index 28e0310..1b28ae8 100644 --- a/omp-docs/tools/cachyos.md +++ b/omp-docs/tools/cachyos.md @@ -108,5 +108,5 @@ CachyOS with Niri ships with a curated set of applications: - ](../system/index.md) — system overview - [niri](niri/index.md) — Compositor documentation -- [[noctalia-shell|noctalia-shell]] — Desktop shell documentation -- [[applications|applications]] — Application reference +- [noctalia-shell](/tools/noctalia-shell.md) — Desktop shell documentation +- [applications](/tools/applications.md) — Application reference diff --git a/omp-docs/tools/gitea/architecture.md b/omp-docs/tools/gitea/architecture.md index a7f5678..0304f94 100644 --- a/omp-docs/tools/gitea/architecture.md +++ b/omp-docs/tools/gitea/architecture.md @@ -83,5 +83,5 @@ Note: the full-stack repo compose (`dankclaw/compose.yaml`) maps Gitea HTTP to h ## See Also - ](index.md) — index with management commands -- [[management|management]] — operations, boot persistence, incident -- [[data-persistence|data-persistence]] — volumes and backups +- [management](/tools/gitea/management.md) — operations, boot persistence, incident +- [data-persistence](/tools/gitea/data-persistence.md) — volumes and backups diff --git a/omp-docs/tools/gitea/client-access.md b/omp-docs/tools/gitea/client-access.md index 40f2ac4..fb8b255 100644 --- a/omp-docs/tools/gitea/client-access.md +++ b/omp-docs/tools/gitea/client-access.md @@ -14,7 +14,7 @@ verified: 2026-10-09 Clients (workstations, agent hosts) reach Gitea over plain HTTP on the LAN: `http://192.168.0.143:3000/dank/.git`. Authentication is a Gitea HTTP token sent as the password for user `dank`. SSH is available on `:2222` (see -[[architecture|architecture]]); HTTP + token is what the agent hosts use. +[architecture](/tools/gitea/architecture.md)); HTTP + token is what the agent hosts use. ## The law: the token never enters a URL, a config file, or a shell rc @@ -93,6 +93,6 @@ until it is deleted in the UI, so delete it there as part of the rotation. ## See Also -- [[index|index]] — Gitea on dankclaw -- [[management|management]] — server-side operations -- [[architecture|architecture]] — ports (3000 HTTP, 2222 SSH) and the Caddy front +- [Gitea directory index](/tools/gitea/index.md) — Gitea on dankclaw +- [management](/tools/gitea/management.md) — server-side operations +- [architecture](/tools/gitea/architecture.md) — ports (3000 HTTP, 2222 SSH) and the Caddy front diff --git a/omp-docs/tools/gitea/data-persistence.md b/omp-docs/tools/gitea/data-persistence.md index 07261a6..5a9e078 100644 --- a/omp-docs/tools/gitea/data-persistence.md +++ b/omp-docs/tools/gitea/data-persistence.md @@ -64,5 +64,5 @@ Set `PGPASSWORD` from `/home/dank/Containers/gitea/env` (mode 0600, `GITEA_DB_PA ## See Also - ](index.md) — index with management commands -- [[management|management]] — operations and boot persistence -- [[architecture|architecture]] — network and ports +- [management](/tools/gitea/management.md) — operations and boot persistence +- [architecture](/tools/gitea/architecture.md) — network and ports diff --git a/omp-docs/tools/gitea/index.md b/omp-docs/tools/gitea/index.md index 2ec5e7a..128d095 100644 --- a/omp-docs/tools/gitea/index.md +++ b/omp-docs/tools/gitea/index.md @@ -39,13 +39,13 @@ All data lives on host bind mounts under `/home/dank/Containers/gitea/` (fleet s | `/home/dank/Containers/gitea/postgres/` | PostgreSQL data files | | `/home/dank/Containers/gitea/env` | EnvironmentFile (mode 0600; `GITEA_DB_PASSWORD`) | -See [[data-persistence|data-persistence]] for backup and restore. +See [data-persistence](/tools/gitea/data-persistence.md) for backup and restore. ## Boot Persistence User-level linger is enabled for `dank`. The units start at host boot without a login. Each unit carries `[Install] WantedBy=default.target`; the podman generator applies it. The legacy podman-compose stack had no units and did not survive reboots (incident 2026-08-16). -See [[management|management]] for boot persistence details and troubleshooting. +See [management](/tools/gitea/management.md) for boot persistence details and troubleshooting. ## Repo Copies diff --git a/omp-docs/tools/gitea/management.md b/omp-docs/tools/gitea/management.md index 3fdb118..ed191ea 100644 --- a/omp-docs/tools/gitea/management.md +++ b/omp-docs/tools/gitea/management.md @@ -100,7 +100,7 @@ Verify the fix on the next host reboot. ## See Also - ](index.md) — index -- [[client-access|client-access]] — how client hosts authenticate (token + credential store) -- [[architecture|architecture]] — network topology -- [[data-persistence|data-persistence]] — backups -- [[containers|containers]] — quadlet reference +- [client-access](/tools/gitea/client-access.md) — how client hosts authenticate (token + credential store) +- [architecture](/tools/gitea/architecture.md) — network topology +- [data-persistence](/tools/gitea/data-persistence.md) — backups +- [containers](/engineering/infra/containers.md) — quadlet reference diff --git a/omp-docs/tools/handy.md b/omp-docs/tools/handy.md index 0351565..a049106 100644 --- a/omp-docs/tools/handy.md +++ b/omp-docs/tools/handy.md @@ -222,12 +222,12 @@ curl -s http://192.168.0.143:7771/v1/chat/completions -d '{ "chat_template_kwargs": {"enable_thinking": false}}' ``` -See [[noctalia-shell]] for IPC integration patterns. +See [Noctalia Shell](/tools/noctalia-shell.md) for IPC integration patterns. --- ## See Also -- [[noctalia-shell|noctalia-shell.md]] — Desktop environment integration +- [noctalia-shell.md](/tools/noctalia-shell.md) — Desktop environment integration - [niri](niri/index.md) — Compositor configuration - [`handy --help`](https://github.com/cjpais/Handy) — CLI reference diff --git a/omp-docs/tools/niri/displays.md b/omp-docs/tools/niri/displays.md index 6d7a41b..364aece 100644 --- a/omp-docs/tools/niri/displays.md +++ b/omp-docs/tools/niri/displays.md @@ -178,17 +178,17 @@ The `environment { render-node "/dev/dri/renderD129" }` pin found in the hybrid; pin kept the iGPU as display owner and sidesteps the suspend/resume deadlock documented in the sleep-wake investigation). Deploying that base file on blankpad pins a render node that does not exist — this is why -blankpad runs the `zz-blankpad` override layer (see [[index|the per-host -symlink matrix]]). +blankpad runs the `zz-blankpad` override layer (see [the per-host symlink +matrix](/tools/niri/index.md)). ## Reload `niri msg action load-config-file` reloads live. Never `SIGHUP` the process -— see [[tips]]. +— see [Niri Tips and Gotchas](/tools/niri/tips.md). ## See Also - [Niri](/tools/niri/index.md) — compositor overview and config layout -- [[tips|tips]] — reload path, `niri msg layers/casts`, includes -- [[noctalia-shell|noctalia-shell]] — notification shown on desk switch -- [[cachyos|cachyos]] — operating system +- [tips](/tools/niri/tips.md) — reload path, `niri msg layers/casts`, includes +- [noctalia-shell](/tools/noctalia-shell.md) — notification shown on desk switch +- [cachyos](/tools/cachyos.md) — operating system diff --git a/omp-docs/tools/niri/index.md b/omp-docs/tools/niri/index.md index 932bef0..06fad4c 100644 --- a/omp-docs/tools/niri/index.md +++ b/omp-docs/tools/niri/index.md @@ -85,18 +85,18 @@ Column-based scrollable tiling. Columns are cycled with Mod+Left/Right (or Mod+H | File | Type | Description | |------|------|-------------| -| [[action-reference|action-reference.md]] | reference | Curated reference of niri actions | -| [[default-bindings|default-bindings.md]] | reference | Default key bindings organized by category | -| [[key-bindings-syntax|key-bindings-syntax.md]] | reference | KDL configuration syntax for key bindings | -| [[tips|tips.md]] | howto | Practical tips for working with niri | -| [[displays|displays.md]] | reference | Desk-profile system: EDID-keyed outputs, hotplug auto-switch, portrait transforms, GPU notes | -| [[keybindings|keybindings.md]] | reference | GENERATED keybinding cheat sheet (scripts/gen-keybinds.sh) | -| [[screenshots|screenshots.md]] | reference | Screenshot bind matrix: flameshot vs niri-native, 2+ output limits, all-monitor capture | +| [action-reference.md](/tools/niri/action-reference.md) | reference | Curated reference of niri actions | +| [default-bindings.md](/tools/niri/default-bindings.md) | reference | Default key bindings organized by category | +| [key-bindings-syntax.md](/tools/niri/key-bindings-syntax.md) | reference | KDL configuration syntax for key bindings | +| [tips.md](/tools/niri/tips.md) | howto | Practical tips for working with niri | +| [displays.md](/tools/niri/displays.md) | reference | Desk-profile system: EDID-keyed outputs, hotplug auto-switch, portrait transforms, GPU notes | +| [keybindings.md](/tools/niri/keybindings.md) | reference | GENERATED keybinding cheat sheet (scripts/gen-keybinds.sh) | +| [screenshots.md](/tools/niri/screenshots.md) | reference | Screenshot bind matrix: flameshot vs niri-native, 2+ output limits, all-monitor capture | --- ## See Also -- [[noctalia-shell|noctalia-shell]] — Desktop shell integration -- [[displays|displays]] — Multi-monitor desk rig configuration -- [[cachyos|cachyos]] — Operating system +- [noctalia-shell](/tools/noctalia-shell.md) — Desktop shell integration +- [displays](/tools/niri/displays.md) — Multi-monitor desk rig configuration +- [cachyos](/tools/cachyos.md) — Operating system diff --git a/omp-docs/tools/niri/screenshots.md b/omp-docs/tools/niri/screenshots.md index a148834..adfa262 100644 --- a/omp-docs/tools/niri/screenshots.md +++ b/omp-docs/tools/niri/screenshots.md @@ -75,6 +75,6 @@ tr '\0' '\n' < /proc/$(pgrep -x niri | head -1)/environ | grep ^PATH # spawn-s ## See Also -- [[displays|displays.md]] — desk profiles: 1 output portable vs 2 docked -- [[keybindings|keybindings.md]] — generated cheat sheet (source of the binds) -- [[noctalia-shell|noctalia-shell]] — shell settings on Mod+Shift+O +- [displays.md](/tools/niri/displays.md) — desk profiles: 1 output portable vs 2 docked +- [keybindings.md](/tools/niri/keybindings.md) — generated cheat sheet (source of the binds) +- [noctalia-shell](/tools/noctalia-shell.md) — shell settings on Mod+Shift+O diff --git a/omp-docs/tools/noctalia-shell.md b/omp-docs/tools/noctalia-shell.md index ad8a265..9bae4f2 100644 --- a/omp-docs/tools/noctalia-shell.md +++ b/omp-docs/tools/noctalia-shell.md @@ -434,6 +434,6 @@ to document). A dependency table is a claim about the machine: check it before w ## See Also - [niri](niri/index.md) — Compositor configuration -- [[displays|displays]] — Display layout -- [[cachyos|cachyos]] — Operating system documentation +- [displays](/tools/niri/displays.md) — Display layout +- [cachyos](/tools/cachyos.md) — Operating system documentation - [`noctalia msg --help`](https://github.com/Noctalia/noctalia) — Full command reference From e49537c2ac9f05dacca49b3cf0c0e4d670e4a201 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:14:39 -0700 Subject: [PATCH 03/16] docs(caddy): where the live Caddyfile is, and what the syntax page's claims cost MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The page explained matcher ordering beautifully and never said where the file lives — so the next agent hunts for a Caddyfile on the host and finds nothing. Measured on Dankclaw 2026-10-21: Caddy is a rootless Quadlet container (`caddy.container`, a *user* unit, so a root `systemctl is-active caddy` answers `inactive` and means nothing), its config is `/etc/caddy/Caddyfile` **inside the container**, `/etc/caddy` does not exist on the host, and `pgrep -a caddy` prints a config path that no host path satisfies. Version is v2.11.7, which closes the one version-dependent rule on the page: the leading-`/` `rewrite` form needs 2.8.0+, so the `*` disambiguator is legacy in this fleet's config. Also: the seven caddyserver.com links sat in a mid-page "Sources:" paragraph. OKF wants external claims under `# Citations`, so they move there and each entry says which claim it supports. The validate section now points at the container instead of telling you to run the validator on a file that is not there. Refs #64 --- omp-docs/caddy/caddyfile.md | 62 ++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/omp-docs/caddy/caddyfile.md b/omp-docs/caddy/caddyfile.md index 67f97e4..859bd33 100644 --- a/omp-docs/caddy/caddyfile.md +++ b/omp-docs/caddy/caddyfile.md @@ -1,13 +1,13 @@ --- type: reference title: Caddyfile Syntax -description: Caddy Caddyfile reference — directive and matcher syntax, named matchers, directive sorting, and the handle/route order rule. Grounded in caddyserver.com (fetched 2026-08-28). +description: Caddy Caddyfile reference — the syntax that bites (matchers, directive sorting, handle vs route), plus where the fleet's live Caddyfile actually is and how to validate it. resource: https://caddyserver.com/docs/caddyfile tags: - caddy - caddyfile - reverse-proxy -verified: 2026-08-28 +verified: 2026-10-21 --- # Caddyfile Syntax @@ -17,13 +17,28 @@ port). Directives inside the block control how Caddy serves requests. Matchers classify requests. This page records the syntax that trips people up, grounded in the official docs. -Sources: [caddyfile-tutorial](https://caddyserver.com/docs/caddyfile-tutorial), -[matchers](https://caddyserver.com/docs/caddyfile/matchers), -[rewrite](https://caddyserver.com/docs/caddyfile/directives/rewrite), -[handle](https://caddyserver.com/docs/caddyfile/directives/handle), -[forward_auth](https://caddyserver.com/docs/caddyfile/directives/forward_auth), -[reverse_proxy](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy), -[concepts](https://caddyserver.com/docs/caddyfile/concepts). +The syntax below is the parts that bite; the sources are listed under +`# Citations` at the end. + +## Where the live Caddyfile is + +Caddy runs as a **rootless Quadlet container** on Dankclaw +(`~/.config/containers/systemd/caddy.container`, unit `caddy.service`). It is a *user* unit: +`systemctl is-active caddy` as root answers `inactive` and means nothing — ask the user +manager (`systemctl --user`). Its config path lives inside the container, so there is **no +`/etc/caddy` on the host**: a host-side search for a Caddyfile comes up empty, and +`pgrep -a caddy` prints `--config /etc/caddy/Caddyfile` for a path that does not exist +locally. Everything about it goes through `podman exec`: + +```bash +ssh dankclaw podman exec caddy cat /etc/caddy/Caddyfile +ssh dankclaw podman exec caddy caddy version # v2.11.7, read 2026-10-21 +ssh dankclaw systemctl --user restart caddy +``` + +The live file's own header says what it fronts (both hosts, `franksdank.cloud` names, +Pi-hole-resolved); its deployment history belongs to the serverAppConfigs vault +(`Infrastructure/Caddy.md`), not to this syntax page. ## Directive and matcher syntax @@ -116,9 +131,10 @@ Use `handle` for mutually exclusive branches (for example, per-path pages). Use rewrite [] ``` -`` is the new URI (path or query). If `` starts with `/`, Caddy 2.8.0+ -parses it directly. Before 2.8.0, a leading `/` was confused with a path -matcher, so a `*` disambiguator was needed: `rewrite * /index.html`. +`` is the new URI (path or query). If `` starts with `/`, Caddy 2.8.0+ parses it +directly. Before 2.8.0 a leading `/` was confused with a path matcher, so a `*` +disambiguator was needed: `rewrite * /index.html`. The fleet runs v2.11.7, so write the +plain form. `try_files ` rewrites to the first listed file that exists. It is the clean way to serve a named file for a path prefix: @@ -244,6 +260,22 @@ wins). Put the specific `handle /admin*` before the catch-all `handle`. ## Validate before you deploy -Run `caddy validate --config --adapter caddyfile` before loading a -Caddyfile. A placeholder hash (not a real bcrypt hash) fails validation with -`base64-decoding password`. Render the real hashes first, then validate. +Run `caddy validate --config --adapter caddyfile` before loading a Caddyfile. A +placeholder hash (not a real bcrypt hash) fails validation with `base64-decoding password`. +Render the real hashes first, then validate. On the fleet's Caddy the file is inside the +container — run the validator there, see +[Where the live Caddyfile is](#where-the-live-caddyfile-is). + +# Citations + +- [Caddyfile tutorial](https://caddyserver.com/docs/caddyfile-tutorial) and + [concepts](https://caddyserver.com/docs/caddyfile/concepts) — directive form, the three + matcher token forms, and the canonical sort order behind the `handle`/`route` rule. +- [matchers](https://caddyserver.com/docs/caddyfile/matchers) — exact vs `*` prefix path + matching, named matchers, `matcher is defined more than once`. +- [rewrite](https://caddyserver.com/docs/caddyfile/directives/rewrite), + [handle](https://caddyserver.com/docs/caddyfile/directives/handle) and + [reverse_proxy](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy) — the + 2.8.0 leading-`/` change, `handle` vs `route` semantics, `trusted_proxies`. +- [forward_auth](https://caddyserver.com/docs/caddyfile/directives/forward_auth) — the + outpost-before-gate ordering and `copy_headers`. From 49f67b21920cb021e436f2a9c4058cc1e5273718 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:14:39 -0700 Subject: [PATCH 04/16] docs(niri): find a keysym without a tool that is not installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `key-bindings-syntax.md` told you to run `wev` and read `sym: XXXX` — a placeholder nobody fills in, for a tool that is not on the machine: `command -v wev` is empty, `pacman -Ss --sync wev` is empty on CachyOS, and the AUR carries it as `wev-git`. The bind token is an XKB keysym, and the complete list is already on every box that links libxkbcommon, so the page now leads with the grep against `/usr/include/xkbcommon/xkbcommon-keysyms.h` (run here: `a A Escape Left Return Tab`), and keeps `wev` as the "press the key and see" option with its real package name and where to get it. `screenshots.md` line 35 read `(FIXME upstream).`, which scans as unfinished work in a page that is finished: the defect is upstream's, tracked in the two issue links six lines below. Reworded to say that. Refs #64 --- omp-docs/tools/niri/key-bindings-syntax.md | 12 +++++++++--- omp-docs/tools/niri/screenshots.md | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/omp-docs/tools/niri/key-bindings-syntax.md b/omp-docs/tools/niri/key-bindings-syntax.md index 020a967..1e00875 100644 --- a/omp-docs/tools/niri/key-bindings-syntax.md +++ b/omp-docs/tools/niri/key-bindings-syntax.md @@ -45,13 +45,19 @@ binds { ## Finding Key Names -Use [`wev`](https://git.sr.ht/~sircmpwn/wev) to find XKB key names: +Niri binds by XKB **keysym name**, and the authoritative list is already on the box — no +tool needed: ```bash -wev -# Then press the key you want to bind — look at `sym: XXXX` in output +grep -oE 'XKB_KEY_[A-Za-z0-9_]+' /usr/include/xkbcommon/xkbcommon-keysyms.h \ + | sed 's/^XKB_KEY_//' | grep -ixE 'a|return|left|tab|escape' ``` +To read the keysym off a key you actually press, use +[`wev`](https://git.sr.ht/~sircmpwn/wev), the Wayland equivalent of `xev`. It is NOT in the +enabled repositories — checked 2026-10-21, `pacman -Ss --sync wev` returns nothing — and the +AUR builds it as `wev-git` (`yay -S wev-git`; `aur/wev-git 1.1.0.r1.gb09b664-1`). + Shifted keys require Shift + the unshifted key (for example, `Mod+Shift+Comma` for `<` on US QWERTY). ## Constraints diff --git a/omp-docs/tools/niri/screenshots.md b/omp-docs/tools/niri/screenshots.md index adfa262..a387439 100644 --- a/omp-docs/tools/niri/screenshots.md +++ b/omp-docs/tools/niri/screenshots.md @@ -32,7 +32,7 @@ Noctalia settings lives on `Mod+Shift+O` (moved off Mod+Shift+S). - Portal chain: flameshot 14 → `xdg-desktop-portal-gnome` → niri D-Bus API → `screenshot_all_outputs` → `ensure!(outputs.len() == 1)` - (FIXME upstream). + (an upstream defect, still unfixed — issue links below). - `flameshot full` on 2 outputs: each press emits **3 refusals** (composited screen + per-output) — `WARN niri::niri: error taking a screenshot: Condition failed: outputs.len() == 1 (2 vs 1)` in From 88d486b37bc4b46de23a99f0fd9816772221f786 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:14:39 -0700 Subject: [PATCH 05/16] docs(okf): make the bundle note describe the bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The note in `references/okf-spec.md` claimed the bundle keeps a "`tags` discipline". It does not: 22 of 76 concept pages carry `tags`, and neither `kb-lint.sh` nor `kb-coverage.py` reads that field or `verified` — so the note was a claim about intent that an agent would believe and pay the wrong cost for (adding tags to a page helps no tool, while `verified` silently does the real work: it is the date someone last re-checked the page against the fleet, and its absence means nobody ever has). The note now states the measured usage instead of the aspiration, records the house link style (bundle-absolute, 99 links already written that way), and says plainly that Obsidian `[[wikilinks]]` are not OKF links — with the count this bundle had and the date they were converted. The spec's own repository is now cited under `# Citations`, which the page was missing while it lectured others about skipping citations. Refs #64 --- omp-docs/references/okf-spec.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/omp-docs/references/okf-spec.md b/omp-docs/references/okf-spec.md index 6add03b..d2a69e7 100644 --- a/omp-docs/references/okf-spec.md +++ b/omp-docs/references/okf-spec.md @@ -20,8 +20,13 @@ timestamp: 2026-01-15 > optional trust/lifecycle fields (`verified: { by, at }`, `status`, > `stale_after`) and the `Attested Computation` type, and permits `okf_version:` > frontmatter in a bundle-root `index.md`. This bundle stays on v0.1 semantics -> (kb-lint enforces them); the bundle-local `verified:` date stamp and `tags` -> discipline are producer extensions compatible with both versions. +> (`kb-lint` enforces them). What the optional fields mean HERE, measured 2026-10-21: +> `description` on every concept page; `tags` on 22 of 76 concept pages and no tool reads +> them; `resource` only where the page describes an external thing; `verified: YYYY-MM-DD` +> = "the facts on this page were re-checked against the fleet that day", and a page with no +> `verified` has never been re-checked. House link style is bundle-absolute +> (`/tools/niri/tips.md`) for anything crossing a directory. `[[wikilinks]]` are not OKF +> links — this bundle's 53 of them were converted to markdown links on 2026-10-21. # Open Knowledge Format (OKF) v0.1 @@ -167,3 +172,9 @@ Everything else is soft guidance. Consumers MUST NOT reject a bundle because of - [OKF Bundle Maintenance](okf-maintenance.md) — How to keep your bundle from rotting. - [Root Index](/index.md) — This bundle's structure and navigation. + +# Citations + +- [GoogleCloudPlatform/open-knowledge-format](https://github.com/GoogleCloudPlatform/open-knowledge-format) + — upstream repository: the v0.1 specification this page summarizes and `SPEC.md` for v0.2 + (read 2026-10-19). From c6e2a1daca7eeda0cd93f8d2f7e2128cad1bab6b Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:16:33 -0700 Subject: [PATCH 06/16] docs(log): the work log stops re-teaching what the concept pages teach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 504 lines, 4,496 words — the log had become the largest page in the bundle, and the reason was not history: twelve entries dated 2026-10-18 → 2026-10-21 each re-narrated the incident AND re-stated the law AND pointed at the page that owns the law. Three copies of one fact, dated three ways. 384 lines / 3,251 words now. What was cut: the restatements. The refuse-by-default guard belongs to `tools/pcie-probe.md`, the completion-sentinel law to `tools/remote-job.md`, the verbatim mode-string law to `tools/niri/displays.md`, the enable-snapshots-a-path trap to `tools/dotfiles-deploy.md`, the sorted-directive rule to `caddy/caddyfile.md`, the effort-map contract to `pi/models.md`, the tier law and the footer/`xdg-open` fleet laws to `AGENTS.md`, and the gate's mechanics to `.githooks/pre-commit` plus its tests — each now named once, from a short entry. What was kept deliberately: laws with no other owner anywhere in the bundle. Fail-closed on a blind `git diff`; a stub that echoes its environment instead of its argument cannot see the bug; a name is data, never let the shell expand it; extensions must never await work that can exceed 30 s; `systemctl --user enable` snapshots an absolute path; agents never run sudo/yay/pacman/pkexec (faillock incident, with the reset command); a claim about what the harness sends is worth exactly its last capture; a number in a page is a claim, not a measurement. Deleting those would have been the actual loss. The 2025-08-05 entry is annotated rather than deleted: this repo's history dates that file's arrival at 2026-08-28 (the OKF rebuild copied it), so 2025-08-05 is a legacy-tree date and the entry now says so instead of silently contradicting `git log`. Refs #64 --- omp-docs/log.md | 608 +++++++++++++++++++----------------------------- 1 file changed, 244 insertions(+), 364 deletions(-) diff --git a/omp-docs/log.md b/omp-docs/log.md index dcf5b87..933debf 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -17,488 +17,368 @@ lesson, a few lines. Full incident narratives are recoverable from git 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. +one-log rule and `scripts/kb-lint.sh` enforces it. A law that a concept page already owns is +not restated here — entry, then pointer. That is what the 2026-10-21 audit pass did to the +newest twelve entries: the facts stayed, the duplicates went, `git log --follow` keeps the +long form. ## 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: +Two-axis review (Standards / Spec) of 51 commits, 30 findings, on `fix/60-review-findings`. +Refs #60, merged PR #61. The findings were one 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. +- A guard that loses half its condition when a tool is missing must refuse, not narrow — + `pcie-probe.sh` measured beside a live endpoint while its own LAW said not to + ([tools/pcie-probe.md](tools/pcie-probe.md)). +- A deployer that prints `ENABLE` for a unit systemd refuses to enable (`live-sync.service` has + no `[Install]`; its timer arms it) reports a belief, not an outcome, and walking `*.service` + only left the heartbeat unarmed on a rebuilt box + ([tools/dotfiles-deploy.md](tools/dotfiles-deploy.md)). +- A gate handed git's quoted path reads an empty diff and passes. Fail closed means `-z` + (`.githooks/pre-commit`, `tests/secret-gate.bats`). -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. +Two laws no other page owns: **a stub that echoes its environment instead of its argument +cannot see the bug** — the probe suite asserted the mask was set and never read what the tool +passed, so per-card bandwidth stayed unobtainable; and **a name is data — split it, never let +the shell expand it** (`remote-job.sh status` globbed its own arguments, so job `x*` was +reported as whatever matched in the caller's directory: [tools/remote-job.md](tools/remote-job.md)). -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. +The heartbeat's three missed ticks were this gate firing on a managed skill that documents the +vendor's all-zeros placeholder id: prose about credentials *is* the machine tier, and a gate +that blocks it gets worked around — the workaround is the hole. Placeholder judgement reads the +matched token and what follows it (a fixed quantifier truncates `EXAMPLE` to `EXA`), never the +whole line. `ALLOW_SECRETS` stays the only override. -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. +The rest of #60 was claims the machine had stopped honouring: `.gitignore` and `deploy.sh`'s +exclusion list drifted apart, `.gitattributes` justified installing `git-lfs` for wallpapers +that same week's diff untracked, and the one-log rule had four owners. A coverage claim names +what was measured; a tool justified by dead config cannot survive the config. ## 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 +Refs #62. `agent/models.yml` rewrote `minimal→low`, `high→xhigh`, `max→xhigh` for frank-dragoon +to dodge 400s the engine does not send — flash-server's own error names its allow-list 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` still sends `reasoning_effort: "minimal"` and pays for a canned thinking block. Contract +owner: [pi/models.md](pi/models.md). Law: **a claim about what the harness sends is worth +exactly its last capture** — settle it through a logging proxy, not by reading config. The same +pass restored the three pre-callsign model ids the 2026-10-08 rename had overwritten inside its +own explanatory comments. ## 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 mode ` — the -temporary per-output path, so no config reload and no logged-out session. +The home ultrawide ran at 60 Hz for weeks because `desk-1-home.kdl` asked for +`3440x1440@239.999` and the panel advertises `@239.998`. One character: niri matches mode +strings verbatim, does not round, and a mode it cannot find does not fail — it falls back to the +EDID preferred mode, 60 Hz, on every connector drop. The page that owns the file documented the +same phantom mode, so every reader trusted it. Laws, triage table and probes: +[tools/niri/displays.md](tools/niri/displays.md). Durable: **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` listed as installed while `pacman -Qs wlr` matches nothing). Read the +hardware, then destroy the stale copies. A correction is itself a measurement: this entry's +first pass published a second wrong hardware fact, with a `verified:` date stamped on it. ## 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. +The bundle had two: this page and `core/log.md` — two entries of pure migration bookkeeping and +a conversion date that neither this page nor git agreed with (2026-07-15 there, 2026-01-15 here, +`git log` dates the OKF rebuild 2026-08-28); `core/index.md` still pointed readers at it, which +is how a stale page stays alive. Trashed to `omp-docs/core/.AGENTTRASH/`. The rule lives in +[references/okf-spec.md](references/okf-spec.md) and +[references/okf-maintenance.md](references/okf-maintenance.md) and is gated by +`scripts/kb-lint.sh`; the agent-side duty is `agent/APPEND_SYSTEM.md` § 1. ## 2026-10-20 — two instruments rescued from `$HOME` script sediment -`$HOME` had accumulated 123 loose scripts at its root (archived to -`.AGENTTRASH/home-scripts-2026-10-20`, ~1.8 GiB of stray audio consolidated into -`~/audiosamples`). Two were durable instruments and now live in the repo with -black-box tests and pages: [tools/remote-job](tools/remote-job.md) and -[tools/pcie-probe](tools/pcie-probe.md). Refs #53. Laws learned: - -- **A readiness pattern in a log is not evidence of completion.** `ssh host 'cmd | - tail -f log | sed -n /ready/q'` fails three independent ways — the broker aborts - the long connection, the pattern can match a header printed before the work - starts, and `sed -n /x/q` exits the moment it matches, tearing down the job it was - watching. The only honest signal is a sentinel the job writes last, read over a - short-lived connection. -- **Never benchmark beside a live service, and never let a tool decide to stop one.** - The guard refuses before anything is built; unloading a production endpoint is a - human's call. A number taken as noise beside serving traffic is both disruptive and - wrong. -- **An instrument that segfaults is not an instrument.** Two of the three shipped - `.hip` probes called `hipGetDeviceProperties`, which crashes on this ROCm build - while `hipGetDeviceCount` works fine; device identity now comes from sysfs, and the - trap is recorded in the sources so it is not reintroduced. +Refs #53. `$HOME` had accumulated 123 loose scripts (archived to +`.AGENTTRASH/home-scripts-2026-10-20`; ~1.8 GiB of stray audio consolidated into `~/audiosamples`). +Two were durable instruments and now live in the repo with black-box tests and pages: +[tools/remote-job](tools/remote-job.md) — a readiness pattern in a log is not evidence of +completion, the pattern can match a header printed before the work starts, and `sed -n /x/q` +exits the moment it matches, tearing down the job it was watching — and +[tools/pcie-probe](tools/pcie-probe.md) — never benchmark beside a live service, never let a +tool decide to stop one, and an instrument that segfaults is not an instrument. ## 2026-10-20 — the secret gate was reading the wrong files, and one credential had already escaped -Found while deciding what else belongs in `dotfiles/`. (1) The gate whitelisted -extensions (`*.md *.yml *.json *.toml *.sh *.ts *.js`), so a `.env`, `.container`, -`.ini` or extension-less dotfile was never read — exactly the shapes the dotfiles -tier is made of. It now scans the whole staged diff for token shapes and, in -non-prose files, for credential assignments, and it fails closed when `git diff` -itself errors. That last clause earned its place: the rewrite's first attempt -passed `-I` to `git diff`, which is not valid there, got an empty file list, and -reported success on every commit — that is what a blind gate looks like, and it -looks identical to a clean tree. (2) A live credential had already escaped into -tracked content: a Caddy basicauth bcrypt hash, its plaintext password, and the -Pi-hole internal password, in an auto-learned memory file. Redacted — but both -credentials need **rotation**, because redaction does not un-leak history that -every machine has already pulled. (3) `home/dank/.omp/dotfiles/**` and -`.pre-pull-backup/**` were tracked fiction invisible to `deploy.sh`, and 23 -wallpaper JPEGs (80 MiB) rode along in every clone. Laws: a gate must fail closed, -since a silently-passing gate is worse than none — it buys the belief of safety; -auto-learned memory is written by a process that has read your terminal, so it is -a leak surface and gets gated like any config; `systemctl --user enable` -snapshots an absolute path, so enabling from a worktree means the seven-day reap +Found while deciding what else belongs in `dotfiles/`. The gate whitelisted extensions, so +`.env`, `.container`, `.ini` and extension-less dotfiles — the shapes the dotfiles tier is made +of — were never read. It now scans the whole staged diff for token shapes and, in non-prose +files, for credential assignments, and fails closed when `git diff` itself errors; the rewrite's +first attempt passed an invalid flag to `git diff`, got an empty file list, and reported success +on every commit — which is what a blind gate looks like, and it looks identical to a clean tree. +Bcrypt hashes and Basic-auth headers joined the shape list (issue #40) because the audit found +a live Caddy basicauth hash, its plaintext password, and the Pi-hole internal password in a +tracked auto-learned memory file. **Redaction does not un-leak history that every machine has +already pulled: both credentials needed rotation.** Auto-learned memory is written by a process +that has read your terminal, so it is a leak surface and gets gated like any config. Also +removed: tracked fiction invisible to `deploy.sh` (`home/dank/.omp/dotfiles/**`, +`.pre-pull-backup/**`) and 23 wallpaper JPEGs (80 MiB) riding in every clone. Mechanism: +`.githooks/pre-commit` and its tests; details in +[dotfiles-deploy](tools/dotfiles-deploy.md) and +[home-config-census](inventory/home-config-census.md). Law no other page owns: **`systemctl +--user enable` snapshots an absolute path** — enabling from a worktree means the seven-day reap silently kills the unit while `is-enabled` goes on saying `enabled` -(`blankpad-desk-autoswitch.service` was dangling that way). Details: -[dotfiles-deploy](tools/dotfiles-deploy.md), -[home-config-census](inventory/home-config-census.md). +(`blankpad-desk-autoswitch.service` was dangling that way). ## 2026-10-20 — engineering skills configured against this KB -`setup-matt-pocock-skills` ran, and its output moved out of the skill's default -`docs/agents/` into the knowledge base: [issue tracker](skills/issue-tracker.md) -(Gitea on Dankclaw is the tracker — no `gh`, no `tea`, token via a `600` curl -config), [triage labels](skills/triage-labels.md) (the five canonical roles, -created on the repo, which carried zero labels before), [domain -docs](skills/domain-docs.md) (single-context; glossary withheld until a term has -no other owner), plus an empty [decisions](decisions/index.md) with its format -rule. `AGENTS.md` gained an `## Agent skills` pointer block. Law: this repo has -one knowledge base, so per-tool configuration is a concept page in `omp-docs/` — -a second `docs/` tree means a second set of index rules, which is where -documentation starts disagreeing with itself. Known cost: re-running that setup -skill will not recognise `omp-docs/skills/*` and will offer to recreate -`docs/agents/`; the `AGENTS.md` pointer is the thing that stops it. +`setup-matt-pocock-skills` ran and its output moved out of the skill's default `docs/agents/` +into the knowledge base: [issue tracker](skills/issue-tracker.md), +[triage labels](skills/triage-labels.md), [domain docs](skills/domain-docs.md), and an empty +[decisions](decisions/index.md) with its format rule. `AGENTS.md` gained the `## Agent skills` +pointer block. Law: this repo has one knowledge base, so per-tool configuration is a concept +page in `omp-docs/` — a second `docs/` tree means a second set of index rules, which is where +documentation starts disagreeing with itself. Known cost: re-running that setup skill will not +recognise `omp-docs/skills/*` and will offer to recreate `docs/agents/`; the `AGENTS.md` pointer +is what stops it. ## 2026-10-19 — dankvoice auto-speak armed at startup -Sessions spoke only on explicit `/say`: the settings file carried no `auto` -key, and arming is session-scoped by design (`/say on` never persists). Set -`"auto": true` in `~/.omp/cache/dankvoice.json`. Same report, second half: -auto-speak listened only to `agent_end` — a settle that fires once per prompt -and is skipped outright when the harness schedules a continuation — so nothing -was narrated while an agent worked; fixed in dank/dankvoice#3, which narrates -`turn_end` as well. Law: silence at session start is a config state — read -`/say status` for `auto-speak on`, then watch `pgrep -a pw-play` for one player -per segment, before blaming the endpoint. Law: whether the harness delivers an -event to extensions is settled in minutes by a throwaway probe extension that -timestamps `turn_end`/`agent_end` — never inferred from audio timing, which the -humanizer lags by seconds. Details: [dankvoice](core/dankvoice.md). +Nothing spoke because the settings file carried no `auto` key (arming is session-scoped by +design) and narration rode only `agent_end`, a settle that a scheduled continuation skips; fixed +upstream in dank/dankvoice#3. Law: silence at session start is a config state — read `/say +status`, then watch `pgrep -a pw-play` for one player per segment — before blaming the endpoint. +Law: whether the harness delivers an event to extensions is settled in minutes by a throwaway +probe extension that timestamps `turn_end`/`agent_end`, never inferred from audio timing. +Details: [core/dankvoice.md](core/dankvoice.md). ## 2026-10-19 — LazyVim installed as an NVIM_APPNAME profile -`lazyvim` had never been installed here — shell history shows the command typed -twice and dropped; `~/.config/nvim` held only an orphaned `matugen.lua` palette -that nothing loaded. Now: LazyVim starter in `dotfiles/lazyvim/` symlinked to -`~/.config/lazyvim`, a `lazyvim` zsh function, and the palette applied through -`RRethy/base16-nvim` on `ColorScheme`. Law: the distro stays out of -`~/.config/nvim` so bare `nvim` keeps working as a vanilla quick-edit editor. -Details: [neovim](tools/neovim.md). +`lazyvim` had never been installed here: shell history shows the command typed twice and +dropped, and `~/.config/nvim` held only an orphaned `matugen.lua` palette nothing loaded. Now the +starter lives in `dotfiles/lazyvim/` symlinked to `~/.config/lazyvim`, a `lazyvim` zsh function +reaches it, and the wallpaper palette applies through `RRethy/base16-nvim` on `ColorScheme`. +Law: the distribution stays out of `~/.config/nvim` so bare `nvim` keeps working as a vanilla +quick-edit editor. Details: [tools/neovim.md](tools/neovim.md). ## 2026-10-19 — dankvoice cut over to the `dank/dankvoice` package (Refs #34) -Drop-in `agent/extensions/dankvoice.ts` and its 26-test suite retired; the -package carries the behaviour (41/41 black-box suite) and is installed with -`pi install git:`. Law: install and prove `/say status` speaks on a box -*before* pulling the cutover commit — the shared settings file keeps menu -state across both transports, so the swap is stateless once installed. +Drop-in `agent/extensions/dankvoice.ts` and its 26-test suite retired; the package carries the +behaviour (41/41 black-box suite) and installs with `pi install git:`. Law: install and prove +`/say status` speaks on a box *before* pulling the cutover commit — the shared settings file +keeps menu state across both transports, so the swap is stateless once installed. ## 2026-10-19 — dankvoice: silence and timeouts, root-caused (Refs #32) -The humanizer's raw `fetch` to frank-zealot omitted `chat_template_kwargs. -enable_thinking: false`, so the Qwen35 hybrid *thought* instead of answering: -6.9 s of `reasoning_content`, `content: ""` — timing out the rewrite twice -(12 s dead air) and, when it did answer inside the window, speaking silence. -Root-caused by replaying the exact wire body against the live endpoint. -Fix: pin the kwarg in the request body; treat empty-content-with-reasoning as -failure (mechanical voice), not "nothing worth saying"; the menu's test phrase -fires without blocking its handler. Lesson: the harness injects thinking-off -for managed roles, but raw HTTP callers must set it themselves — 26/26 green -with three red-captured regressions. +The humanizer's raw `fetch` omitted `chat_template_kwargs.enable_thinking: false`, so the Qwen35 +hybrid *thought* instead of answering: 6.9 s of `reasoning_content`, `content: ""` — dead air +twice, and when it answered inside the window it spoke silence. Root-caused by replaying the +exact wire body against the live endpoint. Fix: pin the kwarg, treat empty-content-with-reasoning +as failure, fire the menu's test phrase without blocking its handler. Lesson: the harness injects +thinking-off for managed roles; raw HTTP callers must set it themselves. +[core/dankvoice.md](core/dankvoice.md). -## 2026-10-19 — dankvoice: humanized narration and thinking aloud (Refs #27) +## 2026-10-19 — dankvoice: humanized narration, segmented speech, `/say` menu (Refs #27, #23, #21) -Spoken units are rewritten by the `@tiny` role (frank-zealot) under the -bundle's speech budgets before synthesis, with mechanical cleanup as fallback; -auto-speak narrates the last message's thinking blocks first; all toggles -persist. 7 new scenarios, 22 green; live smoke through real zealot + frank-tts. - -## 2026-10-19 — dankvoice: segmented sequential speech - -`agent_end` awaiting one whole-answer synthesis hit the harness's 30 s -extension-handler timeout — lesson: extensions must never await work that can -exceed 30 s. Ported the built-in vocalizer's utterance-splitting state machine -(boundary = `[.!?…]+` + whitespace, abbreviation/code guards, per-answer -segment budget); segments synthesize strictly sequentially. `DANKVOICE_MAX` is -now a segment budget. 17/17 in `tests/dankvoice.bats`. - -## 2026-10-18 — dankvoice: `/say` options menu with output-device routing (Refs #23) - -`/say menu` (pi-peon-style): auto-speak, voice, PipeWire output device -(`wpctl` → `pw-play --target`), player, char cap, test phrase. Precedence -env > `~/.omp/cache/dankvoice.json` > defaults — born from the incident where -PipeWire's remembered Bluetooth route played answers into nowhere. - -## 2026-10-18 — dankvoice: the cloned voices finally speak in the TUI (Refs #21) - -The harness gates `--voice` against the bundled Kokoro enum, so frank-tts's -cloned voices were unreachable in-TUI. Added `agent/extensions/dankvoice.ts` -(`/say …`) synthesizing over the OpenAI speech route, endpoint read from the -`api: openai-speech` row of `agent/models.yml` (no host or secret in code). +Spoken units are rewritten by the `@tiny` role under the bundle's speech budgets with mechanical +cleanup as fallback; the utterance-splitting state machine synthesizes segments strictly +sequentially because `agent_end` awaiting one whole-answer synthesis hit the harness's 30 s +extension-handler timeout — **extensions must never await work that can exceed 30 s**. `/say menu` +routes output device (`wpctl` → `pw-play --target`) with precedence env > settings file > +defaults, born from PipeWire's remembered Bluetooth route playing answers into nowhere. The +extension exists at all because the harness gates `--voice` against the bundled Kokoro enum, so +frank-tts's cloned voices were unreachable in-TUI. [core/dankvoice.md](core/dankvoice.md). ## 2026-10-18 — Noctalia bar layout clobbered by trunk-sync stash, recovered -GUI-built bar customizations existed only in the working tree; a trunk-sync -stash-then-drop destroyed them. Recovery: `git fsck --lost-found` surfaced the -dropped stash as a dangling commit; validated in a sandboxed `XDG_*_HOME` -before restore. Lesson: GUI-written config is only durable once committed — -the motivation for the live-sync heartbeat below. - -## 2026-10-18 — Fleet law: issue/PR attribution footers - -Every agent-opened issue/PR body ends with `cwd:` + `session:` footer, in both -`dank/command-center` and `dank/serverAppConfigs` AGENTS.md: machine-made -artifacts must trace to the box and session that produced them. +GUI-built bar customizations existed only in the working tree; a trunk-sync stash-then-drop +destroyed them. Recovery: `git fsck --lost-found` surfaced the dropped stash as a dangling +commit, validated in a sandboxed `XDG_*_HOME` before restore. Lesson: GUI-written config is +durable only once committed — the motivation for the heartbeat below. +[tools/dotfiles-deploy.md](tools/dotfiles-deploy.md). ## 2026-10-18 — live-sync heartbeat for machine-written paths (Refs #11) -A month of silent divergence in the live checkout proved the checkout-is- -deployment model needs a committer: `scripts/live-sync.sh` + systemd user -timer auto-commits allowlisted tiers (memories, managed-skills, live config, -dotfiles) with hooks armed, merges `origin/main`, pushes, reaps stale `wt/*`. -Non-allowlist dirt never staged; conflicts abort-and-alert. See -[tools/live-sync.md](tools/live-sync.md); tier law in AGENTS.md. +A month of silent divergence in the live checkout proved the checkout-is-deployment model needs +a committer: `scripts/live-sync.sh` + a systemd user timer auto-commits the allowlisted tiers +with hooks armed, merges `origin/main`, pushes, and reaps stale `wt/*`. Non-allowlist dirt is +never staged; conflicts abort and alert. [tools/live-sync.md](tools/live-sync.md); tier law in +`AGENTS.md`. Same day, two fleet laws into both repos' `AGENTS.md`: every agent-opened issue or +PR body ends with a `cwd:` + `session:` footer, so machine-made artifacts trace to the box that +spawned them; and `xdg-open ` the moment a PR opens, so Frank never hunts for it in +Gitea. -## 2026-10-18 — Fleet law: filed PRs open in Frank's browser immediately +## 2026-10-18 — SDLC switched to trunk-based development; extension route closed unmerged -`xdg-open ` the moment a PR opens — codified in both repos' git rules. - -## 2026-10-18 — Trunk law: extension route closed unmerged; direct repo alignment - -Operator closed PR #9 (cwd-scoped `before_agent_start` extension) in favor of -editing each repo's AGENTS.md. Durable findings: every context-file provider -(AGENTS.md walk-up, `.omp/` dirs, CLAUDE.md) resolves at or below the repo -root — ancestor-scope files can never load; user-level `APPEND_SYSTEM.md` -broadcasts to all sessions; serverAppConfigs never had git-flow. - -## 2026-10-18 — SDLC switched to trunk-based development - -`develop` retired (single-dev repo). Law in AGENTS.md § Trunk-based SDLC: -branch off freshest `origin/main`, PRs target `main`, `--no-ff` merges, tags -`vX.Y.Z` on main. Managed skill `gitflow-back-merge-plumbing` is historical. +`develop` retired (single-dev repo): branch off freshest `origin/main`, PRs target `main`, +`--no-ff` merges, tags `vX.Y.Z` on main — law in `AGENTS.md` § Trunk-based SDLC, managed skill +`gitflow-back-merge-plumbing` marked historical. The operator also closed PR #9 (cwd-scoped +`before_agent_start` extension) in favour of editing each repo's `AGENTS.md`. Durable finding: +every context-file provider (AGENTS.md walk-up, `.omp/` dirs, CLAUDE.md) resolves at or below +the repo root, so ancestor-scope files can never load; user-level `APPEND_SYSTEM.md` broadcasts +to all sessions. ## 2026-10-18 — Sibling-vault overlap census; system docs realigned to live ground truth -Census of `omp-docs/` vs `serverAppConfigs-docs` (subagents + live-ssh probes) -found four contradicting pairs; ours fixed, theirs flagged for their PR flow. -Recorded then: RTX 3090 moved Dankserver→Dankclaw 2026-09; dankserver = 2× -Radeon AI PRO R9700 (the "board swap" story was false); served models per -live units. Current state always: [/system/index.md](/system/index.md) -(27B-era zealot figures here are historical; live census 2026-10-19 below). +Census of `omp-docs/` vs `serverAppConfigs-docs` (subagents + live-ssh probes) found four +contradicting pairs; ours fixed, theirs flagged for their PR flow. Recorded then: the RTX 3090 +moved Dankserver→Dankclaw 2026-09; dankserver is 2× Radeon AI PRO R9700 (the "board swap" story +was false); served models per live units. Current state always: +[/system/index.md](/system/index.md). ## 2026-10-18 — Status line rebuilt on custom segments; full-source audit -New page [core/statusline.md](core/statusline.md) with an audit log — four -first-pass claims were false and corrected against the minified bundle + a -live screenshot matrix (context meter is `box`/`band` only; `composer.shape` -routes segment arrays; `mode` states = Plan/Prewalk/Goal/Vibe/Loop). Bar -rebuilt on `band` + custom segments; frank-archon display name slimmed in -models.yml. +[core/statusline.md](core/statusline.md) carries the audit log — four first-pass claims were +false and corrected against the minified bundle plus a live screenshot matrix (context meter is +`box`/`band` only; `composer.shape` routes segment arrays; `mode` states = +Plan/Prewalk/Goal/Vibe/Loop). Bar rebuilt on `band` + custom segments. ## 2026-10-09 — Blankpad DE redesign: desk profiles, keybinds, Handy wiring -Per-desk niri profiles keyed by EDID long name (connector names reshuffle -between docks; identity travels) + `scripts/desk.sh`; niri 26.04 has **no -hotplug event** (`OutputConfigChanged` is only an ACK per niri-ipc) — the -autoswitch daemon diffs output sets on `WorkspacesChanged`. Keybind cheat -sheet now generated (`scripts/gen-keybinds.sh`); noctalia docs corrected to -the v5 `noctalia msg` API. Handy: niri owns autostart; open breaker — -frank-secondary answers in `reasoning_content` with empty `content`, and -Handy cannot send `chat_template_kwargs.enable_thinking=false`. 27/27 green. +Per-desk niri profiles keyed by EDID long name (connector names reshuffle between docks; +identity travels) + `scripts/desk.sh`. niri 26.04 has **no hotplug event** (`OutputConfigChanged` +is only an ACK per niri-ipc), so the autoswitch daemon diffs output sets on `WorkspacesChanged`. +Keybind cheat sheet generated by `scripts/gen-keybinds.sh`; noctalia docs corrected to the v5 +`noctalia msg` API. Handy: niri owns autostart; open breaker — frank-secondary answers in +`reasoning_content` with empty `content`, and Handy cannot send +`chat_template_kwargs.enable_thinking=false`. [tools/niri/displays.md](tools/niri/displays.md), +[tools/handy.md](tools/handy.md). ## 2026-10-09 — Incident: agent sudo-loop tripped pam_faillock, locked out the operator -A subagent retried `sudo yay` (no TTY) for ~15 min; `pam_faillock` deny=3 -locked user `blank` — correct-password sudo failed, mimicking a password -change. Recovery: `faillock --user blank --reset`. Law: agents never run -`sudo`/`yay`/`pacman`/`pkexec`; root steps go to the operator as exact -commands. Skill `faillock-sudo-lockout-recovery`. +A subagent retried `sudo yay` (no TTY) for ~15 min; `pam_faillock` deny=3 locked user `blank`, +and correct-password sudo then failed, mimicking a password change. Recovery: +`faillock --user blank --reset`. Law: agents never run `sudo`/`yay`/`pacman`/`pkexec`; root steps +go to the operator as exact commands. Skill `faillock-sudo-lockout-recovery`. ## 2026-10-09 — Full secret audit of the command-center repo: clean -Five layers (live-value scan across 77 revs/765 blobs, whole object store -incl. dangling, shape triage, entropy ≥4.3, LFS payloads) — no secret in any -reachable or dangling object; quarantine list holds. Fixed `hf_token_write` -0644→0600. Open gap: the pre-commit gate lacks `hf_`/`npm_`/`pypi-`/`AIza`/ -Stripe/Telegram shapes and basic-auth URLs — widening it is an open decision. +Five layers (live-value scan across 77 revs/765 blobs, whole object store including dangling, +shape triage, entropy ≥4.3, LFS payloads) found no secret in any reachable or dangling object; +the quarantine list holds. Fixed `hf_token_write` 0644→0600. Open gap: the gate lacks +`hf_`/`npm_`/`pypi-`/`AIza`/Stripe/Telegram shapes and basic-auth URLs — widening it is an open +decision. ## 2026-10-09 — Gitea token moved out of every remote URL Remotes bare; credential in `~/.config/secrets/gitea` (0600) behind a global -`credential.helper store`. Trap (git 2.56.0, measured): `git -credential-store` rejects the `protocol=`/`host=` block shape the man page -shows — the store file must be one URL per line, port included. Verified with -`GIT_TERMINAL_PROMPT=0`. Details: [tools/gitea/client-access.md](tools/gitea/client-access.md). +`credential.helper store`. Trap (git 2.56.0, measured): `git credential-store` rejects the +`protocol=`/`host=` block shape the man page shows — the store file must be one URL per line, +port included. Verified with `GIT_TERMINAL_PROMPT=0`. +[tools/gitea/client-access.md](tools/gitea/client-access.md). ## 2026-10-09 — Working tree reconciled: callsign renames, compaction move, gitignore quarantine -200-entry dirty tree committed as seven atomic units. Harness model ids -renamed `frank-{medium,large,small}` → `frank-{archon,dragoon,zealot}` — -**services keep old names on the wire**; the engines accept any request model -id, so renames are harness-side only. Server-side compaction moved to -zealot's 3090 (threshold 70→85 %). `.gitignore` quarantine for worktree-keyed -memory buckets and runtime dirs. Skill renames are not losses: git renders -directory renames as mass delete+add; pre-cutover content intact at `12c5e88`. +200-entry dirty tree committed as seven atomic units. Harness model ids renamed +`frank-{medium,large,small}` → `frank-{archon,dragoon,zealot}` — **services keep old names on the +wire**; the engines accept any request model id, so renames are harness-side only. Server-side +compaction moved to zealot (threshold 70→85 %). `.gitignore` quarantine for worktree-keyed memory +buckets and runtime dirs. Skill renames are not losses: git renders directory renames as mass +delete+add; pre-cutover content intact at `12c5e88`. ## 2026-09-28 — Retired workstation purged from the knowledge base -Sold box: hostname, both LAN addresses, Wi-Fi SSID gone bundle-wide; the -knowledge distilled into [system/network.md](system/network.md) (multi-link -resolver law: a static resolver must be pinned on each *connection*) and -[tools/niri/displays.md](tools/niri/displays.md) (portrait transforms, the -three ways a multi-monitor rig breaks). Hostname stays only where it is a -lookup key (install.sh `--host`, `packages/.pkgs`). +Sold box: hostname, both LAN addresses, Wi-Fi SSID gone bundle-wide; the knowledge distilled +into [system/network.md](system/network.md) (multi-link resolver law: a static resolver must be +pinned on each *connection*) and [tools/niri/displays.md](tools/niri/displays.md) (portrait +transforms, the three ways a multi-monitor rig breaks). Hostname stays only where it is a lookup +key (install.sh `--host`, `packages/.pkgs`). ## 2026-09-01 — Retry backoff and thinking default (pi era) -Pi `settings.json`: 10 s base retry backoff (10/20/40 s), `defaultThinkingLevel: -medium` (no per-model thinking field exists in `models.json`). +Pi `settings.json`: 10 s base retry backoff (10/20/40 s), `defaultThinkingLevel: medium` (no +per-model thinking field exists in `models.json`). ## 2026-08-28 — KB rebuilt to OKF v0.1; live-verified fleet facts -`omp-docs/` copied from legacy `~/.pi/agent/docs`, rebuilt as git-tracked OKF -v0.1 (checkout *is* the deployment); unverifiable claims tagged `unverified`. -Conformance pass promoted concept-in-disguise indexes, stripped index -frontmatter, resolved all links; `scripts/kb-lint.sh` exits 0. Hardware facts -were corrected *then* (3090-on-dankserver since moved — see 2026-10-18). +`omp-docs/` copied from legacy `~/.pi/agent/docs`, rebuilt as git-tracked OKF v0.1 (checkout *is* +the deployment); unverifiable claims tagged `unverified`. Conformance pass promoted +concept-in-disguise indexes, stripped index frontmatter, resolved all links; `scripts/kb-lint.sh` +exits 0. Hardware facts were corrected *then* (3090-on-dankserver since moved — see 2026-10-18). Inventory census recorded under `inventory/` for the pruning session. ## 2026-08-23 — Homepage deployed to production; Caddy KB doc -Homepage live on Dankclaw via `scripts/deploy.sh` (status checker timer, -deployed.js, rendered basicauth hashes, health check). Hard-won Caddy lesson: -top-level directives are *sorted*, so `rewrite` runs before `basicauth` and -auth matchers saw rewritten paths — serve each page from its own `handle` -block to preserve order; a `handle` inline matcher is a single token; a -leading `@name ` parses as a matcher definition. Now documented on -[caddy/caddyfile.md](caddy/caddyfile.md). A compose mount change needs a -container *recreate*, not a reload. 116 tests green. +Homepage live on Dankclaw via `scripts/deploy.sh` (status checker timer, `deployed.js`, rendered +basicauth hashes, health check). Hard-won Caddy law, now on +[caddy/caddyfile.md](caddy/caddyfile.md): top-level directives are *sorted*, so `rewrite` runs +before `basicauth` and auth matchers saw rewritten paths — serve each page from its own `handle` +block; a `handle` inline matcher is a single token; a leading `@name ` parses as a +matcher definition. A compose mount change needs a container *recreate*, not a reload. ## 2026-08-23 — Homepage design settled (grilling complete) All 38 open questions answered; full record lives in serverAppConfigs -(`docs/Infrastructure/Homepage Design Grilling.md`). Key deltas: per-service -tcp/http probes, 5 s timeout, TUI-style panel grid, no client auto-refresh, -git-driven deploy, full TDD. Correction recorded: the Dankserver media stack -runs as bare processes, not containers — never probe it as a compose stack. +(`docs/Infrastructure/Homepage Design Grilling.md`). Key deltas: per-service tcp/http probes, 5 s +timeout, TUI-style panel grid, no client auto-refresh, git-driven deploy, full TDD. Correction +recorded: the Dankserver media stack runs as bare processes, not containers — never probe it as a +compose stack. ## 2026-08-22 — vllmScripts merged into serverAppConfigs -Full-history two-parent import; one repo, one checkout per host, one -`deploy.sh`. The plaintext sudo password from old docs was NOT carried over -(a bats leak-check guards it); it remains in both repos' history on the LAN -Gitea — rotation recommended. +Full-history two-parent import; one repo, one checkout per host, one `deploy.sh`. The plaintext +sudo password from old docs was NOT carried over (a bats leak-check guards it); it remains in +both repos' history on the LAN Gitea — rotation recommended. ## 2026-08-21 — Fleet ops: passwordless sudo, Hermes SSH keys -`/etc/sudoers.d/dank-nopasswd` on both servers (per-command drop-ins remain); -dedicated ed25519 key for the Hermes container, verified into all three -machines. +`/etc/sudoers.d/dank-nopasswd` on both servers (per-command drop-ins remain); dedicated ed25519 +key for the Hermes container, verified into all three machines. -## 2026-08-19 — pi-web-access: SearXNG endpoint re-pointed to dankclaw (pi era) +## 2026-08-19 — SearXNG + Gluetun bring-up; pi endpoint re-pointed -`searxngBaseUrl` moved off dead `.235:8081` to `.143:8081`; pi caches -`web-search.json` per process — new endpoint needs a new session. `yt-dlp` -installed (2026.07.04). - -## 2026-08-19 — SearXNG + Gluetun bring-up on dankclaw - -Durable field notes (now on concept pages — see +`searxngBaseUrl` moved off the dead `.235:8081` to `.143:8081`; pi caches `web-search.json` per +process, so a new endpoint needs a new session. Durable field notes now on concept pages — [agentic-web-research/vpn-routing.md](agentic-web-research/vpn-routing.md), [searxng-tuning.md](agentic-web-research/searxng-tuning.md), -[engineering/infra/containers.md](engineering/infra/containers.md)): -manual-WireGuard Proton needs `VPN_SERVICE_PROVIDER=custom` + the single-peer -`WIREGUARD_*` env names (not `protonvpn`, not `WIREGUARD_PEERS_*`); gluetun -ships no curl and `:8000` is the control server, not health; the limiter's -JSON cap (`API_MAX=4`/hour) is hardcoded Python — `pass_ip` is the only -bypass; rootless podman strips client IPs on published ports, so SearXNG must -sit behind Caddy for XFF; quadlet generator rejects `CapAdd=`/`Device=` and -generated units enable via `[Install] WantedBy=default.target`. ComfyUI on -gfx1151: build on AMD's ROCm base and filter the torch family out of -ComfyUI's requirements or you clobber the gfx1151 kernels. +[engineering/infra/containers.md](engineering/infra/containers.md): manual-WireGuard Proton needs +`VPN_SERVICE_PROVIDER=custom` + the single-peer `WIREGUARD_*` env names (not `protonvpn`, not +`WIREGUARD_PEERS_*`); gluetun ships no curl and `:8000` is the control server, not health; the +limiter's JSON cap is hardcoded Python and `pass_ip` is the only bypass; rootless podman strips +client IPs on published ports, so SearXNG must sit behind Caddy for XFF; the quadlet generator +rejects `CapAdd=`/`Device=` and generated units enable via `[Install] WantedBy=default.target`. +ComfyUI on gfx1151: build on AMD's ROCm base and filter the torch family out of ComfyUI's +requirements or you clobber the gfx1151 kernels. ## 2026-08-17 — Fleet redo + vLLM tool calling (pi era) -Fleet moved off the pipeline-parallel + NPU topology; Quark W8A8 quant chosen -over the Avesed INT8 for clean Qwen3 XML tool calls. +Fleet moved off the pipeline-parallel + NPU topology; Quark W8A8 quant chosen over the Avesed +INT8 for clean Qwen3 XML tool calls. ## 2026-08-16 — Gitea boot-persistence fix (dankclaw) -`unattended-upgrades` reboot exposed that the compose-era Gitea had no -systemd units (~1 h down). Stack rewritten as quadlet (names, `systemd-gitea` -network, linger + `WantedBy=default.target`, app.ini env-override gotcha, UID -100999 ownership) across `tools/gitea/*`; `podman generate systemd` marked -deprecated in [engineering/infra/containers.md](engineering/infra/containers.md). +`unattended-upgrades` reboot exposed that the compose-era Gitea had no systemd units (~1 h down). +Stack rewritten as quadlet (names, `systemd-gitea` network, linger + `WantedBy=default.target`, +app.ini env-override gotcha, UID 100999 ownership) across `tools/gitea/*`; `podman generate +systemd` marked deprecated in [engineering/infra/containers.md](engineering/infra/containers.md). ## 2026-08-13 — Pi subagent limits recorded (pi era) -Effective caps: globalConcurrencyLimit 2, parallel.maxTasks 8 / concurrency 4, -maxSubagentDepth 2, turnBudget 30; no per-model limit. See +Effective caps: `globalConcurrencyLimit` 2, `parallel.maxTasks` 8 / concurrency 4, +`maxSubagentDepth` 2, `turnBudget` 30; no per-model limit. [pi/packages/pi-subagents.md](pi/packages/pi-subagents.md). -## 2026-08-11 — Engineering research area; skills leaderboard verified +## 2026-08-11 — Engineering research area; agentic web research section -Created `engineering/` (SDLC, agent instruction files, stacks, infra, -plugins, skills ecosystem, Signal remote) via parallel research fleet. -Skills leaderboard replaced with the ranked list from the skills.sh API -(zero-based pagination, 200-skill pages). - -## 2026-08-11 — Agentic web research section; exhaustive bundle audit - -Created `agentic-web-research/` (8 pages) and rewrote them on 2025/2026 -evidence (SearXNG valkey deployment, real engine defaults, limiter budgets, -Gluetun pattern, open-model claim triage, MCP transports). Audit: 154+ -wikilinks resolved, dead external links fixed, `MemorySleepMode=deep` -verified valid (systemd 256+), ASD-STE100 style pass. `AGENTS.md` created as -the system behavior contract. +Created `engineering/` (SDLC, agent instruction files, stacks, infra, plugins, skills ecosystem, +Signal remote) and `agentic-web-research/` (8 pages), the latter rewritten on 2025/2026 evidence +(SearXNG valkey deployment, real engine defaults, limiter budgets, Gluetun pattern, open-model +claim triage, MCP transports). Skills leaderboard replaced with the ranked list from the skills.sh +API (zero-based pagination, 200-skill pages). Same pass: 154+ wikilinks resolved, dead external +links fixed, `MemorySleepMode=deep` verified valid (systemd 256+), ASD-STE100 style pass, +`AGENTS.md` created as the system behavior contract. ## 2026-08-06 — Link fixes; ComfyUI docs; major documentation overhaul -Fixed 15 broken wikilinks (missing frontmatter) + title mismatches → zero -broken. Added ComfyUI pages, per-host system docs, pi config tree, tools -entries; orphaned root duplicates moved to `.AGENTTRASH/`. +Fixed 15 broken wikilinks (missing frontmatter) + title mismatches → zero broken. Added ComfyUI +pages, per-host system docs, pi config tree, tools entries; orphaned root duplicates moved to +`.AGENTTRASH/`. ## 2026-07-18 — Maintenance tooling -Expanded `references/okf-maintenance.md` (cross-links, health metrics, -troubleshooting); created `core/log.md` and `.okf/config.yaml`. +Expanded `references/okf-maintenance.md` (cross-links, health metrics, troubleshooting); created +`core/log.md` (retired 2026-10-20, above) and `.okf/config.yaml`. ## 2026-01-15 — OKF conversion -Docs bundle converted to OKF v0.1; files migrated into `core/`, `tools/`, -`packages/`, `references/` with frontmatter on all files. +Docs bundle converted to OKF v0.1; files migrated into `core/`, `tools/`, `packages/`, +`references/` with frontmatter on all files. ## 2025-08-05 — Gitea documentation -Added [tools/gitea/index.md](tools/gitea/index.md) (topology, management, -data persistence) to the tools index. +Added [tools/gitea/index.md](tools/gitea/index.md) (topology, management, data persistence) to +the tools index — legacy tree, predating the OKF conversion above. From c37ad6e870b813923bd9badd9adf93526b030b7f Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:16:56 -0700 Subject: [PATCH 07/16] docs(indexes): skim layer skimmable, one undescribed entry described MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An index is the skim layer: its entries decide whether a reader opens the page, so an entry that runs 250 characters is a paragraph wearing a bullet. Four entries in `tools/index.md` (`dotfiles-deploy`, `packages`, `pcie-probe`, `remote-job`) now say what the page is for in one line and let the page carry the detail — which is the whole argument for progressive disclosure having an index at all. `engineering/infra/index.md` pointed at `../sdlc.md` with no description at all; it now carries the one from that page's frontmatter, like its four siblings. Refs #64 --- omp-docs/engineering/infra/index.md | 2 +- omp-docs/tools/index.md | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/omp-docs/engineering/infra/index.md b/omp-docs/engineering/infra/index.md index be7a44a..b74dc93 100644 --- a/omp-docs/engineering/infra/index.md +++ b/omp-docs/engineering/infra/index.md @@ -12,4 +12,4 @@ Current (2025/2026) best practices for infrastructure as code, containers, orche ## See Also * [Engineering](../index.md) - The parent area. -* [SDLC and Tooling](../sdlc.md) +* [SDLC and Tooling](../sdlc.md) - git, CI/CD, testing, review, versioning, and dev environments. diff --git a/omp-docs/tools/index.md b/omp-docs/tools/index.md index 866b884..4e4598d 100644 --- a/omp-docs/tools/index.md +++ b/omp-docs/tools/index.md @@ -9,7 +9,7 @@ Configuration guides and reference material for installed software. Each tool ha * [applications](applications.md) — Default userspace applications on CachyOS with Niri — browsers, editor, terminal, file manager, audio, screenshots, gaming. * [cachyos](cachyos.md) — Arch-based rolling release with performance-optimized kernels, repositories, and gaming stack. Running on the workstation. * [comfyui](comfyui/index.md) — Node-based AI image generation engine on dankclaw. FLUX.2 klein 4B, CPU backend, systemd managed. -* [dotfiles-deploy](dotfiles-deploy.md) — How `dotfiles/` packages become live config: `deploy.sh` modes, host override packages, and the tier's hard-won laws, from media and generated theme output to systemd enable/start semantics, dconf dumps, and idle-lock watcher triage. +* [dotfiles-deploy](dotfiles-deploy.md) — How `dotfiles/` packages become live config: `deploy.sh` modes, host overrides, and the tier's laws. * [ghostty](ghostty/index.md) — GPU-accelerated terminal emulator with extensive configuration, keybindings, and shaders. * [gitea](gitea/index.md) — Private Git hosting on dankclaw. Quadlet-managed containers: Gitea, PostgreSQL, Caddy. * [handy](handy.md) — Offline speech-to-text transcription with WebKitGTK backend and Tauri v2 UI. @@ -19,9 +19,9 @@ Configuration guides and reference material for installed software. Each tool ha * [neovim](neovim.md) — Two editors in one binary: vanilla `nvim` for quick edits and the `lazyvim` NVIM_APPNAME profile, with the base16 wallpaper-palette hook and fresh-box install. * [niri](niri/index.md) — Scrollable-tiling Wayland compositor — configuration, keybindings, and architecture for the workstation. * [noctalia-shell](noctalia-shell.md) — Native desktop environment for Niri — launcher, control center, settings, lock screen, session menu, hotkey overlay, media controls, and system management. -* [packages](packages.md) — `packages/*.pkgs` desired state and the two tools that keep a machine and its manifest agreeing: `install.sh` installs the delta, `pkg-inventory.sh` reports what the machine has outgrown. -* [pcie-probe](pcie-probe.md) — HIP probes that separate PCIe link bandwidth from the engine's read path at the expert-slab size, and the refuse-by-default guard against benchmarking beside a live service. -* [remote-job](remote-job.md) — Detached job runner for `ssh` work: completion sentinels on the host replace the `tail -f | sed -n /ready/q` pattern that reported success on jobs that never finished. +* [packages](packages.md) — `packages/*.pkgs` desired state, plus `install.sh` (installs the delta) and `pkg-inventory.sh` (reports drift). +* [pcie-probe](pcie-probe.md) — HIP probes separating PCIe link bandwidth from the engine's read path; refuses to run beside a live service. +* [remote-job](remote-job.md) — Detached `ssh` jobs with completion sentinels; replaces the `tail -f | sed` readiness pattern that lied. * [tmux](tmux.md) — Terminal multiplexer configuration, extended-keys setup, keyboard input best practices, and reload/restart procedures. ## Adding a New Tool From fb36a1b0fdf87b5070fe00d67047384e5992ca74 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:19:23 -0700 Subject: [PATCH 08/16] docs(web-research): the search egress re-read from the live boxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two pages described Dankclaw's search stack from memory, and one of them invented a port. `searxng-tuning.md` claimed "`:8081` (UI) / `:8082` (JSON API)". Re-probed 2026-10-21: JSON is served on **8081** (`/search?q=…&format=json` returns results) and **8082 is not SearXNG at all** — it publishes container port 8085, which is Marreta's nginx, moved off 8080 because SearXNG owns that port inside the shared namespace. Four containers print the same port list in `podman ps` because they share gluetun's namespace, which is exactly the detail that gets mistaken for "the JSON API is on another port". The policy-drift flag now carries today's reading: `/config` still reports 262 engines and zero disabled, unchanged since 2026-10-19, so the tuned engine policy is still not live — still an operator decision, still not silently re-applied. `vpn-routing.md` gave a mullvad-shaped gluetun example and no fleet shape at all, while the bundle's own work log says the manual-WireGuard law lives "on concept pages". It lives here now, read from the running container: `VPN_SERVICE_PROVIDER=custom` + `VPN_TYPE=wireguard` + the single-peer `WIREGUARD_*` names (`protonvpn` and the plural `WIREGUARD_PEERS_*` family are both wrong for this image), `FIREWALL_ENABLED_DISABLING_IT_SHOOTS_YOU_IN_YOUR_FOOT=on` — whose name is the warning that disabling it leaks egress while every check keeps passing — and `tun0` present. The mullvad block stays as the generic illustration, now labelled as one. Refs #64 --- .../agentic-web-research/searxng-tuning.md | 32 ++++++++++--------- omp-docs/agentic-web-research/vpn-routing.md | 31 ++++++++++++++++-- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/omp-docs/agentic-web-research/searxng-tuning.md b/omp-docs/agentic-web-research/searxng-tuning.md index e39734a..4724e68 100644 --- a/omp-docs/agentic-web-research/searxng-tuning.md +++ b/omp-docs/agentic-web-research/searxng-tuning.md @@ -8,7 +8,7 @@ tags: - privacy - configuration - unverified -verified: 2026-10-19 +verified: 2026-10-21 timestamp: 2026-08-11 resource: https://docs.searxng.org --- @@ -23,25 +23,27 @@ Compose (or Podman Quadlet) with a Valkey container. This page lists the settings that matter for speed, privacy, and useful results. -> **Fleet instance:** the harness `web/searxng` provider calls the instance at -> `192.168.0.143:8081` (UI) / `:8082` (JSON API) on dankclaw. The pi-era -> [Pi Web Access](../pi/packages/pi-web-access.md) called the same host. +> **Fleet instance:** the harness `web/searxng` provider calls +> `192.168.0.143:8081` on dankclaw — HTML **and** JSON on that one port +> (`/search?q=…&format=json`, re-probed 2026-10-21). Port **8082 is not a second SearXNG +> API**: it publishes container port 8085, which is Marreta's nginx (moved off 8080 because +> SearXNG owns it in that namespace). Both ports show up against four container names because +> `searxng`, `searxng-valkey`, `gluetun` and the `marreta` pair share one network namespace. +> The pi-era [Pi Web Access](../pi/packages/pi-web-access.md) called the same host. -## Live instance (probed 2026-10-19) +## Live instance (probed 2026-10-21) User Quadlet stack on dankclaw: `searxng.container` (`ghcr.io/searxng/searxng:latest`) + `searxng-valkey.container` -(`docker.io/valkey/valkey:9-alpine`) + `gluetun.container` (VPN egress; the -containers share its network namespace, which is why `podman ps` shows :8081/ -:8082 on several names) + `marreta`/`marreta_browser`. `limiter.toml`: enabled, -`pass_searxng_org: true`. +(`docker.io/valkey/valkey:9-alpine`) + `gluetun.container` (VPN egress; the other containers +join its network namespace, hence the shared port list) + `marreta`/`marreta_browser`. +`limiter.toml`: enabled, `pass_searxng_org: true`. -**Policy drift flag:** this page's tuned engine policy (google and bing -disabled; DuckDuckGo/Wikipedia/Google News enabled — verified via `/config` on -2026-08-16) is NOT live: `/config` on 2026-10-19 reports all 262 engines -enabled, google and bing included. Either the settings override was lost in -the 2026-10 stack reinstatement or the policy was deliberately dropped. -Confirm with Frank before re-applying. +**Policy drift flag:** this page's tuned engine policy (google and bing disabled; +DuckDuckGo/Wikipedia/Google News enabled — verified via `/config` on 2026-08-16) is NOT live. +`/config` re-read 2026-10-21: 262 engines, **zero disabled**, google and bing included — +unchanged since 2026-10-19. Either the override was lost in the 2026-10 stack reinstatement or +the policy was deliberately dropped. Confirm with Frank before re-applying. ## Plan the Deployment diff --git a/omp-docs/agentic-web-research/vpn-routing.md b/omp-docs/agentic-web-research/vpn-routing.md index 78313bb..735ae13 100644 --- a/omp-docs/agentic-web-research/vpn-routing.md +++ b/omp-docs/agentic-web-research/vpn-routing.md @@ -8,7 +8,7 @@ tags: - networking - proxy - unverified -verified: 2026-08-28 +verified: 2026-10-21 timestamp: 2026-08-19 resource: https://docs.searxng.org/admin/settings/settings_outgoing.html --- @@ -19,7 +19,10 @@ SearXNG talks to many upstream engines. Some of them rate-limit or block the sou This page gives four working patterns. Choose the one that matches how you run SearXNG. -> **Your setup:** SearXNG runs on another host at `192.168.0.143:8081` (dankclaw). Apply the Docker patterns on that host. The proxy pattern works from any host. +> **Fleet instance:** SearXNG runs on dankclaw (`192.168.0.143:8081`, HTML and JSON alike) +> inside gluetun's network namespace — Pattern 2 below, in Quadlet rather than Compose. The +> live shape is in [Fleet instance on dankclaw](#fleet-instance-on-dankclaw), read 2026-10-21. +> The proxy pattern (1) works from any host. ## The Goal @@ -156,11 +159,33 @@ This pattern places the whole SearXNG process inside the tunnel namespace. The h | Dedicated network | Medium | High | No | | Inside VPN container | Medium | Highest | No | +## Fleet instance on dankclaw + +Read from the running container 2026-10-21 (`podman inspect gluetun`, `podman ps`): + +* **`VPN_SERVICE_PROVIDER=custom` with `VPN_TYPE=wireguard`.** The provider is Proton VPN, + configured as *manual* WireGuard: the single-peer `WIREGUARD_*` names + (`WIREGUARD_PRIVATE_KEY`, `WIREGUARD_PEER_PUBLIC_KEY`, `WIREGUARD_ENDPOINT_IP`) are what + gluetun reads. `VPN_SERVICE_PROVIDER=protonvpn` is wrong here, and so is the plural + `WIREGUARD_PEERS_*` family — that shape belongs to a different gluetun major. +* **One namespace, four names.** `searxng`, `searxng-valkey`, `marreta` and `marreta_browser` + join gluetun's network namespace, so `podman ps` prints the same published-port list against + all of them: `8081→8080` is SearXNG, `8082→8085` is Marreta's nginx, which moved off 8080 + because SearXNG owns it inside that namespace. +* **`FIREWALL_ENABLED_DISABLING_IT_SHOOTS_YOU_IN_YOUR_FOOT=on`.** The name is the warning: + turning it off sends the container's egress out the plain internet while every "VPN is + configured" check keeps passing — results then quietly come from the host IP. +* **Health:** tunnel presence is `podman exec gluetun sh -c 'test -e /sys/class/net/tun0'` + (true on this box). gluetun ships no curl, so an egress-IP check needs a companion container + on its network — see [Verify the Route](#verify-the-route). + ## Decisions That Matter - A SOCKS proxy keeps the per-engine control. Use Pattern 1. - Gluetun gives the cleanest full-tunnel setup. Use Pattern 2. -- Mullvad ships its own WireGuard keys for Gluetun. +* Copying an upstream example? Read the provider and env shape from + [Fleet instance on dankclaw](#fleet-instance-on-dankclaw) first: the mullvad-shaped block in + Pattern 2 is an illustration, not this fleet's config. - An OpenVPN-only provider works with Pattern 3 or 4. ## Verify the Route From 54bb0202c2e7cb5602d76b87b3c99ff3b05f74f2 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:22:37 -0700 Subject: [PATCH 09/16] docs(system): the fleet re-read from the boxes, and the math that no longer belongs here MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three pages described the fleet; two of them still described the pre-September arrangement. Re-read live on 2026-10-21 (`/v1/models`, `systemctl`, `lspci`, `nproc`, `resolvectl`): - `system/network.md` called Dankclaw the "large inference model" box and Dankserver the "small" one — that is the topology from before the RTX 3090 moved in September. The served ids today are `frank-archon` on Dankserver:7770 (the main model) and `frank-dragoon` (:7770) + `frank-zealot` (:7771) on Dankclaw. The gateway and the DNS pin were re-read too. - `system/dankserver.md` claimed `frank-medium` is *masked*: the unit file does not exist (`is-enabled` answers `not-found`), which is a different fact with a different remedy. And it claimed 8C/16T: this 7700X exposes **8 threads** (`nproc` = 8), so anything sized for 16 is sized for a machine that is not this one. - `system/dankclaw.md` transcribed the engine's slot/KV/context math — `2×262,144 slots over one 524,288 pool, unquantized f16 K+V, MTP off` — while the live unit says `3x200704 slots over one 602112 pool, f16 K + q8_0 V, MTP armed`, and dragoon's ctx had moved too. That math belongs to the serverAppConfigs runbook the page already names: the unit's `ExecStart` is a wrapper script, so any copy here rots on the next engine change. The page now keeps the contract — id, unit, port, engine family, which GPU, and how to prove it — and disowns the numbers, with the reason stated so the next agent does not re-transcribe them. Also recorded, because it is how this audit nearly missed a stopped service: `curl :7770/v1/models` answered normally while `frank-dragoon.service` sat `inactive` with `Result=exit-code` — the container is running outside systemd. A serving port is not a healthy unit. (Flagged for Frank separately; no production unit was touched.) Refs #64 --- omp-docs/system/dankclaw.md | 26 +++++++++++++++++++------- omp-docs/system/dankserver.md | 10 ++++++---- omp-docs/system/network.md | 13 +++++++------ 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/omp-docs/system/dankclaw.md b/omp-docs/system/dankclaw.md index 64b2c6f..22d2cad 100644 --- a/omp-docs/system/dankclaw.md +++ b/omp-docs/system/dankclaw.md @@ -11,7 +11,7 @@ tags: generated: by: chief-designer/agent at: 2026-08-26T00:00:00Z -verified: 2026-10-19 +verified: 2026-10-21 --- # Dankclaw @@ -20,10 +20,12 @@ Strix Halo mini-PC carrying the fleet's secondary models and the bulk of the service stack. Two GPU-class devices: the Radeon 8060S iGPU with 119 GiB unified memory, and the RTX 3090 moved over from Dankserver in September 2026. -## Service Stack (live census, 32 containers, 2026-10-19) +## Service Stack -User-scope Podman Quadlet units (`~/.config/containers/systemd/`) unless -noted. Full names; detail pages where they exist. +Re-derive the census rather than trusting this table — the stack changes weekly: +`podman ps --format '{{.Names}} {{.Ports}}'` (user) plus the root units +(`systemctl list-units --all | grep -E 'gitea|caddy|frank'`). User-scope Podman Quadlet units +(`~/.config/containers/systemd/`) unless noted. Full names; detail pages where they exist. | Group | Containers | |-------|-----------| @@ -47,8 +49,18 @@ noted. Full names; detail pages where they exist. | Model ID | Unit | Endpoint | Engine / weights | |----------|------|----------|------------------| -| `frank-dragoon` | `frank-dragoon.service` (root) | `:7770` (container `frank-dragoon-halogen`, 7770→8731) | halogen-flash-server container (`ghcr.io/peonist-ai/halogen-flash-server`, digest-pinned) — Quat3rnion NATIVE v2 abliterated Qwen3.8-Flash-Next, MTP spec decode, 2 slots over ONE **401,408**-position pool at ctx **200,704**, vision ON, **Strix Halo iGPU** (unified memory) | -| `frank-zealot` | `frank-zealot.service` (root) | `:7771` | llama.cpp (PrismML fork, build 10777) — Ternary-Bonsai-**2-9B** PQ2_0-MTP GGUF (qwen35 hybrid), 2×262,144 slots over one 524,288 pool, **unquantized f16 K+V**, MTP off, mmproj Q8_0 vision, **RTX 3090** | +| `frank-dragoon` | `frank-dragoon.service` (root) | `:7770` (container `frank-dragoon-halogen`, 7770→8731) | halogen-flash-server container (`ghcr.io/peonist-ai/halogen-flash-server`, digest-pinned) — Quat3rnion NATIVE v2 abliterated Qwen3.8-Flash-Next, MTP spec decode, vision ON, on the **Strix Halo iGPU** (unified memory) | +| `frank-zealot` | `frank-zealot.service` (root) | `:7771` (container `frank-zealot`) | llama.cpp, PrismML fork — Ternary-Bonsai-2-9B PQ2_0-MTP GGUF (qwen35 hybrid) + refusal adapter, mmproj vision, on the **RTX 3090**; flags come from `~/serverAppConfigs/scripts/frank-zealot.env` + `serve-frank-zealot.sh` | + +Slot counts, KV-cache types and context math are deliberately **not** transcribed here: the +unit `ExecStart` is a wrapper script and the numbers move with every engine change, so they +belong to the serverAppConfigs runbooks named below. What an agent needs from this page is the +contract — id, unit, port, engine family, which GPU — and the two commands that prove it live. + +**A serving port is not a healthy unit.** On 2026-10-21 `curl :7770/v1/models` answered +normally while `frank-dragoon.service` sat `inactive` with `Result=exit-code`: the container had +been started outside systemd. Health is `systemctl is-active frank-dragoon` (root), not the +curl. Harness-side context windows (`agent/models.yml`): dragoon 155,648, zealot 200,704 per slot. The models.yml display labels "(dankclaw-gpu)" / @@ -62,7 +74,7 @@ curl http://192.168.0.143:7770/v1/models # dragoon curl http://192.168.0.143:7771/v1/models # zealot ``` -## Hardware (verified live 2026-10-18) +## Hardware (verified live 2026-10-21) | Property | Value | |----------|-------| diff --git a/omp-docs/system/dankserver.md b/omp-docs/system/dankserver.md index a96588c..438087e 100644 --- a/omp-docs/system/dankserver.md +++ b/omp-docs/system/dankserver.md @@ -9,7 +9,7 @@ tags: - server generated: by: chief-designer/agent -verified: 2026-10-18 +verified: 2026-10-21 status: stable --- @@ -41,7 +41,9 @@ Dankclaw. | **Engine** | radiance, tensor-parallel 2 across both R9700s | | **Context Window** | 200,000 tokens (harness `models.yml`; served pool may differ — see runbook) | -`frank-medium` (former dual-GPU mid tier) is masked and retired. +`frank-medium` (the former dual-GPU mid tier) no longer exists on this box — +`systemctl list-unit-files | grep frank-` returns only `frank-archon.service`. It was retired, +not masked: on 2026-10-21 `systemctl is-enabled frank-medium` answers `not-found`. ## Usage @@ -59,11 +61,11 @@ curl http://192.168.0.235:7770/v1/chat/completions \ -d '{"model":"frank-archon","messages":[{"role":"user","content":"Hello"}],"max_tokens":32}' ``` -## Hardware (verified live 2026-10-18) +## Hardware (verified live 2026-10-21) | Property | Value | |----------|-------| -| **CPU** | AMD Ryzen 7 7700X (8C/16T, Raphael iGPU on board) | +| **CPU** | AMD Ryzen 7 7700X — 8 cores, **8 threads exposed** (`nproc` = 8; SMT is not enabled, so do not plan for 16) | | **GPU** | 2× AMD Radeon AI PRO R9700 (Navi 48, 32 GiB each) | | **RAM** | 246 GiB | | **Swap** | effectively none (~0.1 GiB) — the 64 GiB swapfile is on Dankclaw, not here | diff --git a/omp-docs/system/network.md b/omp-docs/system/network.md index 6f4ca87..90df44f 100644 --- a/omp-docs/system/network.md +++ b/omp-docs/system/network.md @@ -8,7 +8,7 @@ tags: - systemd-resolved - workstation - unverified -verified: 2026-08-28 +verified: 2026-10-21 --- # Network @@ -23,11 +23,12 @@ bites every Linux workstation with both Wi-Fi and Ethernet plugged in. | Host | IP | Role | |------|----|------| | Router | 192.168.0.1 | DHCP; hands out the DNS server to clients | -| Dankclaw | 192.168.0.143 | Pi-hole DNS, Caddy edge, Gitea, large inference model | -| Dankserver | 192.168.0.235 | small inference model | +| Dankclaw | 192.168.0.143 | Pi-hole DNS, Caddy edge, Gitea, service stack, secondary inference (`frank-dragoon` :7770, `frank-zealot` :7771) | +| Dankserver | 192.168.0.235 | Main inference — `frank-archon` on 2× R9700 (TP=2), :7770 | | Clients | DHCP from 192.168.0.0/24 | workstations, phones | -The router hands clients `192.168.0.143` as their DNS server; Pi-hole answers +The router (default gateway `192.168.0.1`, both re-read 2026-10-21) hands clients +`192.168.0.143` as their DNS server; Pi-hole answers every `*.franksdank.cloud` name with the LAN address of the box that serves it. Names that resolve to the public WAN address time out on the LAN because the G36 has no NAT loopback — a symptom that looks like "the network is @@ -88,8 +89,8 @@ A single successful `dig` proves nothing on a flapping resolver. ## Servers -- [Dankserver](/system/dankserver.md) — small inference model -- [Dankclaw](/system/dankclaw.md) — large inference model, DNS, proxy +- [Dankserver](/system/dankserver.md) — main model, `frank-archon` +- [Dankclaw](/system/dankclaw.md) — secondary models, DNS, edge, forge ## See Also From 047f264fcff881ca8c52e274bf24733589809271 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:23:25 -0700 Subject: [PATCH 10/16] docs(ghostty): the index stops carrying a second copy of the reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `tools/ghostty/index.md` had its own Custom Shaders section and its own eight-row default keybinding table. `configuration.md` owns both, in more detail, and the copies had already drifted: the index claimed `ctrl+` raises the font size where the reference's appendix lists `ctrl+=` / `ctrl++`. An index exists to route a reader to the page that owns the fact; a second summary of the same facts just guarantees two versions that age differently. What the index keeps is the routing plus the one fact worth having at hand (`ctrl+shift+,` reloads the config), and the config-syntax "Key Points" — repeatable options, `option = ""` resets, `?` optional includes — which the reference never states as a set, only per option. Refs #64 --- omp-docs/tools/ghostty/index.md | 36 +++++++++------------------------ 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/omp-docs/tools/ghostty/index.md b/omp-docs/tools/ghostty/index.md index 435824b..8922d28 100644 --- a/omp-docs/tools/ghostty/index.md +++ b/omp-docs/tools/ghostty/index.md @@ -88,35 +88,17 @@ palette-generate = true --- -## Custom Shaders +## Details that live in the reference -Ghostty supports Shadertoy-compatible GLSL shaders: +* [Custom Shaders](configuration.md#custom-shaders) — `custom-shader` (repeatable) and + `custom-shader-animation`, plus the uniform list (`iChannel0`, resolution, time, cursor, + palette, focus). +* [Default Keybindings](configuration.md#appendix-default-keybindings) — the full table. The one + you will want first: `ctrl+shift+,` runs `reload_config`. -```ini -custom-shader = ~/.config/ghostty/shaders/vignette.glsl -custom-shader-animation = true -``` - -Shaders receive uniforms for the terminal screen texture (`iChannel0`), cursor position, palette colors, and focus state. - ---- - -## Default Keybindings - -| Keybind | Action | -|---------|--------| -| `ctrl+shift+,` | `reload_config` | -| `ctrl+` / `ctrl+=` | `increase_font_size:1` | -| `ctrl+-` | `decrease_font_size:1` | -| `ctrl+0` | `reset_font_size` | -| `ctrl+shift+f` | `start_search` | -| `ctrl+shift+a` | `select_all` | -| `ctrl+Enter` | `toggle_fullscreen` | -| `ctrl+shift+P` | `toggle_command_palette` | - -See [Configuration Reference](configuration.md#appendix-default-keybindings) for the full table. - ---- +An earlier copy of this index carried its own eight-row keybinding table and its own shader +example. Both were subsets of the reference and neither was kept in step with it, which is the +usual fate of a second copy. ## Cross-References From 8b87b5ae1afae72268d64c31a210da4c5e7988f1 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:24:11 -0700 Subject: [PATCH 11/16] docs(dedupe): one owner for the finish rule, and the Gitea index routes instead of restating MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two independent copies of the same law, and each page cited its own numbers. The finish rule — stop when independent sources agree, when the retrieval budget is spent, or when every claim is met; report gaps, never invent a source — stood in full in `agentic-search-best-practices.md` (with the loop caps: 20 steps, re-plan every 4) and again in `research-effectiveness.md` without them. The operational page owns it; the measurement page keeps the pointer and the one thing that is its business, which is that unbounded loops are why "infinite re-search" is a named failure mode. `tools/gitea/index.md` was a second copy of its own directory: an architecture table, a management command table, a data-persistence path table and a boot-persistence paragraph, every one of them a subset of `architecture.md`, `management.md` and `data-persistence.md` — verified line by line before cutting, including the two facts that looked unique to the index (the `dns_enabled: false` default network, the `/opt/gitea` leftover) and turn out to be owned upstream with more detail. The index now answers "which page owns my question" and gives three one-line orientation facts. Gitea's live version was re-read while I was in there: still 1.27.3. Refs #64 --- .../research-effectiveness.md | 12 ++-- omp-docs/tools/gitea/index.md | 63 +++++++------------ 2 files changed, 28 insertions(+), 47 deletions(-) diff --git a/omp-docs/agentic-web-research/research-effectiveness.md b/omp-docs/agentic-web-research/research-effectiveness.md index f264fcf..f3db6ba 100644 --- a/omp-docs/agentic-web-research/research-effectiveness.md +++ b/omp-docs/agentic-web-research/research-effectiveness.md @@ -96,13 +96,11 @@ Pi already runs a similar model with its parallel `researcher` and `scout` subag ## The Finish Rule -Research must end. Stop when any one is true: - -1. Two or more independent sources agree on the answer. -2. The retrieval budget is spent. -3. The required confidence is met for every claim. - -Report gaps honestly. Do not invent a source to fill one. +The rule itself — stop when independent sources agree, when the retrieval budget is spent, or +when every claim has the required confidence, and report the gaps instead of inventing a source — +is owned by [Agentic Search Best Practices](agentic-search-best-practices.md#the-finish-rule), +the page an agent reads before it starts a loop. What belongs here is the measurement: unbounded +loops are why "infinite re-search" is a named failure mode in the next section. ## Failure Modes and Guardrails diff --git a/omp-docs/tools/gitea/index.md b/omp-docs/tools/gitea/index.md index 128d095..fb1b4a3 100644 --- a/omp-docs/tools/gitea/index.md +++ b/omp-docs/tools/gitea/index.md @@ -4,56 +4,39 @@ Private Git hosting on dankclaw. Two quadlet-managed containers in the rootless **Host:** dankclaw (192.168.0.143 LAN) **Config:** `~/.config/containers/systemd/gitea-{postgres,server}.container` + `gitea.network` -**Local access:** `http://dankclaw:3000` — Gitea 1.27.3 (`/api/v1/version`, probed 2026-10-19) +**Local access:** `http://dankclaw:3000` — Gitea 1.27.3 (`/api/v1/version`, re-read 2026-10-21) **Domain:** `https://gitea.franksdank.cloud` (live; served by fleet Caddy, DNS-01 TLS) -## Architecture +## What lives where -| Container | Unit | Port | Purpose | -|---|---|---|---| -| `gitea-server` | `gitea-server.service` | 3000 (HTTP), 2222 (SSH) | Git web UI, API, SSH daemon | -| `gitea-postgres` | `gitea-postgres.service` | 5432 (internal) | Database | -| (network) | `gitea-network.service` | — | quadlet `gitea.network` bring-up | - -The containers share the private network `systemd-gitea` (quadlet `gitea.network`, DNS enabled). The automatic `podman` default network has DNS disabled; do not run the stack on it. - -## Management - -All commands run as `dank` on dankclaw. - -| Action | Command | +| Question | Owner | |---|---| -| Start all | `systemctl --user start gitea-postgres gitea-server` | -| Stop all | `systemctl --user stop gitea-server gitea-postgres` | -| Restart Gitea only | `systemctl --user restart gitea-server` | -| Follow logs | `podman logs -f gitea-server` or `journalctl --user -u gitea-server -f` | -| Check status | `systemctl --user status gitea-postgres gitea-server` | +| Containers, unit names, network topology, ports, env wiring | [architecture](architecture.md) | +| Start / stop / logs / upgrade; boot persistence (linger + `[Install] WantedBy=default.target`); incident log | [management](management.md) | +| Data layout on host bind mounts, UID ownership, backup and restore, the `/opt/gitea` leftover | [data-persistence](data-persistence.md) | +| Token-based HTTP access from clients, the credential store, git 2.56 store-format trap | [client-access](client-access.md) | -## Data Persistence +One-line answers, so you know which row to open: -All data lives on host bind mounts under `/home/dank/Containers/gitea/` (fleet storage standard since 2026-09-01; verified live 2026-10-18). Containers are ephemeral. Stopping a quadlet unit removes its container; the next start recreates it. Volumes survive. The old `/opt/gitea/` tree remains on disk as a leftover — do not use it. +* **Two containers** (`gitea-server` :3000/:2222, `gitea-postgres` internal) plus a quadlet + network unit, all in user `dank`'s rootless session. +* **Data survives everything**: host bind mounts under `/home/dank/Containers/gitea/`, env file + mode 0600; stopping a unit removes its container and the next start recreates it. +* **Boot**: user linger for `dank` plus `[Install] WantedBy=default.target` on each unit — no + login session required. The podman-compose stack had neither and did not survive a reboot + (2026-08-16, written up in [management](management.md)). -| Path | Contents | -|---|---| -| `/home/dank/Containers/gitea/data/` | Git repositories, app.ini, attachments | -| `/home/dank/Containers/gitea/postgres/` | PostgreSQL data files | -| `/home/dank/Containers/gitea/env` | EnvironmentFile (mode 0600; `GITEA_DB_PASSWORD`) | - -See [data-persistence](/tools/gitea/data-persistence.md) for backup and restore. - -## Boot Persistence - -User-level linger is enabled for `dank`. The units start at host boot without a login. Each unit carries `[Install] WantedBy=default.target`; the podman generator applies it. The legacy podman-compose stack had no units and did not survive reboots (incident 2026-08-16). - -See [management](/tools/gitea/management.md) for boot persistence details and troubleshooting. +This index used to carry its own copies of all four pages' tables. They went: a second copy of +an operational table is a future contradiction, and every one of them was already a subset. ## Repo Copies -The quadlet files and `.env` templates are tracked in the `serverAppConfigs` deploy repo; the sibling vault page `Infrastructure/Gitea.md` (in `~/Projects/serverAppConfig/serverAppConfigs-docs/`) is the deploy-side source of truth. Secrets live only on the host under `Containers/gitea/` (mode 0600), never in either docs tree. +The quadlet files and `.env` templates are tracked in the `serverAppConfigs` deploy repo; the +sibling vault page `Infrastructure/Gitea.md` (in `~/Projects/serverAppConfig/serverAppConfigs-docs/`) +is the deploy-side source of truth. Secrets live only on the host under `Containers/gitea/` +(mode 0600), never in either docs tree. ## See Also -* [Gitea Architecture](architecture.md) — container architecture, network topology, ports. -* [Gitea Data Persistence](data-persistence.md) — data layout, ownership mapping, backup strategy. -* [Gitea Management](management.md) — operations, upgrades, backups, incident log. -* [Gitea Client Access](client-access.md) — token-based HTTP access from clients, credential store, git 2.56 store-format trap. +* [Dankclaw](/system/dankclaw.md) — the host, and its container census. +* [Caddy](/caddy/index.md) — the fleet edge proxy that fronts `gitea.franksdank.cloud`. From ec19771b8ba83cb370a410670c0485e232483a3b Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:25:14 -0700 Subject: [PATCH 12/16] docs(llama-cpp): deployment detail goes back to the setup page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The overview page carried `docker run` invocations (including a CUDA variant, on a fleet with no NVIDIA card left in it) and the `LLAMA_ARG_` environment rule, while `setup.md` — the page whose own intro promises "build instructions, Docker, and systemd deployment" — carried the Compose example and the same env form in use. Two places to look, one place to be wrong. `setup.md` now states the env-prefix rule where the flags live (and why units prefer it: one flag per line is auditable, a forty-flag command line is not), and the overview keeps only the API surface and the GGUF format, pointing at the sections that absorbed the rest. Refs #64 --- omp-docs/tools/llama-cpp/index.md | 29 +++-------------------------- omp-docs/tools/llama-cpp/setup.md | 8 ++++++++ 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/omp-docs/tools/llama-cpp/index.md b/omp-docs/tools/llama-cpp/index.md index 8425263..b086ec3 100644 --- a/omp-docs/tools/llama-cpp/index.md +++ b/omp-docs/tools/llama-cpp/index.md @@ -124,31 +124,8 @@ print(completion.choices[0].message.content) --- -## Docker - -```bash -docker run -p 8080:8080 -v /models:/models \ - ghcr.io/ggml-org/llama.cpp:server \ - -m /models/model.gguf --host 0.0.0.0 --port 8080 - -# CUDA variant -docker run -p 8080:8080 --gpus all \ - ghcr.io/ggml-org/llama.cpp:server-cuda \ - -m /models/model.gguf --n-gpu-layers 99 -``` - ---- - -## Environment Variables - -All CLI options can be set via `LLAMA_ARG_` prefix: - -```bash -LLAMA_ARG_MODEL=/models/model.gguf LLAMA_ARG_CTX_SIZE=8192 llama-server -``` - ---- - ## Cross-References -- See [Setup Guide](setup.md) for build instructions, GPU offloading, and deployment. +- See [Setup Guide](setup.md) for build instructions, GPU offloading, the `LLAMA_ARG_*` + environment form, the Docker Compose example, and running under systemd. Deployment lives + there; this page is the API surface and the GGUF format. diff --git a/omp-docs/tools/llama-cpp/setup.md b/omp-docs/tools/llama-cpp/setup.md index 1bc7b35..b220d9b 100644 --- a/omp-docs/tools/llama-cpp/setup.md +++ b/omp-docs/tools/llama-cpp/setup.md @@ -111,6 +111,14 @@ cmake --build build --config Release -t llama-server | `-lora FNAME` | LoRA adapter (repeatable) | — | | `-ngl, --gpu-layers N` | Layers to offload to GPU | auto | +### Environment Variables + +Every CLI option can be set from the environment instead: prefix the upper-snake form of the +long flag with `LLAMA_ARG_` — `LLAMA_ARG_MODEL=/models/model.gguf LLAMA_ARG_CTX_SIZE=8192 +llama-server`. This is how the container and systemd units pass configuration (see the Compose +example below), because a unit file with one flag per line is auditable and a shell command line +with forty is not. + ### Context & Performance | Flag | Description | Default | From d3683946566b9b50f763a12f39af8bbfa76e42cd Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:25:56 -0700 Subject: [PATCH 13/16] docs(research): break the two worst wall paragraphs The chezmoi overview was one 488-character sentence-block; the git-crypt mechanism and verdict lines were the same shape. Reflowed to ~100 columns with no wording lost, so the pages read the same in an editor as they do rendered. Refs #64 --- omp-docs/research/chezmoi-evaluation.md | 9 ++++++++- omp-docs/research/secrets-for-agents.md | 9 +++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/omp-docs/research/chezmoi-evaluation.md b/omp-docs/research/chezmoi-evaluation.md index d9233df..d381900 100644 --- a/omp-docs/research/chezmoi-evaluation.md +++ b/omp-docs/research/chezmoi-evaluation.md @@ -17,7 +17,14 @@ tags: ## Overview -**chezmoi** is a Go-binary (MIT license, 21k+ GitHub stars) that manages dotfiles across diverse machines by declaring desired state in a source directory and applying the delta to the target directory. It is the only dotfiles tool in the [dotfiles comparison table][comparison] that provides private files, whole-file encryption, password-manager integration, template-based machine-to-machine differences, partial-file management, and symlink differences — all in a single distribution. +**chezmoi** is a Go binary (MIT license, 21k+ GitHub stars) that manages dotfiles across +diverse machines: you declare the desired state in a source directory and it applies the delta +to the target directory. + +It is the only dotfiles tool in the [dotfiles comparison table][comparison] that provides all +five of these in a single distribution: private files, whole-file encryption, +password-manager integration, template-based machine-to-machine differences, and partial-file +management plus symlink differences. The current release is **v2.72.0** (2026-08-02). It is not yet installed on this machine. diff --git a/omp-docs/research/secrets-for-agents.md b/omp-docs/research/secrets-for-agents.md index fada73e..98e32f0 100644 --- a/omp-docs/research/secrets-for-agents.md +++ b/omp-docs/research/secrets-for-agents.md @@ -42,7 +42,9 @@ The question: for each tool, does a secret ever appear in plaintext where an age ### How it works -git-crypt uses git clean/merge filters to encrypt specific files at commit time and decrypt them at checkout. The `.gitattributes` file declares which paths to encrypt. Encryption uses AES-256-CTR with a synthetic IV derived from SHA-1 HMAC. +git-crypt uses git clean/merge filters to encrypt specific files at commit time and decrypt +them at checkout. The `.gitattributes` file declares which paths to encrypt. Encryption is +AES-256-CTR with a synthetic IV derived from SHA-1 HMAC. ### Agent behavior @@ -64,7 +66,10 @@ git-crypt uses git clean/merge filters to encrypt specific files at commit time ### Agent safety verdict -**Moderate.** Plaintext appears on disk post-checkout but the agent doesn't explicitly decrypt — git does it silently. However, any agent that opens the file sees plaintext. In a shared repo scenario, any collaborator who gets the GPG private key gets full access to all history. +**Moderate.** Plaintext appears on disk post-checkout, but the agent never decrypts — git does +it silently inside the filter pipeline. Any agent that opens the file still sees plaintext. +And in a shared repo, any collaborator who gets the GPG private key gets full access to all +history. --- From e0b1466f74694777307457f0e0226495af7368f6 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:26:24 -0700 Subject: [PATCH 14/16] docs(searxng): the upstream engine count is an order of magnitude, not a fact The intro carried "388 engine entries" from a 2026-10-19 probe; upstream is at 385 today. The number moves every week, which makes a precise one a liability in a runbook. The page now states the size class, names `/config` as the only authority for what an instance loads, and keeps the fleet's live 262 with today's date. Refs #64 --- omp-docs/agentic-web-research/searxng-tuning.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/omp-docs/agentic-web-research/searxng-tuning.md b/omp-docs/agentic-web-research/searxng-tuning.md index 4724e68..ebc8b27 100644 --- a/omp-docs/agentic-web-research/searxng-tuning.md +++ b/omp-docs/agentic-web-research/searxng-tuning.md @@ -15,11 +15,12 @@ resource: https://docs.searxng.org # SearXNG Tuning -SearXNG is a metasearch engine. It aggregates hundreds of upstream search -services into one result set (upstream `settings.yml` defines 388 engine -entries at master, probed 2026-10-19; the live fleet instance loads 262). -Configuration lives in `settings.yml`. The recommended deployment is Docker -Compose (or Podman Quadlet) with a Valkey container. +SearXNG is a metasearch engine: it aggregates upstream search services into one result set. +Upstream ships several hundred engine entries (385 in `settings.yml` at master, read +2026-10-21 — the count drifts every week, so treat it as an order of magnitude, not a fact); +what your instance actually loads is what `/config` reports, and the fleet's loads **262** +(re-read 2026-10-21). Configuration lives in `settings.yml`. The recommended deployment is +Docker Compose (or Podman Quadlet) with a Valkey container. This page lists the settings that matter for speed, privacy, and useful results. From 64bb23b32956df94934d1cae32dfbcae8b346111 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:40:59 -0700 Subject: [PATCH 15/16] docs(log): one dated section per day, and the sweep's lesson learned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The day had three `## 2026-10-21` headings — three logs wearing one date, which is the sprawl the spec's one-dated-section rule exists to prevent. One date now, one `###` per campaign. `okf-maintenance.md` states the multi-campaign shape explicitly so nobody has to infer it, and names `AGENTS.md` as the co-owner of the entry law so an editor changes both. The new entry records what the dedupe sweep actually taught: an index that restates its own reference pages becomes the stale authority, and a subagent ledger is a claim until it is re-read against the file it accuses — most rows returned by the audit fan-out were checkably false, and half the ledgers never arrived at all. `skills/ecosystem.md` gains the `resource:` frontmatter the bundle's other externally-sourced pages carry. Refs #64 --- omp-docs/log.md | 41 ++++++++++++++++++++++++-- omp-docs/references/okf-maintenance.md | 5 +++- omp-docs/skills/ecosystem.md | 1 + 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/omp-docs/log.md b/omp-docs/log.md index 933debf..14e561c 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -22,7 +22,9 @@ not restated here — entry, then pointer. That is what the 2026-10-21 audit pas newest twelve entries: the facts stayed, the duplicates went, `git log --follow` keeps the long form. -## 2026-10-21 — reviewing a day of work, and finding checks that lie +## 2026-10-21 + +### reviewing a day of work, and finding checks that lie Two-axis review (Standards / Spec) of 51 commits, 30 findings, on `fix/60-review-findings`. Refs #60, merged PR #61. The findings were one shape: **a check that reports confidence it did @@ -55,7 +57,7 @@ exclusion list drifted apart, `.gitattributes` justified installing `git-lfs` fo that same week's diff untracked, and the one-log rule had four owners. A coverage claim names what was measured; a tool justified by dead config cannot survive the config. -## 2026-10-21 — an effort map folding against rejections that never existed +### an effort map folding against rejections that never existed Refs #62. `agent/models.yml` rewrote `minimal→low`, `high→xhigh`, `max→xhigh` for frank-dragoon to dodge 400s the engine does not send — flash-server's own error names its allow-list and the @@ -68,7 +70,7 @@ exactly its last capture** — settle it through a logging proxy, not by reading pass restored the three pre-callsign model ids the 2026-10-08 rename had overwritten inside its own explanatory comments. -## 2026-10-21 — a documented mode the panel never had +### a documented mode the panel never had The home ultrawide ran at 60 Hz for weeks because `desk-1-home.kdl` asked for `3440x1440@239.999` and the panel advertises `@239.998`. One character: niri matches mode @@ -81,6 +83,39 @@ at once (`wlr-randr` listed as installed while `pacman -Qs wlr` matches nothing) hardware, then destroy the stale copies. A correction is itself a measurement: this entry's first pass published a second wrong hardware fact, with a `verified:` date stamped on it. +### a dedupe sweep, and scouts whose ledgers did not survive contact with the files + +Refs #64. Owners moved, not words. The ghostty and Gitea indexes stopped carrying second copies of +the pages they route to — Gitea's index restated the container table, the boot-persistence law, the +management commands, and the data layout while its own See Also list named the four pages that own +them, and an index that restates an operational table becomes the second authority, which is the +one that goes stale. The llama.cpp overview handed Docker and the `LLAMA_ARG_*` rule back to the +setup page that had always promised them; the search finish rule has one owner again; fleet +hardware and the SearXNG engine count were re-read from the boxes rather than re-derived, and the +count is now stated as a size class because it moves weekly. + +Two laws no other page owns: + +- **A scout's ledger is a claim, not a finding.** The checkable-false share of the returned rows + was large: three pages "missing an H1" that have one; a 215-line `omp-docs/AGENTS.md` that does + not exist; a wikilink-and-wall defect in `core/settings.md` that a commit from that same morning + had already removed; `core/subagent-limits.md` called superseded while `agent/config.yml` still + carries `maxRecursionDepth: 1` and `maxRuntimeMs: 1800000` on the very lines the page cites. + Nothing entered the diff until it was re-read against the file it accused. +- **A ledger written to a file is not delivered.** One scout yielded against a zero-byte file; + three more wrote their path with the tool-call parameter block embedded in the filename, so their + ledgers exist under names no caller can ask for. Ask for the ledger as the yield text, then read + it before repeating it. + +Fan-out, measured the hard way: seven audit subagents on one local endpoint produced nothing for +seventeen minutes. `task.maxConcurrency` is 2, so five were queued while two thrashed, and +`proc://` labels a queued task `running` — which is not the claim it sounds like. Cancelling five +let the survivors land within minutes. + +The day's three `## 2026-10-21` sections are now one section with four subsections, which is what +the spec's one-dated-section-per-day rule always meant; [references/okf-maintenance.md](references/okf-maintenance.md) +says so explicitly now. + ## 2026-10-20 — one work log per bundle The bundle had two: this page and `core/log.md` — two entries of pure migration bookkeeping and diff --git a/omp-docs/references/okf-maintenance.md b/omp-docs/references/okf-maintenance.md index 2846550..3111e0b 100644 --- a/omp-docs/references/okf-maintenance.md +++ b/omp-docs/references/okf-maintenance.md @@ -49,7 +49,10 @@ Details: [secret gate](/tools/index.md). - **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. + file is added or removed. Several campaigns on one day share the section: a single + `##` date, one `###` per campaign — three `## 2026-10-21` headings is three logs + wearing one date. The entry law is also restated in the repo's `AGENTS.md` (the + always-loaded tier); change one, change the other. ### Common Mistakes diff --git a/omp-docs/skills/ecosystem.md b/omp-docs/skills/ecosystem.md index c785e51..53ea1ef 100644 --- a/omp-docs/skills/ecosystem.md +++ b/omp-docs/skills/ecosystem.md @@ -7,6 +7,7 @@ tags: - skills-sh - ecosystem - registry +resource: https://skills.sh timestamp: 2026-10-19 verified: 2026-10-19 --- From de7dd6f8970238215a9db672f69f53478347b934 Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 21 Oct 2026 02:43:15 -0700 Subject: [PATCH 16/16] docs(links): five See Also links had no text at all A generated See Also pass left `- ](index.md)` style links on the gitea pages, tools/cachyos.md, and tools/applications.md. Rendered, an empty-text link is a bullet with nothing clickable in it, so it reads as a formatting quirk and never as the broken reference it is. All five now name the page they point at and use the absolute bundle path the spec recommends; the grep that finds them is recorded in the log so the sweep is repeatable. Refs #64 --- omp-docs/log.md | 5 +++++ omp-docs/tools/applications.md | 2 +- omp-docs/tools/cachyos.md | 2 +- omp-docs/tools/gitea/architecture.md | 2 +- omp-docs/tools/gitea/data-persistence.md | 2 +- omp-docs/tools/gitea/management.md | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/omp-docs/log.md b/omp-docs/log.md index 14e561c..fee0472 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -112,6 +112,11 @@ seventeen minutes. `task.maxConcurrency` is 2, so five were queued while two thr `proc://` labels a queued task `running` — which is not the claim it sounds like. Cancelling five let the survivors land within minutes. +One mechanical find worth keeping: a generated See Also block had left **five links with empty +text** (`- ](index.md)`) across the gitea pages, `tools/cachyos.md`, and `tools/applications.md` — +rendered, that is a bullet with nothing in it to click, so it reads as a style choice rather than a +broken reference. `grep -rnE '(^|[ ,])\]\('` finds them in one pass. + The day's three `## 2026-10-21` sections are now one section with four subsections, which is what the spec's one-dated-section-per-day rule always meant; [references/okf-maintenance.md](references/okf-maintenance.md) says so explicitly now. diff --git a/omp-docs/tools/applications.md b/omp-docs/tools/applications.md index 0b5eaef..9d3285b 100644 --- a/omp-docs/tools/applications.md +++ b/omp-docs/tools/applications.md @@ -73,4 +73,4 @@ graph LR - [cachyos](/tools/cachyos.md) — Operating system documentation - [noctalia-shell](/tools/noctalia-shell.md) — Desktop shell - [niri](niri/index.md) — Compositor -- ](ghostty/index.md) — Terminal configuration +- [Ghostty](/tools/ghostty/index.md) — terminal configuration diff --git a/omp-docs/tools/cachyos.md b/omp-docs/tools/cachyos.md index 1b28ae8..fc973ff 100644 --- a/omp-docs/tools/cachyos.md +++ b/omp-docs/tools/cachyos.md @@ -106,7 +106,7 @@ CachyOS with Niri ships with a curated set of applications: ## See Also -- ](../system/index.md) — system overview +- [System hosts](/system/index.md) — the fleet's machines and roles - [niri](niri/index.md) — Compositor documentation - [noctalia-shell](/tools/noctalia-shell.md) — Desktop shell documentation - [applications](/tools/applications.md) — Application reference diff --git a/omp-docs/tools/gitea/architecture.md b/omp-docs/tools/gitea/architecture.md index 0304f94..3876762 100644 --- a/omp-docs/tools/gitea/architecture.md +++ b/omp-docs/tools/gitea/architecture.md @@ -82,6 +82,6 @@ Note: the full-stack repo compose (`dankclaw/compose.yaml`) maps Gitea HTTP to h ## See Also -- ](index.md) — index with management commands +- [Gitea on dankclaw](/tools/gitea/index.md) — what lives where - [management](/tools/gitea/management.md) — operations, boot persistence, incident - [data-persistence](/tools/gitea/data-persistence.md) — volumes and backups diff --git a/omp-docs/tools/gitea/data-persistence.md b/omp-docs/tools/gitea/data-persistence.md index 5a9e078..3e3847c 100644 --- a/omp-docs/tools/gitea/data-persistence.md +++ b/omp-docs/tools/gitea/data-persistence.md @@ -63,6 +63,6 @@ Set `PGPASSWORD` from `/home/dank/Containers/gitea/env` (mode 0600, `GITEA_DB_PA ## See Also -- ](index.md) — index with management commands +- [Gitea on dankclaw](/tools/gitea/index.md) — what lives where - [management](/tools/gitea/management.md) — operations and boot persistence - [architecture](/tools/gitea/architecture.md) — network and ports diff --git a/omp-docs/tools/gitea/management.md b/omp-docs/tools/gitea/management.md index ed191ea..4a8956f 100644 --- a/omp-docs/tools/gitea/management.md +++ b/omp-docs/tools/gitea/management.md @@ -99,7 +99,7 @@ Verify the fix on the next host reboot. ## See Also -- ](index.md) — index +- [Gitea on dankclaw](/tools/gitea/index.md) — what lives where - [client-access](/tools/gitea/client-access.md) — how client hosts authenticate (token + credential store) - [architecture](/tools/gitea/architecture.md) — network topology - [data-persistence](/tools/gitea/data-persistence.md) — backups