Frank asked for normal rotation on both PM1601 panels and the HDMI-A-1
panel to the left of eDP-1. rotation drops 90 -> normal, so each panel
goes 1200x1920 -> 1920x1200 and the absolute x of everything to its
right moves with it: HDMI-A-1 now x=0, eDP-1 x=1920 (y=60, centred
against the taller panels), DP-2 x=3840. modes unchanged.
displays.md still described desk-2-portable as an EDID-pending skeleton
and left the PM1601 identity unnamed. record the second desk's rig,
its position math, and the law that a rig with no eligible profile runs
the previous desk's values rather than "unchanged" — the failure that
left the portable panels on 3440x1440@180 fallbacks for weeks.
The portable rig (laptop + two 16" PM1601 panels in portrait) had no
profile of its own, so display.kdl stayed on desk-1-home: the Sceptre
identity block matched nothing, its connector-keyed fallbacks carried
3440x1440@180.000 that no panel here reports, and niri silently drove
every panel at its preferred mode with positions written for the home
desk. key the two panels by captured EDID identity, pin the modes they
actually report, and centre the laptop against the 1920-tall panel band.
desk.sh match now selects this profile, so blankpad-desk-autoswitch no
longer dies at its initial match with "no desk profile matches".
Refs #83
The first vendor-cloud chat row in agent/models.yml. The provider label is the
bare word `deepseek` because pi-catalog/src/hosts.ts keys the direct-DeepSeek
behaviour set (max_tokens field, thinking extraBody, thinking-disable mode) off
that exact name and off api.deepseek.com; the house `<api> (<host>)` pattern
would not match and the row would lose the quirks the endpoint answers to.
The key is command-backed (`!cat ~/.config/secrets/deepseek_api_key`, 600), so
no sk-… value enters the tracked tree, and the row declares the model card's own
pair: contextWindow 1048576 / maxTokens 262144, input [text, image], with the
October 2026 peak rate card in cost. Docs move to the new
omp-docs/core/deepseek-provider.md, indexed and logged in the same commit.
Live probe against frank-archon (212,916-char archive, 8 planted facts):
auto shape 8on22-bw reads back 8/8 recall at 47% of raw-text billed
tokens; both denser grids drop characters. Lands the docs for the paired
live-config changes (thresholdPercent 95->85, imageCountBudget 16) and
records the catalog-only frame-pricing law (radiance ~2,400 tok/frame vs
the harness's blind 5,024 floor).
Refs #81
Every provider in agent/models.yml is a LAN address, so a workstation that
leaves the house has no model at all. Add `openwebui (franksdank.cloud)`:
the already-public OpenWebUI vhost as an OpenAI-compatible provider that
proxies the same engines, with frank-dragoon and frank-zealot rows and the
WAN row as its own compaction target. LAN rows, modelRoles and the speech
runner stay untouched — the door is purely additive.
The key is command-backed (`apiKey: "!cat ~/.config/secrets/openwebui_api_key"`,
mode 600) instead of inline: a literal sk-... in a tracked file is a key in
every agent context on every box that pulls this repo, and the pre-commit
shape gate would block it anyway. Rotate = rewrite the file + restart omp,
because a `!cmd` value is cached for the process lifetime.
Compat blocks copy their LAN twins because the proxy was probed, not assumed
(2026-10-21): streaming SSE carries reasoning_content and tool_calls deltas
plus final usage, image_url content parts reach both vision towers, and
chat_template_kwargs is forwarded — proven on frank-zealot, where
enable_thinking false yields zero reasoning and true a real block.
frank-archon gets no WAN row: OpenWebUI answers 400 "Model not found"
because its picker never published it, so that path is a fleet change.
Verified end-to-end from a scratch HOME whose LAN baseUrls point at a dead
port, so any answer can only have come through the door (text and a real
tool round-trip); `omp models` still lists all four chat providers and the
tts runner, so the file validates. bats 137/137, kb-lint clean.
Docs: omp-docs/core/wan-model-access.md.