Files
serverAppConfigs/serverAppConfigs-docs/Agent/OMP Harness.md
T
Frank 6bdfd2853e docs(vault): service and infrastructure pages stop restating endpoint facts
18 pages cut their per-endpoint shape, payload, port and window claims and
link [[frank-archon]] / [[frank-dragoon]] / [[frank-zealot]] instead. Several
of the cut claims had already drifted -- the stack page still described the
kingjones ROCmFP4 era on ROCmFPX, OpenWebUI carried the 27B/3-slot bench
narrative, Hermes named a retired max_model_len and the old service name, and
the OMP harness page's live-window block had drifted off frank-zealot's real
262,144 (it is now labelled a dated worked example whose authority is the
endpoint page).

Two facts gain a single owner instead of two: the up{job=~"frank-.*"} scope
lives in Infrastructure/Prometheus.md, and the blackbox/Anubis probe form in
Architecture/Stale Signal Audit.md, each cited from the other page.

Voice Packs and Frank TTS carried live-state claims that contradicted the
engine page's 2026-10-18 deploy record (nothing listening on 7772/8880, model
dir absent); they now point at the pages that own host state.
2026-10-19 00:56:02 -07:00

16 KiB
Raw Blame History

type, title, description, tags, timestamp
type title description tags timestamp
reference OMP Harness The omp agent harness as configured on this fleet — compaction chain arithmetic, per-model sampler routing via compat.extraBody, and the static-preamble/skill-library audits that keep session context lean.
omp
harness
compaction
models-yml
context
samplers
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 — via Object.assign(params, extraBody). So extraBody overrides global settings and compat-policy output.

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:
    sed 's|http://<host>:7770/v1|http://127.0.0.1:18099/v1|' \
      ~/.omp/agent/models.yml > ~/.omp/profiles/wiretest/agent/models.yml
    
  3. Run and inspect:
    omp --profile wiretest --model "<provider>/<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:
    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-<topic>/), 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).