From e044700ef7793398046dda059f045546b12f28c5 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 00:39:11 -0700 Subject: [PATCH 01/15] Status line: custom segments tuned for fleet bars; slim frank-archon label Named presets silently ignore leftSegments/rightSegments - the nerd preset's baked arrays were what we saw, and the configured arrays were dead. preset: custom with explicit arrays: chip (right) = token_in, token_out, cost, context_pct, time_spent; strip (left) = session_name, vim, mode, hostname, model, path, git (branch text off - worktree branches are autogenerated; dirty badges kept), pr. Cut tok/s duplicate, clock, cache trio, context_total, subagents, session hash. Composer shape stays claude after live box/band/rule trials. models.yml display name frank-archon drops the (dankserver) suffix; id untouched. Docs: pi/statusline.md records the preset-vs-custom trap, shape->segment routing, and the context-meter truth. --- agent/config.yml | 30 +++++++++++++- agent/models.yml | 2 +- omp-docs/log.md | 9 ++++ omp-docs/pi/index.md | 1 + omp-docs/pi/statusline.md | 86 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 omp-docs/pi/statusline.md diff --git a/agent/config.yml b/agent/config.yml index d79d481..97caa1e 100644 --- a/agent/config.yml +++ b/agent/config.yml @@ -99,11 +99,39 @@ disabledExtensions: [] statusLine: separator: powerline - preset: nerd + preset: custom contextLine: embedded transparent: true compactThinkingLevel: true showHookStatus: true + sessionAccent: true + leftSegments: + - session_name + - vim + - mode + - hostname + - model + - path + - git + - pr + rightSegments: + - token_in + - token_out + - cost + - context_pct + - time_spent + segmentOptions: + model: + showThinkingLevel: true + path: + abbreviate: true + stripWorkPrefix: false + maxLength: 120 + git: + showBranch: false + showStaged: true + showUnstaged: true + showUntracked: true tui: textSizing: true codexResetFireworks: false diff --git a/agent/models.yml b/agent/models.yml index ab7f20e..ae83b5c 100644 --- a/agent/models.yml +++ b/agent/models.yml @@ -23,7 +23,7 @@ providers: # config.yml (192.168.0.143:7771 — frank-zealot's 3090); the # compactionModel candidate keeps the wire model id valid there. compactionModel: llamacpp (dankclaw-3090)/frank-zealot - name: "frank-archon (dankserver)" + name: "frank-archon" # Dankserver: BOTH Radeon AI PRO R9700s (gfx1201, TP=2 over the # 2026-09-20 board swap's root ports, wht6 wire). Engine: # radiance (StillDeadcode, Apache-2.0) from the digest-pinned diff --git a/omp-docs/log.md b/omp-docs/log.md index 0b467fa..4f4fe6e 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,6 +11,15 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — Status line rebuilt on custom segments; frank-archon display name slimmed + +Interactive tune-up of the omp status bar (grilling session, live loop: config edit → watcher repaint → niri screenshot verify). New knowledge page: [OMP Status Line Customization](/pi/statusline.md). + +* **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays); `composer.shape` routes them — in `claude`, right segments paint the top rule chip, left segments the bottom strip. The red rule **is** the context meter (length = usage); `contextLine` label variants only render in `box`/`band`. Working row (`⠿ Working… + tok/s`) is hardcoded — model/thinking can never join it (Tern-only). Eight composer shapes registered in the bundle; box/band previews run live and rejected. +* **Final bar:** chip = `token_in · token_out · cost · context_pct · time_spent` (stable width, pure numbers); strip = `session_name · vim · mode · hostname · model · path · git(no branch text) · pr`. Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash. `path`: full cwd (`stripWorkPrefix: false`, `maxLength: 120`) — worktree branches are autogenerated, so branch text dropped, dirty badges kept. +* **models.yml:** frank-archon display `name` slimmed to `"frank-archon"` (endpoint suffix dropped; `id` untouched). +* **Out of scope noted:** worktree name generation (`wt--`) is omp-internal; possible upstream feature request. + ## 2026-10-09 — Blankpad DE redesign: desk profiles, keybind rework, Handy fleet wiring Branch `blankpad-de-redesign` (worktree `wt/de-redesign-20261009`). Full sweep + redesign of the niri/noctalia/Handy stack on blankpad. diff --git a/omp-docs/pi/index.md b/omp-docs/pi/index.md index 656caf4..9d40d9f 100644 --- a/omp-docs/pi/index.md +++ b/omp-docs/pi/index.md @@ -8,6 +8,7 @@ two llama.cpp servers (Dankserver, Dankclaw). - [Pi Settings System (settings.json)](/core/settings.md) — Main `settings.json` reference. Providers, models, packages, subagent overrides. - [Pi Models Configuration](/pi/models.md) — `models.json` reference. Provider definitions, model specs, endpoints. +- [OMP Status Line Customization](/pi/statusline.md) — How presets, composer shapes, and segment arrays actually interact; the fleet's tuned bar. ## Extensions diff --git a/omp-docs/pi/statusline.md b/omp-docs/pi/statusline.md new file mode 100644 index 0000000..079873e --- /dev/null +++ b/omp-docs/pi/statusline.md @@ -0,0 +1,86 @@ +--- +type: reference +title: OMP Status Line Customization +description: How the omp status line actually works — presets vs custom arrays, composer-shape segment routing, the context meter, and the fleet's tuned segment layout. +tags: + - omp + - tui + - statusline + - config +timestamp: 2026-10-18 +--- + +# OMP Status Line Customization + +Ground truth from the installed bundle (`@oh-my-pi/pi-coding-agent/dist/cli.js`) +verified live on blankpad 2026-10-18. All knobs live in +`agent/config.yml` under `statusLine:` (tracked — see +[Command Center repo](../core/index.md)); the file is file-watched, so edits +repaint a running session without restart (verify visually: niri screenshot, +see [niri capture recipe](../system/index.md)). + +## Preset vs custom — the trap + +`statusLine.preset` ships whole configurations (`default`, `minimal`, +`compact`, `full`, `nerd`, `ascii`, `custom`). **`leftSegments` / +`rightSegments` / `segmentOptions` are only read when `preset: custom`** — +with any named preset they are silently ignored. The fleet config runs +`preset: custom`. + +## Composer shape routes the segments + +`composer.shape` decides *where* the two segment arrays render: + +| shape | leftSegments | rightSegments | +|---|---|---| +| `claude` (fleet) | bottom strip, left-aligned | top rule chip, right-aligned | +| `rule` | same as claude, gap above strip | same | +| `band` | merged single row above editor | same row, right side (title skipped — it rides the working row) | +| `box` | box top border | box top border | +| `borderless` / `pi` / `field` / `rail` | single bottom bar | single bottom bar | + +The `⠿ Working…` row is hardcoded: shimmer text + `composer.tokenRate` readout +(+ session title in `band` only). Model/thinking-level can never join it +despite the settings description — that composer-bar behavior is Tern-only. + +## Segment universe (27) + +`pi`, `status`, `model`, `mode`, `path`, `git`, `pr`, `subagents`, +`token_in`, `token_out`, `token_total`, `token_rate`, `cost`, `context_pct`, +`context_total`, `time_spent`, `time`, `session`, `hostname`, `cache_read`, +`cache_write`, `cache_hit`, `session_name`, `usage`, `collab`, `stream`, `vim`. + +Conditional segments render nothing until their trigger exists: `vim` needs +`tui.vimMode`, `mode` needs plan/yolo, `pr` needs an associated PR, `git` shows +staged/unstaged/untracked badges (branch text optional via +`segmentOptions.git.showBranch`), `status` needs an extension push. +`session` = first 8 chars of the session id (resume-by-id handle). + +## The context meter + +The red rule in the `claude` shape **is** the context meter: its length tracks +context usage. `statusLine.contextLine` (`off` / `percentage` / `annotated` / +`embedded`) only changes rendering in `box`/`band` — in `claude` all non-`off` +values look identical. Docs' claim that contextLine is "box composer only" is +wrong for the meter itself; the labeled inline variant (`57%×—200K←`) is the +box/band-only part. + +## Path segment in worktrees + +Inside a git worktree the `path` segment always renders +`projectName/worktreeName` when `segmentOptions.path.stripWorkPrefix` is true +(default), or the abbreviated cwd otherwise — base-directory-only is not +renderable. `maxLength` clips tail-first. + +## Fleet configuration (2026-10-18) + +- Chip (right): `session_name` was moved to the strip; chip = `token_in`, + `token_out`, `cost`, `context_pct`, `time_spent` — pure numbers, stable width. +- Strip (left): `session_name`, `vim`, `mode`, `hostname`, `model`, `path`, + `git` (no branch text — worktree branches are autogenerated names), `pr`. +- Deliberately cut: `token_rate` (lives on the working row), `time` (panel + clock), `cache_*` (noise), `context_total` (implied by `x/200K`), + `subagents`, `session`. +- Model display names slimmed in `models.yml` (`frank-archon`, no endpoint + suffix); `name:` is free-form display, `id:` is the wiring — renaming ids is + the cutover procedure, see [Pi Models](/pi/models.md). From e0920dc067d2dba54715f79618d6b2248691edc7 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 00:50:57 -0700 Subject: [PATCH 02/15] Status line: path segment keeps logical project/worktree label stripWorkPrefix true: worktree sessions show projectName/worktreeName with the worktree glyph instead of the on-disk cwd; matches the live bar tuned and screenshot-approved 2026-10-18. --- agent/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/config.yml b/agent/config.yml index 97caa1e..dc7aede 100644 --- a/agent/config.yml +++ b/agent/config.yml @@ -125,7 +125,7 @@ statusLine: showThinkingLevel: true path: abbreviate: true - stripWorkPrefix: false + stripWorkPrefix: true maxLength: 120 git: showBranch: false From 04d3628c9454f82513e0eae262913b2691435fc8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 00:51:12 -0700 Subject: [PATCH 03/15] Docs: path segment two-mode stripWorkPrefix behavior; changelog matches shipped true --- omp-docs/log.md | 2 +- omp-docs/pi/statusline.md | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/omp-docs/log.md b/omp-docs/log.md index 4f4fe6e..795ec22 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -16,7 +16,7 @@ timestamp: 2026-08-28 Interactive tune-up of the omp status bar (grilling session, live loop: config edit → watcher repaint → niri screenshot verify). New knowledge page: [OMP Status Line Customization](/pi/statusline.md). * **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays); `composer.shape` routes them — in `claude`, right segments paint the top rule chip, left segments the bottom strip. The red rule **is** the context meter (length = usage); `contextLine` label variants only render in `box`/`band`. Working row (`⠿ Working… + tok/s`) is hardcoded — model/thinking can never join it (Tern-only). Eight composer shapes registered in the bundle; box/band previews run live and rejected. -* **Final bar:** chip = `token_in · token_out · cost · context_pct · time_spent` (stable width, pure numbers); strip = `session_name · vim · mode · hostname · model · path · git(no branch text) · pr`. Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash. `path`: full cwd (`stripWorkPrefix: false`, `maxLength: 120`) — worktree branches are autogenerated, so branch text dropped, dirty badges kept. +* **Final bar:** chip = `token_in · token_out · cost · context_pct · time_spent` (stable width, pure numbers); strip = `session_name · vim · mode · hostname · model · path · git(no branch text) · pr`. Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash. `path`: logical `project/worktree` label (`stripWorkPrefix: true`, kept after a live `false` trial of the full cwd); branch text dropped (worktree branches are autogenerated), dirty badges kept. * **models.yml:** frank-archon display `name` slimmed to `"frank-archon"` (endpoint suffix dropped; `id` untouched). * **Out of scope noted:** worktree name generation (`wt--`) is omp-internal; possible upstream feature request. diff --git a/omp-docs/pi/statusline.md b/omp-docs/pi/statusline.md index 079873e..f922fb2 100644 --- a/omp-docs/pi/statusline.md +++ b/omp-docs/pi/statusline.md @@ -65,12 +65,23 @@ values look identical. Docs' claim that contextLine is "box composer only" is wrong for the meter itself; the labeled inline variant (`57%×—200K←`) is the box/band-only part. -## Path segment in worktrees +## Path segment and `stripWorkPrefix` -Inside a git worktree the `path` segment always renders -`projectName/worktreeName` when `segmentOptions.path.stripWorkPrefix` is true -(default), or the abbreviated cwd otherwise — base-directory-only is not -renderable. `maxLength` clips tail-first. +`segmentOptions.path.stripWorkPrefix` (default **true** — the code tests +`!== false`) gates two behaviors in the `path` renderer: + +1. **Worktree sessions:** `true` never shows the disk path — it renders the + logical `projectName/worktreeName` label (bare `projectName` when the + current branch is named after the worktree) with the worktree glyph. + `false` opts out and shows the real cwd with a folder glyph, plus a + ` ↳ relativeRepoRoot` suffix inside a repo. Base-directory-only is not + renderable in either mode. +2. **All sessions:** `true` uses the pretty display path (scratch sessions + get the scratch-folder icon); `false` uses the raw absolute cwd. + +`abbreviate` and `maxLength` (clip, default 40) apply afterwards in both +modes. The fleet runs `true` — logical label, worktree glyph (verified live +2026-10-18; an earlier trial of `false` for the full cwd was rejected by eye). ## Fleet configuration (2026-10-18) From 246189f0541bedd895900deeb832a296927c6a74 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 00:58:21 -0700 Subject: [PATCH 04/15] Status line: band shape, drop token_in/token_out segments Band merges the segment groups into one row above the editor and paints the labeled inline context meter (60% - 200K); the session title rides the working row. Cumulative token totals live in /session - off the bar. Docs and changelog updated to the band final state. --- agent/config.yml | 4 +--- omp-docs/log.md | 4 ++-- omp-docs/pi/statusline.md | 33 +++++++++++++++++++-------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/agent/config.yml b/agent/config.yml index dc7aede..4128b16 100644 --- a/agent/config.yml +++ b/agent/config.yml @@ -18,7 +18,7 @@ modelRoles: memory: llamacpp (dankclaw-3090)/frank-zealot:medium symbolPreset: nerd composer: - shape: claude + shape: band theme: dark: dark-terminal light: light @@ -115,8 +115,6 @@ statusLine: - git - pr rightSegments: - - token_in - - token_out - cost - context_pct - time_spent diff --git a/omp-docs/log.md b/omp-docs/log.md index 795ec22..ccfc075 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -15,8 +15,8 @@ timestamp: 2026-08-28 Interactive tune-up of the omp status bar (grilling session, live loop: config edit → watcher repaint → niri screenshot verify). New knowledge page: [OMP Status Line Customization](/pi/statusline.md). -* **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays); `composer.shape` routes them — in `claude`, right segments paint the top rule chip, left segments the bottom strip. The red rule **is** the context meter (length = usage); `contextLine` label variants only render in `box`/`band`. Working row (`⠿ Working… + tok/s`) is hardcoded — model/thinking can never join it (Tern-only). Eight composer shapes registered in the bundle; box/band previews run live and rejected. -* **Final bar:** chip = `token_in · token_out · cost · context_pct · time_spent` (stable width, pure numbers); strip = `session_name · vim · mode · hostname · model · path · git(no branch text) · pr`. Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash. `path`: logical `project/worktree` label (`stripWorkPrefix: true`, kept after a live `false` trial of the full cwd); branch text dropped (worktree branches are autogenerated), dirty badges kept. +* **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays); `composer.shape` routes them — `band` merges both arrays into one row above the editor (title rides the working row), `claude` splits them into top chip + bottom strip. The `claude` red rule **is** the context meter (length = usage); `contextLine` label variants render in `box`/`band` — `band` paints the labeled inline meter (`60% ——— × 200K`). Working row (`⠿ Working… + tok/s`) is hardcoded — model/thinking can never join it (Tern-only). Eight composer shapes registered in the bundle. +* **Final bar (`band`, after a `claude` sojourn):** left = `session_name · vim · mode · hostname · model · path · git(no branch text) · pr`; right = `cost · context_pct · time_spent` with the labeled inline context meter between the groups. Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash, and `token_in`/`token_out` (cumulative totals live in `/session`). `path`: logical `project/worktree` label (`stripWorkPrefix: true`, kept after a live `false` trial of the full cwd); branch text dropped (worktree branches are autogenerated), dirty badges kept. * **models.yml:** frank-archon display `name` slimmed to `"frank-archon"` (endpoint suffix dropped; `id` untouched). * **Out of scope noted:** worktree name generation (`wt--`) is omp-internal; possible upstream feature request. diff --git a/omp-docs/pi/statusline.md b/omp-docs/pi/statusline.md index f922fb2..0f86114 100644 --- a/omp-docs/pi/statusline.md +++ b/omp-docs/pi/statusline.md @@ -33,9 +33,9 @@ with any named preset they are silently ignored. The fleet config runs | shape | leftSegments | rightSegments | |---|---|---| -| `claude` (fleet) | bottom strip, left-aligned | top rule chip, right-aligned | +| `band` (fleet) | merged single row above editor | same row, right side (title skipped — it rides the working row) | +| `claude` | bottom strip, left-aligned | top rule chip, right-aligned | | `rule` | same as claude, gap above strip | same | -| `band` | merged single row above editor | same row, right side (title skipped — it rides the working row) | | `box` | box top border | box top border | | `borderless` / `pi` / `field` / `rail` | single bottom bar | single bottom bar | @@ -58,12 +58,14 @@ staged/unstaged/untracked badges (branch text optional via ## The context meter -The red rule in the `claude` shape **is** the context meter: its length tracks -context usage. `statusLine.contextLine` (`off` / `percentage` / `annotated` / -`embedded`) only changes rendering in `box`/`band` — in `claude` all non-`off` -values look identical. Docs' claim that contextLine is "box composer only" is -wrong for the meter itself; the labeled inline variant (`57%×—200K←`) is the -box/band-only part. +In the `band` shape (fleet) the meter is the labeled inline line between the +segment groups (`60% ———— × 200K`). In the `claude` shape the red rule left of +the right-segment chip **is** the meter: its length tracks context usage. +`statusLine.contextLine` (`off` / `percentage` / `annotated` / `embedded`) +only changes rendering in `box`/`band` — in `claude` all non-`off` values +look identical. Docs' claim that contextLine is "box composer only" is +wrong for the meter itself; the labeled inline variant is the box/band-only +part. ## Path segment and `stripWorkPrefix` @@ -85,13 +87,16 @@ modes. The fleet runs `true` — logical label, worktree glyph (verified live ## Fleet configuration (2026-10-18) -- Chip (right): `session_name` was moved to the strip; chip = `token_in`, - `token_out`, `cost`, `context_pct`, `time_spent` — pure numbers, stable width. -- Strip (left): `session_name`, `vim`, `mode`, `hostname`, `model`, `path`, - `git` (no branch text — worktree branches are autogenerated names), `pr`. +- Shape `band`: one merged status row. Left group: `session_name`, `vim`, + `mode`, `hostname`, `model`, `path`, `git` (no branch text — worktree + branches are autogenerated names), `pr`. Right group: `cost`, + `context_pct`, `time_spent`, with the labeled inline context meter + between the groups. `session_name` renders on the working row, not the + status row. - Deliberately cut: `token_rate` (lives on the working row), `time` (panel - clock), `cache_*` (noise), `context_total` (implied by `x/200K`), - `subagents`, `session`. + clock), `cache_*` (noise), `context_total` (implied by the meter), + `subagents`, `session`, `token_in`/`token_out` (cumulative totals are + in `/session` anyway). - Model display names slimmed in `models.yml` (`frank-archon`, no endpoint suffix); `name:` is free-form display, `id:` is the wiring — renaming ids is the cutover procedure, see [Pi Models](/pi/models.md). From c2d855b33da921cd163adce13cb32ad4bcdc1639 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 01:08:26 -0700 Subject: [PATCH 05/15] Status line: move model segment to the right cluster (model, cost, time) Left group keeps the where-am-I identity (host, path, git, pr); the right cluster carries what's-running (model) plus cost and elapsed time, painted in array order. context_pct verified to hoist into the inline meter spacer regardless of array position. Docs + changelog updated. --- agent/config.yml | 2 +- omp-docs/log.md | 2 +- omp-docs/pi/statusline.md | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/agent/config.yml b/agent/config.yml index 4128b16..fb3af30 100644 --- a/agent/config.yml +++ b/agent/config.yml @@ -110,11 +110,11 @@ statusLine: - vim - mode - hostname - - model - path - git - pr rightSegments: + - model - cost - context_pct - time_spent diff --git a/omp-docs/log.md b/omp-docs/log.md index ccfc075..9c348cf 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -16,7 +16,7 @@ timestamp: 2026-08-28 Interactive tune-up of the omp status bar (grilling session, live loop: config edit → watcher repaint → niri screenshot verify). New knowledge page: [OMP Status Line Customization](/pi/statusline.md). * **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays); `composer.shape` routes them — `band` merges both arrays into one row above the editor (title rides the working row), `claude` splits them into top chip + bottom strip. The `claude` red rule **is** the context meter (length = usage); `contextLine` label variants render in `box`/`band` — `band` paints the labeled inline meter (`60% ——— × 200K`). Working row (`⠿ Working… + tok/s`) is hardcoded — model/thinking can never join it (Tern-only). Eight composer shapes registered in the bundle. -* **Final bar (`band`, after a `claude` sojourn):** left = `session_name · vim · mode · hostname · model · path · git(no branch text) · pr`; right = `cost · context_pct · time_spent` with the labeled inline context meter between the groups. Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash, and `token_in`/`token_out` (cumulative totals live in `/session`). `path`: logical `project/worktree` label (`stripWorkPrefix: true`, kept after a live `false` trial of the full cwd); branch text dropped (worktree branches are autogenerated), dirty badges kept. +* **Final bar (`band`, after a `claude` sojourn):** left = `session_name · vim · mode · hostname · path · git(no branch text) · pr`; right cluster in array order = `model · cost · time_spent` (`context_pct` hoists out of the run to paint the labeled inline meter `60% ——— × 200K` as a flexible spacer between the groups). Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash, and `token_in`/`token_out` (cumulative totals live in `/session`). `path`: logical `project/worktree` label (`stripWorkPrefix: true`, kept after a live `false` trial of the full cwd); branch text dropped (worktree branches are autogenerated), dirty badges kept. * **models.yml:** frank-archon display `name` slimmed to `"frank-archon"` (endpoint suffix dropped; `id` untouched). * **Out of scope noted:** worktree name generation (`wt--`) is omp-internal; possible upstream feature request. diff --git a/omp-docs/pi/statusline.md b/omp-docs/pi/statusline.md index 0f86114..b0acc09 100644 --- a/omp-docs/pi/statusline.md +++ b/omp-docs/pi/statusline.md @@ -88,11 +88,12 @@ modes. The fleet runs `true` — logical label, worktree glyph (verified live ## Fleet configuration (2026-10-18) - Shape `band`: one merged status row. Left group: `session_name`, `vim`, - `mode`, `hostname`, `model`, `path`, `git` (no branch text — worktree - branches are autogenerated names), `pr`. Right group: `cost`, - `context_pct`, `time_spent`, with the labeled inline context meter - between the groups. `session_name` renders on the working row, not the - status row. + `mode`, `hostname`, `path`, `git` (no branch text — worktree branches are + autogenerated names), `pr`. Right group (array order = paint order, so the + last entry hugs the screen edge): `model`, `cost`, `context_pct`, + `time_spent` — `context_pct` hoists out of the run to paint the labeled + inline meter (`60% ——— × 200K`) as a flexible spacer between the groups. + `session_name` renders on the working row, not the status row. - Deliberately cut: `token_rate` (lives on the working row), `time` (panel clock), `cache_*` (noise), `context_total` (implied by the meter), `subagents`, `session`, `token_in`/`token_out` (cumulative totals are From 43c19ae74c3c95af8da2fd6521fb4724ca25cd69 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 01:35:26 -0700 Subject: [PATCH 06/15] Task: raise subagent maxConcurrency 2 -> 3 Operator request 2026-10-18: one more concurrent subagent slot. --- agent/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agent/config.yml b/agent/config.yml index fb3af30..6a7bd7f 100644 --- a/agent/config.yml +++ b/agent/config.yml @@ -141,7 +141,7 @@ display: showTokenUsage: true task: showResolvedModelBadge: true - maxConcurrency: 2 + maxConcurrency: 3 agentModelOverrides: designer: llamacpp (dankclaw-3090)/frank-zealot librarian: llamacpp (dankclaw-3090)/frank-zealot From 72a3dbbe08d0f4b68733b2f570dbf0d6c20a51fd Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 02:30:39 -0700 Subject: [PATCH 07/15] Docs: status line page truth-audited against bundle; four claims corrected Full-source re-verification of pi/statusline.md against dist/cli.js plus a live screenshot matrix. False and corrected: claude's top rule is NOT the context meter (meter is box/band only; rule elsewhere is a session-accent filler); 'Tern-only' composer-bar claim (real knob: composer.tokenRate); segmentOptions gated to preset custom (they merge under every preset - only the arrays are gated); mode segment yolo (states are Plan/Prewalk/Goal/ Vibe/Loop). Expanded: 7-preset defaults table, per-shape statusAttachment/ bottomBar model incl. plugin shape registry, contextLine per-value behavior, 27-segment catalog with triggers and options, segmentOptions schema, full statusLine.*/composer.* knob registry. Changelog entry added. Refs #5 --- omp-docs/log.md | 8 +- omp-docs/pi/statusline.md | 206 ++++++++++++++++++++++++++++---------- 2 files changed, 158 insertions(+), 56 deletions(-) diff --git a/omp-docs/log.md b/omp-docs/log.md index 9c348cf..d8846bd 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,11 +11,17 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — Status line docs truth audit (full-source re-verification) + +Exhaustive claim-by-claim re-audit of [OMP Status Line Customization](/pi/statusline.md) against the minified `dist/cli.js` bundle plus a live screenshot matrix (band × {off, percentage, embedded}, claude × off). Page carries an **Audit log** section; four first-pass claims were false and are corrected (see there): claude's rule is *not* the context meter (meter is `box`/`band` only; the "red rule" was the session-accent filler), the "Tern-only" composer-bar claim was bogus (the readout is `composer.tokenRate`, default off), `segmentOptions` are **not** gated to `preset: custom` (only the segment arrays are), and the `mode` segment's states are Plan/Prewalk/Goal/Vibe/Loop (no yolo). Page expanded with: the 7-preset defaults table, per-shape `statusAttachment`/`bottomBar` model (8 built-ins + plugin shape registry), `contextLine` per-value behavior, the 27-segment catalog with triggers/options, `segmentOptions` schema, and the full `statusLine.*`/`composer.*` knob registry. The bundle's own "(box composer only)" knob description for `contextLine` is upstream-wrong — band honors it. + +ServerAppConfigs-docs overlap census: flagged separately for the operator (morning report); no doc merged/changed on either side. + ## 2026-10-18 — Status line rebuilt on custom segments; frank-archon display name slimmed Interactive tune-up of the omp status bar (grilling session, live loop: config edit → watcher repaint → niri screenshot verify). New knowledge page: [OMP Status Line Customization](/pi/statusline.md). -* **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays); `composer.shape` routes them — `band` merges both arrays into one row above the editor (title rides the working row), `claude` splits them into top chip + bottom strip. The `claude` red rule **is** the context meter (length = usage); `contextLine` label variants render in `box`/`band` — `band` paints the labeled inline meter (`60% ——— × 200K`). Working row (`⠿ Working… + tok/s`) is hardcoded — model/thinking can never join it (Tern-only). Eight composer shapes registered in the bundle. +* **Mechanics discovered:** `leftSegments`/`rightSegments` are inert unless `preset: custom` (fleet was on `nerd` with dead arrays; `segmentOptions` *do* merge under named presets); `composer.shape` routes them — `band` merges both arrays into one row above the editor (title rides the working row), `claude` splits them into top chip + bottom strip. The context meter exists **only in `box`/`band`** — `contextLine`: `off` plain rule, `embedded` hoists the context segment into the labeled inline meter (`60% ——— × 200K`), `percentage`/`annotated` paint an unlabeled proportional rule meter (all four live-verified); every other shape's rule is a constant session-accent filler. Working row (session title + tok/s readout gated by `composer.tokenRate`, default off) is composer chrome — model/thinking can never join it. Eight composer shapes ship in the bundle; plugins can register more. * **Final bar (`band`, after a `claude` sojourn):** left = `session_name · vim · mode · hostname · path · git(no branch text) · pr`; right cluster in array order = `model · cost · time_spent` (`context_pct` hoists out of the run to paint the labeled inline meter `60% ——— × 200K` as a flexible spacer between the groups). Cut: tok/s duplicate, clock, cache trio, `context_total`, `subagents`, `session` hash, and `token_in`/`token_out` (cumulative totals live in `/session`). `path`: logical `project/worktree` label (`stripWorkPrefix: true`, kept after a live `false` trial of the full cwd); branch text dropped (worktree branches are autogenerated), dirty badges kept. * **models.yml:** frank-archon display `name` slimmed to `"frank-archon"` (endpoint suffix dropped; `id` untouched). * **Out of scope noted:** worktree name generation (`wt--`) is omp-internal; possible upstream feature request. diff --git a/omp-docs/pi/statusline.md b/omp-docs/pi/statusline.md index b0acc09..734945e 100644 --- a/omp-docs/pi/statusline.md +++ b/omp-docs/pi/statusline.md @@ -1,7 +1,7 @@ --- type: reference title: OMP Status Line Customization -description: How the omp status line actually works — presets vs custom arrays, composer-shape segment routing, the context meter, and the fleet's tuned segment layout. +description: How the omp status line actually works — presets vs custom arrays, composer-shape segment routing, the context meter (box/band only), the 27-segment catalog, and the fleet's tuned layout. tags: - omp - tui @@ -12,36 +12,89 @@ timestamp: 2026-10-18 # OMP Status Line Customization -Ground truth from the installed bundle (`@oh-my-pi/pi-coding-agent/dist/cli.js`) -verified live on blankpad 2026-10-18. All knobs live in -`agent/config.yml` under `statusLine:` (tracked — see -[Command Center repo](../core/index.md)); the file is file-watched, so edits -repaint a running session without restart (verify visually: niri screenshot, -see [niri capture recipe](../system/index.md)). +Ground truth from the installed bundle (`@oh-my-pi/pi-coding-agent/dist/cli.js`), +verified live on blankpad 2026-10-18 and re-audited line-by-line against the +minified source the same day (see [Audit log](#audit-log)). Status knobs live +in `agent/config.yml` under `statusLine:`; the two composer knobs +(`composer.shape`, `composer.tokenRate`) under `composer:`. The file is +file-watched — edits repaint a running session without restart (verify +visually: niri screenshot, see [niri capture recipe](../system/index.md)). +`/settings` (Appearance tab) previews these live with sample data. ## Preset vs custom — the trap -`statusLine.preset` ships whole configurations (`default`, `minimal`, -`compact`, `full`, `nerd`, `ascii`, `custom`). **`leftSegments` / -`rightSegments` / `segmentOptions` are only read when `preset: custom`** — -with any named preset they are silently ignored. The fleet config runs -`preset: custom`. +`statusLine.preset` ships whole configurations. **`leftSegments` / +`rightSegments` are ignored unless `preset: custom`** (`s ? user ?? preset +: preset` in the resolver). **`segmentOptions` is NOT gated** — user options +merge over the preset's own options under *every* preset. + +| preset | leftSegments | rightSegments | separator | +|---|---|---|---| +| `default` | pi vim model mode collab stream path git pr context_pct cost | session_name | powerline-thin | +| `minimal` | vim path git | session_name mode context_pct | slash | +| `compact` | vim model mode git pr | session_name cost context_pct | powerline-thin | +| `full` | pi vim hostname model mode path git pr subagents | session_name cache_hit token_in token_out token_rate cache_read cost context_pct time_spent time | powerline | +| `nerd` | pi vim hostname model mode path git pr session subagents | session_name token_in token_out cache_read cache_write token_rate cost context_pct context_total time_spent time | powerline | +| `ascii` | vim model mode path git pr | session_name token_total cost context_pct | ascii | +| `custom` | your `statusLine.leftSegments` | your `statusLine.rightSegments` | powerline-thin | + +The built-in default for `statusLine.leftSegments`/`rightSegments` themselves +is the `ascii`-era pair (`vim model mode path git pr` / `session_name +token_total cost context_pct`) — a config carrying those arrays with a named +preset is running the preset, not the arrays. The fleet runs `preset: custom`. ## Composer shape routes the segments -`composer.shape` decides *where* the two segment arrays render: +`composer.shape` decides *where* the two arrays render. Eight shapes ship in +the bundle; the plugin API can register more (`zG()` = built-ins + a live +registration map). Internally each shape carries `statusAttachment`, +`bottomBar`, `bottomBarGap`: -| shape | leftSegments | rightSegments | -|---|---|---| -| `band` (fleet) | merged single row above editor | same row, right side (title skipped — it rides the working row) | -| `claude` | bottom strip, left-aligned | top rule chip, right-aligned | -| `rule` | same as claude, gap above strip | same | -| `box` | box top border | box top border | -| `borderless` / `pi` / `field` / `rail` | single bottom bar | single bottom bar | +| shape | attach | bottom bar | leftSegments | rightSegments | +|---|---|---|---|---| +| `band` (fleet, default) | top-band | none | merged single row above editor, left | same row, right (`session_name` skipped — title rides the working row) | +| `box` | top-border | none | box top border | box top border | +| `claude` | top-rule-chip | left | bottom strip, left | top rule chip, right-aligned | +| `rule` | top-rule-chip | left + gap | like claude, gap above strip | like claude | +| `pi` | none | full | single bottom bar | single bottom bar | +| `borderless` | none | full | single bottom bar | single bottom bar | +| `field` / `rail` | none | full + gap (filled surface) | single bottom bar | single bottom bar | -The `⠿ Working…` row is hardcoded: shimmer text + `composer.tokenRate` readout -(+ session title in `band` only). Model/thinking-level can never join it -despite the settings description — that composer-bar behavior is Tern-only. +The chip/strip layouts paint their rules as **session-accent fillers, not +meters** — the context meter exists only in `box` and `band` +(see next section). The top-rule chip renders right segments only, the bottom +strip left segments only. + +The working row is composer chrome, not segment-addressable: session title + +shimmer, plus the tok/s readout when `composer.tokenRate: true` (default +false). The readout is estimated from streamed deltas, corrected by billed +output counts; the bundle also mentions a native-composer-bar placement in +filled styles. + +## The context meter + +`statusLine.contextLine` (enum `off` / `percentage` / `annotated` / +`embedded`, default `embedded`) tunes the rule between the segment groups. +The knob description says "box composer only" — **that is wrong**: the meter +renders in `box` **and** `band`; in every other shape the rule is a constant +session-accent filler regardless of this setting (live-verified claude with +`off` vs `embedded`: identical, and not context-proportional). + +In box/band: + +- `off` → plain accent rule (single color, full width). +- `embedded` → the inline labeled meter: `60% ——— × 200K`. Requires + `contextWindow > 0`, a `context_pct`/`context_total` segment present in + either array, and at least one non-context segment. The context segment(s) + are **hoisted out of the arrays** — they never render as text in this mode. +- `percentage` / `annotated` → unlabeled proportional meter painted into the + rule (live-verified in band: colored fill ∝ usage at the rule's right end); + `context_pct` stays a normal text segment. + +Fill length ∝ `min(100, contextPercent)`; >100% paints the raw percent and +shortens the meter. Meter/segment color thresholds scale with window size — +warning at ≥ min(50 %, 150K tokens), purple ≥ min(70 %, 270K), error ≥ +min(90 %, 500K); below that the theme's `statusLineContext` color. ## Segment universe (27) @@ -50,22 +103,33 @@ despite the settings description — that composer-bar behavior is Tern-only. `context_total`, `time_spent`, `time`, `session`, `hostname`, `cache_read`, `cache_write`, `cache_hit`, `session_name`, `usage`, `collab`, `stream`, `vim`. -Conditional segments render nothing until their trigger exists: `vim` needs -`tui.vimMode`, `mode` needs plan/yolo, `pr` needs an associated PR, `git` shows -staged/unstaged/untracked badges (branch text optional via -`segmentOptions.git.showBranch`), `status` needs an extension push. -`session` = first 8 chars of the session id (resume-by-id handle). +Zero/inactive segments render nothing — dead weight costs no width. -## The context meter - -In the `band` shape (fleet) the meter is the labeled inline line between the -segment groups (`60% ———— × 200K`). In the `claude` shape the red rule left of -the right-segment chip **is** the meter: its length tracks context usage. -`statusLine.contextLine` (`off` / `percentage` / `annotated` / `embedded`) -only changes rendering in `box`/`band` — in `claude` all non-`off` values -look identical. Docs' claim that contextLine is "box composer only" is -wrong for the meter itself; the labeled inline variant is the box/band-only -part. +| segment | renders | trigger / notes | +|---|---|---| +| `pi` | brand glyph + turn-elapsed | while streaming; focused-agent ghost id (warning color) when a subagent is focused | +| `status` | hook/extension statuses, dot-joined | extension pushes via `ctx.ui.setStatus()` (ordered by key); suppressed in footer when `statusLine.showHookStatus: false` | +| `model` | display `name` + thinking level | suffix hidden by `segmentOptions.model.showThinkingLevel: false`; `statusLine.compactThinkingLevel: true` (default) swaps in a compact glyph; fast-mode, advisor/tariff badges attach here | +| `mode` | `Plan` / `Prewalk` / `Goal` / `Vibe` / `Loop …` | only in those modes (paused variants add a pause glyph); there is **no yolo/auto-approval display** in this segment | +| `path` | cwd or `project/worktree` label | see [Path segment](#path-segment-and-stripworkprefix) | +| `git` | branch + dirty badges | `+N` staged, `*N` unstaged, `?N` untracked (`segmentOptions.git.showStaged/showUnstaged/showUntracked`, default true); `showBranch: false` hides branch text and keeps badges (git icon carries them); color flips on dirty | +| `pr` | `#` hyperlink | only once the session has an associated PR | +| `subagents` | count | hidden at 0; when subagents are actively running the composer paints its own live badge and this segment self-suppresses | +| `token_in` / `token_out` | cumulative session input/output tokens | hidden at 0; `cache_read` / `cache_write` likewise cumulative | +| `token_total` | `input + output + cache_write + orchestration_in + orchestration_out` | hidden at 0 | +| `token_rate` | `N.N tok/s` | hidden when not streaming (the working-row readout is a separate, `composer.tokenRate`-gated thing) | +| `cost` | session cost | includes subagent-tree + advisor cost; `↑`/`↓` peak/off-peak arrow for time-priced providers | +| `context_pct` | `NN.N%/200K` (or window alone when percent is unknown) | hoisted into the meter in `embedded` box/band; auto-compact speculation icon rides along | +| `context_total` | absolute context tokens | also hoisted by the embedded meter | +| `time_spent` | active session time | hidden under 1 s; this is *active* time, not wall clock | +| `time` | wall clock | `segmentOptions.time.format: "12h"/"24h"`, `showSeconds` | +| `session` | first 8 chars of session id (`new` before resume exists) | resume-by-id handle | +| `session_name` | session title, accent | in `band` it renders on the working row instead | +| `hostname` | short hostname | accent-tinted with session color | +| `usage` | rate-limit windows | 5h / daily / 7d / monthly bars + tier badge, when the provider reports them | +| `collab` | `collab:N` / `collab guest:N` | only in collab sessions | +| `stream` | `● LIVE ` / `● REC` | only while streaming/recording | +| `vim` | mode glyph/word | only while vim keybindings are active; selected-line count appended (` 3L`); `display` icon vs text comes from the vim runtime, not config | ## Path segment and `stripWorkPrefix` @@ -81,23 +145,55 @@ part. 2. **All sessions:** `true` uses the pretty display path (scratch sessions get the scratch-folder icon); `false` uses the raw absolute cwd. -`abbreviate` and `maxLength` (clip, default 40) apply afterwards in both -modes. The fleet runs `true` — logical label, worktree glyph (verified live -2026-10-18; an earlier trial of `false` for the full cwd was rejected by eye). +`abbreviate` (default **true**: home → `~`; `false` keeps the full path — +also affects tool labels/errors per upstream docs) and `maxLength` (clip, +default 40) apply afterwards in both modes. The fleet runs `true` — logical +label, worktree glyph (verified live 2026-10-18; an earlier trial of `false` +for the full cwd was rejected by eye). + +## Status knobs (registry-verified) + +`statusLine.preset` · `.leftSegments` · `.rightSegments` · `.segmentOptions` · +`.separator` (`powerline` `powerline-thin` `slash` `pipe` `block` `none` +`ascii`; default `powerline-thin`) · `.contextLine` · `.sessionAccent` +(default true; hashes the session name into the editor border *and* the +accent-tinted segments/meter color) · `.transparent` (terminal background, +no status bg fill) · `.showHookStatus` · `.compactThinkingLevel` (default +true). Plus `symbolPreset` at top level (`unicode` `nerd` `ascii` — segment +glyphs follow it). ## Fleet configuration (2026-10-18) -- Shape `band`: one merged status row. Left group: `session_name`, `vim`, - `mode`, `hostname`, `path`, `git` (no branch text — worktree branches are - autogenerated names), `pr`. Right group (array order = paint order, so the - last entry hugs the screen edge): `model`, `cost`, `context_pct`, - `time_spent` — `context_pct` hoists out of the run to paint the labeled - inline meter (`60% ——— × 200K`) as a flexible spacer between the groups. - `session_name` renders on the working row, not the status row. -- Deliberately cut: `token_rate` (lives on the working row), `time` (panel - clock), `cache_*` (noise), `context_total` (implied by the meter), - `subagents`, `session`, `token_in`/`token_out` (cumulative totals are - in `/session` anyway). -- Model display names slimmed in `models.yml` (`frank-archon`, no endpoint - suffix); `name:` is free-form display, `id:` is the wiring — renaming ids is - the cutover procedure, see [Pi Models](/pi/models.md). +- Shape `band`: one merged status row. Left group: `session_name`→working + row (skipped), `vim`, `mode`, `hostname`, `path`, `git` (no branch text — + worktree branches are autogenerated names), `pr`. Right group (array order + = paint order, so the last entry hugs the screen edge): `model`, `cost`, + `context_pct`, `time_spent` — `context_pct` hoists out of the run to paint + the labeled inline meter (`60% ——— × 200K`) between the groups under + `contextLine: embedded`. +- Deliberately cut: `token_rate` (working-row readout covers it), `time` + (panel clock), `cache_*` (noise), `context_total` (implied by the meter), + `subagents` (live badge supersedes it), `session`, `token_in`/`token_out` + (cumulative totals are in `/session` anyway). +- `models.yml`: frank-archon's display `name` slimmed to `"frank-archon"` + (endpoint suffix dropped); frank-dragoon/frank-zealot keep their endpoint + suffixes. `name:` is free-form display, `id:` is the wiring — renaming ids + is the cutover procedure, see [Pi Models](/pi/models.md). + +## Audit log + +2026-10-18 full-source re-audit corrected the first pass of this page: + +- ~~claude shape's top rule is the context meter~~ — **false**. The + meter code path runs only for `box`/`band`; claude/rule/pi/borderless/ + field/rail rules are constant session-accent fillers. (Earlier reading + mistook the accent rule for a meter.) +- ~~working-row composer-bar variant is "Tern-only"~~ — **false**; the + tok/s readout is the `composer.tokenRate` option (default false), title + placement is composer chrome. +- ~~`segmentOptions` gated to `preset: custom`~~ — **false**; options merge + under every preset, only the segment *arrays* are gated. +- ~~`mode` covers yolo~~ — **false**; states are Plan/Prewalk/Goal/Vibe/Loop. +- `contextLine` "(box composer only)" — the *bundle description* is wrong; + band honors it too (verified live: `off` plain rule, `percentage` rule + meter, `embedded` inline meter, screenshots + code). From ae82aa00e49bca23bfa5f68d7860c673e52a9ec2 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 03:39:56 -0700 Subject: [PATCH 08/15] Docs: system pages realigned to live ground truth; sibling-vault overlap census Census against serverAppConfigs-docs + read-only ssh probes on both boxes (2026-10-18). Corrections to OURS, flags only for THEIRS: - Hardware drift: RTX 3090 moved dankserver->dankclaw 2026-09; dankserver now 2x Radeon AI PRO R9700 (Ryzen 7 7700X stays), 246 GiB RAM, no swap; dankclaw 121 GiB RAM + 64 GiB swap. Served: frank-archon radiance TP=2 (:7770 dankserver), frank-dragoon halogen NATIVE v2 (:7770 dankclaw iGPU), frank-zealot llama.cpp PrismML Ternary-Bonsai-2-27B PQ2_0 (:7771 dankclaw, 3090). Kernel 7.0.0-38 both; dankclaw :7770 no auth. - Gitea: gitea-caddy quadlet retired (fleet Caddy fronts the domain); data tree moved to /home/dank/Containers/gitea/ (2026-09-01 standard), /opt/gitea is a do-not-use leftover; LAN IP + live domain. - pi-era pages (core/settings, pi/models + role tables) demarcated as ARCHIVE with banners; llama-cpp fleet note modernized. - AGENTS.md machine-facts table corrected (it still routed prod warnings at frank-default-large). Their-side staleness flagged in the changelog entry for the operator, not edited: OMP Harness thresholdPercent 70 (live is 95), Ubuntu 24.04 OS table, 2026-09-03 stack mapping, superseded pi-KB backlink. Refs #5 --- AGENTS.md | 11 +-- omp-docs/core/index.md | 10 ++- omp-docs/log.md | 10 +++ omp-docs/pi/index.md | 10 ++- omp-docs/system/dankclaw.md | 94 ++++++++++++------------ omp-docs/system/dankserver.md | 58 +++++++++------ omp-docs/system/index.md | 10 +-- omp-docs/tools/gitea/architecture.md | 9 +-- omp-docs/tools/gitea/data-persistence.md | 19 +++-- omp-docs/tools/gitea/index.md | 32 ++++---- omp-docs/tools/gitea/management.md | 9 +-- omp-docs/tools/llama-cpp/index.md | 2 +- 12 files changed, 151 insertions(+), 123 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index ac0e276..70cdbe6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -71,8 +71,8 @@ secret in every agent's context window on every machine that pulls this repo. | box | role | GPU | notes | |-----|------|-----|-------| | Dev workstation | client box, never runs inference | laptop iGPU + dGPU (non-unified) | mainline llama.cpp at `~/GitPackages/llama.cpp/build/bin/`; this repo lives here at `~/.omp` | -| Dankclaw | bench target, runs the large serving model | Strix Halo APU — **unified memory** | ROCmFPX fork at `~/GitPackages/ROCmFPX/build-qwen38/bin/`; prod service `frank-default-large`, port 7770; ssh: `ssh dankclaw` | -| Dankserver | FreeToken box | RTX 3090 dGPU — non-unified | runs `frank-default-small`; ssh: `ssh dankserver` | +| Dankclaw | bench target, secondary models + service stack | Strix Halo APU (**unified memory**) + RTX 3090 (moved over from Dankserver 2026-09) | prod services `frank-dragoon` (halogen-flash-server, :7770, iGPU) and `frank-zealot` (llama.cpp PrismML fork, :7771, 3090); runbooks in the serverAppConfigs vault (`~/Projects/serverAppConfig/serverAppConfigs-docs/LLM/`); ssh: `ssh dankclaw` | +| Dankserver | main-model box | 2× Radeon AI PRO R9700 — non-unified (was RTX 3090 until 2026-09) | runs `frank-archon` (radiance, TP=2, :7770) — **this repo's operator session runs on it**; ssh: `ssh dankserver` | ## Environment gotchas @@ -84,9 +84,10 @@ secret in every agent's context window on every machine that pulls this repo. - SSH into both servers is BatchMode-capable from here: `ssh -o BatchMode=yes dankclaw ''` — read-only probes are always allowed; state changes on remote boxes are not, without a per-run OK. -- `frank-default-large` on Dankclaw is a **production service** shared with this - repo's operator: never stop, mask, or restart it (or any production service on - any box) without an explicit, current, per-run OK from Frank. +- `frank-archon` (Dankserver), `frank-dragoon`, and `frank-zealot` (Dankclaw) + are **production services** shared with this repo's operator: never stop, + mask, or restart them (or any production service on any box) without an + explicit, current, per-run OK from Frank. ## File-organization doctrine (why this repo stays readable) diff --git a/omp-docs/core/index.md b/omp-docs/core/index.md index 4748631..c6c375e 100644 --- a/omp-docs/core/index.md +++ b/omp-docs/core/index.md @@ -1,12 +1,18 @@ # Core Documentation -These files define the structural conventions that govern the pi agent system itself. They are the grammar rules — boring until you break them, then devastating. +These files define the structural conventions that govern the agent system itself. They are the grammar rules — boring until you break them, then devastating. + +> **Archive note (2026-10-18).** The settings pages (`settings.md`, `settings.json`) +> capture the retired **pi** harness configuration (`frank-default-*` endpoints, +> RTX 3090-era hardware) and are historical records. The live config is +> `agent/config.yml` + `agent/models.yml` in this repo; current fleet endpoints +> are on [System hosts](/system/index.md). The AGENTS.md format pages remain current. ## Concept Files * [AGENTS.md Format Specification](agents-md-format.md) — Canonical structure and conventions for the system-level instruction file that governs agent behavior. * [AGENTS.md Index](index-doc.md) — Overview of the cached tool documentation system and how to add new entries. -* [Pi Settings System](settings.md) — Current system configuration: providers, models, packages, subagent overrides, and runtime options. +* [Pi Settings System](settings.md) — pi-era `settings.json` record (archive): providers, models, packages, subagent overrides. ## Change Tracking diff --git a/omp-docs/log.md b/omp-docs/log.md index d8846bd..f844714 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,6 +11,16 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — Sibling-vault overlap census; system docs realigned to live ground truth + +Census of `omp-docs/` against `~/Projects/serverAppConfig/serverAppConfigs-docs/` (subagent + live-ssh verification) found four contradicting pairs; all OURS-side stale pages corrected, THEIRS side flagged only (their repo, their PR flow). Ground truth from read-only probes on both boxes 2026-10-18. + +* **Hardware drift corrected** (`system/dankserver.md`, `system/dankclaw.md` rewritten, `AGENTS.md` machine table): RTX 3090 moved Dankserver→Dankclaw 2026-09; dankserver now 2× Radeon AI PRO R9700 (7700X stays — the "board swap" story is false), 246 GiB RAM, no swap; dankclaw 121 GiB RAM + 64 GiB swap. Served models per live units: frank-archon (:7770 dankserver, radiance TP=2 iq4r), frank-dragoon (:7770 dankclaw, halogen NATIVE v2, iGPU), frank-zealot (:7771 dankclaw, llama.cpp PrismML, Ternary-Bonsai-2-27B PQ2_0, 3090). Both kernels 7.0.0-38-generic; dankclaw :7770 needs no auth. +* **Gitea pages corrected** (`tools/gitea/` index + 3 subpages): `gitea-caddy` quadlet retired — fleet Caddy fronts the domain; data tree is `/home/dank/Containers/gitea/{data,postgres,env}` (2026-09-01 standard), `/opt/gitea/` is a do-not-use leftover; LAN IP, live domain, no WAN address. +* **Pi-era pages demarcated as archive** (`pi/index.md`, `core/index.md` banners; `core/index.md` said settings.md was "Current"): `frank-default-small/large` endpoints, role tables, and 3090/4070 hardware rows now read as pi-era records. `tools/llama-cpp/index.md` fleet note modernized to the one remaining llama.cpp endpoint (zealot :7771). +* **Their-side staleness flagged, NOT edited** (for operator clarification): `Agent/OMP Harness.md` still says thresholdPercent 70 — live config is **95** (`agent/config.yml:84`); `index.md:18` says Dankserver Ubuntu 24.04 vs their own specs page 26.04; `Dankclaw/Stack.md` "current mapping (2026-09-03)" predates the ACTIVE dragoon/zealot runbooks it links beside; `index.md:548` links the superseded pi-era KB at `~/.pi/agent/docs/`. +* Consistent duplicates (no action): niri/noctalia desktop pages, Caddy reference. Divergence clarified: their "composer" = OpenWebUI chat box; ours = omp composer shape — unrelated subjects. + ## 2026-10-18 — Status line docs truth audit (full-source re-verification) Exhaustive claim-by-claim re-audit of [OMP Status Line Customization](/pi/statusline.md) against the minified `dist/cli.js` bundle plus a live screenshot matrix (band × {off, percentage, embedded}, claude × off). Page carries an **Audit log** section; four first-pass claims were false and are corrected (see there): claude's rule is *not* the context meter (meter is `box`/`band` only; the "red rule" was the session-accent filler), the "Tern-only" composer-bar claim was bogus (the readout is `composer.tokenRate`, default off), `segmentOptions` are **not** gated to `preset: custom` (only the segment arrays are), and the `mode` segment's states are Plan/Prewalk/Goal/Vibe/Loop (no yolo). Page expanded with: the 7-preset defaults table, per-shape `statusAttachment`/`bottomBar` model (8 built-ins + plugin shape registry), `contextLine` per-value behavior, the 27-segment catalog with triggers/options, `segmentOptions` schema, and the full `statusLine.*`/`composer.*` knob registry. The bundle's own "(box composer only)" knob description for `contextLine` is upstream-wrong — band honors it. diff --git a/omp-docs/pi/index.md b/omp-docs/pi/index.md index 9d40d9f..a444c85 100644 --- a/omp-docs/pi/index.md +++ b/omp-docs/pi/index.md @@ -1,8 +1,12 @@ # Pi Configuration -Documentation for the pi coding agent installation on ](../system/index.md). -Everything here is configured locally on the workstation; the LLM providers are the -two llama.cpp servers (Dankserver, Dankclaw). +> **Archive note (2026-10-18).** Most pages in this directory document the retired +> **pi** harness (settings.json / models.json, `frank-default-*` endpoints, verified +> 2026-08-28). The live harness is **omp** — its config is `agent/config.yml` + +> `agent/models.yml` in this repo, the current fleet endpoints are on +> [System hosts](/system/index.md), and omp-specific knowledge lands in pages like +> [OMP Status Line Customization](/pi/statusline.md) below. Read pi pages as +> historical records, not current state. ## Configuration Files diff --git a/omp-docs/system/dankclaw.md b/omp-docs/system/dankclaw.md index 91e9f69..5601937 100644 --- a/omp-docs/system/dankclaw.md +++ b/omp-docs/system/dankclaw.md @@ -1,83 +1,79 @@ --- type: reference title: Dankclaw -description: Secondary inference server at 192.168.0.143. Runs llama.cpp (frank-default-large). +description: Inference and services box at 192.168.0.143 — frank-dragoon (halogen-flash-server, Strix Halo iGPU, :7770) and frank-zealot (llama.cpp on the RTX 3090, :7771), plus Gitea/Caddy/OpenWebUI services. tags: - network - llamacpp - inference - server - comfyui - - unverified generated: by: chief-designer/agent at: 2026-08-26T00:00:00Z -verified: 2026-08-28 +verified: 2026-10-18 --- # Dankclaw -Secondary llama.cpp inference server for the Frank Program. Serves larger models used by planner, worker, reviewer, oracle, and context-builder subagents. +Strix Halo mini-PC carrying the fleet's secondary models and most of the +service stack (Gitea, Caddy, OpenWebUI, SearXNG). Two GPU-class devices: the +Radeon 8060S iGPU with 119 GiB unified memory, and the RTX 3090 moved over +from Dankserver in September 2026. ## Connection Details | Property | Value | |----------|-------| | **Hostname** | dankclaw | -| **IP Address** | 192.168.0.143 | -| **Port** | 7770 | -| **Base URL** | `http://192.168.0.143:7770/v1` | -| **API** | OpenAI-compatible completions | -| **Authentication** | API key configured | +| **IP Address** | 192.168.0.143 (LAN) | +| **Auth** | none required on :7770 (verified 2026-10-18) | -## Models +## Inference Services -| Model ID | Context Window | Reasoning | Input Types | -|----------|---------------|-----------|-------------| -| `frank-default-large` | 262,144 tokens | Yes | Text, *Image* (unverified) | +| Model ID | Unit | Endpoint | Engine / weights | +|----------|------|----------|------------------| +| `frank-dragoon` | `frank-dragoon.service` (root) | `:7770` | halogen-flash-server container — Quat3rnion NATIVE v2 abliterated Qwen3.8-Flash-Next, MTP spec decode, 2 slots over ONE 200,704-position pool, vision ON, **Strix Halo iGPU** (unified memory) | +| `frank-zealot` | `frank-zealot.service` (root) | `:7771` | llama.cpp (PrismML fork, build 10754) — Ternary-Bonsai-2-27B PQ2_0 GGUF (qwen35 hybrid), 2×200,704 slots, q8_0 K+V, mmproj vision, **RTX 3090** | -## Services - -### llama.cpp - -| Property | Value | -|----------|-------| -| **Port** | 7770 | -| **Models** | `frank-default-large` | - -## Hardware - -| Property | Value | -|----------|-------| -| **CPU** | AMD Ryzen AI MAX+ 395 (16C/16T, Strix Halo) | -| **GPU** | Radeon 8060S integrated (gfx1151, amdgpu) | -| **RAM** | 119 GiB | -| **Storage** | Kingston SNV3S1000G NVMe (931.5 GB), ~676 GB free | -| **OS** | Ubuntu 26.04 LTS, kernel 7.0.0-30-generic | -| **ROCm** | 7.2.4 (driver: 7.0.0-30-generic) | -| **Python** | 3.14.4 (system) | - -## Usage - -Dankclaw handles complex tasks delegated by subagent overrides. The `frank-default-large` model provides extended context. *Multimodal input support is unverified.* +Harness-side context windows (`agent/models.yml`): dragoon 155,648, +zealot 200,704 per slot. The models.yml display labels "(dankclaw-gpu)" / +"(dankclaw-3090)" predate naming cleanup; the hardware column above is +current. Subagent roles `scout`, `sonic`, `designer`, `librarian`, +`security-reviewer` route to frank-zealot; `reviewer`/`task` run on +frank-archon (Dankserver). ```bash -# Health check -curl http://192.168.0.143:7770/health - -# Test with large model -curl http://192.168.0.143:7770/v1/chat/completions \ - -H "Content-Type: application/json" \ - -H "Authorization: Bearer " \ - -d '{"model":"frank-default-large","messages":[{"role":"user","content":"Hello"}],"max_tokens":32}' +curl http://192.168.0.143:7770/v1/models # dragoon +curl http://192.168.0.143:7771/v1/models # zealot ``` -## Related Knowledge Bases (sibling project on the workstation) +## Hardware (verified live 2026-10-18) -- **serverAppConfigs** — `/home/dank/Projects/serverAppConfigs/` — Podman deployment config for this host (AI, search, development, observability stack, incl. the Hermes compose service) **plus the LLM inference fleet** (frank-default-large, CIRU ROCmFPX llama.cpp, :7770). The former `vllmScripts` repo merged into it on 2026-08-22 (history preserved). Start at `/home/dank/Projects/serverAppConfigs/docs/index.md` (see `dankclaw/compose.yaml` + `docs/Dankclaw/`; LLM fleet at `docs/LLM/index.md`, deploy runbook at `docs/LLM/deployment.md`). +| Property | Value | +|----------|-------| +| **CPU/APU** | AMD Ryzen AI MAX+ 395 (Strix Halo, Radeon 8060S, gfx1151) | +| **dGPU** | NVIDIA GeForce RTX 3090 (moved from Dankserver 2026-09) | +| **RAM** | 121 GiB (mostly usable as GPU unified memory) | +| **Swap** | 64 GiB | +| **OS** | Ubuntu 26.04 LTS, kernel 7.0.0-38-generic | + +The pre-rename story (`frank-default-large` on ROCmFPX llama.cpp/CIRU, +262,144-token context) was superseded by the 2026-10-08/09 model-id rename +and the dragoon/zealot engine swaps; the 119→121 GiB RAM figure follows the +live `free` reading. + +## Related Knowledge Bases (sibling project) + +- **serverAppConfigs** — deployment repo + vault, on this workstation at + `~/Projects/serverAppConfig/` (docs vault: `~/Projects/serverAppConfig/serverAppConfigs-docs/`). + Live runbooks: `LLM/frank-dragoon-flash-server-v2-native.md` and + `LLM/frank-zealot-llama-prism-bonsai2-pq20.md` — deploy-side source of + truth for engines, flags, and restart procedures. Service stack docs under + `Dankclaw/`, `Infrastructure/`. ## See Also -- [[models|models]] — Provider definitions -- [[dankserver|dankserver]] — Primary server +- [[dankserver|dankserver]] — the main-model box (frank-archon) +- [/tools/gitea/index.md](/tools/gitea/index.md) — Gitea stack on this host - [[network|network]] — Network topology diff --git a/omp-docs/system/dankserver.md b/omp-docs/system/dankserver.md index 9c62697..714a617 100644 --- a/omp-docs/system/dankserver.md +++ b/omp-docs/system/dankserver.md @@ -1,22 +1,24 @@ --- type: reference title: Dankserver -description: Primary llama.cpp inference server at 192.168.0.235:7770, serving frank-default-small with 163,840-token context window. +description: Radiance inference server at 192.168.0.235:7770, serving frank-archon (Qwen3.8-Flash-Next iq4r, 2x R9700 TP=2, 200,000-token context). tags: - network - llamacpp - inference - server - - unverified generated: by: chief-designer/agent -verified: 2026-08-28 +verified: 2026-10-18 status: stable --- # Dankserver -Primary llama.cpp inference server for the Frank Program. Serves `frank-default-small` — a fast, capable model with image support. +Primary inference server for the Frank Program. Serves `frank-archon`, the +main agent model, from a pair of Radeon AI PRO R9700s (tensor-parallel 2). +The RTX 3090 that used to live here was removed 2026-09-15 and now runs on +Dankclaw. ## Connection Details @@ -28,19 +30,24 @@ Primary llama.cpp inference server for the Frank Program. Serves `frank-default- | **Base URL** | `http://192.168.0.235:7770/v1` | | **API** | OpenAI-compatible completions | | **Authentication** | None | +| **Systemd unit** | `frank-archon.service` (root) | ## Model | Property | Value | |----------|-------| -| **Model ID** | `frank-default-small` | -| **Context Window** | 163,840 tokens | -| **Reasoning** | Supported | -| **Input Types** | Text, *Image* (unverified) | +| **Model ID** | `frank-archon` (display name `frank-archon`) | +| **Weights** | Qwen3.8-Flash-Next, radiance iq4r (publisher build) | +| **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. ## Usage -Dankserver serves the small subagent model `frank-default-small`. Small subagent roles route here unless a role override picks a different model. +Dankserver carries the main-session model. Subagent role overrides in +`agent/models.yml` route here for `task` and `reviewer`; scout/sonic/designer +route to Dankclaw. ```bash # Health check @@ -49,27 +56,34 @@ curl http://192.168.0.235:7770/health # Quick test curl http://192.168.0.235:7770/v1/chat/completions \ -H "Content-Type: application/json" \ - -d '{"model":"frank-default-small","messages":[{"role":"user","content":"Hello"}],"max_tokens":32}' + -d '{"model":"frank-archon","messages":[{"role":"user","content":"Hello"}],"max_tokens":32}' ``` -## Hardware +## Hardware (verified live 2026-10-18) | Property | Value | |----------|-------| -| **CPU** | AMD Ryzen 7 7700X (8C/16T, Raphael) | -| **GPU** | NVIDIA GeForce RTX 3090 (24 GiB VRAM, EVGA) | -| **RAM** | 60 GiB | -| **Swap** | 64 GiB (/swapfile) | -| **Storage** | Kingston SNV3S1000G NVMe (931.5 GB) + md0 RAID (6×18.2T Toshiba, /mnt/entropy, 91T) | -| **OS** | Ubuntu 26.04 LTS, kernel 7.0.0-30-generic | -| **NVIDIA Driver** | 595.84 | +| **CPU** | AMD Ryzen 7 7700X (8C/16T, Raphael iGPU on board) | +| **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 | +| **Storage** | 7× 18.2 TB HDD + 1.8 TB NVMe (layout owned by the deploy repo) | +| **OS** | Ubuntu 26.04.1 LTS, kernel 7.0.0-38-generic | -## Related Knowledge Bases (sibling project on the workstation) +The old page (verified 2026-08-28) listed an RTX 3090, 60 GiB RAM, and a +64 GiB swapfile here; all three were superseded by the 2026-09 hardware swap. -- **serverAppConfigs** — `/home/dank/Projects/serverAppConfigs/` — Podman deployment config for this host (media and library stack: RomM, Copyparty, Calibre-Web) **plus the LLM inference fleet** (frank-default-small, llama.cpp, :7770). The former `vllmScripts` repo merged into it on 2026-08-22 (history preserved). Start at `/home/dank/Projects/serverAppConfigs/docs/index.md` (see `dankserver/compose.yaml` + `docs/Dankserver/`; LLM fleet at `docs/LLM/index.md`, deploy runbook at `docs/LLM/deployment.md`). +## Related Knowledge Bases (sibling project) + +- **serverAppConfigs** — deployment repo + vault, on this workstation at + `~/Projects/serverAppConfig/` (docs vault: `~/Projects/serverAppConfig/serverAppConfigs-docs/`). + Podman/quadlet deploy config for both servers plus the LLM inference fleet. + The live frank-archon runbook (engine flags, KV pool sizing, restart + procedures) is `LLM/frank-archon-radiance-qwen38-iq4r.md` in that vault — + it, not this page, is the deploy-side source of truth. ## See Also -- [[models|models]] — Provider definitions -- [[dankclaw|dankclaw]] — Secondary server +- [[dankclaw|dankclaw]] — the other inference box (dragoon + zealot) - [[network|network]] — Network topology +- [/pi/statusline.md](/pi/statusline.md) — where these models surface in the TUI diff --git a/omp-docs/system/index.md b/omp-docs/system/index.md index 72f0bab..695eabd 100644 --- a/omp-docs/system/index.md +++ b/omp-docs/system/index.md @@ -11,8 +11,8 @@ Dankclaw are the inference servers it talks to. ## Network Systems -- [Dankserver](/system/dankserver.md) — Primary llama.cpp inference server at 192.168.0.235:7770, serving `frank-default-small` (163,840-token context). AMD Ryzen 7 7700X, RTX 3090, Ubuntu 26.04. -- [Dankclaw](/system/dankclaw.md) — Secondary inference server at 192.168.0.143:7770, serving `frank-default-large` (262,144-token context). AMD Ryzen AI MAX+ 395 (Strix Halo), Ubuntu 26.04, ROCm. +- [Dankserver](/system/dankserver.md) — Main-model inference server at 192.168.0.235:7770, serving `frank-archon` (radiance Qwen3.8-Flash-Next, 200K ctx). AMD Ryzen 7 7700X, 2× Radeon AI PRO R9700, Ubuntu 26.04. +- [Dankclaw](/system/dankclaw.md) — Secondary inference + services box at 192.168.0.143: `frank-dragoon` (:7770, halogen-flash-server on the Strix Halo iGPU) and `frank-zealot` (:7771, llama.cpp on the RTX 3090). AMD Ryzen AI MAX+ 395, Ubuntu 26.04. ## Infrastructure Diagram @@ -26,8 +26,8 @@ graph TB end subgraph Network[192.168.0.0/24] - E[dankserver
192.168.0.235:7770
frank-default-small] - F[dankclaw
192.168.0.143:7770
frank-default-large] + E[dankserver
192.168.0.235:7770
frank-archon] + F[dankclaw
192.168.0.143:7770 dragoon
:7771 zealot] end D --> E @@ -42,7 +42,7 @@ graph TB The fleet has one sibling OKF knowledge base outside this bundle. Read it for deployment and inference details. This bundle links to it from the server pages. -* **serverAppConfigs** — `/home/dank/Projects/serverAppConfigs/` (index: `/home/dank/Projects/serverAppConfigs/docs/index.md`). Podman deployment configuration for both servers: compose files, Caddy reverse proxy, deploy scripts, bats tests, and the Obsidian-vault service docs (Dankclaw, Dankserver, Architecture, Infrastructure) — **plus the LLM inference fleet** (merged from the former `vllmScripts` repo on 2026-08-22, history preserved): llama.cpp on Dankserver (:7770, `frank-default-small`) and CIRU ROCmFPX llama.cpp on Dankclaw (:7770, `frank-default-large`). LLM fleet docs: `docs/LLM/index.md`; deploy runbook: `docs/LLM/deployment.md`. +* **serverAppConfigs** — deploy repo + Obsidian vault, on this workstation at `~/Projects/serverAppConfig/` with docs in `~/Projects/serverAppConfig/serverAppConfigs-docs/` (root `index.md` at the vault top — the old `docs/` prefix is gone). Podman deployment configuration for both servers: compose/quadlet files, Caddy reverse proxy, deploy scripts, bats tests, and the vault service docs (Dankclaw, Dankserver, Architecture, Infrastructure) — **plus the LLM inference fleet** (merged from the former `vllmScripts` repo on 2026-08-22, history preserved): radiance frank-archon on Dankserver (:7770), halogen frank-dragoon (:7770) and llama.cpp frank-zealot (:7771) on Dankclaw. The vault's `LLM/` runbooks are the deploy-side source of truth; this bundle documents the agent/harness side. ## Related diff --git a/omp-docs/tools/gitea/architecture.md b/omp-docs/tools/gitea/architecture.md index e78b1d0..a7f5678 100644 --- a/omp-docs/tools/gitea/architecture.md +++ b/omp-docs/tools/gitea/architecture.md @@ -5,13 +5,12 @@ description: Gitea container architecture, network topology, and Caddy reverse p tags: - gitea - architecture - - unverified -verified: 2026-08-28 +verified: 2026-10-18 --- # Gitea Architecture -Three quadlet-managed containers on a shared private Podman network (rootless, user `dank`). Gitea connects to PostgreSQL via network DNS. Caddy proxies HTTP/S to Gitea. +Two quadlet-managed containers (Gitea, PostgreSQL) on a shared private Podman network (rootless, user `dank`). Gitea connects to PostgreSQL via network DNS. The fleet Caddy (separate stack, see [/caddy/index.md](/caddy/index.md)) proxies HTTP/S to Gitea — the old dedicated `gitea-caddy` container is retired. ## Network Topology @@ -63,10 +62,10 @@ Key environment variables: - `DATABASE_HOST=gitea-postgres` (network DNS) - `DATABASE_NAME=gitea` - `DATABASE_USER=gitea` -- `DATABASE_PASSWD` from `/opt/gitea/.env` (mode 0600) +- `DATABASE_PASSWD` from `/home/dank/Containers/gitea/env` (mode 0600) - `USER_UID=1000`, `USER_GID=1000` -Gitea writes `/data/gitea/conf/app.ini` on first start (host: `/opt/gitea/data/gitea-gitea/gitea/conf/app.ini`). The `[database] HOST` and `PASSWD` lines in app.ini override the env vars. When you rename the database container, edit the `HOST` line in app.ini as well. +Gitea writes `/data/gitea/conf/app.ini` on first start (host: `/home/dank/Containers/gitea/data/gitea/conf/app.ini`). The `[database] HOST` and `PASSWD` lines in app.ini override the env vars. When you rename the database container, edit the `HOST` line in app.ini as well. ## Ports diff --git a/omp-docs/tools/gitea/data-persistence.md b/omp-docs/tools/gitea/data-persistence.md index ec36c83..07261a6 100644 --- a/omp-docs/tools/gitea/data-persistence.md +++ b/omp-docs/tools/gitea/data-persistence.md @@ -1,24 +1,23 @@ --- type: reference title: Gitea Data Persistence -description: Gitea data persistence, volume mounts, ownership, and backup strategy on dankclaw. +description: Gitea data persistence, volume mounts, ownership, and backup strategy on dankclaw (Containers/gitea layout). tags: - gitea - data - - unverified -verified: 2026-08-28 +verified: 2026-10-18 --- # Gitea Data Persistence -All data lives on host bind mounts under `/opt/gitea/data/`. Containers are ephemeral. Quadlet stops remove the container (`podman run --rm --replace`). The next start recreates it. Data survives. +All data lives on host bind mounts under `/home/dank/Containers/gitea/` (fleet storage standard since 2026-09-01; quadlet units re-pointed from the old `/opt/gitea/data/` tree, which lingers on disk as a leftover — never write to it). Containers are ephemeral. Quadlet stops remove the container (`podman run --rm --replace`). The next start recreates it. Data survives. ## Volume Layout | Host Path | Container Mount | Contents | |---|---|---| -| `/opt/gitea/data/gitea-gitea/` | `/data` | Git repositories, avatars, attachments, LFS objects, `app.ini` | -| `/opt/gitea/data/gitea-postgres/` | `/var/lib/postgresql/data` | PostgreSQL WAL, tables, indexes | +| `/home/dank/Containers/gitea/data/` | `/data` (gitea-server) | Git repositories, avatars, attachments, LFS objects, `app.ini` | +| `/home/dank/Containers/gitea/postgres/` | `/var/lib/postgresql/data` (gitea-postgres) | PostgreSQL WAL, tables, indexes | ## Ownership @@ -37,7 +36,7 @@ podman exec --user 1000 gitea-server sed -i 's/old/new/' /data/gitea/conf/app.in Tar the entire data directory. This captures Gitea repos and PostgreSQL data in one archive. ``` -tar czf gitea-backup-$(date +%F).tar.gz -C /opt/gitea data/ +tar czf gitea-backup-$(date +%F).tar.gz -C /home/dank/Containers/gitea data/ postgres/ ``` ### PostgreSQL-only backup @@ -48,17 +47,17 @@ Use `pg_dump` for a portable SQL dump. Useful for restoring to a different Postg podman exec -e PGPASSWORD="$GITEA_DB_PASSWORD" gitea-postgres pg_dump -U gitea gitea > gitea-db-$(date +%F).sql ``` -Set `PGPASSWORD` from `/opt/gitea/.env` (mode 0600). +Set `PGPASSWORD` from `/home/dank/Containers/gitea/env` (mode 0600, `GITEA_DB_PASSWORD`). ### Restore procedure 1. Stop the stack: `systemctl --user stop gitea-server gitea-postgres` -2. Restore data: `tar xzf backup.tar.gz -C /opt/gitea/` +2. Restore data: `tar xzf backup.tar.gz -C /home/dank/Containers/gitea/` 3. Start the stack: `systemctl --user start gitea-postgres gitea-server` ## Data Growth -- Git repositories grow with each commit and push. Monitor with `du -sh /opt/gitea/data/gitea-gitea/git/repositories/`. +- Git repositories grow with each commit and push. Monitor with `du -sh /home/dank/Containers/gitea/data/git/repositories/`. - PostgreSQL WAL files accumulate. VACUUM via `podman exec gitea-postgres psql -U gitea -c "VACUUM;"`. - Gitea's log files grow with usage. diff --git a/omp-docs/tools/gitea/index.md b/omp-docs/tools/gitea/index.md index bd640fc..0368c46 100644 --- a/omp-docs/tools/gitea/index.md +++ b/omp-docs/tools/gitea/index.md @@ -1,11 +1,11 @@ # Gitea on dankclaw -Private Git hosting on dankclaw. Three quadlet-managed containers in the rootless podman session of user `dank`: Gitea, PostgreSQL, Caddy. +Private Git hosting on dankclaw. Two quadlet-managed containers in the rootless podman session of user `dank`: Gitea and PostgreSQL, sharing a dedicated network unit. The former `gitea-caddy` container is **retired** — the canonical fleet Caddy (`caddy.service`, see [/caddy/index.md](/caddy/index.md)) fronts `gitea.franksdank.cloud`; do not recreate the retired unit. -**Host:** dankclaw (71.227.227.172) -**Config:** `~/.config/containers/systemd/gitea-{postgres,server,caddy}.container` + `gitea.network` -**Local access:** `http://dankclaw:3000` -**Domain:** `https://gitea.franksdank.cloud` (DNS pending) +**Host:** dankclaw (192.168.0.143 LAN) +**Config:** `~/.config/containers/systemd/gitea-{postgres,server}.container` + `gitea.network` +**Local access:** `http://dankclaw:3000` (verified 2026-10-18) +**Domain:** `https://gitea.franksdank.cloud` (live; served by fleet Caddy, DNS-01 TLS) ## Architecture @@ -13,7 +13,7 @@ Private Git hosting on dankclaw. Three quadlet-managed containers in the rootles |---|---|---|---| | `gitea-server` | `gitea-server.service` | 3000 (HTTP), 2222 (SSH) | Git web UI, API, SSH daemon | | `gitea-postgres` | `gitea-postgres.service` | 5432 (internal) | Database | -| `gitea-caddy` | `gitea-caddy.service` | 80, 443 | Reverse proxy, auto-TLS | +| (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. @@ -23,21 +23,21 @@ All commands run as `dank` on dankclaw. | Action | Command | |---|---| -| Start all | `systemctl --user start gitea-postgres gitea-server gitea-caddy` | -| Stop all | `systemctl --user stop gitea-caddy gitea-server gitea-postgres` | +| 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 gitea-caddy` | +| Check status | `systemctl --user status gitea-postgres gitea-server` | ## Data Persistence -All data lives on host bind mounts under `/opt/gitea/data/`. Containers are ephemeral. Stopping a quadlet unit removes its container; the next start recreates it. Volumes survive. +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. -| Volume Path | Container Mount | Contents | -|---|---|---| -| `/opt/gitea/data/gitea-gitea/` | `/data` | Git repositories, app.ini, attachments | -| `/opt/gitea/data/gitea-postgres/` | `/var/lib/postgresql/data` | PostgreSQL data files | -| `/opt/gitea/data/caddy/data/` | `/root/.local/share/caddy` | Caddy configuration, TLS certs | +| 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. @@ -49,7 +49,7 @@ See [[management|management]] for boot persistence details and troubleshooting. ## Repo Copies -The quadlet files, Caddyfile, and `.env.example` are tracked in the `serverAppConfigs` repo under `dankclaw/gitea/`. The live password lives only in `/opt/gitea/.env` on the host (mode 0600). +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 diff --git a/omp-docs/tools/gitea/management.md b/omp-docs/tools/gitea/management.md index 5d577b5..3fdb118 100644 --- a/omp-docs/tools/gitea/management.md +++ b/omp-docs/tools/gitea/management.md @@ -6,8 +6,7 @@ tags: - gitea - management - quadlet - - unverified -verified: 2026-08-28 +verified: 2026-10-18 --- # Gitea Management @@ -45,8 +44,8 @@ After editing a quadlet file: run `systemctl --user daemon-reload`, then restart | File | Path | |---|---| | Quadlet units | `~/.config/containers/systemd/gitea-*.container`, `gitea.network` | -| Secrets | `/opt/gitea/.env` (mode 0600, `GITEA_DB_PASSWORD`) | -| Gitea app config | `/opt/gitea/data/gitea-gitea/gitea/conf/app.ini` (container-owned, UID 100999) | +| Secrets | `/home/dank/Containers/gitea/env` (mode 0600, `GITEA_DB_PASSWORD`) — quadlet `EnvironmentFile` | +| Gitea app config | `/home/dank/Containers/gitea/data/gitea/conf/app.ini` (container-owned, UID 100999) | Repo copies: `serverAppConfigs/dankclaw/gitea/` (quadlet files, `.env.example`). Copy edits to the target, then `daemon-reload` + restart. @@ -62,7 +61,7 @@ Gitea handles database migrations automatically on startup. PostgreSQL version b ### Gitea cannot reach the database -Check the `[database] HOST` line in `/opt/gitea/data/gitea-gitea/gitea/conf/app.ini`. It must be `gitea-postgres:5432`. app.ini overrides the `DATABASE_HOST` env var. +Check the `[database] HOST` line in `/home/dank/Containers/gitea/data/gitea/conf/app.ini`. It must be `gitea-postgres:5432`. app.ini overrides the `DATABASE_HOST` env var. Then check name resolution from the container: diff --git a/omp-docs/tools/llama-cpp/index.md b/omp-docs/tools/llama-cpp/index.md index 35e2d99..ca0a4ff 100644 --- a/omp-docs/tools/llama-cpp/index.md +++ b/omp-docs/tools/llama-cpp/index.md @@ -50,7 +50,7 @@ llama-server -hf ggml-org/gemma-3-1b-it-GGUF **Defaults**: Listens on `127.0.0.1:8080`. Single slot (one concurrent request). -> **System note:** On this network the Pi inference hosts do not use the default port. Pi serves `frank-default-small` (dankserver) and `frank-default-large` (dankclaw), each on port `7770`. The local Ornith host runs on port `8080`. See [Pi Models Configuration](../../pi/models.md). +> **System note:** On this network the fleet inference hosts do not use the default port. As of 2026-10 the only llama.cpp endpoint is `frank-zealot` on dankclaw **:7771** (PrismML fork build); `frank-dragoon` (dankclaw :7770, halogen-flash-server) and `frank-archon` (dankserver :7770, radiance) are different engines — see [System hosts](/system/index.md). The pi-era `frank-default-small`/`frank-default-large` llama.cpp pair (each :7770) is retired; [Pi Models Configuration](../../pi/models.md) is a pi-era archive. --- From 8089693e589a5e75a31c9a0fa8cd5d4badcab75d Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 13:18:31 -0700 Subject: [PATCH 09/15] Chore: switch SDLC to trunk-based; retire develop Single-dev repo: develop bought nothing and cost a back-merge every release. New law: branch off freshest origin/main per work item, PRs target main directly, --no-ff merges kept, tags vX.Y.Z straight on main. No release branches, no back-merges. Changelog entry added. --- AGENTS.md | 22 +++++++++++++--------- omp-docs/log.md | 4 ++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 70cdbe6..1b4d879 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -13,26 +13,30 @@ with YAML frontmatter, spec at `omp-docs/references/okf-spec.md`). (`dank/command-center` on the Dankclaw instance). There is no staging copy: **the checkout is the deployment.** Branch switches swap live config files out from under the running agent — keep feature branches short-lived, and never end - a work session checked out on anything but `develop` or `main`. + a work session checked out on anything but `main`. - Contents that belong here: `AGENTS.md`, `omp-docs/` (knowledge base), `scripts/` (small system utilities), `agent/` + `plugins/` (harness config and plugin manifests). Everything else under this directory is runtime state and is gitignored on principle — see `.gitignore`. History must mean "the configuration changed", not "the agent breathed". -## Git-flow SDLC (full flow, from day one) +## Trunk-based SDLC (since 2026-10-18; replaces the old git-flow regime) +- `main` is the only long-lived branch. Frank is the only dev; `develop` and the + release-branch ceremony are retired — no `develop`, no back-merges, no + `release/X.Y.Z` branches. +- **Before every work item: fetch and branch off the freshest `origin/main`.** + If main moves under you, merge it into your branch (never rebase — Frank + dislikes the history rewrite). - Work item = Gitea issue on `dank/command-center` (Gitea at 192.168.0.143:3000, owner `dank`). Small fixes may go straight to a branch; anything with more than one moving part gets an issue first. - Branch naming: `/-`, type ∈ feat|fix|docs|chore. - Release cuts: `release/X.Y.Z` from `develop`; hotfixes: `hotfix/` from `main`. -- **Merge, never rebase**, when updating a branch against a moved main — Frank - dislikes the history rewrite. **Never merge a PR without an explicit OK from Frank.** -- Merge into `develop` with `--no-ff` so feature arcs stay visible in history. -- Releases: cut `release/X.Y.Z` from `develop`, verify, merge to `main`, tag - `vX.Y.Z`, back-merge `main` into `develop`. Tags are the deploy markers every - other machine pulls to. +- **Pull requests target `main` directly.** Merge with `--no-ff` so feature arcs + stay visible in history. **Never merge a PR without an explicit OK from Frank.** +- Releases: tag `vX.Y.Z` straight on `main` after verifying; tags are the deploy + markers every other machine pulls to. Hotfixes are ordinary branches off the + tag you are fixing forward from. - Trash convention: never `rm` anything user-authored — move it to `.AGENTTRASH/` (gitignored). Deletion is a pruning session with Frank present. diff --git a/omp-docs/log.md b/omp-docs/log.md index f844714..6eb1c76 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,6 +11,10 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — SDLC switched to trunk-based development + +`develop` retired by operator decision (single-dev repo; the branch bought nothing and cost a back-merge every release). New law in AGENTS.md § *Trunk-based SDLC*: branch off the freshest `origin/main` before every work item, PRs target `main` directly, `--no-ff` merges, tags `vX.Y.Z` straight on main. No back-merges, no release branches. The last back-merge (`527596a`) lands with this switch; the managed skill `gitflow-back-merge-plumbing` is now historical. + ## 2026-10-18 — Sibling-vault overlap census; system docs realigned to live ground truth Census of `omp-docs/` against `~/Projects/serverAppConfig/serverAppConfigs-docs/` (subagent + live-ssh verification) found four contradicting pairs; all OURS-side stale pages corrected, THEIRS side flagged only (their repo, their PR flow). Ground truth from read-only probes on both boxes 2026-10-18. From 7a0062e9e82d7b85a94f1c058d501f2b3beaddc1 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 15:41:27 -0700 Subject: [PATCH 10/15] Docs: filed PRs must be opened in Frank's browser immediately Trunk-based SDLC gains the delivery habit: xdg-open the PR URL the moment it's filed (mirrors dank/serverAppConfigs step 6, riding their PR #6). Changelog entry added. --- AGENTS.md | 2 ++ omp-docs/log.md | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 1b4d879..4351b70 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,6 +34,8 @@ with YAML frontmatter, spec at `omp-docs/references/okf-spec.md`). - Branch naming: `/-`, type ∈ feat|fix|docs|chore. - **Pull requests target `main` directly.** Merge with `--no-ff` so feature arcs stay visible in history. **Never merge a PR without an explicit OK from Frank.** +- **The moment a PR is open, open it in Frank's browser** — `xdg-open ` + lands a tab in his Firefox; never make him hunt for it in Gitea. - Releases: tag `vX.Y.Z` straight on `main` after verifying; tags are the deploy markers every other machine pulls to. Hotfixes are ordinary branches off the tag you are fixing forward from. diff --git a/omp-docs/log.md b/omp-docs/log.md index 6eb1c76..82841dd 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,6 +11,14 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — Fleet law: filed PRs open in Frank's browser immediately + +Both repos' git rules now require it: the moment a PR is opened, `xdg-open ` drops a Firefox tab on the workstation (AGENTS.md § Trunk-based SDLC here; § Git Workflow step 6 in `dank/serverAppConfigs`, riding their PR #6 branch). + +## 2026-10-18 — Trunk law: extension route closed unmerged; direct repo alignment instead + +Built and live-verified a cwd-scoped `before_agent_start` extension (quote probes: fires under `~/Projects`, silent elsewhere; kept as `.AGENTTRASH/trunk-law.js`). **Operator closed PR #9 unmerged** in favor of editing each repo's own AGENTS.md. Durable finding from the exploration: every context-file provider (AGENTS.md walk-up, `.omp/` dirs, CLAUDE.md) resolves at or below the repo root — a `~/Projects/AGENTS.md` for ancestor scope can never load; user-level `APPEND_SYSTEM.md` broadcasts to *all* sessions. Second finding: **serverAppConfigs never had git-flow** — its AGENTS.md mandated no branch regime at all (direct commit+push); a trunk + PR law is being added there by PR in that repo, not corrected. + ## 2026-10-18 — SDLC switched to trunk-based development `develop` retired by operator decision (single-dev repo; the branch bought nothing and cost a back-merge every release). New law in AGENTS.md § *Trunk-based SDLC*: branch off the freshest `origin/main` before every work item, PRs target `main` directly, `--no-ff` merges, tags `vX.Y.Z` straight on main. No back-merges, no release branches. The last back-merge (`527596a`) lands with this switch; the managed skill `gitflow-back-merge-plumbing` is now historical. From e52aa6a4a31c38eaefc7e119a44e28da33c1fbcd Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 16:13:59 -0700 Subject: [PATCH 11/15] feat: live-sync heartbeat commits machine-written paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The checkout IS the deployment and symlinks make live state (memories, auto-learned skills, agent config, desktop settings) dirty tracked files continuously; no commit law covered machine churn, so it accumulated unpushed for weeks — one disk held the only copy of ~61 managed skills, a settings fix stayed local-only, and dirty trees refused merges. scripts/live-sync.sh (systemd user timer, ~15 min) commits ONLY the allowlisted machine/desktop-live tiers with hooks armed, merges origin/main, pushes, and reaps wt/* worktrees older than 7 days. Task dirt outside the allowlist is never staged; conflicts abort and alert via ntfy; branch guard refuses off main; never --no-verify, never force. Black-box contract pinned by tests/live-sync.bats (9 tests, suite 36/36). Refs #11 --- AGENTS.md | 19 ++ .../.config/systemd/user/live-sync.service | 12 ++ .../.config/systemd/user/live-sync.timer | 11 ++ omp-docs/log.md | 4 + omp-docs/tools/index.md | 1 + omp-docs/tools/live-sync.md | 76 ++++++++ scripts/live-sync.sh | 131 +++++++++++++ tests/live-sync.bats | 176 ++++++++++++++++++ 8 files changed, 430 insertions(+) create mode 100644 dotfiles/systemd-user/.config/systemd/user/live-sync.service create mode 100644 dotfiles/systemd-user/.config/systemd/user/live-sync.timer create mode 100644 omp-docs/tools/live-sync.md create mode 100755 scripts/live-sync.sh create mode 100755 tests/live-sync.bats diff --git a/AGENTS.md b/AGENTS.md index 4351b70..8b89db9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -62,6 +62,25 @@ commit"). Applied here: gate is the point of the hook). - Messages: imperative mood, what + why, `Refs #N` when a Gitea issue exists. +### Live-sync tiers (the machine is a committer too — Refs #11) + +The checkout is the deployment, so several tracked files are live state +written continuously through symlinks by the harness, auto-learn, and +desktop GUIs. "Dirty tree" is judged by the writer: + +| Tier | Paths | Commit law | +|------|-------|------------| +| Human/agent prose | `AGENTS.md`, `omp-docs/`, `scripts/`, `plugins/`, `agent/skills/` | branch → PR → Frank OK (unchanged) | +| Machine live | `agent/memories/`, `agent/managed-skills/`, `agent/config.yml`, `agent/models.yml` | `scripts/live-sync.sh` heartbeat | +| Desktop live | `dotfiles/` | same heartbeat | + +The heartbeat (systemd user timer, ~15 min) stages ONLY allowlisted tiers, +commits with hooks armed — the secret gate included — merges `origin/main`, +pushes, and reaps `wt/*` worktrees older than 7 days. Non-allowlist dirt is +never committed, only surfaced. Conflicts abort and alert (ntfy); it never +force-pushes, auto-resolves, or uses `--no-verify`. Details: +[omp-docs/tools/live-sync.md](omp-docs/tools/live-sync.md). + ## Secret gate (HARD rule) The pre-commit hook (`.githooks/pre-commit`, enabled via diff --git a/dotfiles/systemd-user/.config/systemd/user/live-sync.service b/dotfiles/systemd-user/.config/systemd/user/live-sync.service new file mode 100644 index 0000000..dcbe80d --- /dev/null +++ b/dotfiles/systemd-user/.config/systemd/user/live-sync.service @@ -0,0 +1,12 @@ +[Unit] +Description=command-center live-sync heartbeat (commits machine-written paths) +Documentation=file://%h/.omp/omp-docs/tools/live-sync.md + +[Service] +# One-shot heartbeat: commit allowlisted live paths, merge trunk, push, reap +# stale wt/* worktrees. Self-healing by design — a failed fetch/merge alerts +# via ntfy and simply retries on the next tick; nothing to restart. +Type=oneshot +Nice=19 +IOSchedulingClass=idle +ExecStart=%h/.omp/scripts/live-sync.sh diff --git a/dotfiles/systemd-user/.config/systemd/user/live-sync.timer b/dotfiles/systemd-user/.config/systemd/user/live-sync.timer new file mode 100644 index 0000000..adbfd15 --- /dev/null +++ b/dotfiles/systemd-user/.config/systemd/user/live-sync.timer @@ -0,0 +1,11 @@ +[Unit] +Description=command-center live-sync heartbeat every 15 minutes + +[Timer] +OnBootSec=5min +OnUnitActiveSec=15min +RandomizedDelaySec=3min +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/omp-docs/log.md b/omp-docs/log.md index 82841dd..8fd64d9 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,6 +11,10 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — live-sync heartbeat for machine-written paths (Refs #11) + +Root-caused the month of silent divergence in the live checkout (~61 auto-learned skills on one disk only, a stranded `unexpectedStopDetection` fix, merge-refusals from dirty trees): the checkout IS the deployment, symlinks make machine writes dirty tracked files by design, and no commit law covered that churn. Added `scripts/live-sync.sh` + systemd user timer — allowlist tiers (memories, managed-skills, live config, dotfiles) auto-commit with hooks armed, merge `origin/main`, push, and reap stale `wt/*` worktrees; everything outside the allowlist is never staged, conflicts abort-and-alert. Black-box suite `tests/live-sync.bats` (9 tests). Tier law in AGENTS.md § Live-sync tiers; reference page `tools/live-sync.md` registered in the tools index. + ## 2026-10-18 — Fleet law: filed PRs open in Frank's browser immediately Both repos' git rules now require it: the moment a PR is opened, `xdg-open ` drops a Firefox tab on the workstation (AGENTS.md § Trunk-based SDLC here; § Git Workflow step 6 in `dank/serverAppConfigs`, riding their PR #6 branch). diff --git a/omp-docs/tools/index.md b/omp-docs/tools/index.md index fee4d75..d6314c8 100644 --- a/omp-docs/tools/index.md +++ b/omp-docs/tools/index.md @@ -8,6 +8,7 @@ Configuration guides and reference material for installed software. Each tool ha * [[cachyos|cachyos]] — 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. +* [live-sync](live-sync.md) — Heartbeat daemon committing machine-written paths (memories, managed-skills, live config, dotfiles) in the command-center checkout; systemd user timer, alerts via ntfy. * [[applications|applications]] — Default userspace applications on CachyOS with Niri — browsers, editor, terminal, file manager, audio, screenshots, gaming. * ](ghostty/index.md) — GPU-accelerated terminal emulator with extensive configuration, keybindings, and shaders. * ](gitea/index.md) — Private Git hosting on dankclaw. Quadlet-managed containers: Gitea, PostgreSQL, Caddy. diff --git a/omp-docs/tools/live-sync.md b/omp-docs/tools/live-sync.md new file mode 100644 index 0000000..32e4c87 --- /dev/null +++ b/omp-docs/tools/live-sync.md @@ -0,0 +1,76 @@ +--- +type: reference +title: live-sync heartbeat +description: Auto-commit daemon for machine-written paths in the command-center checkout — the deployment is live, so symlinks make config churn dirty tracked files continuously. +tags: + - git + - automation + - systemd + - deployment + - fleet +verified: 2026-10-18 +timestamp: 2026-10-18 +--- + +# live-sync heartbeat + +The command-center checkout IS the deployment, and several tracked files are +live state: the harness writes memories and managed-skills, the operator +edits `config.yml`/`models.yml` through the `cfg://` surface, and desktop +GUIs (Noctalia, niri) rewrite dotfiles through symlinks. Before Refs #11 this +machine churn just piled up unpushed — one disk holding the only copy of ~61 +auto-learned skills, a settings fix stranded locally, and merge-refusals +because the dirty tree collided with incoming work. + +`scripts/live-sync.sh` is the pressure valve: a heartbeat that commits the +machine's own writes on a schedule. Tier law (which paths it may touch) lives +in [AGENTS.md](../../AGENTS.md) § Live-sync tiers. + +## Contract (black-box) + +Run = one pass: flock → branch guard (`main` only) → stage allowlisted dirty +tiers → commit (hooks armed) → fetch + merge `origin/main` → push → reap +stale `wt/*` worktrees. Exit `0` = quiet heartbeat (synced/no-op/reaped); +exit `1` = an alert was raised. Pinned by `tests/live-sync.bats`. + +Hard properties: + +- **Allowlist only.** Task dirt (`plugins/`, `omp-docs/`, prose) is never + staged — merge conflicts with in-flight work become impossible by + construction. +- **Hooks stay armed.** A secret-gate refusal aborts the commit, leaves + changes staged for a human, and alerts. Never `--no-verify`, never force. +- **Never fights the remote.** Merge conflict → `merge --abort`, local + commit kept, alert. Retries are automatic next tick. +- **Branch guard.** On a non-`main` checkout it refuses and alerts — same + rule as the doctrine ("never end a session off main"). +- **Polite reaper.** `wt/wt-*` worktrees untouched for 7+ days are removed + with their `wt/*` branch; dirty or process-occupied worktrees are skipped. + +## Alerts + +`notify()` writes stderr always; pushes to ntfy when the topic file +`~/.config/ntfy/live-sync` (mode 600, topic text only) exists, throttled to +one push per hour. Test seams: `LIVE_SYNC_REPO`, `LIVE_SYNC_NOTIFY`, +`LIVE_SYNC_NTFY_TOPIC_FILE`, `LIVE_SYNC_AGE_DAYS`. + +## Install + +Units ship in `dotfiles/systemd-user/.config/systemd/user/` and propagate +with the usual dotfiles chain: + +```bash +systemctl --user enable --now live-sync.timer # after the unit is linked +systemctl --user start live-sync.service # one manual heartbeat +journalctl --user -u live-sync.service -n 20 # observe +``` + +## Failure modes + +| Event | Result | +|-------|--------| +| Harness mid-write | commits a valid snapshot; remainder lands next heartbeat | +| Gitea down | commit safe locally; push retries next tick (≤15 min stale window) | +| Same file edited on two boxes | real merge conflict → ntfy → human resolves (visible, not silent) | +| Feature branch checked out in main checkout | refuses + alerts; protects the never-end-off-main rule | +| Secret shape in machine-written content | hook blocks, changes stay staged, alert | diff --git a/scripts/live-sync.sh b/scripts/live-sync.sh new file mode 100755 index 0000000..0743012 --- /dev/null +++ b/scripts/live-sync.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# live-sync.sh — heartbeat committer for machine-written paths in the command +# center checkout. Refs #11 (see omp-docs/tools/live-sync.md). +# +# The checkout IS the deployment; the harness, auto-learn, and desktop GUIs +# write tracked files continuously through symlinks. This script commits ONLY +# the allowlisted machine-live + desktop-live tiers (AGENTS.md § Live-sync +# tiers), merges the trunk, pushes, and reaps stale session worktrees. Task +# dirt outside the allowlist is NEVER committed — only surfaced. +# +# Black-box contract (tests/live-sync.bats pins it): +# input git state of $LIVE_SYNC_REPO +# output exit 0 = quiet heartbeat (synced, no-op, or reaped) +# exit 1 = alert raised (branch guard, hook refusal, merge conflict, +# fetch/push failure); alert goes to stderr + notify sink +# Env overrides (test seams): +# LIVE_SYNC_REPO target repo (default $HOME/.omp) +# LIVE_SYNC_NOTIFY alert command, message on stdin +# LIVE_SYNC_NTFY_TOPIC_FILE ntfy topic file, mode 600 (default +# ~/.config/ntfy/live-sync; absent/empty = no push) +# LIVE_SYNC_AGE_DAYS wt/wt-* reap threshold (default 7) +set -euo pipefail + +REPO="${LIVE_SYNC_REPO:-$HOME/.omp}" +AGE_DAYS="${LIVE_SYNC_AGE_DAYS:-7}" +TOPIC_FILE="${LIVE_SYNC_NTFY_TOPIC_FILE:-$HOME/.config/ntfy/live-sync}" +STATE_DIR="${XDG_RUNTIME_DIR:-/tmp}" + +# Allowlist tiers. Human prose (AGENTS.md, omp-docs, scripts, plugins, +# agent/skills) is deliberately absent: branch -> PR territory, never swept. +LIVE_PATHS=( + agent/memories + agent/managed-skills + agent/config.yml + agent/models.yml + dotfiles +) + +notify() { + local msg="live-sync: $1" stamp now + stamp=$(cat "$STATE_DIR/live-sync.alert" 2>/dev/null || echo 0) + now=$(date +%s) + if (( now - stamp >= 3600 )); then + date +%s > "$STATE_DIR/live-sync.alert" + if [[ -n "${LIVE_SYNC_NOTIFY:-}" ]]; then + printf '%s' "$msg" | $LIVE_SYNC_NOTIFY || true + elif [[ -s "$TOPIC_FILE" ]]; then + curl -fsS -m 10 -d "$msg" "https://ntfy.sh/$(<"$TOPIC_FILE")" >/dev/null 2>&1 || true + fi + fi + printf '%s\n' "$msg" >&2 +} + +cd "$REPO" 2>/dev/null || exit 0 +exec 9>"$STATE_DIR/live-sync.lock" +flock -n 9 || exit 0 # another heartbeat owns the repo +git rev-parse --verify -q HEAD >/dev/null || exit 0 + +branch=$(git symbolic-ref --quiet --short HEAD || true) +if [[ "$branch" != main ]]; then + notify "on branch '$branch', not main — refusing to sync, nothing changed" + exit 1 +fi + +# 1. stage only allowlisted paths that actually hold dirt (pathspecs that +# match nothing would make git add fatal, so check per-path) +for p in "${LIVE_PATHS[@]}"; do + if [[ -n $(git status --porcelain -- "$p") ]]; then + git add -A -- "$p" + fi +done + +if ! git diff --cached --quiet; then + # 2. commit WITH hooks armed — the secret gate stays pointed at the diff; + # a refusal here is the system working, never bypassed + if ! git commit -q -m "live: heartbeat sync $(date -Is) (Refs #11)"; then + notify "commit rejected by hook/secret gate — live changes left STAGED for a human" + exit 1 + fi + + # 3. integrate trunk and publish; never fight the remote + if git remote get-url origin >/dev/null 2>&1; then + if ! git fetch -q origin; then + notify "fetch failed — commit is safe locally, will retry next heartbeat" + exit 1 + fi + if git rev-parse --verify -q origin/main >/dev/null \ + && ! git merge -q --no-edit origin/main; then + git merge --abort || true + notify "merge conflict with origin/main — local commit kept; needs you at the keyboard" + exit 1 + fi + if ! git push -q origin main; then + notify "push failed — commit is safe locally, will retry next heartbeat" + exit 1 + fi + fi +fi + +# 4. reap session worktrees older than AGE_DAYS. Dirty worktrees are left +# alone (git refuses), and so are any with a live process cwd inside. +in_use() { + local p + for p in /proc/[0-9]*/cwd; do + if [[ "$(readlink "$p" 2>/dev/null || true)" == "$1"* ]]; then + return 0 + fi + done + return 1 +} + +git worktree prune +reap_path="" reap_ref="" +reap_flush() { + if [[ "$reap_path" == "$REPO/wt/wt-"* && -d "$reap_path" ]] \ + && [[ -z $(find "$reap_path" -mtime "-$AGE_DAYS" -print -quit) ]] \ + && ! in_use "$reap_path"; then + git worktree remove -- "$reap_path" 2>/dev/null && git worktree prune || true + if [[ -n "$reap_ref" ]]; then + git branch -q -d "${reap_ref#refs/heads/}" 2>/dev/null || true + fi + fi + reap_path="" reap_ref="" +} +while IFS= read -r line; do + case "$line" in + "worktree "*) reap_flush; reap_path="${line#worktree }" ;; + "branch "*) reap_ref="${line#branch }" ;; + esac +done < <(git worktree list --porcelain) +reap_flush diff --git a/tests/live-sync.bats b/tests/live-sync.bats new file mode 100755 index 0000000..74e9a8a --- /dev/null +++ b/tests/live-sync.bats @@ -0,0 +1,176 @@ +#!/usr/bin/env bats +# tests/live-sync.bats — black-box tests for scripts/live-sync.sh (Refs #11) +# +# Contract (see script header): the heartbeat commits ONLY allowlisted live +# paths, merges origin/main, pushes, and reaps stale wt/* worktrees. Nothing +# is observed except exit status, stderr, the notify-sink log, and git state. + +setup() { + TEST_TMP="$(mktemp -d)" + REPO="$TEST_TMP/repo" + export XDG_RUNTIME_DIR="$TEST_TMP/xdg" + mkdir -p "$XDG_RUNTIME_DIR" + export GIT_CONFIG_GLOBAL="$TEST_TMP/gitconfig" GIT_CONFIG_SYSTEM=/dev/null + export GIT_AUTHOR_DATE="2026-10-18T12:00:00+00:00" GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE" + export LIVE_SYNC_REPO="$REPO" + export LIVE_SYNC_NTFY_TOPIC_FILE="$TEST_TMP/no-topic-file" + export LIVE_SYNC_NOTIFY="$TEST_TMP/notify-stub" + export LIVE_SYNC_AGE_DAYS=7 + + # notify sink: appends each alert line to alerts.log + cat > "$LIVE_SYNC_NOTIFY" <> "$TEST_TMP/alerts.log" +echo >> "$TEST_TMP/alerts.log" +STUB + chmod +x "$LIVE_SYNC_NOTIFY" + + # bare "origin" + seeded clone with all tiers represented + git init -q --bare -b main "$TEST_TMP/origin.git" + git clone -q "$TEST_TMP/origin.git" "$REPO" + git -C "$REPO" config user.email frank@dank + git -C "$REPO" config user.name Frank + git -C "$REPO" checkout -q -b main + mkdir -p "$REPO/agent/memories" "$REPO/agent/managed-skills/s1" \ + "$REPO/dotfiles/bash" "$REPO/plugins/bench" "$REPO/scripts" + echo base > "$REPO/agent/config.yml" + echo base > "$REPO/agent/models.yml" + echo base > "$REPO/agent/memories/learned.md" + echo base > "$REPO/agent/managed-skills/s1/SKILL.md" + echo base > "$REPO/dotfiles/bash/bashrc" + echo base > "$REPO/AGENTS.md" + echo base > "$REPO/scripts/deploy.sh" + echo base > "$REPO/plugins/bench/server.py" + git -C "$REPO" add -A + git -C "$REPO" commit -q -m seed + git -C "$REPO" push -q origin main + SEED="$(git -C "$REPO" rev-parse HEAD)" +} + +teardown() { rm -rf "$TEST_TMP"; } + +@test "heartbeat commits allowlisted live dirt only; task dirt stays dirty" { + echo new > "$REPO/agent/memories/learned.md" + mkdir "$REPO/agent/managed-skills/s2" + echo new > "$REPO/agent/managed-skills/s2/SKILL.md" + echo new > "$REPO/AGENTS.md" + echo new > "$REPO/plugins/bench/server.py" + + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 0 ] + + # exactly one new commit ahead of the seed + [ "$(git -C "$REPO" rev-list --count "$SEED"..main)" -eq 1 ] + # it touches ONLY live paths (untracked new dirs picked up too) + run git -C "$REPO" show --pretty=format: --name-only HEAD + printf '%s\n' "${lines[@]}" | grep -v '^$' | sort | diff -q - <( + printf '%s\n' agent/managed-skills/s2/SKILL.md agent/memories/learned.md) + # task dirt untouched, uncommitted + [ -n "$(git -C "$REPO" status --porcelain -- AGENTS.md plugins)" ] + # pushed: origin caught up + [ "$(git -C "$REPO" rev-parse origin/main)" = "$(git -C "$REPO" rev-parse main)" ] +} + +@test "clean tree is a silent no-op" { + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 0 ] + [ "$(git -C "$REPO" rev-list --count "$SEED"..main)" -eq 0 ] + [ ! -e "$TEST_TMP/alerts.log" ] +} + +@test "non-allowlist dirt alone never commits" { + echo new > "$REPO/AGENTS.md" + echo new > "$REPO/scripts/deploy.sh" + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 0 ] + [ "$(git -C "$REPO" rev-list --count "$SEED"..main)" -eq 0 ] + [ -n "$(git -C "$REPO" status --porcelain -- AGENTS.md scripts)" ] +} + +@test "merge conflict aborts, keeps local commit, alerts, leaves remote alone" { + # a second clone advances origin/main on the same live file + git clone -q -b main "$TEST_TMP/origin.git" "$TEST_TMP/pocket" + git -C "$TEST_TMP/pocket" config user.email pocket@dank + git -C "$TEST_TMP/pocket" config user.name Pocket + echo remote-side > "$TEST_TMP/pocket/agent/models.yml" + git -C "$TEST_TMP/pocket" commit -qam "remote advance" + git -C "$TEST_TMP/pocket" push -q origin main + + echo local-side > "$REPO/agent/models.yml" + + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 1 ] + [[ "$output" == *"merge conflict"* ]] + [ -s "$TEST_TMP/alerts.log" ] + # HEAD is my local commit, merge left no residue, remote untouched + [[ "$(git -C "$REPO" log --format=%s -1)" == live:*"heartbeat sync"* ]] + [ ! -e "$REPO/.git/MERGE_HEAD" ] + [ "$(git -C "$REPO" rev-parse origin/main)" = "$(git -C "$TEST_TMP/pocket" rev-parse main)" ] +} + +@test "branch guard refuses to sync on a feature branch" { + git -C "$REPO" checkout -q -b feature/whatever + echo new > "$REPO/agent/config.yml" + + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 1 ] + [[ "$output" == *"not main"* ]] + [ "$(git -C "$REPO" rev-list --count "$SEED"..feature/whatever)" -eq 0 ] + [ -n "$(git -C "$REPO" status --porcelain -- agent/config.yml)" ] +} + +@test "pre-commit hook refusal is surfaced, not bypassed; changes stay staged" { + mkdir "$TEST_TMP/hooks" + printf '#!/bin/sh\nexit 1\n' > "$TEST_TMP/hooks/pre-commit" + chmod +x "$TEST_TMP/hooks/pre-commit" + git -C "$REPO" config core.hooksPath "$TEST_TMP/hooks" + echo new > "$REPO/dotfiles/bash/bashrc" + + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 1 ] + [[ "$output" == *"rejected"* ]] + [ -n "$(git -C "$REPO" diff --cached --name-only -- dotfiles)" ] + [ "$(git -C "$REPO" rev-list --count "$SEED"..main)" -eq 0 ] +} + +@test "repo without origin still commits locally and exits clean" { + git -C "$REPO" remote remove origin + echo new > "$REPO/dotfiles/bash/bashrc" + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 0 ] + [ "$(git -C "$REPO" rev-list --count "$SEED"..main)" -eq 1 ] +} + +@test "stale wt/* worktree and its branch are reaped; fresh one survives" { + git -C "$REPO" worktree add -q wt/wt-old -b wt/old + git -C "$REPO" worktree add -q wt/wt-new -b wt/new + touch -d "10 days ago" "$REPO/wt/wt-old" + find "$REPO/wt/wt-old" -exec touch -d "10 days ago" {} + + + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 0 ] + [ ! -d "$REPO/wt/wt-old" ] + [ -d "$REPO/wt/wt-new" ] + run git -C "$REPO" show-ref --verify refs/heads/wt/old + [ "$status" -ne 0 ] + git -C "$REPO" show-ref --verify --quiet refs/heads/wt/new +} + +@test "second heartbeat inside the throttle window alerts once, never twice" { + # two conflicting heartbeats: alert sink must see exactly one line + git clone -q -b main "$TEST_TMP/origin.git" "$TEST_TMP/pocket" + git -C "$TEST_TMP/pocket" config user.email pocket@dank + git -C "$TEST_TMP/pocket" config user.name Pocket + echo remote-side > "$TEST_TMP/pocket/agent/config.yml" + git -C "$TEST_TMP/pocket" commit -qam "remote advance" + git -C "$TEST_TMP/pocket" push -q origin main + echo local-side > "$REPO/agent/config.yml" + + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 1 ] + rm -f "$XDG_RUNTIME_DIR/live-sync.lock" # flock releases with the process; state stays + echo local-side2 > "$REPO/agent/config.yml" + run "$BATS_TEST_DIRNAME/../scripts/live-sync.sh" + [ "$status" -eq 1 ] + [ "$(wc -l < "$TEST_TMP/alerts.log")" -eq 1 ] +} From 0edae3323d93f041a448d1bf95a0ab1be4ead716 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 14:57:20 -0700 Subject: [PATCH 12/15] chore: re-sync agent skills to upstream mattpocock/skills Local snapshots dated 2026-09-16; upstream has moved on since. Refreshes 7 SKILL.md files (code-review, diagnosing-bugs, implement, tdd, grilling, handoff, teach): tracker-doc references generalized, CONTEXT.md renamed GLOSSARY.md, implement gains ticket-fetch step + Skill-tool invocation, grilling question wording rule, handoff names the OS temp dir, teach workspace-path resolution + quiz answer-position rules, tdd seam notes. find-skills (vercel-labs) and prototype already current. Skill updates take effect next session (discovery runs at startup). --- agent/skills/code-review/SKILL.md | 8 +++++--- agent/skills/diagnosing-bugs/SKILL.md | 4 ++-- agent/skills/grilling/SKILL.md | 2 ++ agent/skills/handoff/SKILL.md | 2 +- agent/skills/implement/SKILL.md | 6 ++++-- agent/skills/tdd/SKILL.md | 4 ++-- agent/skills/teach/SKILL.md | 4 ++-- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/agent/skills/code-review/SKILL.md b/agent/skills/code-review/SKILL.md index e28d7ac..373a4f2 100644 --- a/agent/skills/code-review/SKILL.md +++ b/agent/skills/code-review/SKILL.md @@ -10,7 +10,7 @@ Two-axis review of the diff between `HEAD` and a fixed point the user supplies: Both axes run as **parallel sub-agents** so they don't pollute each other's context, then this skill aggregates their findings. -The issue tracker should have been provided to you. If `docs/agents/issue-tracker.md` is missing, tell the user to run `/setup-matt-pocock-skills`. +The issue tracker should have been provided to you. If not, tell the user to run `/setup-matt-pocock-skills`. ## Process @@ -26,14 +26,14 @@ Before going further, confirm the fixed point resolves (`git rev-parse ``` +Word each question so "yes" accepts your recommended answer. + Each round the user answers reshapes the tree: settled decisions push the frontier outward and unblock questions that depended on them. Recompute the frontier and ask the next round. A question whose answer depends on another question still open in this round belongs to a _later_ round, not this one. Finding _facts_ is your job, never the user's. When a frontier question needs a fact from the environment (filesystem, tools, etc.), dispatch a sub-agent to find it; don't ask the user for anything you could look up yourself. Don't block on it: a running exploration is an unsettled prerequisite, so only the questions downstream of it wait for the sub-agent to report; ask the rest of the frontier now. The _decisions_ are the user's: put each to them and wait. diff --git a/agent/skills/handoff/SKILL.md b/agent/skills/handoff/SKILL.md index 2eb98a5..a224edc 100644 --- a/agent/skills/handoff/SKILL.md +++ b/agent/skills/handoff/SKILL.md @@ -5,7 +5,7 @@ argument-hint: "What will the next session be used for?" disable-model-invocation: true --- -Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS - not the current workspace. +Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS (`$TMPDIR`, else `/tmp`; `%TEMP%` on Windows) - not the current workspace. Include a "suggested skills" section in the document, naming which skills the next agent should call the Skill tool for. diff --git a/agent/skills/implement/SKILL.md b/agent/skills/implement/SKILL.md index 7a0b11f..bd49e47 100644 --- a/agent/skills/implement/SKILL.md +++ b/agent/skills/implement/SKILL.md @@ -6,10 +6,12 @@ disable-model-invocation: true Implement the work described by the user in the spec or tickets. -Use /tdd where possible, at pre-agreed seams. +If the user passes a ticket reference, fetch it from the issue tracker and state its title before starting. If the reference is ambiguous, ask. + +Call the Skill tool with "tdd" where possible, at pre-agreed seams. Run typechecking regularly, single test files regularly, and the full test suite once at the end. -Once done, use /code-review to review the work. +Once done, call the Skill tool with "code-review" to review the work. Commit your work to the current branch. diff --git a/agent/skills/tdd/SKILL.md b/agent/skills/tdd/SKILL.md index 8fc0867..01eadaa 100644 --- a/agent/skills/tdd/SKILL.md +++ b/agent/skills/tdd/SKILL.md @@ -7,7 +7,7 @@ description: Test-driven development. Use when the user wants to build features TDD is the red → green loop. This skill is the reference that makes that loop produce tests worth keeping: what a good test is, where tests go, the anti-patterns, and the rules of the loop. Every section applies on every cycle: consult them before and during the loop, not after. -When exploring the codebase, read `CONTEXT.md` (if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching. +When exploring the codebase, read `GLOSSARY.md` (if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching. ## What a good test is @@ -21,7 +21,7 @@ A **seam** is the public boundary you test at: the interface where you observe b **Test only at pre-agreed seams.** Before writing any test, write down the seams under test and confirm them with the user. No test is written at an unconfirmed seam. You can't test everything, so agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case. -Ask: "What's the public interface, and which seams should we test?" +Ask: "What's the public interface, and which seams should we test?" Give each proposed seam a one-line note on what it catches and what it misses. When the shape of that interface is itself in question (how deep the module is, where the seam belongs, what the interface should expose), call the Skill tool with "codebase-design" for the vocabulary. It is the shared source of the module, interface, depth, seam, adapter, leverage and locality terms, and it is a reference to consult, not a session to run. diff --git a/agent/skills/teach/SKILL.md b/agent/skills/teach/SKILL.md index c679eec..493fa3b 100644 --- a/agent/skills/teach/SKILL.md +++ b/agent/skills/teach/SKILL.md @@ -9,7 +9,7 @@ The user has asked you to teach them something. This is a stateful request - the ## Teaching Workspace -Treat the current directory as a teaching workspace. The state of their learning is captured in this directory in several files: +Treat the current directory as a teaching workspace. Workspace paths (`./lessons/` and the rest) resolve from the directory `/teach` was run in; only the `*-FORMAT.md` links resolve from this skill's folder. The state of their learning is captured in this directory in several files: - `MISSION.md`: A document capturing the _reason_ the user is interested in the topic. This should be used to ground all teaching. Use the format in [MISSION-FORMAT.md](./MISSION-FORMAT.md). - `./reference/*.html`: A directory of reference materials. These are the compressed learnings from the lessons - cheat sheets, reference algorithms, syntax, yoga poses, glossaries. They are the raw units of learning. They should be beautiful documents which print out well, and are designed for quick reference. @@ -107,7 +107,7 @@ For skill acquisition, difficulty is the tool. Effortful retrieval is what build Each of these should be based on a **feedback loop**, where the user receives feedback on their performance. This feedback loop should be as tight as possible, giving feedback immediately - and ideally automatically. -For quizzes, each answer should be exactly the same number of words (and characters, if possible). Don't give the user any clues about the answer through formatting. +For quizzes, each answer should be exactly the same number of words (and characters, if possible). Vary which position holds the correct answer across questions. Don't give the user any clues about the answer through formatting or order. ## Acquiring Wisdom From 54f55554701f95bd3f11d3775d87ca17f0ce2a4f Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 16:28:50 -0700 Subject: [PATCH 13/15] chore: run live-sync heartbeat hourly Operator decision at review: 15 min is aggressive for config churn; hourly keeps the stale-window acceptable (<=1 h) with a fifth of the wakeups. Timer unit, tier law, and failure-table wording aligned. --- AGENTS.md | 2 +- dotfiles/systemd-user/.config/systemd/user/live-sync.timer | 4 ++-- omp-docs/tools/live-sync.md | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8b89db9..d163362 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -74,7 +74,7 @@ desktop GUIs. "Dirty tree" is judged by the writer: | Machine live | `agent/memories/`, `agent/managed-skills/`, `agent/config.yml`, `agent/models.yml` | `scripts/live-sync.sh` heartbeat | | Desktop live | `dotfiles/` | same heartbeat | -The heartbeat (systemd user timer, ~15 min) stages ONLY allowlisted tiers, +The heartbeat (systemd user timer, hourly) stages ONLY allowlisted tiers, commits with hooks armed — the secret gate included — merges `origin/main`, pushes, and reaps `wt/*` worktrees older than 7 days. Non-allowlist dirt is never committed, only surfaced. Conflicts abort and alert (ntfy); it never diff --git a/dotfiles/systemd-user/.config/systemd/user/live-sync.timer b/dotfiles/systemd-user/.config/systemd/user/live-sync.timer index adbfd15..6b102f1 100644 --- a/dotfiles/systemd-user/.config/systemd/user/live-sync.timer +++ b/dotfiles/systemd-user/.config/systemd/user/live-sync.timer @@ -1,9 +1,9 @@ [Unit] -Description=command-center live-sync heartbeat every 15 minutes +Description=command-center live-sync heartbeat every hour [Timer] OnBootSec=5min -OnUnitActiveSec=15min +OnUnitActiveSec=1h RandomizedDelaySec=3min Persistent=true diff --git a/omp-docs/tools/live-sync.md b/omp-docs/tools/live-sync.md index 32e4c87..5d66bb3 100644 --- a/omp-docs/tools/live-sync.md +++ b/omp-docs/tools/live-sync.md @@ -70,7 +70,6 @@ journalctl --user -u live-sync.service -n 20 # observe | Event | Result | |-------|--------| | Harness mid-write | commits a valid snapshot; remainder lands next heartbeat | -| Gitea down | commit safe locally; push retries next tick (≤15 min stale window) | -| Same file edited on two boxes | real merge conflict → ntfy → human resolves (visible, not silent) | +| Gitea down | commit safe locally; push retries next tick (≤1 h stale window) | | Feature branch checked out in main checkout | refuses + alerts; protects the never-end-off-main rule | | Secret shape in machine-written content | hook blocks, changes stay staged, alert | From 8fb357b4832e5b3cbe9a26e285f9aac4babe743f Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 16:32:48 -0700 Subject: [PATCH 14/15] docs: attribution footer for machine-opened issues and PRs Every agent-opened issue/PR body ends with cwd + omp session id so machine filed artifacts trace back to the box and session that produced them - the live-sync daemon and filing agents make 'who/what wrote this' a real question now. Mirrored in dank/serverAppConfigs by its own PR. Refs #11 --- AGENTS.md | 4 ++++ omp-docs/log.md | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index d163362..5b0afb3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,6 +36,10 @@ with YAML frontmatter, spec at `omp-docs/references/okf-spec.md`). stay visible in history. **Never merge a PR without an explicit OK from Frank.** - **The moment a PR is open, open it in Frank's browser** — `xdg-open ` lands a tab in his Firefox; never make him hunt for it in Gitea. +- **Attribution footer (issues and PRs)** — every issue or PR body you open ends + with a `---` line, then `cwd: · session: ` + (session id from `/status`). Machine-made artifacts stay attributable to the + box and session that spawned them. - Releases: tag `vX.Y.Z` straight on `main` after verifying; tags are the deploy markers every other machine pulls to. Hotfixes are ordinary branches off the tag you are fixing forward from. diff --git a/omp-docs/log.md b/omp-docs/log.md index 8fd64d9..db60c72 100644 --- a/omp-docs/log.md +++ b/omp-docs/log.md @@ -11,6 +11,10 @@ timestamp: 2026-08-28 # Bundle Change Log +## 2026-10-18 — Fleet law: issue/PR attribution footers + +Every agent-opened issue and PR body now ends with a `cwd:` + `session:` footer (session id from `/status`), in AGENTS.md of both `dank/command-center` and `dank/serverAppConfigs` (mirrored PRs). Born from the live-sync review: once the machine starts filing and committing on its own, its artifacts must trace back to the box and session that produced them. + ## 2026-10-18 — live-sync heartbeat for machine-written paths (Refs #11) Root-caused the month of silent divergence in the live checkout (~61 auto-learned skills on one disk only, a stranded `unexpectedStopDetection` fix, merge-refusals from dirty trees): the checkout IS the deployment, symlinks make machine writes dirty tracked files by design, and no commit law covered that churn. Added `scripts/live-sync.sh` + systemd user timer — allowlist tiers (memories, managed-skills, live config, dotfiles) auto-commit with hooks armed, merge `origin/main`, push, and reap stale `wt/*` worktrees; everything outside the allowlist is never staged, conflicts abort-and-alert. Black-box suite `tests/live-sync.bats` (9 tests). Tier law in AGENTS.md § Live-sync tiers; reference page `tools/live-sync.md` registered in the tools index. From 942787f0f2eb3b9697524159e894405039e6f4f8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 18 Oct 2026 16:40:14 -0700 Subject: [PATCH 15/15] docs: pin cwd meaning in attribution footer First dogfooding round stamped the build worktree instead of the session's own working directory (the value the paired session id resolves to). Rule now says so explicitly. Refs #11 --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 5b0afb3..25ce8e6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,7 +38,8 @@ with YAML frontmatter, spec at `omp-docs/references/okf-spec.md`). lands a tab in his Firefox; never make him hunt for it in Gitea. - **Attribution footer (issues and PRs)** — every issue or PR body you open ends with a `---` line, then `cwd: · session: ` - (session id from `/status`). Machine-made artifacts stay attributable to the + (session id from `/status`; `cwd` is that session's own working directory — + not the build checkout, when they differ). Machine-made artifacts stay attributable to the box and session that spawned them. - Releases: tag `vX.Y.Z` straight on `main` after verifying; tags are the deploy markers every other machine pulls to. Hotfixes are ordinary branches off the