--- type: reference title: OMP Harness description: "The omp harness as configured here: compaction chain arithmetic, per-model sampler routing, and the context-lean audits." tags: [omp, harness, compaction, models-yml, context, samplers] timestamp: 2026-10-18T00:00:00Z --- # OMP Harness The harness reaches the fleet through `~/.omp/agent/models.yml` (model registry; single source of truth — there is no copy on the servers) and `~/.omp/agent/config.yml` (roles, compaction, global samplers). Nothing in this file is repo-managed; edits are harness-side only. **Model ids are the StarCraft unit names (official 2026-10-08, on the wire since 2026-10-09):** `frank-archon` (Dankserver), `frank-dragoon` and `frank-zealot` (Dankclaw) — engine, port and card behind each id live on [[frank-archon]], [[frank-dragoon]] and [[frank-zealot]] — formerly `frank-archon`, `frank-dragoon` `frank-zealot`. The SERVICES keep the old names (units, scripts, env files, runbook pages); pages written in the old era name the service, the harness names the model. The wire now matches: each endpoint's `/v1/models` answers with the Protoss name, so `models.yml`, the OpenWebUI picker/grants, the homepage docs and the raw API all use one id per model (see [[OpenWebUI]] for the grant re-key and the [[frank-archon]] note on the endpoint echoing the requested name). Verified 2026-10-18: `/v1/models` on 192.168.0.235:7770, 192.168.0.143:7770 and 192.168.0.143:7771 each answer with the unit name (`frank-archon` `frank-dragoon`, `frank-zealot`). ## Compaction chain arithmetic (verify before ANY window change) When a fleet endpoint's context grows past its harness-declared window, OMP compacts by sending the conversation to a designated target model. Before changing any `contextWindow`/`maxTokens`, verify the chain arithmetically — a target whose window is smaller than the source's trigger point silently breaks compaction. Where the contract lives: - `~/.omp/agent/config.yml` → `compaction.remoteEndpoint` (target URL: `http://192.168.0.143:7771/v1/chat/completions`, `config.yml:76`) `methodOrder` (`shake → snapcompact → soft → handoff → remote` `config.yml:77-82`), `thresholdPercent` (**95**, `config.yml:84`), idle settings (`idleEnabled`, `idleThresholdTokens: 100000`) - `~/.omp/agent/models.yml` → per-model `contextWindow`, `maxTokens` `compactionModel` references - Implementation: omp plugin `~/.omp/plugins/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js` The arithmetic (the coupled contract every endpoint's maxTokens comment encodes). The authoritative `contextWindow`/`maxTokens` pair per model is on the endpoint page ([[frank-archon]], [[frank-dragoon]], [[frank-zealot]]); what follows is a **worked example** recomputed from the live files on 2026-10-18 — a snapshot, not fleet state — with windows 200,000 / 155,648 / 200,704 and maxTokens 16,384 / 45,056 / 16,384 (archon / dragoon / zealot): 1. **Source trigger** = `thresholdPercent × source contextWindow`; at the live 95 % that is 190,000 (archon), 147,866 (dragoon), 190,669 (zealot). The bundle clamps the percentage to 1–99 and converts it to a token threshold. 2. **Output budget** = capped at the target model's `maxTokens` — every role here compacts to frank-zealot, so the budget is its 16,384. 3. **REQUIREMENT**: prompt (≤ trigger) + budget ≤ the target's per-slot window. **At 95 % this no longer holds**: 190,000 + 16,384 = 206,384 > 200,704. The registry's own comments design around an 85 % trigger (archon: 170,000 est × 1.03 + 16,384 ≈ 191,500 ≤ 200,704, `models.yml:49-56`), which does hold. While `config.yml` and those comments disagree, treat 85 % as the design point and re-run this arithmetic after ANY window change — a 400 from the compaction target mid-conversation is the symptom. (Historic 2026-10-05 pairs, at `thresholdPercent: 70`: 140,492 + 36,864 = 177,356 ≤ 200,704 archon; 140,490 + 57,344 ≤ 200,700 dragoon; 140,492 + 32,768 ≤ 200,704 zealot.) Procedure (recompute from the LIVE files every time — windows rotate with every swap; the block above is the 2026-10-18 state): 1. Read the `config.yml` compaction block + `models.yml` entries for source and target. Every endpoint's `contextWindow` comment carries its own arithmetic — read them. 2. Read the target checkpoint's `max_position_embeddings` (native window) — the served window may be a concurrency contract, not the native limit (`--parallel N` DIVIDES the pool; the per-slot number is the contract). 3. If native ≥ trigger + budget, reslice the pinned pool: `MAX_RUNNING × CONTEXT_LENGTH ≤ MAX_TOTAL_TOKENS` (the pin exists because the runtime's autofit leaves too little for deferred loads + prefill workspace; check `nvidia-smi` headroom — expect ~0.8 GB workspace margin). 4. Changes: host env (CONTEXT_LENGTH-style env tunables) via git → `./tests/run.sh` → deploy; harness `models.yml` directly (no repo copy). 5. Verify: `/health` 200, boot log's pool line, then a long-prompt probe at ≥ trigger height (the compaction-fit guarantee), then concurrent requests. Known reconciliation trap: comments in models.yml have described compaction as running "on the dankserver endpoint" while `remoteEndpoint` pointed at dankclaw — the comment/config disagreed. The comments now name the real target (`compaction.remoteEndpoint` = dankclaw :7771). **The config is truth; fix comments in the same change.** A fresh instance of the same trap is open 2026-10-18: every `maxTokens` comment still says "compaction fires at 85 % of the window" while `config.yml:84` sets `thresholdPercent: 95`. `maxTokens` never changes for a reslice — it caps output; `contextWindow` caps input. Boot-shape OOM laddering is a different problem ([[Llama Cpp Serving Sizing]]). ## Per-model sampler config (`compat.extraBody`) `models.yml` has **no per-model sampler keys**. The global knobs are `config.yml`'s `temperature`, `topP`, `topK`, `minP`, `presencePenalty` `repetitionPenalty`, and a knob reaches the wire only when it is set — the bundle guards each one with `!== void 0` (e.g. `minP!==void 0)e.min_p=t.minP`) and this workstation's `config.yml` sets **none** of them. The 2026-10-05 note that the unset default is `-1`/"not sent" is retired: there is no `-1` sentinel, an unset key is simply absent. The per-model hook is `compat.extraBody`: **Why it works:** `applyOpenAIExtraBody` runs **last** in the openai-completions builder — after `applyChatCompletionsCompatPolicy` — `Object.assign(params, extraBody)`. So extraBody overrides global settings and compat-policy output. ```yaml compat: reasoningEffortMap: max: xhigh extraBody: temperature: 1.0 top_p: 0.95 top_k: 20 min_p: 0.0 presence_penalty: 0.0 repetition_penalty: 1.0 preserve_thinking: true ``` Use **wire names**, not OMP camelCase. Two traps: 1. **`preserve_thinking` is not user-settable as a compat key.** `qwenPreserveThinking` is auto-derived in `buildOpenAICompat` from the *auto-detected* `thinkingFormat` + local baseUrl, **before** `applyCompatOverrides`. Setting `compat.thinkingFormat: qwen` in models.yml does **not** enable it. Custom-named models (`frank-dragoon`) match no Qwen id pattern → use `extraBody: {preserve_thinking: true}`. 2. **Never put `chat_template_kwargs` in `extraBody`** where a compat policy also sets it — `Object.assign` is shallow and replaces the whole object, silently dropping the `reasoning_effort` kwarg the compat policy set. Top-level `preserve_thinking` is honoured by frank-dragoon ([[frank-dragoon]]). Live state 2026-10-18: frank-archon's `extraBody` carries `chat_template_kwargs: {enable_thinking, preserve_thinking}` (`models.yml:89-91`) and nothing else sets that object at that endpoint, so the shallow-merge hazard is latent rather than firing. **Gotcha (retired 2026-10-17): silent greedy decode.** Earlier frank-dragoon builds carried **no server-side sampler defaults**, so an absent `temperature` decoded greedy while the endpoint looked configured. That era is over — the endpoint now ships its own defaults ([[frank-dragoon]]), so an absent temperature is no longer greedy. frank-dragoon's `extraBody` still carries the same trio — consistent, not load-bearing. Treat any older "do not drop it or you get greedy" wording as history. **Reasoning-effort folding.** The set `reasoning_effort` accepts is a property of the endpoint, not of the harness: [[frank-dragoon]] and [[frank-zealot]] raise on anything outside `xhigh|medium|low`, while [[frank-archon]] does not gate the set. Every endpoint's `reasoningEffortMap` folds all harness levels onto whichever set that endpoint ships; a role whose level is unmapped fails on its FIRST request, and the failure names Jinja, not the model. ### Verify the exact wire body (echo-capture) Don't trust the YAML — capture what OMP actually emits: 1. Echo server on `127.0.0.1:18099` that dumps the JSON body to a file and returns a canned `chat.completion` (include `reasoning_content` so thinking paths exercise). 2. Copy the **real** models.yml into a throwaway profile with only the baseUrl swapped: ```bash sed 's|http://:7770/v1|http://127.0.0.1:18099/v1|' \ ~/.omp/agent/models.yml > ~/.omp/profiles/wiretest/agent/models.yml ``` 3. Run and inspect: ```bash omp --profile wiretest --model "/" -p "hi" \ --no-tools --no-session --no-extensions --no-skills --no-rules jq 'del(.messages, .tools)' last-body.json ``` Confirm every intended field is present with the right value. **Verify a field is *honoured*, not just accepted** — unknown fields often return 200 anyway. Prove behavior with a **prompt-token delta**: replay an assistant turn carrying a long `reasoning_content`, then compare `usage.prompt_tokens` with the flag true vs false. A large delta means the field changed template rendering; zero delta means it was ignored. Cleanup: trash the probe profile and echo server (`profiles/.AGENTTRASH`) never `rm`. ## Static-preamble audit ("the context feels bloated") What loads statically every session (measure each, don't guess): 1. Repo rules file — `wc -c AGENTS.md` (baseline 2026-10-05: 72,179 B, ~18k tokens — historically the largest block; re-measured 2026-10-18: 89,723 B). 2. Skill index — only names+descriptions load; bodies are on-demand. Sum description bytes across all SKILL.md frontmatter: ```bash python3 - <<'EOF' import glob, re, os for root in [os.path.expanduser("~/.omp/agent/managed-skills") os.path.expanduser("~/.omp/agent/skills"), ".omp/skills"]: n=tot=0 for sk in glob.glob(root+"/*/SKILL.md"): fm=re.search(r'^---.*?---', open(sk, encoding="utf-8", errors="replace").read(), re.S) d=re.search(r'description:\s*(.*(?:\n(?:\s{2,}.*)*)?)', fm.group(0) if fm else "") n+=1; tot+=len(d.group(1)) if d else 0 print(root, n, tot) EOF ``` Baseline 2026-10-05: 171 managed skills / ~38 KB descriptions + 9 user skills. Re-measured 2026-10-18 with the same glob: **87** managed `SKILL.md` + 9 user skills — the dedupe happened. 3. Memory lessons — `find ~/.omp/agent/memories -name 'MEMORY.md' -o -name 'memory_summary.md' | xargs wc -c`. The injected block ≈ the largest MEMORY.md. 4. Harness/system prompt + tool schemas + CLI persona: fixed cost, ~20 KB; not prunable. Known rot patterns: - Near-duplicate auto-captures of the same lesson (three "authentik ORM" entries, the freetoken-build pair, the three job-runner skills) — dedupe via `manage_skill delete`/`update`. - Corrupted captures containing stray CJK characters (`一团ly`, `等方法`) mid-sentence — a known defect of the auto-capture path; delete the corrupt copy, keep the clean one. - Stale host/service names in consolidated memory (pre-rename `frank-default-large`) — the repo's AGENTS.md topology table is ground truth. Fix strategy: AGENTS.md prose (measured runbook narratives) → move to `serverAppConfigs-docs/LLM/` runbooks, keep laws + tables in AGENTS.md (the repo's own docs-tree rule mandates this). Managed skills live in `~/.omp/agent/managed-skills` (safe to edit); user-authored skills in `~/.omp/agent/skills` are NEVER touched by tooling. Memory store edits: stage removed captures to trash first (`~/.AGENTTRASH/YYYYMMDD-topic`). Baseline to beat (2026-10-05): ~35–40k tokens of preamble — AGENTS.md ~18k, skill index ~10k, lessons ~3.5k, harness ~6k. Proposed reductions: −12k (AGENTS.md slimming), −3k (skill dedupe). ## Managed-skill library hygiene Use when auditing, pruning, or consolidating `~/.omp/agent/managed-skills` (the auto-captured skill store; the full index is injected into EVERY session's context, so dead or duplicate entries are a permanent tax). The static-context budget (measured 2026-10-05): | Block | Size | Notes | |---|---|---| | `AGENTS.md` (repo) | 72,179 B ≈ 18k tok | biggest single item | | Skill index (~173 skills) | ~40 KB ≈ 10k tok | names+descriptions only | | Learned-lessons block | ~14 KB | can contain corrupted/duplicate captures | | Harness system prompt + tool schemas | ~23 KB | fixed | Re-measured 2026-10-18: the skill library is down to 87 managed skills, but `AGENTS.md` has grown to 89,723 B — the dedupe landed, the slimming did not. Audit procedure: 1. Build an inventory: per skill, name, mtime, body word count, description, first 12 headings (one pass, out to a file; read in pages). 2. Group into: retired-runtime (host/service gone; its runbook already lives in `serverAppConfigs-docs/LLM/`), verbatim-duplicate pairs/triples (same lesson captured twice — often the SAME DAY), same-lesson families (N skills = one procedure), keep. 3. Read full bodies of every merge candidate before proposing a merge; description alone proves overlap only for exact dupes. 4. Present A/B/C/D verdicts for user approval. NEVER execute before the discussion concludes — the user gates execution explicitly. 5. On approval: stage everything to trash first (`~/.AGENTTRASH/YYYYMMDD-/`), never rm. After merging: recreate the surviving skill via `manage_skill` update with body = superset, delete absorbed ones. Known rot classes (this library, 2026-10-05): - Same-day duplicate captures (freetoken-build-dankclaw vs freetoken-dankclaw-build; the three job-runner skills). - CUDA-skill vs its self-declared HIP twin (pcie-link-capability-probe vs pcie-link-probe-hip-rdna4) → merge. - Era ghosts: skills naming retired runtimes (freetoken, lucebox, r9v/ radiance, old halogen-flash-server image, ik_llama, GLM-5.3 experiment). - Skills contradicting user standing orders (live-bench-dashboard vs the 2026-09-29 delete-dashboards order) — flag for user, don't assume. - Memory store: corrupted captures with CJK noise from broken ssh commands; stale host/service renames. Sizing verdicts that worked: bench family → 2 skills (harness vs discipline), not one mega-skill; keep profiler/kernel-time attribution standalone; fold go/no-go arithmetic skills into their probe skill (heterogeneous-accelerator-split-triage → PCIe probe). ## Related - [[Agent Docs and Edits]] — editing vault docs and hashline targets safely - [[Command Center Dotfiles]] — the `~/.omp` repo deploy this config rides - [[Llama Cpp Serving Sizing]] — the engine-side sizing the chain must fit - measuring the endpoints this config routes