live: heartbeat sync 2026-10-29T15:29:37-07:00 (Refs #11)

This commit is contained in:
Frank committed 2026-10-29 15:29:37 -07:00
1 parent 3eb1ada55b
commit 4d76c6038e
13 files changed
+333 -14

No files matched your search

@@ -0,0 +1,53 @@
---
name: live-checkout-landing-chain
description: "Prove whether a merged change is actually deployed in ~/.omp: walk origin/main vs the checked-out main commit vs the worktree, then fast-forward in place — git refuses fetch into a checked-out branch, and a running harness caches its config."
---
## When to use
The user says a merged/committed change is "not there" (`/switch` can't see a model, a doc is missing, a config edit is invisible) while the repo work itself is green. In a repo where **the checkout is the deployment** (`dank/command-center` at `~/.omp`), "merged" and "deployed" are different commits — measure both before believing either claim.
## The ladder — read all four levels, in order, before answering
```bash
cd ~/.omp && git rev-parse HEAD origin/main # 1+2: deployment vs forge
git ls-tree --name-only HEAD # is the file even in HEAD?
git worktree list # 3: which worktree am I in vs main?
```
1. `origin/main` — the forge truth (`gh`/`tea` absent; Gitea API or a fetch).
2. `/home/blank/.omp` HEAD — **what the running agent actually reads.** Merged PR at `3eb1ada` while this sat at `53025a6` is the whole bug.
3. The feature/session worktree — where the edit was authored; its files can be correct while main is stale, which is why the author sees the change and the user does not.
4. The running process — `models.yml` / `config.yml` are read at startup and cached for the process lifetime; an open session needs a restart even after level 2 is correct.
Belt-and-braces when the answer hinges on file contents, not just commit ids: parse the live file (`python3 -c "import yaml; ..."` for `agent/models.yml`, or grep the doc) rather than inferring from `git log`.
## Landing main without touching the tree
`git fetch origin main:main` **fails**: `fatal: refusing to fetch into branch 'refs/heads/main' checked out at '/home/blank/.omp'`. The fetch-into-ref trick only works for branches that are *not* checked out anywhere.
For a checked-out main, prove then fast-forward:
```bash
git merge-base --is-ancestor main origin/main && echo "ff possible"
git merge --ff-only origin/main # no rebase — Frank dislikes history rewrites
```
`--ff-only` is the safe verb on a dirty main checkout: tracked-file commits (the live-sync tier) and runtime dirt (`agent/memories/*/state.json`, `dotfiles/**`) survive untouched, and it cannot rewrite history. Confirm the dirt is still there afterwards — if it vanished, something else clobbered the tree.
## The heartbeat is not instant
`scripts/live-sync.sh` (systemd user timer, hourly) is what normally advances main. Check its cadence rather than assuming it ran:
```bash
systemctl --user status live-sync.timer --no-pager # "Trigger: …" = next beat
systemctl --user status live-sync.service --no-pager # "since …" = last run
```
A merge made minutes after the last beat waits until the next one; fast-forwarding by hand is legitimate (idempotent with the script) and is the answer when the user wants it now. Never `--no-verify`, never force-push.
## Do not confuse with
- `omp-worktree-path-gotchas` — edits landing in the *wrong* worktree (cwd drift), not a stale main.
- `dotfiles-deployment-truth-audit` — classifies which files are tracked vs runtime state.
- `serverappconfigs-vault-page-registration` / `gitea-merge-form-encoded` — the PR mechanics upstream of this question.
@@ -0,0 +1,86 @@
---
name: omp-provider-row-sources
description: "Add a provider/model row to ~/.omp/agent/models.yml by reading the harness's own catalog tables (provider name, api, window, cost, compat) instead of guessing or web-searching, then prove it with a scratch-HOME call."
---
# Scope
Adding (or correcting) a provider/model row in `~/.omp/agent/models.yml` for a
provider the harness may already know: an upstream vendor API (DeepSeek, OpenAI,
Anthropic, Moonshot, …) or a new local endpoint. The rule this skill exists for:
**almost every field you are about to research on the web already exists in the
installed bundle** — research there first, and the web only confirms it.
Canonical install root (resolve the symlink first if the repo lives under a
worktree): `/home/blank/.bun/install/global/node_modules/@oh-my-pi/`.
# The three tables that own the facts
| You want | Read | Why |
|---|---|---|
| The legal **provider name**, its URL marker, and which quirks the harness gates on it | `pi-catalog/src/hosts.ts` (minified in `dist/cli.js` as the `VIn` map; consumed by `pi-catalog/src/compat/resolve.ts`) | The provider key is a **lookup key, not a display name**. e.g. `deepseekDirect: { providers: ["deepseek"], urlMarkers: ["api.deepseek.com"] }`, `anthropic: { providers: ["anthropic"], urlMarkers: ["api.anthropic.com"] }`. A label matching neither loses the behaviour set silently (the `max_tokens` field name, the thinking `extraBody`, the thinking-disable mode). |
| Model rows: ids, `contextWindow`, `maxTokens`, `cost` rates, per-model `compat` | `pi-catalog/src/provider-models/<api>.ts` — `openai-compat.ts`, `anthropic-messages.ts`, …; factory helpers `openAiCompletionsDescriptor(...)` / `anthropicMessagesDescriptor(...)` | Rows are fetched from the provider's models-dev key, so the bundle carries the same numbers the vendor publishes (and often rates the vendor page hides). The descriptor's `compat:` block is the authored truth for that provider's transport shape — copy it, never simplify it. |
| The **schema** itself (which keys are legal on a provider vs a model, and which are required) | `pi-coding-agent/src/config/models-config-schema-bundle.ts` (and the identical `dist/` copy) | Provider level and model level have *different* field sets. Cheaper and more correct than reading the minified bundle. |
Also in `pi-coding-agent/src/config/`: `model-registry.ts` (how `models.yml`
merges onto the built-in catalog — `#mergeCustomModels` → `mergeByModelKey`),
`resolve-config-value.ts` (how `apiKey` values resolve).
# Two traps this table pays for
- **Provider-level `api` only accepts chat/judge transports.** Naming a runner API
(`openai-speech`, `openai-images`, `openai-embeddings`) at provider level fails
whole-file validation and disables *every* custom provider, not just yours
(`omp models` prints "models.yml validation failed", lists only built-ins).
Runner api belongs on the **model** entry; see the managed skill
`omp-models-yml-runner-api-placement`.
- **A `models.yml` entry is an overlay, not a replacement.** Adding one model to a
provider the catalog already ships does not remove the provider's other rows —
`omp models <provider>` will show yours (yours wins on shared fields, including
`cost` and `contextWindow`) plus the catalog rows. Same-id rows patch with the
`replace` transport policy; a row that omits `contextWindow` inherits the
bundled row's window and its policy tiers.
# Secret handling (non-prose tracked file)
Never inline the credential. `apiKey` resolution order is: leading `!` → command
(`stdout` trimmed, cached for the process lifetime) → exact env-var name → literal.
So write `apiKey: "!cat ~/.config/secrets/<name>_api_key"` with mode 600.
- A bare `sk-…` trips the pre-commit **shape** gate (every file type).
- An unquoted `!` is a YAML tag — quote it.
- A quoted command value also dodges the *assignment* gate: the gate's value
charset rejects `~` and spaces, so only a literal key matches.
# Procedure
1. Read the three tables above for the provider you are adding. Take the exact
provider name, base URL, api, and compat from them; take `contextWindow` /
`maxTokens` / `cost` from the model row and cite the source in a comment.
2. Write the secret to `~/.config/secrets/<name>_api_key`, `chmod 600`.
3. Edit `models.yml`; for a repo-tracked config, follow that repo's own
branch/log/index rules (in `dank/command-center`: branch → issue → PR).
4. Verify with a **scratch HOME**, because `models.yml` loads at process start
and a live session keeps the old registry:
```bash
mkdir -p /tmp/ompx/.omp/agent /tmp/ompx/.config
cp agent/models.yml agent/config.yml /tmp/ompx/.omp/agent/
ln -sfn ~/.config/secrets /tmp/ompx/.config/secrets # ~ in a !cmd value = $HOME
HOME=/tmp/ompx omp models --no-extensions | grep -c 'validation failed' # must be 0
HOME=/tmp/ompx omp --model '<provider>/<id>:off' --tools bash --no-session \
--no-skills --no-rules --no-extensions --no-pty --no-lsp --no-title \
--cwd /tmp -p 'Reply with exactly the word OK and nothing else.'
```
Then the **negative control**, the step that separates a real auth success from
a cached/built-in answer: `sed` the `baseUrl` to a dead port in the scratch copy
and repeat — it must hang or error, not answer. Then `rm -rf /tmp/ompx` (it
holds endpoint URLs).
# Voice notes
- Prefer the bundled schema file over grepping `dist/cli.js`; grep the bundle only
when the source and the bundle disagree, or for a runtime default the schema does
not state (e.g. `reasoningDisableMode` per provider family).
- `cost` is optional in the schema. Omitting it makes the status line report `$0`
forever; one rate per direction is all the schema models, so a peak/off-peak
vendor table can only carry the peak value.
@@ -0,0 +1,91 @@
---
name: quant-pipeline-adversarial-review
description: "Review or audit the serverAppConfigs quantization campaign pipeline (convert-calib-kldref-score plus corpus builder/rechunker/pusher, fp8 stacker) for defects that silently waste GPU-hours or produce a number that is not the claimed number."
---
# Adversarial review of the quant campaign pipeline
Friction-earned 2026-10-29 across `scripts/convert-qwen38-w5nl64.sh`,
`run-qwen38-bf16.sh`, `make-calib-corpus.py`, `rechunk-kld-corpus.py`,
`calib-push.py`, `stack-qwen38-official-fp8.py`. Every class below was a real
defect in that tree. Review code, not vibes: quote the line, name the cost.
## Files in scope (one campaign = this set)
| Stage | File | Owns |
|---|---|---|
| corpus | `make-calib-corpus.py` | calib + kld JSONL, manifest |
| corpus | `rechunk-kld-corpus.py` | windows that fit the container's KV window |
| convert | `convert-<model>.sh` | plan → artifact per arm |
| tape | `run-qwen38-bf16.sh` | the rig's argv, mount, trap, gates |
| drive | `calib-push.py` | prefill traffic into the tap |
| fp8 path | `stack-qwen38-official-fp8.py` | expert stacking + PLE merge |
## Defect classes to hunt, in cost order
1. **Claimed identity that is only a length check.** A tool that writes TEXT back
out (`tok.decode(window)`) and asserts only counts is not proving token
identity — decode→encode can merge/split. Fix: re-encode each window and
compare id lists; refuse on mismatch. Same shape: an assertion on "N tokens
in, N tokens out" that counts windows rather than re-encoded text.
2. **A loop that cannot finish and cannot notice.** Drivers that re-push a corpus
`--max-rounds` times without measuring the sink. Compare the tap/output dir's
byte total per round and abort on zero growth; abort on zero successes; abort
on zero prompts. Without this the loss is silent for hours.
3. **`set -u` argument holes.** `--flag) VAR="$2"; shift 2` dies with "unbound
variable" when the flag is last. Guard `[ "$#" -ge 2 ]` in every such arm.
4. **Restore paths that leave the GPU holder alive.** `--rm` only fires when
podman itself exits, so a killed run leaves conmon holding the cards while
`systemctl is-active` says `active`. `restore()` MUST `podman rm -f "$C"`
before starting production. Prove with `podman ps` after a `kill`.
5. **Gates that run after the expensive phase.** A leftover container name, an
existing output, a non-writable tap dir, an unset figure — check them before
the plan/boot, not when podman/sed fails mid-run.
6. **Zero-exit with no artifact.** After `rc=0`, assert the output file exists
before `stat`/arithmetic on it.
7. **`assert` in a verifier.** `python -O` deletes it silently. Use `raise`.
8. **Warning where the code's own assumption breaks.** e.g. a 128-lane pack fed
an HD that is not a multiple of 128 — warning + continue writes a wrong table.
9. **Re-tokenization in loops.** A pool cycled 40× re-tokenizing each fragment
per pass; and a manifest recomputing a total by re-encoding the whole corpus.
Cache counts once, accumulate during generation.
10. **Substring tests standing in for key tests.** `"content" in line` before
`json.loads(line)["content"]` crashes on any list-shaped `.json`.
11. **Path guards compared as strings.** `A/../A/x` defeats an equality check;
add `realpath -m` on both sides.
12. **Shellcheck's honest false positive**: single-quoted `$CALIB` passed to a
converter that expands it from the container env is correct (SC2016). Say so
in the PR; do not "fix" it.
## Procedure
1. Inventory: `wc -l` the scripts + recipes; read each **completely** (ranges,
not whole files, but every line). Recipes carry the numbers scripts assert.
2. Read the tests that pin the argv (`tests/qwen38_reference.bats`'s `SHAPE`
fixture, `tests/qwen38_campaign.bats`) — they are the contract you must not
break, and the first place a stale number hides.
3. Mechanical gates:
```bash
python3 -m py_compile scripts/*.py
bash -n scripts/<each>.sh
shellcheck -S warning scripts/<each>.sh # never pass .py to it
./tests/run.sh
```
4. Fix the real ones only; keep diffs surgical; re-run the suite after the batch.
5. Smoke the changed path for real — smoke beats the suite:
```bash
# run a local script on the server's data via the corpus venv, no checkout needed
ssh dankserver '/home/dank/venvs/radcorpus/bin/python - --corpus <corpus> --out /tmp/<x> --force' \
< scripts/rechunk-kld-corpus.py
```
Import-level helpers can be exercised with
`importlib.util.spec_from_file_location` (e.g. a new `tap_bytes()`: empty→0,
one file→its size, absent→0).
6. Stacked PRs: a review commit goes on its own branch off the open one; a diff
that includes the parent commit is a stacked PR, not a mistake — say which PR
merges first in the body.
## Report
One table of defect → cost → fix. Name the failure the defect produced in
practice (hours wasted, wrong number), because that is what makes it reviewable.
@@ -0,0 +1,77 @@
---
name: script-refactor-parity-vs-previous-revision
description: "Prove a refactor of sibling data-pipeline scripts is behavior-identical: run the pre-refactor revision from git HEAD beside the new one on the host that owns the venv/data, compare parsed records rather than bytes, and bootstrap the shared module so file, runpy and container paths all import it."
---
Refactoring a pipeline of sibling scripts (corpus builders, re-chunkers, pushers) whose only
tests are smokes. The test suite cannot see a moved record boundary, so parity has to be
*measured against the previous revision*.
## 1. Keep the old revision runnable
Never trust the diff. Materialize both sides where the data lives (the host with the venv,
tokenizer, parquet, corpus):
```sh
git show HEAD:scripts/<tool>.py > /tmp/qc-old/<tool>.py # pre-refactor
cp scripts/<tool>.py scripts/<newmodule>.py /tmp/qc-new/ # post, module beside it
```
`ssh host 'cat > /tmp/...'` with the bytes on stdin is enough; no repo on the target needed.
Run both with **identical seeds and flags**, into separate output dirs.
## 2. Compare records, not bytes — unless the escaping is unchanged
- If the refactor funnels writes through one helper, that helper's `ensure_ascii` choice moves
every non-ASCII character. A byte diff then reports a false regression while the rows are
identical.
- Prove it correctly: parse both sides and compare in order —
```python
a = [json.loads(l) for l in open(old) if l.strip()]
b = [json.loads(l) for l in open(new) if l.strip()]
print(a == b, hashlib.sha256(json.dumps(a, sort_keys=True).encode()).hexdigest()[:16])
```
- Where the refactor does not touch serialization (a tool that already wrote literal UTF-8),
demand the stronger proof: `cmp` + md5 equality of the artifact.
- Order matters: shuffled output with an unchanged seed proves the RNG draw sequence survived
the hoist of nested closures. Compare the *sorted* file only as a second check — permutation
equality alone hides a reordered `rng.choice`.
- Hashing the record set with `sort_keys=True` also catches dict-key drift in manifests.
## 3. Sibling-module bootstrap that survives every invocation
A new shared module must import when the tool is run three ways: `python3 scripts/tool.py`,
`runpy.run_path('/work/tool.py')` from a container mount (`-v scripts:/work:ro`), and from any
cwd. Standard preamble:
```python
_HERE = os.path.dirname(os.path.abspath(__file__)) if "__file__" in globals() else os.getcwd()
if _HERE not in sys.path:
sys.path.insert(0, _HERE)
import <module> # noqa: E402 - sibling module, same directory
```
Piping the script through `python3 -` has no `__file__` and defeats this — run tools by path and
say so in the module docstring. Verify both forms explicitly before claiming it works.
## 4. Unit-test the refusals without the heavy dependency
Hoist validation into a function so it can be driven with a stub, in-process:
```python
spec = importlib.util.spec_from_file_location("<module>", ".../<module>.py")
m = importlib.util.module_from_spec(spec); sys.modules["<module>"] = m; spec.loader.exec_module(m)
```
Registering the module in `sys.modules` under its real name *before* loading the dependent
script is required, or the script's own `import <module>` re-imports it and your stub is invisible.
A stub object with `encode`/`decode` that mangles ids past a threshold exercises a
"token identity" check in one page of output, no tokenizer file needed.
## 5. Gate order
`py_compile` every touched file → run the tool's `--help` through both invocation forms →
old-vs-new parity on the host → suite → commit. A suite that enumerates shell seams will not
notice a corpus-tool regression; the parity run is the only evidence there.
@@ -1,4 +1,4 @@
{ {
"v": 1, "v": 1,
"lastConsolidatedAt": 1793215693649 "lastConsolidatedAt": 1793310987675
} }
@@ -1,7 +1,5 @@
## Product Quality Standards ## Product Quality Standards
Quant quality must hold regardless of implementation choice.
## Behavioral Updates ## Behavioral Updates
Hourly updates on a deterministic timer. Hourly updates on a deterministic timer.
@@ -1,8 +1,8 @@
{ {
"v": 1, "v": 1,
"lastConsolidatedAt": 1793309257223, "lastConsolidatedAt": 1793312933825,
"lastResult": { "lastResult": {
"at": 1793218183375, "at": 1793312601006,
"sessions": 1, "sessions": 1,
"deltas": 1, "deltas": 1,
"model": "frank-zealot" "model": "frank-zealot"
@@ -0,0 +1,4 @@
## Models
- Models settings include /switch to switch to it
- hdmi monitor to the left and the other to the right of eDP-1
@@ -1,4 +1,10 @@
{ {
"v": 1, "v": 1,
"lastConsolidatedAt": 1793309241992 "lastConsolidatedAt": 1793312076890,
"lastResult": {
"at": 1793312076890,
"sessions": 1,
"deltas": 1,
"model": "frank-zealot"
}
} }
@@ -0,0 +1,4 @@
{
"v": 1,
"lastConsolidatedAt": 1793310990286
}
@@ -107,11 +107,11 @@ widget_order = [
[lockscreen_widgets.widget."lockscreen-login-box@DP-2"] [lockscreen_widgets.widget."lockscreen-login-box@DP-2"]
box_height = 196.0 box_height = 196.0
box_width = 810.0 box_width = 810.0
cx = 1719.999755859375 cx = 599.9998779296875
cy = 1221.60009765625 cy = 1628.8001708984375
output = "DP-2" output = "DP-2"
placement_height = 1440.0 placement_height = 1920.0
placement_width = 3440.0 placement_width = 1200.0
rotation = 0.0 rotation = 0.0
type = "login_box" type = "login_box"
@@ -279,13 +279,13 @@ transition = [ "fade" ]
interval_seconds = 1200 interval_seconds = 1200
[wallpaper.default] [wallpaper.default]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-mlomkm.png" path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png"
[wallpaper.last] [wallpaper.last]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-mlomkm.png" path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png"
[wallpaper.monitors.DP-1] [wallpaper.monitors.DP-1]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-mlomkm.png" path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png"
[wallpaper.monitors.DP-2] [wallpaper.monitors.DP-2]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png" path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png"
@@ -294,7 +294,7 @@ transition = [ "fade" ]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png" path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png"
[wallpaper.monitors.eDP-1] [wallpaper.monitors.eDP-1]
path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-mlomkm.png" path = "/home/blank/.local/share/noctalia/wallpapers/wallhaven-gjqmel.png"
[weather] [weather]
unit = "imperial" unit = "imperial"