diff options
10 files changed, 888 insertions, 0 deletions
diff --git a/prompts/skills/agent-task-management/references/verification-honesty.md b/prompts/skills/agent-task-management/references/verification-honesty.md new file mode 100644 index 0000000..e083e92 --- /dev/null +++ b/prompts/skills/agent-task-management/references/verification-honesty.md @@ -0,0 +1,57 @@ +# Verification Honesty + +General discipline for claiming that work is verified. This is task-lifecycle +policy, not specific to any language — language skills (e.g. `go-best-practices`) +link here and add only their toolchain-specific concrete actions. + +The core rule: **be explicit about what actually ran.** A *missing or incomplete* +toolchain (not slow tests) is the most common cause of false "verified" claims. + +## Before claiming any verification + +Confirm the local toolchain can actually build and run the relevant tests. + +- **Preflight first.** Verify the build/test path works before trusting it — + run the project's build command, confirm required native headers / CGO + dependencies are present, and confirm required external tools are on `PATH`. + If preflight fails, do not claim the project builds. +- **Run the smallest verifying subset that DOES work.** When part of the + toolchain is missing, still verify what you can — linters, formatters, + `build` on the packages that do not need the missing headers, and the unit + tests that do not require the missing tool. Use build tags or explicit + package paths to skip the unbuildable parts. +- **Annotate the blocker explicitly.** Record what is missing and the impact + with `ask annotate <id> "<note>"` — name the missing header/tool, what you + verified, and what you could not. +- **Never claim full verification when it did not run.** State precisely what + was and was not verified (e.g. "vet + gofmt clean; package X not built — + header Y missing; tests for X not run"). Do not imply a green build or + passing tests that never executed. + +## Long-running / timeout-exceeding test suites + +Distinct from a *missing* toolchain: here the toolchain works, but the full +suite is too slow to finish within the command timeout. Run a focused subset +rather than nothing, and be explicit that you did so. + +- **Run a representative subset within the timeout.** Scope to the package(s) + the change touches and skip the slow target. Prefer a "short" flag (have slow + tests honor it), build tags, or a run-pattern filter to exclude expensive + integration/E2E tests; run the unit subset when the full integration suite + cannot complete. +- **Annotate the intentional skip.** Record with `ask annotate <id> "<note>"` + that the full suite was *intentionally* skipped, why (exceeds timeout, not a + failure), which subset ran, and the result — e.g. "integration suite skipped + (>30m, timeout); ran unit subset for package X → pass". +- **Acceptance implications.** A focused subset is NOT full verification. Be + explicit about residual risk — untested integration paths, packages not + touched — so the reviewer/orchestrator can decide whether to accept or run + the full suite out-of-band. Never imply the full suite passed when it never ran. + +## Summary checklist + +- [ ] Toolchain preflight passed before claiming a build +- [ ] The smallest verifying subset that works was run +- [ ] Missing headers/tools named explicitly in an `ask annotate` note +- [ ] No implication that unrun tests passed +- [ ] Slow suites: subset run, skip annotated, residual risk stated
\ No newline at end of file diff --git a/prompts/skills/blog-writing-style/references/gemtext-conventions.md b/prompts/skills/blog-writing-style/references/gemtext-conventions.md new file mode 100644 index 0000000..6c2178c --- /dev/null +++ b/prompts/skills/blog-writing-style/references/gemtext-conventions.md @@ -0,0 +1,103 @@ +# foo.zone Gemtext Authoring Conventions + +Shared conventions for foo.zone blog content. Used by `compose-blog-post`, +`update-blog-post`, and `blog-writing-style`. Follow these for any `.gmi.tpl` +(or standalone `.gmi`) authoring or editing. + +## File rules + +- **Only write or modify `.gmi.tpl` template files** under + `~/git/foo.zone-content/gemtext/gemfeed/`. +- **Never modify a `.gmi` file that has a `.gmi.tpl` counterpart** — those are + generated by Gemtexter from the template. +- Standalone `.gmi` files (no `.tpl`) are usually older posts outside the + target window and generally don't need changes. +- The gemfeed index is `index.gmi.tpl` (edit the template, never `index.gmi`). + +## Gemtext format constraints + +Gemtext (`.gmi` / `.gmi.tpl`) does **not** support Markdown bold (`**text**`) or +italic (`*text*`). Gemtexter will not render these. Never use `**...**` or +`*...*` for emphasis. Instead, rely on sentence structure, word choice, or +`backticks` for inline emphasis. + +- Sections: `##` and `###` (no `#` except the title). +- Code blocks: triple backticks; list blocks with `*` or `-`. +- No HTML; plain gemtext only. + +## Post structure (in order) + +1. `# Title` (first line) +2. `> Published at YYYY-MM-DDTHH:MM:SS+02:00` (ISO 8601 with timezone) +3. Optional: ASCII art in a fenced code block (triple backticks) — only if it + fits the topic (diagram, device, logo). +4. Short intro paragraph(s) +5. First image (if any) and/or main product/external links +6. **Table of Contents** (see below) +7. Body with `##` and `###` sections +8. Optional: "Other related posts:" with `=> ./YYYY-MM-DD-slug.gmi YYYY-MM-DD Title` lines +9. `E-Mail your comments to \`paul@nospam.buetow.org\` :-)` +10. `=> ../ Back to the main site` (last line) + +## Table of Contents + +Add a hand-written TOC after the intro/first image, before the first `##`: + +- Heading: `## Table of Contents` then a blank line +- List with `* ⇢` and indentation by section level: + - `* ⇢ Post title` (one arrow = document title) + - `* ⇢ ⇢ Section name` (two arrows = each `##` section) + - `* ⇢ ⇢ ⇢ Subsection name` (three arrows = each `###` subsection) +- List every `##` and `###` in the same order as in the body. + +## Links + +- External: `=> URL Description` +- Internal/images: `=> ./path Description` +- **Inline project links.** When the body first mentions an external tool, + project, or library by name (e.g. eBPF, libbpf, ClickHouse), add a + `=> URL Description` line immediately after that paragraph rather than + waiting for a bottom-of-post link block. Bottom blocks are reserved for + source repos and related posts. + +## Images and diagrams + +- Store images in a subfolder under the gemfeed (e.g. `gemfeed/slug-name/`). + Reference them as `=> ./slug-name/filename.jpg Description`. +- Resize large images for web (longest side ~1200px, JPEG quality 85) so the + post stays fast to load. +- **Image density.** Prose-heavy posts with long `##` sections benefit from at + least one image or diagram per major section. Walls of text without visual + anchors feel dense; when a section runs to several screens of prose, propose + a diagram or screenshot to break it up. +- **ASCII diagrams.** Use a fenced code block (triple backticks). Use Unicode + box-drawing characters — `─`, `│`, `▼`, `▶`, `┼`, `┌`, `└`, `┐`, `┘` — for + cleaner output than `|` / `-` / `+`. + - For two-section comparison diagrams (e.g. kernel vs userspace, before vs + after), place a vertical `│` divider at a single fixed column on every + row, rather than relying on whitespace alone to separate the sides. + - After drafting a multi-column diagram, verify the divider column is + identical on every line (e.g. with a small `awk` check) before accepting + it. Misaligned dividers are the most common rendering bug. + - Anchor labels above content: each section header should sit visually over + the column it describes, not floating off to one side. + +## Multi-part series + +When a post is part of a series (Part 1 / Part 2 / Part 3 …): + +- Cross-link to siblings near the top (right after the intro paragraph) and + again at the bottom, using the dated filename: + `=> ./YYYY-MM-DD-slug-part-N.gmi Part N: short subtitle`. +- **Shared hero image.** Use the same hero image at the same position + (typically immediately after the intro) in every part. Store hero and other + shared assets in one subfolder, e.g. `gemfeed/series-slug/`. +- **Drafts.** Unpublished parts can live as `DRAFT-slug-part-N.gmi.tpl`; their + cross-references use `=> ./DRAFT-slug-part-N.gmi`. Promoting a draft to a + dated post requires (a) renaming the `.gmi.tpl` and removing the stale + generated `.gmi`, then (b) updating every `=> ./DRAFT-…` or + `=> ./old-date-…` reference in every OTHER `.gmi.tpl` in the series to point + at the new filename. Search with `grep -rn "old-filename" gemtext/` to catch + them all. +- The dated filename controls gemfeed ordering. Fix the filename first, then + propagate to references — never the other way around.
\ No newline at end of file diff --git a/prompts/skills/blog-writing-style/references/patterns-and-examples.md b/prompts/skills/blog-writing-style/references/patterns-and-examples.md new file mode 100644 index 0000000..cc4b513 --- /dev/null +++ b/prompts/skills/blog-writing-style/references/patterns-and-examples.md @@ -0,0 +1,111 @@ +# LLM Patterns to Remove (foo.zone working set) + +The foo.zone-focused catalog of LLM tells to hunt for. For the exhaustive +29-pattern catalog with voice calibration and the full worked example, see +[signs-of-ai-writing.md](signs-of-ai-writing.md). + +## Opening structures +- "This [noun] [verb]..." → Start with action or personal observation +- "As a [role], you..." → Use direct "You" or "I" statements +- "In today's world..." → Cut entirely or rephrase + +## Corporate/marketing language +- "robust", "vital", "ensuring", "leveraging", "enabling", "facilitating" +- "comprehensive", "seamless", "powerful", "efficient" +- Replace with simpler words or remove + +## Hedging language +- "often", "typically", "can help", "may", "might" +- "tends to", "generally", "usually" +- Replace with definitive statements or personal experience + +## Over-explanation +- Sentences explaining *why* something is useful after stating it +- Redundant clarifications +- Paragraphs that summarize what was just said +- Remove these entirely + +## Formal transitions +- "Furthermore", "Additionally", "Moreover", "In conclusion" +- "It's worth noting that", "It's important to understand" +- Replace with conversational transitions or just cut + +## Passive constructions +- "This can be achieved by..." → "You can do this by..." +- "It is recommended to..." → "I'd recommend..." or just state it directly + +## Third-person distance +- "The author suggests..." → "Larson suggests..." or "The book says..." +- "One should consider..." → "You might consider..." or "I'd..." + +## Concrete rewrite examples + +**Before (LLM):** +> "This blog post describes my hackathon efforts adding observability to X-RAG..." + +**After (Human):** +> "This post describes my hackathon efforts adding observability to X-RAG..." + +--- + +**Before (LLM):** +> "This thesis aims to make it easier for users to view distributed systems from a different perspective. Here, the viewpoint of an end user is not adopted; instead, the functional methods of protocols and their processes in distributed systems should be made comprehensible, while simultaneously making all relevant events of a distributed system transparent." + +**After (Human):** +> "This thesis aims to make distributed systems easier to understand from a different angle. Instead of the end-user perspective, it focuses on the functional methods of protocols and their processes, making all relevant events of a distributed system transparent." + +--- + +**Before (LLM):** +> "In the previous posts, I deployed applications to the k3s cluster using Helm charts and Justfiles—running `just install` or `just upgrade` to imperatively push changes to the cluster. While this approach works, it has several drawbacks:" + +**After (Human):** +> "In previous posts, I deployed applications to the k3s cluster using Helm charts and Justfiles—running `just install` or `just upgrade` to imperatively push changes to the cluster. Works fine, but has some drawbacks:" + +--- + +**Before (LLM):** +> "I especially made time available over the weekend to join his 3-day hackathon..." + +**After (Human):** +> "I made time over the weekend to join his 3-day hackathon..." + +--- + +**Before (LLM):** +> "It is insane how times have changed." + +**After (Human):** +> "Times have changed." + +--- + +**Before (LLM):** +> "Larson breaks down the role of a Staff Engineer into four main archetypes, which can help frame how you approach the role:" + +**After (Human):** +> "Larson defines four archetypes. You'll probably recognize yourself in one (or a mix):" + +--- + +**Before (LLM):** +> "As a Staff Engineer, influence is often more important than formal authority. You'll rarely have direct control over teams or projects but will need to drive outcomes by influencing peers, other teams, and leadership. It's about understanding how to persuade, align, and mentor others to achieve technical outcomes." + +**After (Human):** +> "You won't have direct authority over most people or teams you work with. Influence is the actual tool here. You have to persuade, align, sometimes just nudge people in the right direction. No one reports to you, but you still need to drive outcomes." + +--- + +**Before (LLM):** +> "Robust monitoring is vital to any infrastructure, especially one as distributed as mine. I've thought about a setup that ensures I'll always be aware of what's happening in my environment." + +**After (Human):** +> "I want to know when stuff breaks (ideally before it breaks), so monitoring is a big part of the plan." + +--- + +**Before (LLM):** +> "The Beelink S12 Pro with Intel N100 CPUs checks all the boxes for a k3s project: Compact, efficient, expandable, and affordable. Its compatibility with both Linux and FreeBSD makes it versatile for other use cases, whether as part of your cluster or as a standalone system." + +**After (Human):** +> "Honestly, the Beelink S12 Pro with the N100 is kind of perfect for this — tiny, cheap, sips power, and runs both Linux and FreeBSD without drama. I'm pretty happy with it."
\ No newline at end of file diff --git a/prompts/skills/f3s/references/console-jetkvm-shutdown.md b/prompts/skills/f3s/references/console-jetkvm-shutdown.md new file mode 100644 index 0000000..273dce3 --- /dev/null +++ b/prompts/skills/f3s/references/console-jetkvm-shutdown.md @@ -0,0 +1,118 @@ +# Console (HDMI/JetKVM) & Shutdown Behavior on f-hosts + +Findings from troubleshooting f1 on 2026-06-27, after the FreeBSD **15.1** upgrade +(`@pre-15.1-upgrade` ZFS snapshots, taken 2026-06-19/06-20). Applies to the Beelink +S12 Pro / Intel N100 hosts **f0, f1, f2, f3**. + +A JetKVM (KVM-over-IP) is currently attached to **f1** (USB + HDMI). It enumerates on +the FreeBSD USB bus as `ugen0.X: <Multifunction Composite Gadget Linux Foundation>` +(idVendor `0x1d6b`, idProduct `0x0104`, iManufacturer `JetKVM`) — 3× HID interfaces +(keyboard/tablet/mouse) + 1× mass storage (virtual media). The generic "Composite +Gadget" product string means a name-grep for "JetKVM" can miss it; match on the gadget +descriptor instead. + +## 1. HDMI / console regressed to 640x480 in FreeBSD 15.1 (breaks JetKVM) + +**Symptom:** JetKVM shows no HDMI signal from a host. + +**Cause:** FreeBSD 15.1's stock `/boot/defaults/loader.conf` ships +`efi_max_resolution="1x1"` (uncommented) and it is not overridden in +`/boot/loader.conf`. Capped at 1x1, the loader cannot hand off a usable EFI GOP +framebuffer, so `vt(4)` falls back from `efifb` to the legacy **`vga`** backend at +**640x480**. No `drm-kmod`/`i915kms` is loaded on any host, so nothing reinitializes +the GPU afterward. Proven on f2, whose logs span the upgrade: +`VT(efifb): resolution 1920x1080` (FreeBSD 14) → `VT(vga): resolution 640x480` (15.1). + +**Fix (applied to all f-hosts 2026-06-27):** add to `/boot/loader.conf`: +``` +efi_max_resolution="1080p" +``` +Effective on next reboot → restores `VT(efifb): resolution 1920x1080`. Verify with: +``` +kenv efi_max_resolution +grep -F 'VT(' /var/log/messages | tail -1 +``` + +### Resolution / JetKVM capture notes +What this particular JetKVM locks onto from the firmware's static GOP framebuffer +(no KMS, so timings are firmware-defined and non-standard): + +| `efi_max_resolution` | console backend | JetKVM result | +|---|---|---| +| (15.1 default `1x1`) | `vga` 640x480 | **no signal** | +| `1080p` | `efifb` 1920x1080 | **signal OK** (the one that works) | +| `720p` | `efifb` 1280x720 | **no signal** | + +So **use `1080p`** — 720p produced no signal at all. A transient flicker / "no signal" +after changing modes was cleared by **rebooting the JetKVM device itself** (it caches +EDID/sync), not by changing the host. If 1080p still flickers, suspect the HDMI +cable/seating, or install `drm-kmod` + load `i915kms` for proper KMS (clean +EDID-negotiated timings + hotplug) — not yet done; bigger change on a headless host. + +**Each host's resolution depends on ITS JetKVM's emulated EDID, not just loader.conf.** +All four hosts have a JetKVM attached and identical `efi_max_resolution="1080p"`, yet +only **f1** negotiates efifb 1920x1080; f0/f2/f3 fall back to `vga 640x480`. The firmware +GOP builds its mode list from the EDID the JetKVM presents, and **f1's JetKVM advertises +a 1080p-capable EDID while f0/f2/f3's only advertise up to 640x480** (no KMS driver to +override). This is a per-JetKVM **EDID configuration** difference, NOT something a reboot +changes: f1 came up 1080p on its first 1080p boot, *before* its JetKVM was ever rebooted +(that later JetKVM reboot only cleared a flicker). Verified 2026-06-27: rebooting the f3 +*host* twice (JetKVM untouched) stayed 640x480. + +**To make a host do 1080p:** set/raise that host's JetKVM emulated **EDID/resolution to +1080p** in the JetKVM web UI to match f1 — a host reboot alone does nothing. Alternative +host-side fix that is EDID-independent: install `drm-kmod` + load `i915kms` (Intel KMS), +which drives the output itself regardless of the firmware GOP/EDID — not yet done; bigger +change on headless hosts. + +## 2. Shutdown hangs → host stuck in single-user → un-wakeable by WoL + +**Symptom:** a host "won't boot" / is unreachable in the morning; WoL does nothing. + +**Cause:** on `shutdown -p`/`-r`, `rc.shutdown` exceeds the 90s `rcshutdown_timeout`, +so `init` logs "terminated abnormally, going to single user mode" and the host drops to +**single-user instead of powering off**. It stays powered on with no network/sshd, so +**Wake-on-LAN cannot wake it** (WoL only wakes a powered-off NIC). Recovery then needs a +console (JetKVM) or physical power-cycle. + +Recurring and near-simultaneous on **f0/f1/f2** (powered down together via +`wol-f3s shutdown`). **f3 never hangs.** The differentiator: the `vm` rc script stops +guests with `vm stopall -f`, which waits for each bhyve guest to ACPI-power-off. The +k3s cluster guests (on f0/f1/f2) take ~45–92s to stop (k3s/containerd teardown) — +measured 92s once, over the 90s watchdog — while f3's plain Rocky guest stops in ~2s. + +**Mitigation (APPLIED 2026-06-28 to f0/f1/f2):** `rcshutdown_timeout="300"` in +`/etc/rc.conf` (`sysrc rcshutdown_timeout=300`). Default was 90s. This gives +`vm stopall -f` enough time to finish the slow k3s-guest ACPI poweroff (observed +45–92s) before `init`'s watchdog would otherwise drop the host to single-user. + +Confirmed root cause of an f0 incident on 2026-06-28: a `power-down by paul` at +22:49:30 hit the 90s watchdog at 22:51:00 (`rc.shutdown[...]: 90 second watchdog +timeout expired` → `init: /etc/rc.shutdown terminated abnormally, going to single +user mode`). The host stayed powered-on in single-user, so WoL could not wake it the +next morning and it needed a hard power-cycle. + +**Note — no `stop_timeout` lever here:** these hosts run **vm-bhyve 1.7.3**, whose +`rc.d/vm` stop path is `vm stopall -f` → ACPI-kill all guests then `wait_for_pids` +(from `rc.subr`), which waits **indefinitely** for the bhyve processes to exit. That +version has **no per-guest `stop_timeout` / force-`bhyvectl --destroy` option**, so +raising `rcshutdown_timeout` is the only effective mitigation on 1.7.3. (If a guest +ever truly hangs and never ACPI-powers-off, even 300s won't help — but observed +worst case is ~92s.) + +## 3. Safe remote-reboot procedure for an f-host + +Because a hung `rc.shutdown` can strand a host in single-user (unrecoverable remotely +unless the JetKVM is attached — it lives on f1 only): + +1. Gracefully stop guests first, outside the 90s watchdog: `doas vm stopall` (wait for + `vm list` to show none `Running`). This also avoids an ungraceful guest kill. +2. Reboot with **`doas reboot`** (NOT `shutdown -r`): `reboot` bypasses the + `rc.shutdown` watchdog path, so it cannot drop to single-user. +3. Poll for return, then verify `kenv efi_max_resolution` and the `VT(...)` log line. +4. Guests with `AUTO` start back on boot; a stale vm-bhyve `Locked` state clears on + reboot. + +Sequence multiple hosts **one at a time** (do storage MASTER **f0 last** — rebooting it +fails the `f3s-storage-ha` CARP VIP over to f1) so only one k3s node is down at once +(etcd quorum preserved). f1 is normally CARP BACKUP; f0 is MASTER. diff --git a/prompts/skills/f3s/references/shelly-plug.md b/prompts/skills/f3s/references/shelly-plug.md new file mode 100644 index 0000000..ebeff7b --- /dev/null +++ b/prompts/skills/f3s/references/shelly-plug.md @@ -0,0 +1,110 @@ +# Shelly Plug (Rack Fans) + +A **Shelly Plug M Gen 3** powers the rack fans for the f3s rack. The f-hosts +switch it **on at boot**; `wol-f3s` switches it **on when waking all hosts** and +**off when shutting all hosts down**. + +## Device + +| Field | Value | +|-------|-------| +| Model | `S3PL-30110EU` (Shelly Plug M Gen 3) | +| ID / MAC | `shellyplugmg3-0892725e366c` / `0892725E366C` | +| IP | `192.168.1.28` (note: distinct from pi3 at `.128`) | +| Firmware | `1.8.99-plugmg3prod0` (app `PlugMG3`, gen 3) | +| Auth | **enabled** — HTTP digest, user `admin` | +| Max load | 16 A / ~3680 W | + +The relay is `switch:0`. The open `/shelly` endpoint needs no auth; all `/rpc/*` +control and status calls require digest auth. + +## Secret + +The plug password is stored in plain files (first line of the file), **never in +git**: + +| Host(s) | Location | Perms | +|---------|----------|-------| +| earth | `~/.shelly_plug` | `0600 paul` | +| pi0/pi1/pi2 | `~/.shelly_plug` (`/home/paul/.shelly_plug`) | `0600 paul` | +| f0/f1/f2/f3 | `/keys/shelly_plug.secret` (on the UFS USB key stick) | `0400 root:wheel` | + +On the f-hosts the secret lives on the read-only `/keys` USB stick alongside the +ZFS encryption keys. Adding/updating it requires a temporary remount: + +```sh +doas mount -u -o rw /keys +printf '%s\n' '<password>' | doas tee /keys/shelly_plug.secret >/dev/null +doas chmod 0400 /keys/shelly_plug.secret +doas chown root:wheel /keys/shelly_plug.secret +doas mount -u -o ro /keys +``` + +## HTTP RPC API + +Base URL `http://192.168.1.28/rpc/<Method>`. With digest auth via curl: + +```sh +pass=$(head -n1 ~/.shelly_plug) # or /keys/shelly_plug.secret on f-hosts +A=(--digest -u admin:$pass) + +curl -s "${A[@]}" "http://192.168.1.28/rpc/Switch.GetStatus?id=0" # power/V/A/energy/temp +curl -s "${A[@]}" "http://192.168.1.28/rpc/Switch.Set?id=0&on=true" # on +curl -s "${A[@]}" "http://192.168.1.28/rpc/Switch.Set?id=0&on=false" # off +curl -s "${A[@]}" "http://192.168.1.28/rpc/Switch.Toggle?id=0" # toggle +curl -s "http://192.168.1.28/shelly" # info (no auth) +``` + +`Switch.GetStatus` reports `output` (on/off), `apower` (W), `voltage`, `freq`, +`current`, `aenergy` (Wh total + per-minute), `ret_aenergy`, and internal +`temperature`. Useful config (`Switch.GetConfig`): `initial_state` +(`off`/`on`/`restore`/`match_input`), `auto_on`/`auto_off` timers, and safety +limits (`power_limit` 3000 W, `voltage_limit` 280 V, `current_limit` 13 A). + +Beyond switching, the device also supports: Schedules, Webhooks, on-device JS +Scripts, KVS, Matter (enabled), MQTT/Cloud/KNX (disabled), BLE + BTHome gateway, +Wi-Fi, RGB status LED ring (`plugs_ui`), OTA updates, and virtual components. +List everything with `Shelly.ListMethods` and `Shelly.GetComponents?dynamic_only=false`. + +## Boot-time auto-on (FreeBSD f-hosts) + +Each f-host turns the plug on at boot via an rc.d service, so the fans always run +while any host is up. Source + runbook in the conf repo: +**`f3s/freebsd-hosts/shelly-fans/`** (`shelly-fans-on`, `shellyfans.rc`, +`README.md`). + +- `/usr/local/sbin/shelly-fans-on` — helper that calls `Switch.Set?on=true`, + retrying ~60s. **Sets `PATH` explicitly** (rc.d boots with a minimal PATH that + excludes `/usr/local/bin` where `curl` lives — omitting this silently breaks it). +- `/usr/local/etc/rc.d/shellyfans` — `REQUIRE: NETWORKING f3skeys` (so `/keys` is + mounted first), runs the helper backgrounded so a slow/unreachable plug never + delays boot. Enable with `sysrc shellyfans_enable=YES`. +- Reads the password from `/keys/shelly_plug.secret`; missing stick = fans not + switched (logged, non-fatal). + +Install per host (scripts + `sysrc`), then put the secret on `/keys` (see above). +Verify: `doas service shellyfans start` then `grep shellyfans /var/log/messages` +(expect `Rack fans switched on`). Confirmed working via real reboot on f3. + +**Deployment status:** f0, f2, f3 done. **f1 pending** (was offline / would not +wake via WoL when this was set up — deploy when it is back online). + +## wol-f3s integration (earth + Pis) + +`wol-f3s` (dotfiles `scripts/wol-f3s`; deployed to `/home/paul/scripts/wol-f3s` +on earth and `/usr/local/bin/wol-f3s` on pi0/pi1/pi2) controls the plug as part +of bulk power actions: + +- `wol-f3s` / `all` → `shelly_set true` **before** sending WoL packets (fans on). +- `wol-f3s shutdown-all` → `shelly_set false` **after** all hosts/Pis are down + (fans off last). + +The `shelly_set` helper reads the password from `~/.shelly_plug` and uses digest +auth; it no-ops gracefully if the file is missing. Partial actions (`shutdown`, +per-host wakes) leave the plug untouched. + +## Standalone control script + +`~/git/conf/playground/shelly-plug.sh` — convenience CLI: +`shelly-plug.sh [host] <status|on|off|toggle|info>`. Password from `SHELLY_PASS` +env or `~/.shelly_plug`; host defaults to `192.168.1.28`. diff --git a/prompts/skills/skill-maintenance/SKILL.md b/prompts/skills/skill-maintenance/SKILL.md new file mode 100644 index 0000000..faed9f3 --- /dev/null +++ b/prompts/skills/skill-maintenance/SKILL.md @@ -0,0 +1,51 @@ +--- +name: skill-maintenance +description: "Maintain the ~/.agents/skills collection: audit skills against the Agent Skills spec, enforce DRY across skills (one canonical home for shared knowledge; cross-link, don't duplicate), sub-divide oversized skills into focused references/ (the f3s/rocky-vm-setup index pattern), and commit/sync changes. Use when asked to review, refactor, audit, or health-check skills, or to apply best practices to the skills collection. Triggers on: skill maintenance, audit skills, review skills, refactor skills, skill health, DRY skills, sub-divide skill, skill best practices." +--- + +# Skill Maintenance + +Maintain the `~/.agents/skills/` collection: keep each skill compliant with the +[Agent Skills spec](https://agentskills.io/specification), DRY across skills, +and sub-divided so `SKILL.md` stays a slim index (progressive disclosure). + +## When to Use + +- Audit one skill or the whole collection for spec compliance and health +- Refactor a skill that has grown too large or duplicated content +- Review whether shared knowledge has a single canonical home (DRY) +- Sub-divide a skill whose `SKILL.md` re-inlines its own `references/` +- Commit and sync skill changes (delegates to `commit-skills`) + +## Reference Files + +Detailed reference documentation is in the `references/` subfolder: + +- [Best Practices](references/best-practices.md) — the Agent Skills spec rules + pi specifics: progressive disclosure (metadata ~100 tokens always loaded; instructions <5000 tokens / <500 lines; resources on demand), frontmatter, name rules, description, structure, scripts, references, file refs one level deep, validation. +- [DRY Across Skills](references/dry-across-skills.md) — DRY principles for a skill collection: one canonical home for shared knowledge; cross-link via `../sibling/SKILL.md`; when to share a reference vs. keep a skill self-contained; how to resolve conflicts; worked examples from this collection. +- [Sub-division](references/sub-division.md) — when and how to sub-divide an oversized skill into focused `references/`: the index pattern (`f3s`, `rocky-vm-setup`), the "SKILL.md must not duplicate its own references" rule, naming and sizing reference files, keeping refs one level deep. +- [Audit Checklist](references/audit-checklist.md) — the concrete step-by-step checklist for reviewing a single skill or sweeping the whole collection. + +## Quick Checklist + +- [ ] `SKILL.md` < 500 lines / < 5000 tokens; detail moved to `references/` +- [ ] `SKILL.md` does not re-inline content already in its own `references/` +- [ ] Each reference file is focused (one topic); refs one level deep +- [ ] Frontmatter valid: `name` (lowercase, hyphens, ≤64), `description` (≤1024, specific keywords + when-to-use) +- [ ] `description` includes trigger phrases so the agent loads it on match +- [ ] File references use relative paths from the skill root +- [ ] Shared knowledge has exactly one canonical home; others cross-link, don't duplicate +- [ ] Cross-skill links resolve: `../sibling/SKILL.md` or `../sibling/references/x.md` +- [ ] Scripts self-contained, helpful errors, edge cases handled +- [ ] No spec warnings (run `pi` validation; missing `description` = not loaded) + +## Workflow + +1. **Scope.** Confirm which skills to review (e.g. changed in the last N months: `find ~/.agents/skills -name SKILL.md -newermt "-N months"`). +2. **Audit.** Load [references/audit-checklist.md](references/audit-checklist.md) and apply per skill. Record findings. +3. **DRY.** Load [references/dry-across-skills.md](references/dry-across-skills.md); identify duplicated knowledge and propose a single canonical home. **Surface conflicts to the user before changing skills** — do not silently decide ownership disputes. +4. **Sub-divide.** Load [references/sub-division.md](references/sub-division.md); split oversized `SKILL.md`s into an index + focused references. +5. **Decide.** Present the plan; get approval on conflicts before editing. +6. **Refactor.** Apply edits; preserve all information (move, don't delete). +7. **Verify.** Re-check link targets resolve and no `SKILL.md` duplicates its own references. +8. **Commit.** Use the `commit-skills` skill to summarize and push changes from `~/git/dotfiles`.
\ No newline at end of file diff --git a/prompts/skills/skill-maintenance/references/audit-checklist.md b/prompts/skills/skill-maintenance/references/audit-checklist.md new file mode 100644 index 0000000..3ec8b21 --- /dev/null +++ b/prompts/skills/skill-maintenance/references/audit-checklist.md @@ -0,0 +1,78 @@ +# Skill Audit Checklist + +Step-by-step checklist for reviewing a single skill or sweeping the whole +`~/.agents/skills/` collection. + +## Scope the sweep + +```sh +# Skills changed in the last N months +find ~/.agents/skills -maxdepth 2 -name SKILL.md -newermt "-2 months" | sort + +# Sizes — flag anything over ~500 lines or notably large +for f in ~/.agents/skills/*/SKILL.md; do printf "%5s %s\n" "$(wc -l < "$f")" "$f"; done | sort -rn +``` + +## Per-skill audit + +### Frontmatter +- [ ] `name` present, 1-64 chars, lowercase a-z/0-9/hyphens, no leading/trailing/consecutive hyphens. +- [ ] `description` present, ≤1024 chars, states **what** and **when to use**, includes trigger keywords. (Missing ⇒ not loaded.) +- [ ] Optional fields (`license`, `compatibility`, `metadata`, `allowed-tools`, `disable-model-invocation`) used only when relevant. + +### Structure / progressive disclosure +- [ ] `SKILL.md` < 500 lines / < 5000 tokens. +- [ ] `SKILL.md` does **not** re-inline content that exists in its own `references/` (the key DRY-within-skill check). +- [ ] Detail moved to `references/`; each reference file focused on one topic. +- [ ] File references one level deep; relative paths from skill root. +- [ ] Scripts self-contained, helpful errors, edge cases handled. + +### DRY across skills +- [ ] Identify shared knowledge (snippets, conventions, discipline) used by >1 skill. +- [ ] Each piece of shared knowledge has exactly **one canonical home**. +- [ ] Consumers cross-link (`../owner/SKILL.md` or `../owner/references/x.md`) instead of duplicating. +- [ ] Prerequisites declared up front in the consumer's `SKILL.md`. +- [ ] No two skills silently own the same knowledge (conflict ⇒ surface to user with a recommendation). + +### Links +- [ ] All internal links resolve. Verify: + ```sh + for l in $(grep -roh '\.\./[A-Za-z0-9_./-]*\.md' skill/SKILL.md skill/references/*.md); do + test -f "skill/$l" || echo "MISS skill/$l" + done + ``` +- [ ] Cross-skill links use `../sibling/...` and resolve. + +### Description / discovery +- [ ] Description would cause the agent to load the skill for the intended tasks (specific keywords, "when to use"). +- [ ] No duplicate `name` across locations (collisions warn; first wins). + +## Sweep workflow + +1. List changed skills (scope). +2. For each, run the per-skill audit; record findings. +3. Group findings: spec violations, sub-division candidates, DRY candidates, link breakage. +4. For DRY ownership conflicts, prepare a recommendation per [dry-across-skills.md](dry-across-skills.md) and **surface to the user** — do not silently decide. +5. Present the plan with proposed refactors and the conflicts requiring decisions. +6. After approval, apply edits (move, don't delete); re-verify links and "no SKILL.md duplicates its references". +7. Commit/sync via the `commit-skills` skill from `~/git/dotfiles`. + +## Health quick-stat (whole collection) + +```sh +# Largest SKILL.md files +for f in ~/.agents/skills/*/SKILL.md; do printf "%5s %s\n" "$(wc -l < "$f")" "$f"; done | sort -rn | head + +# Skills without a description (would not load) — empty output = healthy +for f in ~/.agents/skills/*/SKILL.md; do + awk '/^---$/{c++; next} c==1 && /^description:/{print FILENAME; found=1} c==2{exit}' "$f" | grep -q . || echo "NO DESCRIPTION: $f" +done + +# Cross-skill link check (relative ../ links) +for d in ~/.agents/skills/*/; do + for l in $(grep -roh '\.\./[A-Za-z0-9_./-]*\.md' "$d/SKILL.md" "$d"references/*.md 2>/dev/null); do + target="$(dirname "$d/SKILL.md")/$l" + test -f "$target" || echo "MISS ($d): $l -> $target" + done +done +```
\ No newline at end of file diff --git a/prompts/skills/skill-maintenance/references/best-practices.md b/prompts/skills/skill-maintenance/references/best-practices.md new file mode 100644 index 0000000..86fe99b --- /dev/null +++ b/prompts/skills/skill-maintenance/references/best-practices.md @@ -0,0 +1,97 @@ +# Skill Best Practices (Agent Skills spec + pi) + +Source: the [Agent Skills specification](https://agentskills.io/specification) +and pi's `docs/skills.md`. The rules below are what to enforce when auditing a +skill. + +## Progressive disclosure (the core model) + +Skills load progressively — take advantage of it: + +| Layer | Loaded | Target size | +|-------|--------|-------------| +| Metadata | Always in context at startup | ~100 tokens (name + description) | +| Instructions | When the skill is activated (the full `SKILL.md` body) | **< 5000 tokens, < 500 lines** | +| Resources | On demand only (`scripts/`, `references/`, `assets/`) | As small as focused | + +Keep `SKILL.md` under 500 lines. Move detailed reference material to separate +files so the activated body stays small. + +## Frontmatter (required) + +| Field | Required | Constraints | +|-------|----------|-------------| +| `name` | yes | 1-64 chars; lowercase a-z, 0-9, hyphens; no leading/trailing hyphen; no consecutive hyphens. Pi relaxes "must match parent dir" for shared skill dirs. | +| `description` | yes | 1-1024 chars; non-empty; what it does **and** when to use it. **Missing description ⇒ skill is not loaded.** | +| `license` | no | Short license name or bundled file reference. | +| `compatibility` | no | 1-500 chars; environment requirements (product, system packages, network). Most skills omit it. | +| `metadata` | no | Arbitrary string→string map; use reasonably unique keys. | +| `allowed-tools` | no | Space-separated pre-approved tools (experimental). | +| `disable-model-invocation` | no | `true` hides skill from system prompt; requires `/skill:name`. | + +## Description best practices + +The description determines when the agent loads the skill. Be specific and +include trigger keywords. + +Good: +```yaml +description: Extracts text and tables from PDF files, fills PDF forms, and merges multiple PDFs. Use when working with PDF documents or when the user mentions PDFs, forms, or document extraction. +``` + +Poor: +```yaml +description: Helps with PDFs. +``` + +This collection's convention: end descriptions with `Triggers on: <comma-separated phrases>.` + +## Structure + +``` +skill-name/ +├── SKILL.md # required: frontmatter + instructions +├── scripts/ # optional: executable code +├── references/ # optional: detailed docs loaded on demand +└── assets/ # optional: templates, images, data +``` + +Everything besides `SKILL.md` is freeform. + +### scripts/ + +- Self-contained or document dependencies clearly +- Helpful error messages +- Handle edge cases gracefully +- Common languages: Python, Bash, JavaScript + +### references/ + +- Detailed technical reference, form templates, domain-specific docs +- **Keep individual reference files focused** — agents load them on demand, so + smaller files mean less context consumed +- One topic per file + +## File references + +Use relative paths from the skill root: + +```markdown +See [the reference guide](references/REFERENCE.md) for details. +Run the extraction script: scripts/extract.py +``` + +Keep file references **one level deep** from `SKILL.md`. Avoid deeply nested +reference chains. (An index file like `references/storage.md` that links into +`references/storage/*.md` is acceptable but at the limit — prefer flat.) + +## Validation + +Pi validates skills against the Agent Skills standard. Most issues warn but +still load. **Exception: skills with missing `description` are not loaded.** +Name collisions (same name from different locations) warn and keep the first. + +## Skill commands + +Skills register as `/skill:name`. Arguments after the command are appended as |
