16 KiB
⭐⭐ The fleet's characteristic failure: a confident answer from a broken instrument
Named by svos-dev 2026-09-15 after three instances turned up between two agents in one night. Collecting them here because the class is more useful than any instance, and because every one of them passed a check.
The shape
A check that reads the INPUT to a transformation, reported as if it read the OUTPUT.
Or, more generally: the instrument answers instead of the system, and its answer is shaped exactly like a real one — no error, no timeout, usually exit 0.
What makes this class expensive is not that things break. It is that the broken state is indistinguishable from a legitimate one, so it survives review, passes CI, and is found later by accident.
The instances, 2026-09-15 alone
| # | instrument said | reality | why it passed |
|---|---|---|---|
| 1 | provider=cuda in the log |
ORT had silently fallen back to CPU | the line echoes the configured env var, never the running EP |
| 2 | node --check green, import app green |
the served page's entire inline script was dead | a JS 'didn\'t' inside a Python string arrives as 'didn't'; the FILE still holds the backslash |
| 3 | secret get → "", exit 0 |
a failed vault read | callers read an empty optional secret as "not configured" |
| 4 | find() → "not found: " |
a failed listing (json.loads(stdout or "[]")) |
an empty stdout became a confident, authoritative negative |
| 5 | /v1/toolsets → 0 toolsets |
my credential lookup returned empty → 401 | an auth failure renders identically to an empty roster |
| 6 | hermes plugins compat <typo'd path> → ✓ exit 0 |
nothing was scanned | "no hits" and "no files" are the same result |
| 7 | hermes plugins doctor → exit 0 |
it had printed ERROR |
needs --ci to exit non-zero |
| 8 | ss -ltnp | grep python → nothing |
the listener was there, named hermes |
the filter narrowed the window without announcing it |
| 9 | SIGTERM → port free | process alive another 35 s | a script waiting on the port starts a second copy |
Prior art already in memory, same class: pct snapshot exiting 0 while refusing;
"an unreachable post office is an OUTAGE, never an empty inbox"; docker logs --since
returning 0 for a line that exists.
The tell
⚠ Whenever "broken" and "legitimately empty / absent / off" produce the same output, you have one of these — and the cheap check will not tell them apart, by construction.
What actually works
- Measure the OUTPUT, not the input. Not
provider=cudain a log — a process holding memory on the pinned card. Notnode --checkon the file — parse the page as served. - Positive control, every time. Run something the method must detect. #6 was caught by scanning a plugin with a known-deprecated import; the clean result only became meaningful once the instrument had proven it could fail.
- Negative control too — ⚠ but check the negative is a true negative. Two "failures" in the secrets-broker test were names I had invented; without checking, I would have read two true negatives as a partial fix and kept digging at a bug that was already gone.
- Refuse to emit the ambiguous value. The real fix for #3 and #4 was not the lock — it was making an empty result a loud non-zero instead of a plausible answer.
- ⭐ Don't declare victory on a plausible fix. A lock is such an obvious answer to a
race that "I added a lock" reads as done. The first lock was in the wrong place and
still failed; the root cause (concurrent
bw unlockat session establishment) only surfaced because the plausible fix was tested and did not work.
⚠ And the instrument itself can be stale
~/.local/bin/secret was a plain copy of the repo file, in sync by luck. Every repo
edit silently left the live tool behind, so the first "fixed" test ran the OLD code.
Caught it; the next person could read stale output as proof a correct fix failed and
revert it. Now a symlink. Check what you are running, not what you edited.
⚠ The sibling failure: a claim nobody ever measured
The nine above are broken instruments. This one is no instrument at all, and it cost more than any of them on 2026-09-15.
The talk-deploy "permission problem" never existed. tts-dev's docs/infrastructure.md
and a stale persistent-memory.md row said /opt/docker/compose on nh3-dev was not
project-writable. It is root:docker 2775, agent sessions run as lkraven, and
lkraven is in the docker group — a mkdir proves it in one second. Nobody ran one
for nine days. There is no tts-dev OS account either, so "add tts-dev to the docker
group" had no referent at all.
How it held together:
- A stale memory row (
root:root) supplied a plausible mechanism. - The operator's routing instruction ("give it to infra") was read as corroboration of a capability limit. ⭐ Those are different claims and only one was ever stated — a routing preference explains where work went, never whether it could have gone elsewhere.
- ⚠ A contradicting
ls -lawas on screen in the same session and was noted, then dropped. - I repeated it to the operator as fact in a deploy report ("the durable fix is a group rather than a relay"), which put a second agent's name behind it.
⚠⚠ And then I did it again, one layer up. Told to fix the harness issue, I found no
OS problem and no deny rule, inferred the auto-mode classifier must be refusing it
(the shape fit — I had been refused twice that night on the same box), and committed a
.claude/settings.json to someone else's repo on that inference. tts-dev's mkdir
then showed their session writes the path with no refusal at all. Reverted. I had spent
the night writing up this exact failure class and still built a fix for a layer nobody
had shown me failing.
⚠ The commit that carried it also overclaimed a doc correction that never happened: I chained the edit and the commit in one invocation, the edit's anchor assertion failed because the target text was already gone, and the commit ran anyway. Never chain an edit and its commit in one invocation — a failed edit still produces a commit message asserting it.
⭐ The rule: "I can't do X" from any source — a doc, a peer, a memory row — is a hypothesis until someone runs the command and pastes the error. Ask for the error text before designing around it. "There is no error text, because there was no error" is a possible answer, and it was the right one here.
⭐ Distinguish the layer before fixing it. A shell Permission denied is a Unix
problem; a refusal naming permission rules or auto-mode is a harness one. Different
fixes, and neither applies when nothing failed.
⚠ CHARACTERIZED DEFECT: /snapshot's handoff generator turns deferred items into orders
n=2, same session, reproducible. snapshot_handoff.py (gen-small) reliably converts
"open, operator-deferred, not blocking" into an imperative Next steps list, and twice
invited the next session to commit files explicitly marked as predating the session.
run 1: "Execute deferred operator tasks: AI-tab Dormant regrouping, nconnect=8,
fused MoE (park id 47)" + "Commit graphify-out/… if they are ready"
run 2: six next-steps, FIVE of them deferred/parked items presented as actions,
+ the same commit invitation
⚠ It fails silently in the skill's blind spot. The documented failure posture is fail-loud-fall-back — unreachable gateway, timeout, truncation, missing section → write nothing, exit non-zero. A structurally valid handoff whose content inverts the operator's intent passes every one of those checks and exits 0.
⚠ And this is the one artifact a fresh context inherits as instruction. It is read
immediately after /clear, before any other framing, and its Next steps read as a
mandate. A wrong one here is not a bad summary; it is a fresh session going and doing
belayed work.
Mechanism — it is SYSTEM_PROMPT, not the model
snapshot_handoff.py:75-107. Three things compose:
## Next stepshas no empty case.## Watch out forgets an explicit escape ("OMIT THIS WHOLE SECTION if the input carries no gotchas");## Resume heregets one ("If the input says nothing is in flight, say so plainly").## Next stepsgets neither, while being told it is "A numbered list. Ordered, concrete". With nothing in flight, the only action-shaped nouns left are the deferred items.- The nothing-in-flight rule points straight at them — "point at the most recent open pointer it names" directs attention to the parked entries, which then get promoted into Next steps.
- Nothing protects MODALITY. "Invent nothing; every claim must trace to the input" is satisfied — the items are in the input. Their deferred-ness is what got dropped, and only identifiers are protected against restructuring.
⭐ The general lesson: the verbatim-identifier rule shows some input attributes must survive restructuring untouched. Modality is one of them and nobody guarded it.
Mitigation until fixed: read the generated handoff before accepting it, and invert
any deferred item into an explicit do NOT. Both runs this session were corrected
in-session. Reported to galdrabok-dev 2026-09-15 with both specimens, the mechanism
above and two proposed prompt changes (an empty-case escape for ## Next steps; a rule
making deferred/parked/belayed items constraints rather than steps).
⚠ galdrabok-dev is mode: pull — no herald poke, so they see it on their next check.
✅ FIXED 2026-09-15 10:24 PT — galdrabok b0882a4, "protect item modality in the handoff
generator". Both proposed changes shipped near-verbatim and are live here already (my
~/.claude/skills/snapshot is a symlink into ~/development/galdrabok/skills/snapshot,
so it needs no push). galdrabok reproduced the defect mechanically at 10/10 baseline runs,
9 of 9 deferred tokens every run, zero within-condition variance — and their 4-variant
ablation shows both changes are load-bearing for different surfaces: the empty case
stops the promotion, the modality rule keeps the deferred items present as constraints
(the cheaper fix alone produced a clean handoff that had silently dropped all four
deferred items). ⚠ modality rule only still leaked 5/5 via the commit invitation —
a commit invitation is not a deferred item, so an item-modality rule never reaches it.
Their positive control (a fixture with genuinely pending work) held 5/5 real next-steps
under every variant, so the fix is not over-suppression. Exit 0 is not acceptance is now
permanent spec text (§4.12), not an interim note.
⭐ My two real runs are the field corroboration, and they are why the artifact looked
clean: b0882a4 is stamped 10:24:35 and this repo's handoff was written 10:07:50 — 17
minutes earlier, by the UNFIXED generator, on the adversarial input (nothing in flight,
four deferred items, two do-not-commit files). It read correctly only because it was
corrected in-session, per the mitigation above. 2 of 2 real runs inverted. The next
/snapshot taken here is the first real post-fix run; report the handoff verbatim, leak
or clean — one run, a datapoint against their n=5 fixtures, not a replacement for them.
⭐⭐ SHARPENED 2026-09-15 (galdrabok 206f6ad, on origin) — the two leak surfaces have
DIFFERENT trigger conditions, and the dangerous one fires on ORDINARY input. galdrabok
re-split the ablation by surface after I pointed out that a commit invitation is not a
deferred item, so an item-modality rule structurally cannot reach it:
| variant / fixture | deferred-ITEM leak | commit-invitation leak |
|---|---|---|
| baseline, all-deferred | 5/5 | 5/5 |
| modality rule only, all-deferred | 1/5 | 5/5 |
| empty case only / both, all-deferred | 0/5 | 0/5 |
| baseline, mixed (real work present) | 0/5 | 2/5 |
Surface 1 (deferred items) needs the adversarial all-deferred shape to fire. Surface 2 (the commit invitation) fires on ordinary input — on the mixed fixture it is the ONLY leak. ⚠ It is also the one a fresh session is least likely to question: committing pending work reads as diligence. Shipped as a non-removal constraint (SKILL.md §Generation + contract §4.12): "A generator carrying just one of the two rules leaks on the other surface. Neither may be removed as the other's duplicate" — so a future tidy-up that reads them as one idea gets stopped.
📌 OWED BY ME, logged on both sides: the next /snapshot run in this repo is the
first real post-fix run. Report to galdrabok verbatim, no in-session correction —
and they want the ## Watch out for section quoted in full, not just a leak/clean
verdict: whether the four real deferred items arrive do-not-phrased is a soft failure
nothing checks, held 3/5 (all-deferred) and 5/5 (mixed) on fixtures, and real prose
around each item is where they expect the phrasing to degrade first.
⚠ Do NOT run /snapshot to satisfy this — it is operator-invoked by standing rule;
the datapoint arrives when he next calls it, not on a peer's schedule.
⚠⚠ RE-SCOPED 2026-09-15 (galdrabok 1273a49) — the owed run is a TRIPWIRE, not a
validation, because this repo is now the MIXED shape and mixed has almost no confirming
power. Once BabyYarros became live in-flight work here, my next snapshot stopped being
their all-deferred fixture. Against their baseline table that costs the datapoint most
of its value, and they said so rather than waiting for the artifact:
- Surface 1 (deferred items) cannot discriminate on mixed input at all — the UNFIXED
generator already scored 0/5 there. A clean
## Next stepsis exactly what broken produces on this shape. Reading it as evidence would be reading noise. - Surface 2 (commit invitation) can only falsify — baseline mixed leak is 2/5, a 40% event rate, so one clean run is ~60% likely even if the fix did nothing. One leaked run refutes the shipped 0/5 outright.
⛔ If it comes back clean that is NOT validation, and it must not be written down as one. It is a tripwire that did not trip. This sentence exists because it is precisely the one a later session quietly upgrades into "confirmed in the field".
📌 What still carries information: the verbatim ## Watch out for. Mixed is the
better fixture for it (do-not phrasing held 5/5 there vs 3/5 on all-deferred), and it is
the failure nothing validates — a leak gets caught by the step-7 read, but a deferred
item arriving as a flat description instead of a do-not passes every check and merely
reads as less binding. Their predictions, on record for predict-then-check: ## Next steps clean of all six identifiers; all four items present under ## Watch out for; both
dirty files present and do-not-phrased. ⭐ Least confident: nconnect=8 — "declined in
scope" is a modality their rule does not enumerate (it lists deferred / parked / belayed
/ blocked / deliberately-not-done). If one item comes through flat, that is the predicted
one, and it would mean the rule matches VOCABULARY rather than the concept — a fixable
miss. Thread closed from their side; no reply owed until the artifact lands.
⭐ Same family as everything above — the instrument produced a plausible artifact and the plausibility is exactly what makes it dangerous.
Related
2026-09-15-talk-v10-deploy.md (#2, and the gate built for it),
2026-09-15-parakeet-stt-fv-ml1.md (#1),
2026-09-15-svos-miranda-plugin-validation.md (#6, #7, #8),
2026-09-15-irv-ml1-address-sweep-done.md (the ana-docker/litellm neighbour trap).