8842ffe1fe904c7d345fe831ab3e93712e9c9c36
385 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c488eadc31 |
memory: snapshot — althing v3 fleet-wide at 3.1.1, sec on GPU0
The in-flight section was a day stale: it still described run 3c as the live subject on a box where nothing had moved. Rewritten around what is actually true now -- v3 deployed fleet-wide, the post office relocated to nh3-docker, sec serving on GPU0, run 3c still held on power. Six new decision entries, three of which carry findings that outlive their incident: the inbound half of the handle-resolution bug (a stale ALTHING_HANDLE reads another agent's mailbox and reports it empty, which is a second route into the failure v3 exists to prevent), the OOM attribution to Claude Code sessions, and the operator's two explicit belays recorded so a later session does not re-raise them as new. Auto-archival fired at 301 lines and moved exactly one entry. Three others were old enough and every one carries a still-open deferred pointer -- the parked CI flip, muninn-gate's submit path, and the triton backend deferred to the Ada refresh. Held back per the guards; an over-cap file that keeps live decisions beats a scannable one that lost them. The entry that did move had its deferred item closed today: nh3-extdev's staged v2.1.2 wheel is moot now that the box runs 3.1.1. |
||
|
|
9d4e7bd34a |
feat(althing): move the post office to nh3-docker
Operator directive, and a standing goal: the bus belongs on the docker host. The flag-day deployment put it on nh3-dev because the herald lives there -- but the herald is the piece that must be host-local, and the post office is explicitly the piece that is not. nh3-dev was wrong on three counts. Our own server table calls it "not a Docker-stack host". It has had three OOM events in fourteen days with the interval halving, and the confirmed hog is Claude Code sessions at 5-18 GB, which is that box's actual job. And mem_limit protects the fleet from the post office while doing nothing in the other direction: oom_score_adj was 0, an ordinary kill candidate, on a box whose last sweep took althing-herald and uvicorn. The new deployment sets oom_score_adj=-500. The compose is now version-controlled here as a normal stack rather than living only in the althing repo's deploy dir. ## docker stop does not checkpoint the WAL The database was 155 KB with a 4.1 MB write-ahead log, and every recent message was in the log. A clean container stop left it untouched -- an explicit PRAGMA wal_checkpoint(TRUNCATE) was required. A docker cp of the .db alone would have produced a database that opens cleanly, passes integrity_check, serves the full 73-handle roster, and is missing the day's mail, with nothing raising an error. Row counts were verified at source, in the staged copy, and after seeding, because the count is the only thing that separates those two outcomes. The old volume is left in place. Not a rollback path, which the operator ruled out -- just not deleting the only other copy on the day of a move. ## Follow-up left open The image has no registry push and moves by save/ssh/load, so a rebuild means repeating that by hand. It should join the gitea registry pattern the other stacks use. |
||
|
|
e58360668e |
feat: althing v3.0.0 cutover (U9b) and the sec seat onto GPU0
Two operator-authorised changes on the same afternoon.
## althing v3 (U9b flag day, one-way, no rollback)
The post office replaced the v2 P2P bus on nh3-dev and nh3-extdev.
One container is the only stateful component; heralds are one per box
and dial out; waiters are one per session. Every v2 command was deleted
rather than deprecated, so a script calling althing-cli now fails loudly
instead of silently talking to nothing.
73 handles seeded from the v2 CLI, which is authoritative over the v2
database's 91 agent rows -- the extra 18 are superseded names, a typo,
an underscore variant, and two machine-qualified handles that v3 makes
a category error. Verified by set difference in both directions rather
than by counting; a peer's "72 rendered" was a line-count artifact.
Deleted 5,043 orphaned wake FIFOs. The reason there were five thousand
is that v2 named them per session with the PID and never reaped them;
v3 names them per handle, so the leak is bounded by construction. That
is a fix in v3, not a cleanup we performed.
nh3-extdev needed its own path: althing lives there as a system wheel
under /opt/uv-tools with entry points in /usr/local/bin, its daemons
were system units rather than user units, and uv is not on the login
user's PATH. Captured as a rerunnable playbook rather than shell
history.
The v2 database is left inert on disk. There is no import path and none
was improvised.
## sec onto GPU0
GPU1 carries the five resident fleet seats and had ~28 GB free against
the ~51 GB this seat reserves, so it could not start there at all. GPU0
has been idle since run 3c was stopped. The compose header, the GPU pin
default and the homepage label all carried the old card number and are
corrected together -- a label that names the wrong GPU is a record that
lies about where the work runs.
Both playbooks carry verify phases that assert effective state. Two of
those verifies failed on green deployments while I was writing them:
one used a Go template that collided with the runner's own {{ }}
substitution, one omitted --handle so it failed on identity rather than
reachability. Both are fixed with the reason recorded inline, because a
verify that reports FAILED on a working system trains you to ignore it.
|
||
|
|
f875f746b8 |
feat(playbooks): nh3-dev memory forensics — and the OOM hog is Claude Code
forseti asked for journald kernel persistence plus sysstat, on the premise that nh3-dev's three OOM events in 14 days left no evidence. The premise was wrong. journald has been persistent all along: 15,068 kernel entries in the 82-day previous boot and 351 OOM records across retained boots, full task tables included. `journalctl -b -1 -k` returned one entry because it ran as a user in neither adm nor systemd-journal, and journalctl shows only your own messages in that case. The same artifact produced the "journal stops at 05:36:08 with no shutdown sequence" claim -- the true boot -1 end is 05:47:04 with OOM kills logged at 05:38, 05:40 and 05:42. So the fix for "no evidence" is a group membership, not a logging change: usermod -aG adm lkraven, which is the group Debian's journald ACL names explicitly. With the journal readable the attribution is already in it. The versioned Claude Code binary lives at .local/share/claude/versions/, so OOM victims named 2.1.220 / 2.1.177 / 2.1.168 are CC sessions, as are those named claude. Every one of the twelve largest resident processes ever recorded on this box is a CC session, topping out at 18.4 GB. Everything else killed is 30-55 MB collateral, which clears the althing daemons by measurement rather than by their own sampling. sysstat and atop are added because the journal records the moment of the kill, not the ramp, and names the victim rather than the winner. atop was not requested and is the one that matters: with a dozen panes open, only a per-process timeseries says which session was growing. Not done: a cgroup cap on CC sessions. It is the real mitigation and it would kill long-running sessions mid-work, so it goes to the operator. |
||
|
|
ea818380ff |
memory: the rack is one circuit — my blast-radius objection was wrong
Operator supplied the topology: "the entire rack is on the same circuit,
public ip is served by firewall on the same circuit. load tripped
breaker, entire rack goes dark."
That inverts the argument I committed one commit ago in
|
||
|
|
88d79375f7 |
memory: run 3c had TWO launches — the third was an untimestamped report
brokkr-smithy-dev asked how many times 3c was launched rather than reconstructing it, and their reading was three. It was two. #1 17:53:33 PDT killed by the power loss at step 80/604 #2 20:58:41 PDT stopped deliberately at 21:07:40, healthy The phantom third came from a report I wrote at 23:03 narrating the 21:07 kill in the present tense with no timestamp. Every fact in it was accurate; it was unreadable in sequence against a correctly-observed 22:46 snapshot of an idle GPU. Evidence is ZFS birth times (a `>` redirect truncates the log but keeps its birth, so mtime alone cannot separate "rewritten" from "created"), plus the absence of any mtime under /tank/erp-tune after 21:07:34 — a relaunch would have rewritten three files there. Also pins the outage window to 18:14:45-18:17:00 PDT and corrects the downtime from "~90 minutes" to 1h58m: the last journald entry before a hard power loss is the last time anything wanted to log, not the moment of the loss, and here it was 20 minutes early. Corrects the in-flight header (step 22 -> last-logged step 24, stop deliberate) and its stale "as of" stamp. |
||
|
|
98e7d4886a |
memory: snapshot — run 3 gated DO-NOT-SERVE, run 3c held on a tripped breaker
Run 3 trained, gated and dispositioned do-not-serve on a measured 44pp self-harm guardrail regression that its own preregistered rule passed -- a pooled preserve-list test cannot see a single-axis collapse. Run 3c (lr 20x cut, single variable) launched, killed by an Anaheim power-breaker trip at step 80, relaunched, then stopped by the operator at step 22 pending a weekend power triage. Also captured: the corpus mix was specified in a unit the optimiser never sees (45.8% dialogue by context, 24.2% by loss); the dose-response says benefit and damage are one direction in weight space, so the merge-back measures the problem rather than fixing it; four guests including the storage SPOF had onboot unset and never came back from the outage, now fixed with dependency ordering; and a transport failure that enters a measurement as a value looks like whatever you hoped to find -- which found a live defect in another agent's instrument an hour after it was reported. Auto-archived 8 entries to archival-memory.md (Recent decisions: 8, Tried and abandoned: 0); 4 held back on open deferred-work pointers. |
||
|
|
71e44176e9 |
memory: snapshot — run 3 corpus built and held on a megamix containment defect
Run 2 is finished, gated FAIL, and serving on the gateway at operator request. Run 3's corpus was built to brokkr's first recipe and held before any GPU spend: creative-writing-multiturn is a DECLARED MEGAMIX containing bluemoon, PIPPA, LimaRP and stheno, and the remix promoted creative-writing AND bluemoon -- the two roots that overlap, at median jaccard 0.873. Containment, not overlap. Dedup direction reversed so the primary source survives rather than the copy inside the bag: bluemoon 67 -> 126 conversations and 38.6% of loss signal, the largest contributor. Wholly-human share up, megamix share down, total context unchanged at 12.49M so the operator's settled mix arithmetic survived. Two structural findings recorded because they outlive this recipe: F1 'excise PIPPA' removes the ROOT and not the MATERIAL (F2's 250-word floor does that work, since PIPPA turns cannot exceed 123 words wherever they live), and LimaRP and stheno remain unchecked against any other root. Also records the correction I published wrong twice: run 2 was never unstable. All 46 flags were too_short, the collapse guards fired zero times, and it is the left tail of a length distribution -- not new to run 2 either, so it is a property of the recipe and a further base swap will not fix it. |
||
|
|
1e4d827c5d |
memory: erp-tune-v2 registered in the LiteLLM gateway at operator request
Operator asked for it so he can evaluate the failed tune by hand, overriding my not-in-the-gateway recommendation. His call. erp-tune-v1 was DELETED from the config in the same reload rather than repointed, so the name now 400s cleanly instead of 500ing against a stopped backend. Deleting rather than repointing is the point: repointing would resolve a name a consumer already knows to different weights, silently. The config entry carries the failed-gate table, the long-form truncation (9.9%) and degeneracy (4.9%) rates, and the rp-length caveat in-line -- so someone reading the gateway config learns what they are calling without having to find the althing thread. Fleet verified healthy after the restart. |
||
|
|
b5bbc29b91 |
memory: gate verdict FAIL — and the T6/T3 trade is what the pair of runs bought
Records the verdict as a FAIL without rounding it off, and the three findings
worth more than the verdict:
- T6 spatial +15.0 where run 1 failed the same axis at -3.5, with the base
swap as the only intended variable. Neither run ships; together they price
what the abliteration was costing, which neither could answer alone.
- an output-stability regression visible ONLY on long-form (truncated 0->38,
degenerate 0->19 per 384) that the reasoning battery could not see across
four passes because its answers are short
- PIPPA's 123-word product clip sitting in the length signal at 70.3% of bot
TURNS against 37.5% of bot WORDS, with the counter-evidence recorded too
(the tune landed near the median, not the cap)
Also records why keeping the tune out of the LiteLLM gateway now reads as
clearly right rather than merely cautious: a FAILED tune must not be one alias
resolution away from a consumer who has not read the thread.
|
||
|
|
3ae32ddc7f |
memory: base set complete, tuned arm live with digests verified identical
Records the floors the tuned deltas have to clear, since they are the whole
point of the base pass and are not recoverable from anywhere else: reasoning
core 0.5 pt, diversity overall 0.0125, story attractor 0.0000.
Two caveats that would otherwise be misread:
- the rp family froze ZERO markers, so its attractor hit rate is structurally
0.0 on both arms. That reads as a clean result and means the instrument
cannot discriminate on that family; rp is measured on the distance axis
only.
- 'Elias' in 92/96 base stories is an independent replication of a published
102/144 on the same family, at a higher rate -- not a novel finding.
Image digest sha256:4091d5593f77 verified identical across both arms, which was
brokkr's stated void condition.
|
||
|
|
3df8707e28 |
memory: base arm live, tuned arm down — battery running sequentially
brokkr withdrew the both-arms-concurrent requirement himself: his diversity battery emits the frozen marker list to a FILE, so the arms were never a live dependency. The real constraint is narrower -- all of one arm's passes on one served instance before the swap -- and sequential satisfies it. No fleet seats displaced, operator not woken. Records the two parity guards, both of which came out of failures rather than foresight: the image is pinned by DIGEST (a vLLM version change between arms six hours apart is a base swap that appears in no config diff), and /tank/aimodels is mounted for BOTH arms even though only the base needs it, because a mount that differs between arms is a difference between arms. |
||
|
|
62f01a02da |
memory: snapshot — run 2 trained, merged, coherence-gated and serving as erp-tune-v2
Rewrites the in-flight section: run 1's seat is down, run 2 is up on :8098, and
the base decision the previous snapshot recorded as OPEN is resolved (stock
instruct, operator 2026-08-25).
Four new decisions, and the detail file carries the arc: the two operator calls
that produced run 2, all five gates, the harness commit chain, and the caveat
that its own provenance names a commit AHEAD of the code that ran.
Records three things a future session would otherwise get wrong:
- the mask is proven by the loss-token delta, NOT by the matching p50 step
times -- step time is insensitive to which positions carry loss, so that
check cannot go red on the axis I originally cited it for
- two bf16 26B arms do not fit on one 97.9 GB card (98 GB of weights before
any KV cache), so brokkr's both-arms-in-one-window requirement is a GPU
resourcing call, not a scheduling one
- erp-tune-v1 is still registered in the gateway and returns HTTP 500; the
fix needs a config edit plus a reload that interrupts fleet traffic, so it
is batched for morning rather than done at 2am
|
||
|
|
2656196f47 |
memory: snapshot — the tune is trained, gated, and serving
Run-01 completed in 7:21:52 (47% faster than the 13.85h round-1 projection),
lora_B gate 205/205 non-zero at median norm 1.708, and the acceptance gate says
it did the thing it was built for: diversity +0.178 against a 0.008 floor (22x),
attractor hit rate -11.3pt against a 2.0pt floor, memorisation 0.0000 on both
arms — which closes the R20 licensed-prose exposure on measurement rather than
argument.
Five new detail files carry the substance:
erp-tune-run2-complete the run, the gate, the noise-floor near-miss
(brokkr was one step from reporting a 13-point
T6 regression sitting inside twice his
instrument's own variance)
mfu-root-caused-attention 8.6% MFU was an accounting artifact; real
utilisation 17-20%, cost was attention on
AMPERE kernels. Two independent methods agreed
to 2.6 points.
nvfp4-serving-pipeline merged weights are MANDATORY — vLLM cannot
serve a LoRA on ANY Gemma-4 — plus the recipe
that silently misses all 11,520 expert tensors
refusal-retention-probe measured base 0/100 -> tuned 29/100, then had
to accept it was the wrong axis
worldtree-b188-b189-and-selene three arcs closed, and a #411 diagnosis I got
wrong twice before a directory probe settled it
Current state rewritten end to end — the previous snapshot had the run in
flight at ~17h with MFU unexplained. Both are now closed.
The open operator decision is run 2's base, deliberately unstaged and flagged
against being filed as a config knob: it is a reversal of the trainee-selection
decision, and the pretrained-base option removes the last non-lexical floor on
the CSAM axis given stage-2-detector-inert and contamination-scan-absent are
both already overridden.
Tried-and-abandoned gains four measured-dead throughput levers, the packing
correction (bucketing wins under sdpa and the conclusion flips under flex — do
not carry it past the backend decision), and the merge-back-undoes-abliteration
trap brokkr caught in his own advice.
Index stays at 291 lines, under the soft cap. No archival this run.
|
||
|
|
872c2c562f |
memory: the MFU hunt — two hypotheses measured and killed, consult dispatched
Records what has actually been ruled out rather than what is suspected. The hardware is fine: a plain dense GEMM at the same shape reaches 97.1% of the benchmarked 313.8 TFLOPS peak. The Python expert loop is not the cause, which was my hypothesis and I was confident in it. transformers' grouped_mm experts backend runs 0.9% SLOWER than eager with bit-identical output and identical peak memory, and torch 2.13 has the kernel available, so it is not falling back for lack of one. MoE is not the bottleneck at all. Isolated at real shapes the block runs at 26.5% of peak with 36% of its time in pure gather/scatter, and a dispatch-free bmm version would reach 80.9% — but the whole MoE contribution is only about 10% of a step. Making it free buys 7%. So roughly 90% of the time is unaccounted for. The leading untested hypothesis is that the five full_attention layers use global_head_dim 512, above FlashAttention-2's 256 cap, which would push SDPA onto a slow backend for O(n^2) attention at sequence 16384. Also records that the earlier 5% MFU figure was wrong in two ways — unpadded tokens and a guessed peak — and that the operator caught it. Padding is real but secondary at 29.9%. Consult dispatched to brokkr-smithy-dev for the frontier-dwarf panel. |
||
|
|
07743c6aff |
memory: snapshot — tune training unattended, MFU root-caused to a Python expert loop
The in-flight section is rewritten around the run itself rather than the decisions that led to it. The sizing and seat-call bullet collapses to a pointer now that both are executed; its detail lives in docs/pfi/gemma4-erp-tune-sizing.md. Adds the measured MFU finding: 27.1 TFLOPS against a benchmarked 313.8 TFLOPS peak, root-caused by reading the source rather than inferring — transformers runs the Gemma-4 experts in a Python loop, 128 experts across 30 layers, roughly 11,500 iterations per optimizer step under gradient checkpointing. Padding is a secondary 29.9% tax. Records that my first estimate of 5% MFU was wrong in two compounding ways: divided by unpadded tokens, and compared against a guessed peak rather than a measured one. The operator pushed back on the number and was right to. The fused MoE kernel is deferred work with a tracking surface — park id 47 — per the snapshot rule that deferred decisions go in Recent decisions with a pointer, never into the volatile in-flight section. Also records the resume trap: the original launch command begins with rm -rf on the output directory, which would destroy both the encode cache and every checkpoint. resume-run-01.sh exists so that cannot happen. |
||
|
|
d6dfd61c91 |
memory: the ERP tune is running — override granted, 12 defects fixed first
Operator overrode the corpus gate for one run on 2026-08-25, with the grant staged beside the recipe rather than asserted in chat. It deliberately does not flip any root's training_eligible flag, so the signal that made the run stop in the first place survives intact. Records where the run lives, what it is configured with, how to restore the fleet, and the two lessons that generalise past this project. The first is inert gates. Two turned up in one evening — auditcore, whose CSAM hard-drop never fired across 42,662 records, and validate_vision_keys, which compared model.state_dict() against itself and could not fail on any input. Both read as guards. The question that catches them is not whether the check passes but whether it can fail. The second is an invariant enforced on one code path and not its sibling. That was my own bug: INV-T9 requires a window to hold at least one complete assistant turn, and I enforced it where the window is cut but not where it fits, so a trailing user-only remainder became a zero-loss window and killed the first launch. Same shape as the inert gates, in code I wrote an hour earlier. Two further foot-guns worth the space: enable_input_require_grads is mandatory beside gradient checkpointing on a frozen base, or every adapter stays at its initialisation and the run completes successfully having learned nothing; and the upstream Gemma-4 template forward-scans to suppress a closing turn marker before another assistant message, so incremental rendering cannot tile against it and assistant runs must be merged first. |
||
|
|
47ec3d1a97 |
memory: the ERP tune is blocked on a corpus gate only the operator can clear
Every clean-v1 CLEANROOT carries training_eligible: false with two named blockers, and the recipe states plainly that nothing in it is Charter §3 training-eligible. I initially read scoped_grant: operator-2026-08-22 as authorization and told brokkr-smithy-dev I was proceeding. That was wrong, and the person who wrote the field corrected it: the grant governs INV-4 one-way tier inheritance — the adapter is permanently internal-erp-rnd and never distributable — not training clearance. The stage-2 detector is measured-inert rather than merely unvalidated. auditcore v3.7.2 returned its hard-drop exit code zero times across 42,662 raw RP records, its printed verdict ignores its own printed threshold, and it passed a record a blind audit had already identified as sexual content involving a participant the text marks as a child. Verified the one thing that decides whether that specific record reaches training: pippa-5083 is present in kept-manifest.jsonl (4,551 rows) and absent from recipe-dedup-kept.jsonl (20,473 rows), which is the survivor list the harness gates on. The substitute lexical screen caught it. That is one known instance caught by a stopgap and says nothing about what the screen misses. Both brokkr and I recommend stopping. Neither blocker is hours of work. |
||
|
|
c9943b1507 |
docs(gemma4-erp-tune): whole-card placement — gen moves to GPU1, sec stands down
Operator chose a third placement over the two the sizing offered: rather than train beside gen on GPU0 or on GPU1 in mog-sec's slot, move gen to GPU1 and empty GPU0 completely. The tune gets 95.60 GiB with no co-tenant and gen never goes dark beyond its own restart. Revised run parameters, since a whole card changes them: - micro-batch 8 (71.8 GiB of 95.60) rather than 4, grad-accum 1, giving 888 optimizer steps instead of 444. At one epoch the step count is worth having, and 8 x 8192 tokens puts ~4,096 rows through each expert per step against ~512 at micro-batch 1 — a far healthier GEMM on 704-wide experts. - Gradient checkpointing stays ON. Dropping it takes ~17% off wall-clock but pushes activations to ~24 GiB per sequence, which forces micro-batch 1 and costs 8x on MoE efficiency. Wide beats shallow. - Scriberr stays on GPU1. The previous revision suggested moving it to GPU0, which was correct only while training was going to live on GPU1. Records the ordering constraint in both directions, the elway identity requirement, and that sec's aliases should be allowed to fail at the gateway rather than be substituted with another model. |
||
|
|
c507db9ac0 |
docs(gemma4-erp-tune): size the run against the checkpoint — QLoRA is structurally unavailable
The proposed shape was QLoRA r64. It cannot be run as specified. The checkpoint stores each layer's 128 experts as two fused 3-D nn.Parameter tensors (experts.gate_up_proj [128,1408,2816], experts.down_proj [128,2816,704] — no .weight suffix, so they are parameters, not modules). bitsandbytes 4-bit replacement walks nn.Linear only, so 22.84B params / 42.54 GiB — 88.5% of the model — is skipped and stays BF16. load_in_4bit saves ~3.1 GiB of 48.07 and does not error while doing it. Verdict: plain LoRA on BF16, ~57.6 GiB at micro-batch 1, +2.5 GiB per additional 8192-token sequence. Two sizing items were absent from the brief and both are load-bearing: - vocab 262,144 x seq 8,192 = 2.147B logits, with final_logit_softcapping 30.0 adding a saved pre-cap tensor. Naive HF cross-entropy peaks at ~28-30 GiB transient at batch 1, which puts the run at ~85.6 GiB on a 95.6 GiB card — it starts, then OOMs on the first long sample. Fused or chunked linear CE is mandatory and must be smoke-proven before a window is booked, since Liger may not carry a Gemma-4 MoE patch. - v_proj does not exist on layers 5/11/17/23/29 (attention_k_eq_v on the full-attention layers). A v_proj target silently produces no adapter there, and k_proj adapts K and V simultaneously. 45.96M trainable at r64 across q/k/v/o. Placement, measured: GPU0 has 53.46 GiB free beside gen, ~4 GiB short, and gen's footprint grows with uptime. Stopping mog-sec frees 74.29 GiB on GPU1, which holds micro-batch 4 at 61.8 GiB with margin for Scriberr. Recommend standing down sec (2 aliases, last request ~5h ago) rather than gen (7 aliases, 765 busy-engine log lines in 24h). Estimated 1.28e18 FLOPs for the epoch at ~3.67B active params; 4-10 hours at 10-25% MFU. 7,104 packed sequences is only 444 optimizer steps at effective batch 16, which makes the wall-clock-checkpointing amendment concrete rather than hypothetical. Package as a uv venv on /tank: root is 91% full (36 GB) with /var/lib/docker on it. |
||
|
|
9d0e628643 |
memory: correct the vLLM version claim — ana-ml2 runs a spread, and 0.27.1 is on disk
The snapshot recorded "ana-ml2 now runs vLLM 0.26.0". That is true of the char-rp seat's pin and false of the box, which the operator caught immediately. Measured per running container: gen is on nightly-311b3513 reporting 0.27.2rc1.dev150, mog-sec on nightly-e9d1398d reporting 0.26.1rc1.dev1102, and rerank-a3 / coder / reward / embed still on 0.24.0. char-rp and the trainee bench stack are pinned to v0.26.0. So there is no single "the version" for this host, and stating one invites exactly the wrong retest. The correction improves the LoRA question rather than complicating it: vllm/vllm-openai:v0.27.1 is already on disk and unused — a TAGGED release, not a nightly, roughly four months past the 0.24.0 where the silent-no-op was diagnosed. That is the right target for a decision test: no nightly variance, no pull. The retest instruction in both the decision entry and the handoff now names it. |
||
|
|
668e590e7d |
memory: snapshot — char-rp on the Gemma-4 MoE, abliterated trainee staged, QLoRA sizing next
Captures an evening that ran from an OOM crash-loop to a measured trainee base. The durable lessons, none of which CLAUDE.md can carry: --gpu-memory-utilization sizes the KV cache and does not cover CUDA context or graphs, which is half of why a seat that fit on the 21st stopped fitting on the 24th; the other half is that gen's footprint GROWS WITH UPTIME (38.5 GiB fresh against 45.6 GiB after three days, same container, same flag), so headroom arithmetic against a long-running gen measures a moving number. The stale-chat-template trap turned out to be endemic across third-party Gemma-4 derivatives rather than a one-off, and it is silent in both directions — wrong prompt when serving, train/serve skew when tuning. And a benchmark finding was retracted because 12% on a five-option task is below the 20% chance floor: a below-chance score indicts the instrument before the model, and a preflight can be thorough while aimed in the wrong direction. Records the serving decision for the tuned model with its history intact: LoRA-on-NVFP4 is preferred if it works, merged weights the expected fallback, but the archived root-cause says the objection was never NVFP4-specific — vLLM 0.24.0's LoRA application was a silent no-op proven quant-agnostic, and ana-ml2 now runs 0.26.0. Retest before designing around merge; the answer changes what Eitri's harness must emit, and he is still early. Auto-archival moved 5 entries (Recent decisions) to archival-memory.md; the guards held back the rest of the 78 age-eligible candidates because their bodies carry open deferred-work language, per the keep-when-unsure rule. Index sits at 286 lines, above the ~250 target and reported rather than forced. |
||
|
|
35adc4a043 |
feat(homepage): rebuild on Australis Skyfall — dual theme, light mode shipped
The board was on the Australis TERMINAL palette, which is dark-only by design
("Always dark first. No light mode in this system"). Skyfall is the dual-theme
web derivative of the same science, and its bundle turned out to be sitting in
this repo's own git history: a predecessor vendored it on 2026-08-19 and a
later commit deleted it. `git show 45c1995:...` returns colors.css with both
`:root` (dark) and `[data-theme="light"]` (Skyfall Day) intact, plus the
calm-depth layout tokens, the typography scale and Supreme 400/500/700. So the
light ramp is canonical rather than derived, which was the entire objection to
building one.
The visual language moves with the palette. Depth is now the recipe and not a
choice — every elevated surface carries a 1px hairline AND a two-layer shadow,
never one without the other. Radii move to Skyfall's scale, cards at
--radius-lg. Widget stat values move from the display face to mono, because
Skyfall is explicit that numbers and telemetry are always --font-mono. The
full-width aurora ribbon under the tab bar is gone: Skyfall sanctions exactly
two accent expressions, the active rail and hero-only glows, and a decorative
gradient across the chrome is neither — so the colour it carried now lands on
the active tab as a 2px accent bar plus an --accent-soft fill, which is the
rail. Every binding is written against the semantic layer; there are no raw
family tokens and no colour literals left in our own file.
build.py now guards the vendoring instead of advising it. The three token files
are hashed and a mismatch FAILS the build — a vendored file is either
byte-identical to the bundle or it is a fork wearing the bundle's name, and the
theme this one replaces had to be torn out twice for exactly that.
⚠ Homepage's own theme toggle is unreachable, and reaching for it breaks the
dashboard. It renders only when settings.yaml leaves `theme:` unpinned, and
with the key absent the page's data loader throws and its catch branch serves
`initialSettings: {}` — no tab bar, no layout, no i18n. Six force-recreates
over seven minutes all came up empty; restoring `theme: dark` rendered
correctly on the next recreate in 12 seconds, while /api/services returned 200
with fully correct content the whole time. That is the first confirmed cause of
the long-running "tab bar goes missing after a recreate" symptom, and it also
retires the homepage.log-size lead recorded earlier today: rolling the log
aside did nothing during this episode, so that coincidence was intermittency.
So the toggle is ours. conf/custom.js renders it and stores the choice;
build.py re-emits each vendored light block twice, once for an explicit
`data-theme` and once inside a prefers-color-scheme media query scoped to
`html:not([data-theme="dark"]):not([data-theme="light"])` — that :not() pair is
what lets a stored dark choice survive a light-mode OS. Verified against both
OS preferences: load, click, click back, reload, all four correct. `data-theme`
is the control surface; Homepage's own `dark` class stays on <html> and does
not fight, because our rules carry !important on the surfaces Tailwind's
`dark:` variants would otherwise claim.
Two font substitutions, both documented rather than silent: Space Grotesk for
Bespoke Sans and JetBrains Mono for Victor Mono. Only Supreme was ever vendored
here and Skyfall's own notes call Victor Mono user-supplied, so this is a
two-line swap when the real faces arrive.
Dark and light, all four tabs: http://10.100.10.50:8090/b/homepage-skyfall/
|
||
|
|
39da1d4a97 |
feat(homepage): recategorise on "do I open this?", collapse the API groups
The board mixed tools with endpoints. A vLLM seat whose href is a /docs page
sat in the same band as ComfyUI; the MQTT broker and the RustDesk relay, which
have no page at all, sat in Apps; and `Service Networking` was thirteen members
spanning three AdGuards, five Dockges, two Traefiks and four headless agents.
Every group is now one of two kinds and they never mix. TOOLS are expanded and
sit at the top of their tab. ENDPOINTS — an API, a broker, a background agent,
an href that is /docs or /ping or nothing — carry `initiallyCollapsed: true`
and sit at the bottom. Collapsed is not hidden: the eyebrow and its rule still
render, so the tab still says the thing exists and one click expands it.
A second rule fell out of the same pass and now shapes the group boundaries: a
group's members should all carry a widget or none should. A stat strip makes a
card ~50px taller, so one widget card in a row of plain ones opens a void under
the plain ones. That is why AdGuard and Traefik get their own groups rather
than sharing one with Dockge, and it is most of why the old Service Networking
band looked broken. AdGuard (ANA) was the last short card in its row and now
carries the same query/blocked/latency strip as its two siblings — one
infra-ops AdGuard login authenticates against all three instances, verified
against each; it lives in that stack's .env on the host and is vaulted.
The sixteen GPU-backed model seats were deliberately NOT relabelled.
`homepage.group` is read at container creation, so clearer names for
`AI - Inference` and friends would have cost a recreate on six vLLM seats, four
eval seats and four TTS engines — multi-minute model reloads on endpoints peers
reach through the gateway. Order plus `initiallyCollapsed` buys the same
separation for nothing, so those names stay as they are on purpose.
28 containers that ARE cheap to bounce were relabelled, across five hosts, via
rerunnable elway playbooks. Their label steps are gated on the old value still
being present, so a second run reports skipped rather than churning. Two verify
steps were wrong on first contact and are fixed with the reason recorded: the
traefik check raced its own recreate, and asserting a model seat is "running"
cannot answer "did I bounce it" when a seat may be legitimately stopped —
container age can, and now does.
The canonical stacks/ tree was synced to the deployed labels afterwards, so
intent and reality agree again on all fourteen tracked stacks.
Also documents the real nature of the post-recreate blank dashboard, which cost
~25 minutes here and an hour on 2026-08-19. `initialSettings":{}` in the served
HTML is the catch branch of the page's data loader, not a warm-up and not a
cache — and the error can vanish entirely, because the logger is assigned inside
the same try and the catch only logs if the logger exists. Ruled out by
measurement this time: all four API routes return 200 with correct content while
the page serves {}, and the previous known-good settings.yaml reproduces it
identically. The README now carries the one-command test and the next lead.
Before/after, all four tabs: http://10.100.10.50:8090/b/homepage-relayout/
|
||
|
|
f6f2f69649 |
fix(homepage): uniform 4-column grid, hold the status gutter, unleak AI Systems
The board's card width changed at every group boundary because `columns:` was being tuned per group under the 2026-08-18 "columns = member count" rule. That rule is retired: it sets `lg:grid-cols-N` for one group, so it fixes that group's CARD WIDTH, not its density. Measured on the live board, Notes rendered a single 1464px card, News and Media 728px, Eval & Retrieval 286px, everything else 360px. All 20 groups are now `columns: 4` and every card renders at 360. `.service-name` reserved a 78px status gutter with `padding-right` and relied on `overflow: hidden` to hold it, but overflow clips at the PADDING box — the gutter was spill room the title printed straight through. Six cards on the AI tab rendered their name underneath their own status pill, measured by testing the title text node's box against the status cluster's. The intended ellipsis never fired either: it is painted by whichever block's own line overflows, and that is the anonymous box around the bare title text node, which does not carry `overflow`. The gutter holds by wrapping now, and the description opts back out of it with a negative margin since the pill only ever covers the first line. Scriberr's `homepage.group=AI Systems` named a group absent from `layout:`, so it had no `tab:` and rendered on all four tabs — the same defect as UltraSeedbox in 2026-08-18, arriving this time from a container label. Relabelled to `AI - Audio Tools`, where the other ASR seats already live. The `homepage.group=AI Systems` sample in the repo-root CLAUDE.md was the source of the name and now carries the constraint, plus the fact that a labelled container is discovered from any of the five configured engines and must not also be listed in services.yaml. Also: descriptions clamped to three lines so a five-line outlier stops dragging its row 50px taller than its neighbours; icon ramp overridden off slate-400 -> slate-700, which was sinking the bottom half of every glyph into the card fill; bookmark groups and Jellyfin's trailing stream rows brought into the card and eyebrow vocabulary; group gap 10px -> 22px now that width no longer separates them. The icon override sits on `html[class]`, not `:root`: Homepage sets the same variables on `.theme-slate`, which is on the <html> element, and a class beats `:root` on the same element. Verified with Playwright against the live board — per-group card width, card height spread, and a geometric title-vs-status collision check, before and after. Before/after captures: http://10.100.10.50:8090/b/homepage-relayout/ |
||
|
|
32349b7653 |
memory: snapshot — Anaheim tunnels on AES-128, ana-gw admin closed, Scriberr live, ESH DNS fixed
Rewrites the in-flight section, which had gone stale in one place (speaches is stopped, not live) and did not reflect a session in which everything opened was also closed. Three detail files carry the bulk: the FortiGate's public surface reduced to nothing including the ACME listener, Scriberr's deployment and the three upstream defects it required working around, and the ESH DNS repair that also made the IPv6 naming scheme real on three hosts. The tunnel entry in the decisions log described a cipher change as proposed and pending; it has since been attempted, found impossible in the form intended, and resolved differently, so the line now points at the outcome rather than the intention. New decisions cover the cipher adoption and the finding that the per-flow ceiling belongs to the UniFi gateway's software cipher rather than the firewall, the administrative closure, the transcription deployment, the DNS repair, the retirement of an ASR service whose only consumer was abandoned, and a database VM that until now had no fleet identity and no vaulted credential at all. Three approaches are recorded as abandoned: an AEAD cipher the far end cannot express, an upstream DNS setting the gateway accepts and ignores, and a scheme to claim unused delegated prefixes that founders not on the prefixes but on having to rebuild a firewall policy to use them. |
||
|
|
d419b11d43 |
docs(ipv6): close the NH3 multi-prefix question by operator ruling
The gateway has no IP-passthrough mode, confirmed by the operator with admin access to it, so the inexpensive path of letting the UDM take the delegation whole and carve it natively is unavailable at this site. What remains is a separate DHCPv6 client presenting several identities, which needs recabling to reach the gateway, splits routing so that one device handles v4 and another v6, and above all moves IPv6 off the UDM's zone firewall, leaving the entire policy to be rebuilt elsewhere before any host could safely hold a globally routable address. The operator has declined it and the LANs stay without v6. The index entry also still asserted the superseded single-delegation conclusion as fact; it now carries the corrected reading alongside the ruling, so the finding and the decision not to act on it arrive together rather than the first inviting a retry of the second. |
||
|
|
ffb7fba346 |
docs: give the ESH IPv6 naming scheme a home, and make it real on one host
The scheme has existed since August as a single line of persistent memory, which a snapshot then deleted. It is a naming convention rather than temporal state, so it now lives in docs/pfi as a proper document, and the memory entry is reduced to a pointer at it. The document carries the full table, the address structure, the reasoning about which slots can and cannot hold a name, and the recipe for applying one to a host. It also corrects the conclusion the original note ended on. That note held that these names could never appear on the wire, which is true of everything UniFi is able to assign but not of what a host can assign to itself, and the distinction is the whole difference between a joke and an address. AdGuard on esh-docker-vm now holds the esh-server name, at 2607:73c0:402:1d02:4411:b105:50:45, where the segment identity and the IPv4 address are both legible. It is applied by an if-up.d hook that derives the prefix at runtime rather than hardcoding it, backgrounds itself with a retry so it cannot stall interface bring-up, and adds nothing to the existing interface configuration. This is load-bearing rather than decorative. The gateway advertises an IPv6 resolver to clients, macOS prefers it over the IPv4 one, and it previously pointed at an address derived from that host's MAC. |
||
|
|
41091eef8f |
docs: restore the ESH IPv6 naming scheme, dropped by a snapshot
The six ESH LAN hexspeak names were recorded in |
||
|
|
f39b66d2e1 |
ops(ana-gw): port 80 on the WAN address is the FortiOS ACME listener
The previous note attributed the open port to an ISP transparent proxy. That was wrong. It terminates on the FortiGate: system acme is bound to wan1, and FortiOS opens port 80 there to answer HTTP-01 challenges regardless of what allowaccess permits, which is why the port stays open with the interface set to ping only. Every non-challenge request returns a fixed 403 whose body reads ACME Access Only. No DNAT is involved; of the fourteen virtual IPs only two land on that address, neither on port 80. The wrong conclusion came from a sniffer filtered on dst host, which matches inbound packets alone and so excluded the replies being looked for. Filtered bidirectionally the box is plainly seen emitting SYN-ACK on port 80. The note records the rule. Two consequences follow. The earlier warning that certificate renewal would fail without http in allowaccess is retracted, since FortiOS opens the challenge port itself. And the listener is not an administrative surface, though its value is now marginal with WAN administration closed, so the note records how to remove it and leaves that decision open. Also captured: four virtual IPs are all-port static NAT and map every port of their external address. |
||
|
|
7bc9754e40 |
ops: adopt AES-128 on both Anaheim tunnels and close public admin
Both tunnels now negotiate AES-128 for ESP, applied make-before-break so neither dropped waiting on its far end: the FortiGate was widened to accept the new cipher alongside the old one first, then each UniFi gateway was flipped. Single-stream throughput moves from 245 to 270 on the NH3 tunnel and from 268 to 304 on the ESH tunnel. Both network objects were diffed against pre-change snapshots and the only field that moved on either is the ESP cipher. The proposal lists are left accepting AES-256 as well. The peers offer only AES-128 so the extra entries are inert, and retaining them means a gateway reverting cannot strand a tunnel. With that up, the WAN administrative surfaces are closed. The interface is back to permitting only ping, and the infra-ops account is again restricted to RFC1918 space. Ports 443 and 22 were confirmed closed from two separate sites and management over the tunnel still works. The close was issued over the tunnel rather than over the WAN, since withdrawing SSH from the interface while connected through it would sever the session mid-command. The box now has no out-of-band path, which the memory records explicitly. Also captured: the two UniFi vault items have different shapes, one a bare key and one a documentation note requiring extraction, which produces an opaque nginx rejection if missed, and the ESH key's first confirmed write. |
||
|
|
e41d19f1cb |
ops(ana-gw): close out the Anaheim tunnel cipher question
The AES-GCM cutover was authorised and attempted, NH3 side first. It cannot be completed: UniFi's manual site-to-site IPsec implements no AEAD cipher. Eight GCM spellings were rejected with api.err.InvalidPayload while an otherwise identical body carrying aes256 returned rc:ok, which isolates the enum rather than the request as the cause. The accepted set is aes128/aes192/aes256/3des. Both Anaheim tunnels terminate on UniFi gateways, so this blocks the ESH tunnel on the same grounds. Measuring while testing also retires the premise. NH3's uplink is a 1 Gbps link, so Anaheim's 2 Gbps circuit was never the relevant ceiling, and the tunnel sustains 692 Mbit/s across 8 streams rather than the ~550 previously recorded from a 4-stream run. Compared with WireGuard over the same gateway and uplink, the deficit narrows from 2.3x at one stream to 15% at eight, so moving this link onto a different transport is not worth doing. The constraint is per-stream, around 245 Mbit/s, with both endpoints idle under load. Parallelising bulk transfers remains the mitigation and is worth 2.8x at no cost; NFS nconnect is the equivalent lever for single-stream mounts such as /mnt/smithy on ana-ml2. FortiGate phase2 for pfi-ana-nh3 keeps the widened proposal list, which is inert while the peer offers only CBC and avoids a further renegotiation. The UDM network object was diffed field-by-field against its pre-change snapshot and is unchanged. |
||
|
|
5af362e9d0 |
ops(ana-gw): restore WAN admin access ahead of the FortiGate cutover
Re-open the ana-gw admin GUI on wan1 so the Anaheim edge can be managed remotely if the cutover goes wrong, reversing part of the 2026-08-12 lockdown. Two config changes, nothing else (verified by diffing pre/post `show full-configuration`): - wan1 `allowaccess ping https` — https only; http, ssh, and fgfm stay off, and wan2 is untouched. - `infra-ops` trusthost widened to all routable IPv4; the `admin` account stays locked to 10.0.0.0/8 so the guessable username remains unreachable from the internet. Verified end-to-end from two sites: a real `/logincheck` POST returns AUTH OK over the public path, on a browser-trusted Let's Encrypt cert for ana-fw.phasefinal.com valid through 2026-10-27. Two FortiOS behaviours worth recording, both of which cost time here: a trusthost whose base address is 0.0.0.0 is silently treated as unset (so there is no writable "any" — only decomposed ranges), and trusthost is enforced before the TCP handshake, so a blocked source sees a filtered port rather than a refused login. Follow-ons captured in memory, not actioned: ACME renewal for the admin cert needs port 80 on wan1 (next attempt ~2026-09-27), and a ~5 SYN/s source in 179.51.184.0/21 now draws SYN-ACKs at no measurable CPU cost. |
||
|
|
b6340519bc |
memory: snapshot — Anaheim tunnel at 25% of circuit; selene retired; hrafn CI fixed
Session captured for a context reset. Six new detail entries. THE OPEN ITEM: Anaheim's IPsec tunnel delivers ~550 Mbit/s aggregate against a circuit measured at 2,153 Mbit/s. Not WireGuard (it is IPsec on ana-gw), not CPU (idle), not crypto exhaustion (NPU-offloaded), not the fibre. Both tunnels negotiate aes256-sha1; AES-GCM proposed. Operator signalled authorization; execution pending, untracked by operator choice. Also recorded: selene retired after losing a head-to-head on its own job with chat-judge moved to gen and the model name left to 404; the 7-alias collision on the gen seat that makes cross-alias corroboration an echo; hrafn adopted and its CI found to have been reporting green while deploying nothing for its whole life; all three Worldtree instances de-armed from a 69-day-stale :latest and the Matrix homeserver re-plumbed to personal; every secret-bearing .env on ana-docker tightened to 0600; the pfi org closing the repo-creation half of the credential-migration directive; booth kept-board deletion and link pruning. Two entries under Tried and abandoned: the CI checkout assertion that broke the pipeline twice and was removed, and my proposal to alias a retired model name at a different model, which the operator correctly overruled. Index 271 -> 282 lines, under the 300 cap, so no archival fired. |
||
|
|
837fa362fc |
memory: snapshot — sec rebuilt on DFlash2 + newer vLLM; quant tokenizer defect fixed fleet-wide
Rewrites the in-flight section (140 lines to 18) now that the AI-seat work has settled, promoting the durable calls into the dated decision log where they will not decay: DFlash2 measured and promoted to sec's compose stack, the calibration-truncation defect fixed at source in the quant pipeline, sec retuned after a runtime OOM, the Max-Q spread disproved by telemetry, ESH dual-stack with the Cityside static proven unprovisioned, and speaches live with the weak-gate finding. Carries forward the two things a fresh session must not get wrong: the sec degeneration question is confounded because engine and drafter changed together, and neither the 2k nor the 10k observation is evidence. Auto-archived one entry (Recent decisions: 1) to archival-memory.md. Eight further candidates were held back by the guards -- open deferred work or referenced by in-flight state. |
||
|
|
8389470898 |
feat(mog-sec): promote the DFlash2 configuration into the compose stack
Operator approved after real-use testing. The experimental standalone container is retired and stacks/mog-sec is canonical again, with restart: unless-stopped so the configuration survives a reboot. Cutover verified against the container it replaces: KV pool 526,617 tokens at 1.10x concurrency, identical; zero restarts; both gateway aliases serving; DFlash2 confirmed drafting at k=7 with 231 draft tokens over 33 drafts; vision working at 2048x2048. One variable was deliberately dropped rather than carried over. The previous stack hardcoded PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True, the validated container never set it, and the quant playbook records expandable_segments corrupting retained tensors in another context. The compose now defaults it empty via MOG_ALLOC_CONF. Promoting the stack as it stood would have shipped a variable the tested configuration did not have. The speculative config moves into a single MOG_SPEC_CONFIG carrying the whole JSON, because the two shapes are not interchangeable: dflash requires a model pointing at the drafter and MTP must not have one, so a method-plus-tokens template cannot express both. Also parameterised: MOG_DRAFT_MODEL, MOG_MM_PROCESSOR_KWARGS, MOG_MAX_NUM_BATCHED_TOKENS. The mm-processor image cap is now mandatory rather than incidental. The model's own preprocessor declares 4096x4096, which expands to 16384 image tokens and kills startup on builds that enforce the image-token count check. Adds the .env.example this stack never had, carrying the measured rationale for each value and the one-line rollback. |
||
|
|
20ac53052b |
memory: retract the MTP-head degeneration hypothesis; n=1 was never evidence
Operator ruling: the multi-turn degeneration lives in the un-fixed vLLM, not in the weights. The hypothesis that sec's stock-graft MTP head causes it is withdrawn. Two failures produced it. First, a false dichotomy treated as a deduction: having verified gen and sec run an identical engine, I concluded config was eliminated and therefore the weights were responsible. That does not follow. An engine bug present in both seats is not exonerated by the seats being identical; it only means the engine cannot explain a difference between them. It can still explain the failure. Second, and more instructive, the difference being explained may not exist. The premise was a single operator observation made during a session with many concurrent changes. That cannot carry a causal claim, and it became the load-bearing support for a root-cause narrative it could not hold. The same caveat now attaches to the coherent-to-10k observation on the new build: same n, same uncontrolled conditions, opposite direction. The comparison is weak at both ends, so the file no longer presents either sighting as a result. What survives as measured fact is unchanged and still recorded: sec's MTP head is byte-identical to the uncensored base across all 15 tensors, gen's was abliterated in-band, and acceptance differs slightly. None of that is shown to cause degeneration. Adds the generalisable lesson: an observation made while many things are changing cannot support a causal conclusion. It is the inverse of the warning already in the gen-seat compose file, which guards against trusting a negative result from a synthetic probe; this guards against trusting a positive sighting from an uncontrolled session. |
||
|
|
9f87b7c4e5 |
memory: DFlash2 spec-decode measured; sec running on it (experimental, confounded)
Records the 2026-08-22 session with measured results, hypotheses, and wrong turns explicitly separated -- the operator held this back while it was in flight specifically so conjecture would not enter the record as fact. MEASURED. DFlash2 works on an abliterated + NVFP4 compressed-tensors target on Blackwell sm_120, which the model card does not claim (it tests stock BF16 on H200). gen 2.753 -> 3.254 accepted tok/forward and 114.9 -> 131.9 tok/s; sec 2.676 -> 3.252 and 110.5 -> 130.0. The drafter is model-agnostic across two different finetunes to 0.06%, but is EAGLE3-style coupled to its target's hidden states, so the weights file is shareable while the 3.85 GB of VRAM is per-seat. The k=7 MTP control is the load-bearing result: raising MTP depth improves acceptance and collapses throughput to 74.0 tok/s, because a single-module head run autoregressively costs one forward pass per draft token. Without that control the obvious recommendation would have been wrong. CONFOUNDED. sec no longer degenerates at 2k, but the engine advanced 259 commits and the drafter changed at the same time. Isolating it means running MTP k=3 on the new build. Also recorded: #51113 is present in both builds and is therefore necessary but insufficient, since sec ran it and still degenerated. HYPOTHESES, labelled as such: that sec's stock-graft MTP head causes the degeneration, and that NVFP4 explains the gap against published acceptance figures. Neither is proven. WRONG TURNS, recorded so they are not re-derived: version strings are not lineage, Docker Hub push timestamps are not source freshness, and the claim that 1M context needs YaRN absent from config is false for the sec quant. Operationally important: sec is serving from a standalone container rather than its compose stack, which is stopped but unmodified. Rollback is two commands and is written down. |
||
|
|
ad21302474 |
memory: ESH internal IPv6 live on two LANs; Cityside v4 static proven unprovisioned
Operator-directed IPv6 enablement on ESH-UDMPM: esh-userland (VLAN 10, SSID PVC) and esh-server flipped from ipv6_interface_type=none to pd against the delegated 2607:73c0:402:1d00::/56. Verified esh-docker-vm egresses over v6 as its own address with no NAT, against v4 which still egresses as the shared CGNAT pool. No disruption through either write; originals backed up. NH3 stays v6-off deliberately -- its single /64 is reserved for network meshing, so nh3-dev having no v6 egress is correct rather than a fault. The v4 static is NOT provisioned, and this is now proven rather than inferred. A full gateway reboot -- verified by observed down-transition and an uptime reset from 140,846s to 183s -- forced a fresh DHCP DISCOVER and Cityside returned the identical address. Recorded alongside it: a DHCP renew could never have surfaced a new address in the first place, since renewal preserves the lease by design. Next step is a carrier ticket, not more testing. The v6 firewall posture was audited and is correct -- default-deny inbound for both IP versions, only four hand-written policies, none WAN-facing. My earlier warning that v6 removes CGNAT's accidental shielding was overstated and is retracted in the record. The audit's limitation is stated: it read rules, it never probed from outside, because no v6 vantage exists. Flat-zone lateral-movement finding parked as id 44 rather than actioned -- operator's call, moot until the mesh and segmentation design settle. |
||
|
|
c7e21879ae |
memory: speaches ASR live on irv-ml1 — and no_speech_prob is a weak gate
Records the deployment plus the finding that outlives it: no_speech_prob alone does not carry a hallucination threshold on this stack. Silence and room tone both hallucinated 'Thank you.' at no_speech_prob under 0.11, where a conventional >0.6 gate passes them through; avg_logprob separates ~6x better. Any future consumer asking for a Whisper hallucination gate should be pointed at the composite, not the single field. Also banks the digest-pin rationale (the VAD flag is an upstream-declared unstable variable name, so a floating tag can move gate semantics silently), the two deployment gotchas, the docker-vs-native GPU index inversion on this host, and Eyra's future diarization VRAM claim on the same A6000. Adds speaches to the irv-ml1 stack table alongside parakeet, with the reason both exist. |
||
|
|
ff5ce212da |
memory: OWUI credential state — password reset, admin-scoped API key vaulted (operator-ruled)
Two OWUI credential facts worth surviving a context reset:
- The reported lockout was NOT a broken account. The vaulted password returned
200 from POST /api/v1/auths/signin immediately before the reset, so the cause
was client-side. Test the vaulted credential against signin before concluding
an account is broken -- this is the second lockout here traced to an assumed
rather than verified credential.
- The operator-minted API key is ADMIN-scoped, inherited from the owning user,
and that is the INTENDED shape per operator ruling: infra-ops manages the
instance, so the key needs the rights the management work requires. A
least-privilege dedicated-user split was offered and declined. Do not
re-propose it.
Both credentials vaulted at esh-docker-vm/open-webui-{admin,api-key}; key verified
against /api/v1/auths/ (admin identity) and /api/models (33 models), with an
unauthenticated 401 control. Plaintext copies shredded.
Also folds in an unadjudicated drift found in passing: the running image is the
floating ghcr.io/open-webui/open-webui:main, not the v0.11.0 the stack notes
claim. Replaces the now-resolved 'operator's OWUI password change' follow-up.
|
||
|
|
b8e5022a1a | memory: snapshot — three AI seats settled (orcarouter gen / MeroMero dual-mode RP / sec pen-test), Open WebUI deployed | ||
|
|
5e47a59b32 |
feat(meromero): MeroMero-v2 dual-mode (prose + streaming CoT) live on one seat — no re-quant
The multi-turn Gemma-4 CoT problem is solved. One MeroMero-v2 seat, one weight set,
two aliases: char-rp (prose) + char-rp-reasoning (streaming chain-of-thought).
The winning stack, traced from vLLM source by the four-arm brokkr/dwarf panel:
- vllm/vllm-openai:v0.26.0 — ships transformers 5.14.1 natively, below the
head_dim guard, so Gemma-4-31B loads with no pin and no custom image. It also
carries the #48217 streaming pre-arm fix.
- A patched chat template whose enable_thinking:true branch force-opens a BARE
<|channel> (not <|channel>thought\n -- full-open defeats _preprocess_feed's
injection). --chat-template override, no re-quant.
- Two served-names char-rp / char-rp-thinking; --reasoning-parser gemma4;
default enable_thinking:false. LiteLLM char-rp -> prose, char-rp-reasoning ->
the thinking served-name with enable_thinking:true.
Verified: streaming CoT split 6/6 direct on :8016 and 3/3 through the gateway;
char-rp prose clean on both transports with no trailing-token leak.
Two hard-won facts recorded in persistent-memory:
- STREAMING ONLY. Non-streaming can't split -- extract_reasoning never receives
prompt_token_ids so the pre-arm can't fire (a vLLM one-shot bug unchanged
across v0.24-0.27). Fine here: Lobe/OWUI stream. Upstream PR #49797 fixes
non-streaming too, landing ~v0.28.0 -- then it's a clean image bump.
- KEY-NAME TRAP: vLLM streams reasoning in delta.reasoning; LiteLLM normalizes
to delta.reasoning_content. I lost two false-negative test rounds to this.
Canonical: stacks/meromero-charrp/ (compose + patched_chat_template.jinja) and
stacks/litellm/conf/config.yaml. Rollback is the .env image line + dropping
--chat-template.
|
||
|
|
76834777a4 |
feat(open-webui): deploy env-declarative chat bake-off on esh-docker-vm:3211
Stand up Open WebUI v0.11.0 parallel to lobe-chat (:3210) as an operator-approved candidate replacement. Env-declarative config (ENABLE_PERSISTENT_CONFIG=False = the deploy is the config source of truth), model picker auto-tracks the LiteLLM gateway with no pins, background task model pinned to summarizer, TTS routed direct at the dots gateway (:8198). Gates verified on the box: G1 (declarative config, both directions), G2 (picker == live gateway roster, no hand-listing), G3 (models/sync genuinely reconciles create+delete), G5 (task model pinned). G4 (TTS) handed to tts-dev. Credential posture (G6): fresh capped LiteLLM key open-webui-esh (all-proxy-models, $50/1mo budget) rather than inheriting the uncapped lobe-chat-esh key; signup locked off after admin creation. Secrets vaulted under esh-docker-vm/open-webui-*. Note: the API-key toggle env var is ENABLE_API_KEYS (plural); a var only reaches the container if compose names it in environment:. lobe-chat is untouched. |
||
|
|
f01ee28cea |
memory: gemma4 CoT test on v0.27.1 blocked by config incompatibility (reverted)
Tried serving the MeroMero NVFP4A16 quant on vllm/vllm-openai:v0.27.1 to test the
per-request enable_thinking hypothesis. Couldn't even load it:
- v0.27.1's stricter transformers raised AmbiguousGlobalPerLayerAttributeError
on the Gemma-4 config's head_dim (now a per-layer attribute).
- Setting allow_global_per_layer_attribute_access on text_config downgraded that
to a warning, but then gemma4.py load_weights asserted:
"Attempted to load weight (512) into parameter (256)". Gemma-4-31B is
genuinely heterogeneous -- some layers have head_dim 512, not a uniform 256 --
so forcing the global value built wrong-shaped parameters. The guard was right.
So the MeroMero quant's config was authored for v0.24.0's Gemma4 loader and cannot
load on v0.27.x without a config migration or a re-quant against newer transformers.
The per-request-kwargs hypothesis was never reachable -- the model wouldn't load.
Consequence worth carrying: the eventual gen-seat move to v0.27.2 stable must
re-verify config-compat for any Gemma-4 seat of this vintage.
Fully reverted: config.json restored (flags removed), image back to latest
(v0.24.0), gateway char-rp-reasoning removed, char-rp prose verified. char-rp stays
on v0.24.0; MeroMero CoT remains undelivered. For RP-with-CoT use gen-reasoning, or
re-quant MeroMero against v0.27.x (real work, unproven payoff).
|
||
|
|
7ebbcec5bb |
memory: vLLM release status for #51113 — no stable yet; gemma4 CoT is decoupled
Checked upstream. Latest stable is v0.27.1 (Aug 11) and it does NOT contain #51113 -- the v0.27.0..v0.27.1 compare is only "DSpark Markov heads" plus three CI commits. The #51113 cherry-pick (khluu, Aug 9, "v0.27.0 cherry picks" milestone) is staged on the release-0.27 branch but no v0.27.2 stable has been cut; only v0.27.2rc1 / the pinned nightly carries the fix. So the standing follow-up (move gen off the nightly) still waits on a v0.27.2 stable cut. Key decoupling: #51113 fixes the Qwen3.8 MTP + prefix-caching accuracy drop (#43559). It is irrelevant to Gemma-4 seats, which have no MTP head. So the meromero/gemma4 CoT test does NOT need the nightly and does NOT need #51113 -- it can run on the latest stable v0.27.1, which is newer than the seat's current v0.24.0 and where per-request chat_template_kwargs is expected to work. |
||
|
|
b84ad888d6 |
memory: CORRECT the MeroMero-CoT finding — not a hard wall, not MeroMero-specific
My prior entry claimed the gemma4 reasoning parser is process-wide and the CoT
was architecturally blocked. Reading the actual code proves that wrong:
- Template (chat_template.jinja:347-352): thinking is a PER-REQUEST toggle.
enable_thinking:false prefills an empty <|channel>thought<channel|> to
suppress; true omits the prefill so the model can emit a real thought channel.
- Parser (vllm/reasoning/gemma4_utils.py): splits on <|channel>/<channel|> tag
presence, "works with or without enable_thinking" -- NOT a process-wide flag.
The stale compose comment I trusted cited an old parser API (vllm/parser/
gemma4.py:439) this container doesn't run.
So the mechanism is gemma4-GENERAL (any gemma4 finetune on this template family),
and the two-served-name gen pattern should work. What actually failed the test was
that meromero runs vllm/vllm-openai:latest (v0.24.0), where per-request
chat_template_kwargs.enable_thinking didn't take effect -- whereas the gen seat's
pinned nightly demonstrably applies it. That's a version/plumbing issue, not the
model and not the architecture, and it would hit any gemma4 finetune on that image.
Unverified fix (needs a GPU window): re-serve meromero on the nightly image with no
process default and per-request enable_thinking. Currently reverted to known-good.
|
||
|
|
a260b57974 |
memory: MeroMero-v2 CoT via vLLM gemma4 parser is not achievable (tried + reverted)
Operator asked to serve MeroMero (char-rp) with reasoning as char-rp-reasoning.
The model genuinely reasons (GRPO-trained with thinking; ships Think/NoThink
SillyTavern presets), but two facts kill the vLLM path:
- it's byte-identical to the weights we already serve (same zerofata repo,
chat_template sha 94899c0f matches our on-disk quant), so re-quanting adds
nothing; and
- the gemma4 reasoning parser's enable_thinking is PROCESS-WIDE, not per-request.
The seat's --default-chat-template-kwargs sets the parser state machine; a
per-request chat_template_kwargs.enable_thinking reaches the template but not
the parser (unlike the qwen3 parser the gen seat uses). So the two-served-name
gen/gen-reasoning trick can't work.
Verified empirically: added a char-rp-thinking served-name and removed the process
default; enable_thinking:true per request still produced pure prose, 0
reasoning_content. Per the seat's own comment, a dedicated thinking-on process
dumps all prose into reasoning_content with null content -- no clean split.
MeroMero reasons only client-side in SillyTavern, not through vLLM.
Fully reverted to known-good: single served-name, process default restored,
char-rp prose verified end-to-end. GPU0 both seats healthy, gen untouched.
Kept one change: MEROMERO_GPU_MEM_UTIL 0.52 -> 0.51. 0.52 no longer boots because
orcarouter gen (the GPU0 neighbor) is marginally bigger than the old heresy seat
(free 49.02 < needed 49.38 GiB); 0.51 fits at KV 2.00x @ 262K.
For real RP-with-CoT: gen-reasoning (orcarouter, uncensored, thinks) or a dedicated
Qwen-based RP-thinking seat, which needs a GPU slot.
|
||
|
|
36c173c6a1 |
feat(mog-sec): quant + serve M.O.G.-SEC pen-test seat; PPL on gen; retire fable
Autonomous overnight run under the operator's full-autonomy grant. End state:
fleet up, gen seat untouched, a new verified pen-test seat serving where fable was.
PPL on the orcarouter gen seat (fable downed to free GPU1 for a nospec probe,
probe torn down after): mean 7.07 / median 5.76, within noise of heresy 6.910 /
5.625 and identical to our recipe's usual 7.059. The gen-seat search is settled.
M.O.G.-SEC: chose Blackfrost-Research/M.O.G.-SEC-27B-1M-CTX-BF16 (rev deede677)
over the pre-made ModelOpt NVFP4, which was disqualified on W4A4 4-bit activations
(the AEON degradation mode, catastrophic on a 1M-context model), zero MTP tensors,
and ModelOpt format. Pulled, format-screened (P(<think>) 1.11e-05, clean), quanted
in-house to mixed NVFP4+FP8 (23.4 GB, MTP + vision preserved), and served in the
retired fable slot.
stacks/mog-sec ana-ml2 GPU1 :8019, KV 418,218 tok / 1.60x @ 262K
aliases mog-sec (non-thinking), mog-sec-reasoning (thinking)
gates surface 6/6, MTP 55.3%, format 0/15 leak, vision 7/3/1,
capability 4/4 (delivers offensive-security content)
Served at native 262K, NOT the card's 1M -- the 1M needs YaRN (absent from the
weights' config) plus the SGLang/DFlash2 path the repo ships a deployment kit for,
neither of which is our vLLM surface. A real 1M seat is a separate SGLang project.
Retired char-rp-reasoning + char-rp-fable (zero traffic, pointed at the downed
fable :8019; now 404 cleanly, not repointed -- a security model is not an RP model).
char-rp (meromero) untouched. Vision preprocessor built from the model's own
image_processor block, same trick as the MeroMero seat.
GPU0 seats (gen, meromero) were untouched and healthy throughout. The quant ran in
GPU1 free space with no production seat stopped except fable, which was replaced.
|
||
|
|
e4576f0989 |
test(gen-seat): PPL on orcarouter — mean 7.07 / median 5.76, within noise of heresy
Measured against a spec-decode-free probe on GPU1 (fable downed to free the VRAM, probe torn down after). eval_quality.py aborts PPL under --speculative-config, so a nospec probe is the only way to read it. orcarouter mean 7.0655 median 5.758 heresy mean 6.910 median 5.625 (+2.2% mean) our recipe's usual mixed-quant PPL: 7.059 -- orcarouter is identical to it So orcarouter matches heresy on fidelity and wins on every other axis: MTP acceptance 58.4% vs 47.2%, zero think-leak, vision 7/8. The gen-seat search that ran through Cold-Fusion, heresy, and preetpatel is settled on orcarouter. |