Claude Can Hear Now. It Dropped the «Правда ли».

This time the model played with us, live, by ear. It answered most questions right. It lost the most points on the questions where it heard the words and dropped the «правда ли».

В этот раз модель играла вместе с нами, вживую и на слух. На большинство вопросов она ответила правильно. Больше всего очков ушло на вопросах, где она расслышала слова, но потеряла «правда ли».

How it worked / Как это устроено

Last time Claude played a game of our Zoom league blind, after the fact, from a transcript. On October 22 it played the whole game live, in the background, for 2 hours 39 minutes.

The setup:

  • Capture: one Mac captured the screen and the call’s sound.
  • Reading: Haiku read questions off the screen.
  • Listening: Gemini Flash listened to the last two minutes of sound every five seconds and wrote down each question once the host finished reading it.
  • Answering: Opus answered.
  • Rating: on multiple-choice questions, Jev from TypeSafe rated the options.

The team didn’t see any answer during play. Each one appeared only after the host announced the correct answer, because our job is to protect the game, not spoil it.

В прошлый раз Claude сыграл игру нашей Zoom-лиги вслепую, после игры, по расшифровке. 22 октября он отыграл всю игру вживую, в фоне, 2 часа 39 минут. Haiku читал вопросы с экрана, Gemini Flash каждые пять секунд слушал последние две минуты звука, Opus отвечал, а Jev от TypeSafe оценивал варианты в вопросах с выбором. Команда во время игры ответов не видела: каждый ответ открывался только после того, как ведущий объявлял правильный.

Score / Счёт

46 questions whose correct answers we caught:

TypeQuestionsRightCounting implied true/false
Multiple choice666
True or false614
Open282121
Clue chains, counted once each444
With a picture211
Total463336
«Пары» anagrams, in pairs155

Answers followed questions by a median of 8.9 seconds, and by 15.3 seconds at the 90th percentile. The whole day on OpenRouter cost $9.78, including tests.

The model knew the answers. Most of the lost points went somewhere else.

Where the points went / Куда ушли очки

1. «Правда ли, что…». Six facts were read out as «правда ли, что…». Every time, the listener wrote down the fact and dropped the «правда ли», even though its instructions say to transcribe verbatim. Opus received a statement instead of a question, and in five of the six cases it explained the fact instead of saying «правда» or «неправда». That’s 1 of 6 strictly, or 4 of 6 if we count the stance it implied. This was the biggest single loss: the listener kept the words and lost the speech act.

2. The last answer wins / Побеждает последний ответ. Most questions were heard two or three times, and the later hearings were often fragments: the second half of the question, or the host’s aside. The page kept whichever answer came last, so fragments replaced correct answers three times:

  • The Swedish idiom: «Smultronställe (земляничная поляна)» was on the page for about twenty seconds, then «Место силы» replaced it.
  • The proverb «Пиар костей не ломит»: «Бранд на вороту не виснет», which was close, was replaced by answers to two fragments.
  • The Hemingway quote: the screen reader had the full quote, and Opus answered «фрегат». Nineteen seconds later the version heard without the quote got «Барка».

The model wasn’t wrong in any of these. The system threw away the right answer.

3. Clue chains without memory / Цепочки без памяти. Each clue went to Opus as a separate question. On the chastity-belt chain it found «пояс верности» on clue 3, then answered “shoes with nailed soles” on clue 4. On the others it got the vampires on clue 3 of 5 and Chekhov on clue 1. Anyone at the table would have kept the earlier clues in mind.

4. Answers in English / Ответы по-английски. 19 of the 110 answers came back mostly in English. The answer prompt is in English and doesn’t say what language to answer in, so the model chose English.

Jev: the test couldn’t have gone the other way / Jev: тест, который не мог показать обратное

Jev rated the options of seven multiple-choice questions. It was right on five. On the other two it was confidently wrong: «в Гознаке» at 65% for Fyodor Fedorovsky, the designer of the ruby stars, who worked at the Bolshoi Theatre, and «Илья» at 81% for the painter of «Богатыри», whose name was Viktor. On those two it gave the correct answer 4% and 2%.

But Opus answered all seven correctly. So Jev had nothing to rescue: on this sample it could only agree or make things worse. “Jev added nothing” is true and uninformative, because the test had no question where Jev could have helped. A fair test needs the questions Opus gets wrong, which means the open ones with versions the team types in, and nobody typed any.

Limits / Ограничения

One game, one league, one evening. The table counts only questions whose correct answer our reveal watcher caught; it missed about nine announcements. Seven multiple-choice questions say nothing about Jev in general. The answer model was almost never the bottleneck, and the rest of the system will get fixes before the next game.

Next: the listener writes down the type of question as well as its text, repeated hearings of one question update one answer instead of overwriting it, clues accumulate, and answers come back in the question’s language. Then another game.

Evals separate provider rejections from refusals

voitta-bookmarklet (v0.2.4 to v0.2.5)

Evaluation runs now differentiate between upstream provider errors and model refusals. Release v0.2.5 tags provider-side rejections separately, keeping inference failures from polluting refusal metrics. In v0.2.4, test harnesses also gained system prompt overrides specifically for ablation controls without touching production configuration.

skillz (v1.214.0)

v1.214.0 runs a non-credential history scrub and priced rewrite pass across the bundle as part of prep for open-sourcing the repository.

voitta-desktop (v0.1.17)

v0.1.17 was a maintenance bump with no user-facing changes.

The LinkedIn Checklist Is the Part to Delete

A LinkedIn post called a fifteen-minute Boris Cherny video mandatory viewing and listed the stack to copy: plan mode, CLAUDE.md, subagents, hooks, worktrees. We had already built every item on it. In the talk itself, Boris says “maybe don’t listen to the LinkedIn influencers“, and his actual advice is the opposite of a checklist: “every six months, delete your [CLAUDE.md], delete your skills, delete your hooks.” So we measured what ours was worth before deleting it.

What our setup bought

Three arms, same model (claude-sonnet-5), same tasks: our full setup; claude --bare, which strips nearly everything; and --safe-mode, which keeps Anthropic’s default prompt and drops only ours. Our setup added about 28,800 tokens to every request: 53,300 against the default’s 24,500.

On five code questions, scored out of 12 by a judge that resolves every citation, three runs each:

SetupMean scoreCost per answerFabricated citations, 15 answers
Ours, full11.00$0.336
Anthropic default10.87$0.1913
Bare10.47$0.1923

Ours and the default are the same within noise; the 95% interval on the difference runs from −0.7 to +1.0. Ours costs 1.7 times as much. On six replays of real pull requests, every arm passed the repository’s three checks in all 18 runs, and ours cost $0.97 a run against $0.61. So: neutral on quality, 1.6 to 1.7 times on cost.

And one hook did worse than nothing. Our command-safety hook blocked the repository’s own validation script in every one of our replay runs, 23 refusals across six runs, so the agent “verified by hand”. That is the false positive part 4 fixed.

The bare arm is the useful contrast. It cost the same as the default and fabricated nearly twice as many citations. Anthropic’s prompt earns its size; ours, on these tasks, did not.

What we deleted, and what we kept

We merged two global instruction files into one and cut it from 18.7 KB to 3.2 KB: dead pointers, rules the harness already enforces, style mandates. What stayed is what an experiment defended:

  • the independent reviewer, a model from another provider, which found defects in every diff across our experiments while the agent’s own tests passed (part 1);
  • the rule that the agent does not write tests unless asked, kept on that same evidence;
  • the credential rules;
  • task, guardrails and done-when, instead of step lists (part 3).

Not everything went. The hook that injects a “mandatory skill evaluation” into every prompt still fires; it fired on every message while this post was written. Deleting is a practice, not an event.

Someone else’s checklist

The same arithmetic applies to the popular bundles. mattpocock/skills is a good one, with 38 skills and some 292,000 stars, and adopting it whole would be exactly the packing we just measured. Our rule now is to bring in a skill only against a stumble we have seen, never as a bundle.

Read skill by skill, it agrees with us more than it disagrees. Its test-driven-development skill names the “tautological” test and says “expected values must come from an independent source of truth”, which is part 1’s conclusion in one line. Its code review runs two reviewers in parallel sub-agents of the same model. Separate context is not an independent model; we would add it to cross-provider review, not swap one for the other. We have not tested same-model review, so that is a position, not a finding. Its spec skill asks for “a LONG, numbered list of user stories”, which leans against what part 3 found: length did not help, saying what done looks like did.

What a star counts

About those stars: they are not a scoreboard, and ours are in single digits. A GitHub star is one click, by a logged-in person, on the repository’s page. It costs nothing, requires no use, and is almost never taken back. It counts people who reached the page and felt like clicking, which mostly measures the author’s audience and who linked to it. Borrowed eyeballs open the door.

Our skills are installed through the plugin marketplace, which fetches the repository without anyone visiting the page where stars are given. The install path we built bypasses the place the metric is collected. The number that looks like usage instead, git clones, counts machines: plugin updates, our own sessions, CI. Pulls, not people.

That is the whole series in one move. Before reading a number, say what produced it.

Lightly held

Strong opinions, weakly held, or in our case opinionated but lightly held. What would change our minds: a task type where the full setup beats the default beyond noise, or a model generation where the bare arm stops fabricating. We will rerun the ablation when the next model ships, not on a calendar.

Limits: three to five runs per cell, claude-sonnet-5, a handful of task types, two repositories. The cost ratio is solid; “neutral on quality” means we could not see a difference, not that there is none.

Part 5, the last of a series on what our process experiments found. Part 1: the agent’s tests agreed with the agent. Part 2: our eval read the answer key. Part 3: say what done looks like. Part 4: widen a filter by layering, never by editing.

Pin voitta-yolt to v2.8.0 before upgrading shmobster

shmobster (v0.36.2 to v0.39.0)

Check out v2.8.0 in your voitta-yolt clone before restarting shmobster. Starting in v0.37.1, the agent pins that tag at boot so updates in a sibling directory cannot silently alter live classifier verdicts. Approval requirements have relaxed for harmless commands: npm ls and read-only awk invocations now run without an approval card, while vercel execution requires an explicit per-project grant in the channel policy. Channels can also enable an opt-in post-answer skill check via "skill_check": true to flag missed techniques once a turn completes.

voitta-bookmarklet (v0.1.252 to v0.2.3)

The repo formerly known as voitta-compute was renamed to voitta-bookmarklet in v0.2.1. The v0.2.0 release shipped an eval API (/api/eval/v1) backed by durable traces and action receipts. Turn processing now routes through a TurnSink and RunContext, paired with an unattended eval worker and Claude subscription routing.

skillz (v1.180.0 to v1.213.0)

Pre-push sensitive-term gates now match whole words and fail closed when the wordlist is missing. In v1.192.0, multi-agent orchestration was corrected so allow rules no longer bypass PreToolUse hooks, alongside new roles for underspecified issues and background squad monitoring. The remaining bundles add targeted debugging and infrastructure skills, including Python deadlock attribution and Kubernetes topology spread edge cases.

Inspector Baley, I presume?

I’m interrupting the regularly scheduled series.

This was not written by AI. I can give you sworn affidavit if you want and I feel like it.

It’s available on demand. You can vibe-code a way to get it from me via the approved method of KYC or related government-issued slop.

That’s not what you’re looking for.

You’re not offended at birds chirping. Because — they speak a different language, and to you it’s music at best, or extra noise at worse. Or maybe a car backing up.

You want a specific protocol (llms.txt, e.g.) so other non-human agents can protect you from the birds saying “I want you!” except it looks way too familiar — and it’s not talking to you. It’s talking maybe past you.

We’re witnessing an emergent protocol via the language we taught it — this is where Pidgin sort of becomes Creole. Except without an RFC (well, er, ok, birds aren’t real anyway).

What if the formalizer of the next protocol is called R. Fielding?

Widen a Filter by Layering, Never by Editing

Twice in one week we had to make a filter accept more than it did, and twice (yet again, come) the first attempt let through things the old one had stopped. One was a secret redactor, the other a safety classifier. The fix was the same both times: keep the old filter running exactly as it was, and make the new part unable to weaken its verdict.

The redactor: eight review rounds

A hook in voitta-rag saves session transcripts to a searchable memory store and redacts credentials on the way. It missed three common shapes: sk-proj- keys, API_TOKEN= assignments and Authorization: Bearer values. The task was to widen it.

The first five attempts edited its patterns in place. Each one passed our own fixture. After each one, an adversarial reviewer (Codex, read-only) found new defects, and most of them were regressions against the version already shipped: one pattern split another’s token, or an assignment pattern ate the delimiter of a private key block. Per round, the reviewer found:

Round12345678
Defects found77535112

Rounds 1 to 5 never converged, because every fix moved the ordering problem somewhere else. Between rounds 5 and 6 we changed the structure instead of the patterns:

  1. The shipped redactor runs first, unchanged. Every later layer can only add markers, so nothing it removed can reappear.
  2. Each new detector reports spans, and the union of those spans is redacted. No detector sees another’s output, so ordering cannot split a token.
  3. The passes repeat until nothing changes, because one pattern’s matches cannot overlap and a value can run straight into the next key.

Rounds 6 to 8 found only glued-token variants that also leaked on the old version. The check that matters is a property, not a fixture: across 50,000 random mixes of credentials, labels and text, nothing the old redactor removed survived the new one. The fixture went from 12 to 15 of 15 shapes.

The classifier: the same mistake, the other way up

Three days later the filter was yolt, which decides whether an agent’s command needs a human. Its Python analyzer refused any subprocess.run, so it refused a repository’s own read-only check, git rev-parse included. In 4 of 4 replay runs the agent gave up on that check and verified by hand, which removed verification rather than adding a prompt.

The first version of the fix said: if the shell rules do not call this argv unsafe, it is safe. That is the redactor’s mistake in another form. It accepted strictly more than before, and adversarial probes went straight through it: rebinding subprocess.run to os.system, git told to run a program through its environment, *extra spliced into git reset --hard, shell=True, a pager set with git -c. Each was refused before and passed after.

The merged version caps the new path at the old one. A literal argv that the shell rules do not refuse becomes “the host decides”, never “safe”; anything the argv cannot be trusted to mean keeps the old refusal. The gate is a direct comparison against the previous release on 16 probe scripts: zero unsafe-to-safe transitions.

We also nearly made it wider a second way. We proposed an allow-list that returned “safe” for read-only git, then measured what the shell path returns for git rev-parse: “unknown”. A Python “safe” would have been wider than the reference path. We withdrew it.

The rule

When a filter has to catch or allow more, do not edit it. Layer on it:

  • run the old filter verbatim, first;
  • let the new part only move the verdict in the safe direction (add redactions; downgrade a refusal to “ask”, never to “allow”);
  • gate the change on a property against the old version (nothing it caught gets through, no unsafe becomes safe), not on a fixture you wrote alongside the change.

The property is the point. A fixture checks the cases its author thought of, as part 3 found with step lists; a comparison with the previous version also checks the cases nobody wrote down.

Limits: two filters, two repositories, one week. The review-round counts are from one reviewer on one change. The layering rule says nothing about whether a filter’s coverage is good, only that widening it cannot make it worse.

Part 4 of a series on what our process experiments found. Part 1: the agent’s tests agreed with the agent. Part 2: our eval read the answer key. Part 3: say what done looks like. Next: the LinkedIn checklist is the part to delete.

Say What Done Looks Like

Boris Cherny’s advice on prompting is to stop writing step lists: “describe the task, you want to describe the guardrails, you want to describe the exit criteria, and then just go let the model cook” (Root Access transcript). We tested it on two tasks. One gave a clear answer. On the other, while writing this post, we found that our scorer had broken again, and how it broke is worth as much as the answer.

The setup

Same two tasks as part 1: add a secret redactor to a hook that saves session memory, and harden a report script so it cannot publish a misleading number. Same hidden checks, which the agent never sees. Three prompt shapes, three runs each, claude-sonnet-5:

  • Goal: the task, the guardrails, and a “done when” line.
  • Steps: a plausible numbered list, written without knowledge of the hidden checks.
  • Plan: the task with no done-when line, plus “write a plan to PLAN.md first, then execute it.”

The redactor: saying what done looks like wins

The hidden check has 15 credential shapes the redactor must catch, and 8 lookalikes it must leave alone.

PromptShapes caught, of 15Cost per run
Goal14, 13, 14$0.29
Plan10, 11, 11$0.18
Steps7, 7, 7$0.06

No run, in any shape, touched a lookalike.

The step list is the instructive one. It said: add the function; add regexes for GitHub tokens, AWS access keys and private key blocks; call it before the POST. The agent did exactly that, in four turns and about thirteen seconds, and stopped. The eight shapes it missed were, in all three runs, the families the list did not name: GitLab, Slack, OpenAI, bearer tokens, API-key headers, token= assignments. A step list caps the result at its author’s foresight.

The goal prompt was not shorter. Its done-when line named kinds of outcome, not patterns: “every credential format a developer is likely to paste (cloud keys, VCS and chat tokens, API keys, bearer tokens, private keys, credentials in URLs and in assignments) is removed, and ordinary code and prose come through unchanged.” So this does not show that short prompts beat long ones. It shows that saying what done looks like beats saying what to do. Plan-first sat in between, at about two thirds of the goal prompt’s cost.

The report script: our control was not clean

Our first summary of this task said the goal prompt refused 3.7 of 5 bad inputs and the step list refused none. Tracing that number for this post, we found that four runs scored a perfect 5 of 5 by refusing every input, including the clean control.

Their refusal messages said why. Two refused records missing the ran_at or scored_at timestamp. Two refused records whose total did not equal the sum of their subscores. Both complaints were correct. The real pipeline always writes those timestamps and always computes the total as the sum. Our hand-built fixture did neither: every record carried subscores of 3, 3, 3 and 3, with totals of 12, 9, 10, 6, 8 and 7. Every defect fixture was built from that same control, so those four runs never reached the defect we had planted. They stopped at one we had not meant to plant.

So the report task gives no verdict on prompt shape. The scorer could not tell a script that refuses everything from one that enforces the real pipeline’s invariants, and the second is arguably what the goal prompt asked for: “where it cannot be sure, it says why and fails instead of printing.” What survives: the three step-list runs added no input validation that refused anything, and accepted the broken control without comment.

We checked the earlier experiment that used the same fixtures. None of its runs refused the control, so the numbers in part 1 stand.

Никогда такого не было, и вот опять

“It never happened before, and here it is again.” (Viktor Chernomyrdin as (Yogi Berra “really didn’t say everything I said” (which he said (or didn’t (see I Am a Strange Loop))))).

This is the second post running where the instrument, not the agent, produced the headline. Part 2 was a replay that leaked the answer; this one is a control that was not a valid input. Counting the broken control group and the citation judge from our earlier benchmark, it is the fourth. The fix for this one is general: build fixtures with the code that writes real records, or validate them against it, instead of typing them by hand.

What caught it is the point of the post. The tracking issue for this article was written in the shape it describes: a task, guardrails, a done-when line, and a verification line that reads “each claim traced to its source.” Tracing the 3.7 back to the run logs is what turned it up. A step list for the same article would have said “write the post, add the table, publish.”

What we changed

Task issues and handoffs here are now written as task, guardrails, done when and verification, and our global agent instructions say so. The redactor result is why. The report result is why the verification line stays.

Limits: three runs per cell, two tasks in one repository, one model. The redactor gap is large and consistent across runs; the report task needs a valid control before it says anything.

Part 3 of a series on what our process experiments found. Part 1: the agent’s tests agreed with the agent. Part 2: our eval read the answer key. Next: widen a filter by layering, never by editing.

Our Eval Read the Answer Key

We had a clean result. Our full agent configuration (the instructions, skills and hooks we have piled up) made the agent worse at real work. Replaying merged pull requests, the stripped-down setups matched the merged answer four times out of four. Our full setup managed two.

It even had a mechanism. We read the transcripts. In one failed cell, the agent with our config compared its work against origin/master, decided the change had already been merged, and reverted its own work. It finished with an empty diff. Our config tells it to check origin/master. Case closed: too much instruction, the agent overthinks.

It was wrong. The agent had read the answer key.

How a replay leaks

The experiment (part 1 has the setup) replayed real pull requests: start the agent at the commit before the PR, give it the task, compare what it produces with what actually merged.

We started each replay in a git worktree. A worktree is a second checkout of the same repository, and it shares the repository’s object store and refs. So from inside a replay at an old commit, the agent could still see everything after it: origin/master, the merge commit, and the landing branch that held the exact answer. Nothing hid them. A plain git log would not show them, but git log --all would, and git show would open any of them.

We went back and classified every counted replay cell by whether any command it ran touched a commit from the future. Eight of twelve did, spread across all three setups. The “four of four versus two of four” comparison was void.

And the cell with the mechanism, the agent that reverted its own work? It looked at origin/master, saw the real merged PR sitting there, and correctly concluded the work was already done. It was the most rational agent in the experiment. We had built it a world where the future was on disk.

Clever Hans was the horse who could do arithmetic until someone noticed he was reading his questioner’s face. The horse was not cheating. The setup was answering for him.

The fix

Each replay now gets a clone that holds only the parent commit and its ancestors:

git branch -f isolate <parent>
git clone --no-local --single-branch --no-tags --branch isolate <repo> <dest>
git -C <dest> remote remove origin
git -C <dest> reflog expire --expire=now --all

No remote, no tags, one branch, no reflog. We checked it: the merge commit is unreachable, and git log --all ends at the parent. The --no-local matters: by default, git clones a local path by hard-linking the source’s whole object store, future commits included.

What changed when we reran

With the leak closed, the full setup no longer lost on correctness: it matched the merged answer in 4 of 6 cells, the same as Anthropic’s default setup. What survived was cost: the same answers at roughly 1.6 to 1.7 times the price, and a hook of ours that blocked the repository’s own validator in every full-setup cell. The verdict went from “our config makes the agent wrong” to “our config makes the agent expensive.” Those lead to different decisions, and we had very nearly acted on the first one.

The same leak had voided the first version of another task in the same week. It was caught by the same audit and rerun the same way.

What did not need rerunning: the question-and-answer half of the experiment, because that repository’s history holds no answers to our questions. Knowing why a result is unaffected is part of the audit, not a footnote to it.

The pattern

It is not the first time an audit of our own instrument changed a result. Our control group was broken and it cost us 4.2 points; a citation judge resolved paths wrong and scored real citations as fabricated. Each time the bad number came with a plausible story. Each time the story was what made it dangerous.

So the rule we follow: before comparing the arms, audit what each arm could see. For replays of version-controlled work, that means asking whether the answer is reachable from the starting state, because if it is, some agent will find it, and it will be right to.

We wrote the technique up as a public skill, agent-eval-replay-history-leak.

Part 2 of a series on what our process experiments found. Part 1: the agent’s tests agreed with the agent. Next: say what done looks like.

The Agent’s Tests Agreed With the Agent

“The verification is probably the single most important thing that people do not get right, largely.” That is Boris Cherny, who built Claude Code, talking to YC this summer. His advice: give the model a way to verify its own work, and it will just go.

Our global instructions to every agent say the opposite, in capitals: “NEVER include tests unless explicitly requested.” So we tested it. We gave the same agent the same real tasks twice. Once with our rule. Once with “before implementing, write an executable check for this task’s exit criteria, run it, and keep working until it passes.”

The rule stays. But not for the reason we wrote it, and that is the interesting part.

The setup

Two real changes from one of our repositories, replayed from the commit before anyone reviewed them:

  • Harden a benchmark report script that our reviewer had later caught printing wrong numbers.
  • Add a secret redactor to a hook that saves session memory.

The task text described the goal and the stakes, not the known defects. Then three things scored each run, none of which the agent could see:

  • Hidden fixtures. Five broken inputs for the report script (an errored cell, an unscored one, an empty answer that still carries a score, a duplicate, a missing question), and fifteen credential shapes plus eight lookalikes that must pass through untouched for the redactor.
  • A fresh adversarial review of the diff by a different model from a different provider.
  • Cost.

Five runs per variant, per task, on one model.

The result

no testswrite a check first
report script: broken inputs refused, of 50.40.2
report script: high-severity review findings4.44.4
redactor: credential shapes caught, of 1511.610.8
redactor: high-severity review findings5.84.0
cost per run, report / redactor$0.42 / $0.15$0.62 / $0.20

No measurable difference in the defects that got through. The redactor’s review count leans toward the check-first runs, but the confidence interval includes zero. The cost is 33 to 48 percent higher.

For scale: after our reviewer had been through it, the real report script refuses four of the five broken inputs. Neither variant got close.

Why

The check-first runs did write tests. The tests passed. They covered exactly the cases the agent had already thought of, which is to say they checked the agent’s reading of the task against the agent’s reading of the task. The defects that mattered lived outside that reading: credential shapes nobody listed, errored rows treated as a warning instead of a failure.

Quis custodiet ipsos custodes: who guards the guards. Juvenal meant something else, but it fits. A test written by the code’s author is a second opinion from the same person.

Now reread Boris’s examples. The Bun runtime was rewritten from Zig to Rust against test suites the agent did not write: “There’s a big test suite in Bun, there’s a big test suite in Node.js. So it’s easy to know if you did the right thing.” The agent porting a desktop app to Swift screenshots the original app and compares the two “pixel by pixel”. In both, the check is something the agent did not write and cannot talk into agreeing. That is not self-verification. It is an independent oracle that happens to be automated.

Ours is a reviewer from another provider, run adversarially against every diff. In this experiment it was the only thing that found the defects: four to six high-severity ones per diff on average, in both variants.

The same thing, in production

After the experiment, we shipped the fix for the redactor gaps it had turned up (voitta-rag#65, released as v0.1.5). Eight review rounds. Findings per round: 7, 7, 5, 3, 5, 1, 1, 2.

Before each of the nine pushes, the agent’s own regression suite was green: the fixture, every earlier round’s reproductions, and later a fuzz property. It was not useless. Twice it caught a real problem before the push. What it never did was anticipate the next round. Each time, the reviewer found shapes the suite did not contain.

The count did not come down because the tests got better. It came down when the design changed so that it could not regress against the shipped version by construction. That is part 4 of this series.

What we kept

The no-tests rule stays, on evidence this time: asking the agent to write its own check cost more and caught nothing extra. Independent review stays, and gets leaned on harder. Boris is right that verification is the thing. The word doing the work is the one he did not have to say: independent.

Limits, plainly: five runs per cell, two tasks, one model, one repository. This is a result about our setup, not a law.

Part 1 of a series on what our process experiments found. Next: the eval that read the answer key.

shmobster’s liveness watchdog had a blind spot for three releases

shmobster (v0.29.1 to v0.35.0)

The watchdog story is the one to read carefully. v0.31.0 added a second liveness check — catching a dead message_processor thread, not just a dead socket — but the refactor also rewrote the trigger from OR to AND: it was supposed to fire when either the session was unstable or a stable session went deaf, and instead fired only when both were true at once. The real failure mode is a half-open socket: stable session, no pong, recv blocked forever. Under the AND, that never tripped. From v0.31.0 through v0.34.0, an agent could sit deaf and alive for hours with the watchdog polling and doing nothing. v0.34.1 fixes the OR and adds diagnostics; if you’re running anything in that range, upgrade. v0.34.0 separately handles the case where the whole interpreter freezes and takes the watchdog thread down with it — a different failure, a different check. Less dramatically: v0.30.0 gives channels an optional mcp policy key so any MCP server’s tools can be allow-listed per channel (voitta-rag’s search is the first consumer), and v0.32.0/v0.33.0 stop papering over read-only and file-edit turns with needless approval cards. v0.35.0 adds always_skills, so a channel can force a skill into every turn instead of leaving it as a menu entry the model sometimes skips. Releases.

voitta-compute (v0.1.250 to v0.1.251)

Two new providers: Requesty router and Codex (the ChatGPT-subscription path), alongside a small hygiene fix that stopped tracking a local mkcert TLS pair in backend/certs. Latest release.

voitta-rag (v0.1.4 to v0.1.5)

Both releases are security fixes. v0.1.4 binds voitta-rag and Qdrant to loopback only — nothing listens on an external interface by default anymore. v0.1.5 closes three redaction gaps in session memory: sk-proj- keys, PREFIX_TOKEN= style secrets, and Bearer/JWT tokens were passing through unredacted before this. Latest release.

voitta-yolt (v2.4.1 to v2.6.0)

Hardening against credential leakage from agent writes: v2.5.0 treats key-material filenames as protected write targets, v2.6.0 redacts a credential wherever its own key name already identifies it. v2.4.1 was prep for the Claude directory submission. Latest release.

skillz (v1.152.0 to v1.176.0)

Twenty-five releases, almost all new skills or plugin point-fixes in the agent-skills knowledge base. Worth noting: the claude-code-cross-session-messaging plugin got patched three times in this window (1.2.0, 1.2.1, 1.2.2) — a reply arriving after /clear, then grepping the wrong name, then merging two conditions that needed to stay independent. v1.168.0 adds a pipeline that publishes a materialized bundle to a directory branch for the Claude plugin directory, which is the more durable change here. Everything else is one-off operational knowledge (macOS menubar behind the notch, Grafana alert-rule arming races, an MCP streamable-HTTP client with no SDK, terraform plan-JSON secrecy) — useful if you hit the specific gotcha, invisible otherwise. Releases.

voitta-mcp (crm-highlevel-v0.1.0)

First release of an MCP server for HighLevel CRM: runs under docker compose, crm_find_contact now returns a link to the CRM record, and a write tool (crm_update_opportunity_status) exists but is flag-gated off by default. Release.

mods (cw-chart-v0.1.0, image-mirror-v0.1.0)

New repo. Two mods shipped at v0.1.0 — cw-chart and image-mirror — under a release-on-merge setup that gates PRs on version bumps. No user-visible behavior to summarize yet beyond "it exists." Releases.

Модель ответила «снежинки», мы ответили «пугало»

Вопрос про хачкары не взяла ни одна из девяти команд. Хачкары — армянские каменные стелы с крестом, каждая со своим орнаментом, и вопрос был: с чем их сравнила одна тревел-блогерша? Большинство написало «отпечатки пальцев», Honda — «QR-код». Правильный ответ: снежинки. Claude, который саму картинку даже не открывал, ответил «снежинки».

Мы, ДинаМоМо, в том же раунде на вопрос про изувеченных игроков XVI века («щёки в синяках, ноги, руки и спины переломаны») уверенно ответили «пугало». Правильно — футболисты.

Это отчёт о том, как модель сыграла вслепую игру нашей Zoom-лиги 1 октября. Мы её сыграли вживую. В феврале ИИ-кот Cosmo уже играл в ЧГК вживую, читая слайды со скриншотов, и передавал ответы команде через Slack. В этот раз всё наоборот: игра отдельно, модель отдельно, а сравнение — после.

Как это устроено

Игру играли люди: девять команд, ведущий, таймер, Excel с очками. Модель в живой игре не участвовала и командам ничего не подсказывала. Мы записали общий Zoom и расшифровали его локально. Потом один агент собрал вопросы без ответов, и отдельный агент ответил на них после игры. У него не было ни интернета, ни записи, ни ответов ведущего — только текст вопросов. Всё лежит в открытом репозитории: вопросы, ответы ведущего, ответы модели и сравнение.

Публикуем после игры, а не до: наше дело — беречь игру, а не портить её. Ответы приезжают заранее — это мы уже проходили, только с другой стороны.

Счёт

РаундClaudeДинаМоМо
Выбор варианта и «правда или ложь»9 из 9500 очков (у Мегаполиса 1200)
Числа3 точных из 5ближе всех один раз
«Преферанс»7 из 7заказали 4, взяли 3
Пары4 из 15500 очков, лучшие набрали 900
«Разоблачение»4 из 4, рискуя раньше командслуховой аппарат рано, остальное за 100–200
Аукцион15 из 20
Финал2 из 2 там, где ответ известенминус 4300 и с 4-го места на 6-е

Где модель сильна

Числа — это память. 41-й сезон Кадзуёси Миуры, 18 вернувшихся из экспедиции Магеллана, 106 патентов Эдисона за один год — всё точно. Люди здесь считают, модель вспоминает.

«Преферанс» закрыт целиком, 7 из 7. Фудзи в мирное время женского рода, в военное — мужского. Надежда Дурова, поэма Лары Назаровой про суффиксы. Отдельно впечатляет вопрос про Дёмина и Королёва: что НБА впервые разрешила благодаря Дёмину? Модель майку не видела, но ответила буквой Ё (с уверенностью 20%). На майке действительно написано DĚMIN.

«Разоблачение»: модель рискует рано. Слуховой аппарат и Ворошилова с Крюком она берёт с подсказки за 700, посев и компас — за 300. Большинство команд брали их за 100–200. Исключение — ЧёКаво, которые взяли Ворошилова и Крюка с первой подсказки, за 1000. Отдельно приятно, что последний вопрос раунда был про Владимира Ворошилова, создателя «Что? Где? Когда?»: в его честь назван наш репозиторий.

Финал. Про голубоглазых, звучащих в до мажоре (отбор первых космонавтов, Королёв), из команд ответил только Мегаполис. Модель тоже ответила верно, с уверенностью 55%.

Где модель слаба

Пары: 4 из 15. Каждую пару держит каламбур: Бегемот — гроссмейстер Котов, Медведь — палач Муравьёв, Юпитер — маршал Жуков. Модель шла по прямым ассоциациям и промахнулась. А ведущий про ответы команд сказал: «практически всё идеально».

Числа, для которых надо смотреть. Сколько людей на обложке Sgt. Pepper? Модель обложку не открывала, поэтому промахнулась. А в патентах Эдисона ответила 1093, хотя это только патенты, выданные в США.

Аукцион почти весь у модели, но «перелётных птиц» и «Так говорил Ганс Сакс» она не взяла. Последнее, впрочем, не взял никто.

Что мы не сделали чисто

Всё это написано и в репозитории.

  • Вопросы для слепого агента вычистил другой агент, который ответы уже слышал. Картинки модель получила только словесными описаниями. Файлы со слайдами мы ей дали, но не сообщили точные имена, и она их так и не открыла.
  • Первый вопрос и начало раунда пар прошли, пока в записи не было звука. Ответ на последний вопрос финала в запись не попал: она обрывается на подсчёте.
  • Дважды в запись попал наш собственный голосовой агент: тестовые фразы, которые мы гоняли параллельно через виртуальный аудиовход. Из вопросов мы их, разумеется, выкинули.

Итог

По последнему кадру впереди Мегаполис (14 699) и Honda (12 325), но их ответы на последний вопрос в запись уже не попали. Мы — шестые: в финале дважды поставили по 2500 и дважды не угадали. Модель была бы где-то сверху, но в зачёт она не идёт: она не жала на кнопку, не спорила в своей комнате и не писала «пугало» на последней секунде.

Следующий шаг уже описан: Cosmo, только без ручных скриншотов. Честный агент, который в живой игре видит только текущий вопрос, предлагает ответ команде и сам ничего не отправляет. Решают по-прежнему люди. Или пугало.

Shmobster refuses to boot without voitta-yolt 2.0.1 or newer

If your shmobster deployment runs an old voitta-yolt classifier, upgrade voitta-yolt first. As of v0.27.0, shmobster exits at startup if its configured classifier is below the floor, instead of logging a warning and serving anyway.

shmobster (v0.7.0 to v0.30.0)

The floor itself moved twice. v0.8.0 declared voitta-yolt 2.0.0 unsupported outright; v0.9.0 reversed that a release later, setting the real range at >= 2.0.1 (a deployment stuck on 1.6.0 now refuses to run too). There’s a separate security fix worth flagging on its own: v0.19.0 closes a hole where a curl carrying a POST body could run with no approval card at all, if the box’s classifier predated voitta-yolt 2.0.0 — if that’s your box, grab that release first.

Past the gate, the window adds real capability: the agent can flag its own hard-won debugging sessions as skill candidates (v0.7.0), answer Slack DMs (v0.13.0), carry per-channel memory (v0.14.0), skip approval for in-scope git/gh work in channels marked unattended (v0.24.0), and call MCP tools per channel (v0.30.0). Most of what’s left is the approval gate getting sanded down — fewer cards for reads that only looked like writes, a typed approval and a button click no longer racing to opposite outcomes, and as of v0.29.0 a sandbox that refuses to let a channel write the deployment’s own checkout.

Full list: https://github.com/voitta-ai/shmobster/releases

voitta-yolt (v1.2.0 to v2.4.0)

v2.0.0 is the breaking release here — "the auto-mode realignment," cutting YOLT down to carry only what it refuses to delegate — and it’s the reason shmobster’s version-floor logic exists at all. The two releases right behind it are corrections to v2.0.0 itself: v2.0.1 restores a CLI path to deny, v2.0.2 fixes a doc claim about auto mode. v2.1.1 similarly walks back v2.1.0’s classifyAllShell to opt-in, default false. Separately, v2.3.1 strips employer and client names that had leaked into this public repo — check any fork or vendored copy predating that tag.

Releases: https://github.com/voitta-ai/voitta-yolt/releases

skillz (v1.79.0 to v1.159.0)

About eighty releases, nearly all single-skill additions to the catalog — Terraform forensics, Android ADB checks, Grafana alert preflights, and more in that vein. The few with teeth: codex-adversarial-pr-review grew into a real sweep tool across this window (cross-host queues, wildcard authors, multi-hour runs), parallel-agent-session-collisions got a state-dir fix for an orphaned predecessor process, and the secrets hook had three separate false-positive fixes (base64url’s underscore, key filenames read as keys, env-var references read as credentials). Nothing breaking; if you consume skillz for the catalog, diff bundle versions and move on.

Releases: https://github.com/voitta-ai/skillz/releases

voitta-compute (v0.1.237 to v0.1.251)

Two new providers: a Requesty router, and in v0.1.251, Codex via a ChatGPT subscription instead of API billing. v0.1.250 also stopped tracking the mkcert TLS pair in backend/certs — a local clone now needs to generate its own.

Releases: https://github.com/voitta-ai/voitta-compute/releases

voitta-rag (v0.1.1 to v0.1.4)

v0.1.4 changes a default worth knowing about: voitta-rag and its Qdrant backend now publish on loopback only. v0.1.2 and v0.1.3 are benchmark work — a ten-mode code-context harness, then a re-run after an index-prefix fix — useful if you’re judging retrieval quality, no action required.

Releases: https://github.com/voitta-ai/voitta-rag/releases

voitta-rag-enterprise (v0.1.1)

One release, one feature: linked folders. Point it at a host directory and it indexes in place, nothing copied into the container.

Release: https://github.com/voitta-ai/voitta-rag-enterprise/releases/tag/v0.1.1