Merge pull request 'docs(#64): one owner per fact, truth re-read from the boxes, readable pages' (#65) from docs/64-bundle-audit-dedupe into main
This commit was merged in pull request #65.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -8,40 +8,43 @@ tags:
|
||||
- privacy
|
||||
- configuration
|
||||
- unverified
|
||||
verified: 2026-10-19
|
||||
verified: 2026-10-21
|
||||
timestamp: 2026-08-11
|
||||
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.
|
||||
|
||||
> **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
|
||||
|
||||
@@ -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
|
||||
|
||||
+47
-15
@@ -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 [<matcher>] <to>
|
||||
```
|
||||
|
||||
`<to>` is the new URI (path or query). If `<to>` 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`.
|
||||
`<to>` is the new URI (path or query). If `<to>` 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 <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 <file> --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 <file> --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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
+283
-363
@@ -17,488 +17,408 @@ 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
|
||||
## 2026-10-21
|
||||
|
||||
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:
|
||||
### reviewing a day of work, and finding checks that lie
|
||||
|
||||
- **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.
|
||||
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.**
|
||||
|
||||
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.
|
||||
- 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`).
|
||||
|
||||
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.
|
||||
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)).
|
||||
|
||||
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 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.
|
||||
|
||||
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
|
||||
### 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
|
||||
### 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`.
|
||||
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.
|
||||
|
||||
Laws, now on [tools/niri/displays](tools/niri/displays.md):
|
||||
### a dedupe sweep, and scouts whose ledgers did not survive contact with the files
|
||||
|
||||
- **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.
|
||||
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.
|
||||
|
||||
Fix: `desk-1-home.kdl` 239.999 → 180.000 on the identity block and both legacy
|
||||
connector blocks, applied live with `niri msg output <panel> mode <mode>` — the
|
||||
temporary per-output path, so no config reload and no logged-out session.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
## 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 <PR-URL>` 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 <PR-URL>` 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/<hostname>.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/<hostname>.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 <directive>` 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 <directive>` 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ tags:
|
||||
- skills-sh
|
||||
- ecosystem
|
||||
- registry
|
||||
resource: https://skills.sh
|
||||
timestamp: 2026-10-19
|
||||
verified: 2026-10-19
|
||||
---
|
||||
|
||||
@@ -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 |
|
||||
|----------|-------|
|
||||
@@ -88,6 +100,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
|
||||
|
||||
@@ -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 |
|
||||
@@ -84,6 +86,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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
- [Ghostty](/tools/ghostty/index.md) — terminal configuration
|
||||
|
||||
@@ -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|noctalia-shell]] — Desktop shell documentation
|
||||
- [[applications|applications]] — Application reference
|
||||
- [noctalia-shell](/tools/noctalia-shell.md) — Desktop shell documentation
|
||||
- [applications](/tools/applications.md) — Application reference
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
- [[management|management]] — operations, boot persistence, incident
|
||||
- [[data-persistence|data-persistence]] — volumes and backups
|
||||
- [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
|
||||
|
||||
@@ -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/<repo>.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
|
||||
|
||||
@@ -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
|
||||
- [[management|management]] — operations and boot persistence
|
||||
- [[architecture|architecture]] — network and ports
|
||||
- [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
|
||||
|
||||
@@ -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|data-persistence]] 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.
|
||||
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`.
|
||||
|
||||
@@ -99,8 +99,8 @@ 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
|
||||
- [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
|
||||
- [containers](/engineering/infra/containers.md) — quadlet reference
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+11
-2
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user