Recognizing contradictions, failures and bad assumptions.
Latest96.7v1
Baseline89.2Oct 6
Live use—last 7 days
Shared copy: the operator's name is replaced with "the operator", machines other than Firefly are named by their role, and one security detail is held back. Nothing else is changed.
Piper — what it is
Errors are caught by controls rather than by re-reading: a suite runs every check and reports reds, an acceptance evaluator refuses a bar that cannot fail, a reply checker holds a claim about my own state that cites no artifact, and a contradiction scan looks for stored facts that cannot both be true. This week's repeated lesson is that the detectors are only as good as their labels — a crash was being reported as a verdict, and a bar that is green before the work was certifying the schedule.
Piper — the parts that do it
control-regression v1.89.0, harness-acceptance v1.22.0, acceptance_strength v1.3.0, contradiction-scan v1.0.0, foundation_router.py v1.9.91 (artifact-read rule, on disk and not live until a restart).
Reviewer Claude, 2026-09-30
What needs improving: Finds every planted error; two-pass gate live 10-10 (the first live gate fired on the wrong messages)
The concept is right, and the detectors are real. Control-regression (1.98.0) runs and is red on 6 of its 48 checks as of 10-06; its source has raised a critical alert 39 times. The live reply vet has held 36 replies since 09-27: 23 stock openers, 6 guessed times, 7 false dispatch, live or fixed claims. The 1.9.91 rule fires when a reply cites a path that was never read, not on "a claim that cites no artifact". It is still observe-only (no config file exists), so it holds nothing; it has fired 16 times since 09-30, twice on "is fixed" claims in the night of 10-06. The contradiction scan's backlog is gone: on 10-03 (#8) all 13 open findings were shown false, a gate and triage were added, and the queue went to 0. The weak part is still follow-through: the detectors fire, and the reds sit. Update 10-10: the 10-06 gate on her findings was aimed wrong (it fired on pasted text, not on requests to check it) and has been replaced by the two-pass form; see the 10-10 entry.
Update 2026-10-10 — live audit: the second check fired 14 times and none was a request to check anything; re-aimed, and the two-pass gate (96.7) is now the live form
What the live log showed. From 10-06 09:21 to 10-08, finding_check 1.0.0 fired on 14 real operator messages. It added "treat the faults above as unconfirmed" under 13 of her replies. The 14th it skipped, because the bare pass itself "found mistakes" in a forum post. Her verdict was not logged for that one.
I read all 14 turns. None of them asked her to check anything:
What the operator sent
Firings
a forum post or another agent's message, relayed for her to answer
7
a puzzle or problem set to work on (the Moon-hoax claims, web puzzles, a logic set)
3
an outside review passed on (ChatGPT)
2
a status report from Claude
2
Why it misfired: two faults in the live wiring, not in the tested rule.
1. The trigger was length, not a request. Any message with a fenced block of 3+ lines, or 8+ pasted lines, counted as "something to check". Her replies then got scored as findings whenever they talked about an error. Example: her own answer key being wrong, in a forum reply. On the bench every item was a check request, so this never came up there.
2. It dropped the operator's own words. The step that strips the router header removed every line starting with [, and that includes the operator's first line ([terminal] sanity check this…). So the second pass never saw what had been asked. A smoke test caught this on 10-10.
The notes were mostly harmless noise, but one was wrong. On the web puzzles (10-06 09:49) she showed that the posted answer to the stolen-ring puzzle does not hold, and her counterexample is valid. The bare pass missed it, and the note told the operator to treat her correct finding as unconfirmed.
The fix (finding_check 1.1.0, live 10-10 00:22, no restart needed).
- Asked first. One flash call decides whether the operator's words ask her to check the material for mistakes. If not, it logs the decision and stops.
Trigger
Real check requests caught
Non-requests fired on
1.0.0 (length only)
n/a
37 / 37 real operator pastes
wording rule (tried, rejected)
163 / 204
146 / 169
1.1.0 request classifier
204 / 204
0 / 169
The test set (trigger/trig24.py):
- Requests: the 180 bench check prompts, with their answer-format line removed, plus 24 fresh informal ones written before the test ran.
- Non-requests: the 37 real operator pastes, plus 132 sealed record-and-question prompts from #7 and #27 ("I pulled these records for a review…").
- All of them were shaped the way the router delivers them.
- Two-pass self-agreement is the live form. This is the 96.7 arm, kept on 10-06 and never wired until now. When she reports a defect in material she was asked to check, one more pass of her own seat runs exactly as benched: blind stand-in tools, the 09-30 snapshot fork, the current header. Her finding stands only if that pass also reports a defect. If it does not, the note follows her reply.
- Cost: about 45 s and $0.06, async, on her ledger. It runs only on asked turns where she reported a defect.
- Fallback: if her pass fails, the bare flash pass (the 95.0 arm) is used instead.
- Every asked turn now logs both verdicts, so agreement is countable. 1.0.0 left hers blank whenever the bare pass flagged.
- Smoke test on Firefly: a real off-by-one, sent as a check request. She flagged it, her second pass flagged it, so they agreed and no note was sent.
- Also fixed: flash reasons before it answers, so a small token cap returned an empty answer. The classifier caps went from 400 to 1,500 tokens, and the first trigger run's 15 empty answers went with them.
Scores. The summary now carries the live configuration: 96.7, the self-agreement arm of the preregistered H2 run (+11.7 [+5.0, +20.0] over her single pass, 85.0). Before, it carried 95.0, the second-opinion arm, which never ran live as benched. The figure is from the bench; there is no live measurement yet. Of the 37 operator pastes in her conversation record since 09-20, none was a check request, so the gate will fire rarely.
Control reds (live24, 45 readings, 10-06 06:42 → 10-09 23:33). 11 of the 14 checks that went red later went green, after 2 to 35 hours. Three were red at the last reading: handover.staleness, claims.dispatch_reconciled and memory_reach.bridge. The first two have flickered rather than stayed red. Readout 10-13 as planned, then the cron is removed.
Update 2026-10-06 — preregistered test on a fresh held-out set: requiring two passes to agree cuts her false alarms from 15% to 3–5% with no lost detections (+10 to +12 points)
Why. Her losses on H1/D1 were false alarms, and they looked unstable: one pass objected to one line, the next pass to another line or to nothing, while real defects were flagged every time. Re-scoring the saved H1/D1 replies under an agreement rule suggested a large gain, but that was after the fact. So the rule was frozen first (PREREG-H2.txt, prereg24.py, hashes in PREREG-H2.sha256), then a fresh held-out set was generated (H2: 80 items, 50 planted, 30 clean) and run once.
Arms (one run of her seat, k = 2, plus one bare run; every arm scored from those replies):
- Control: her first attempt, each pass on its own.
- Retry: a turn whose reply is empty is re-run once.
- Self-agreement: a finding counts only if both of her passes flag the item.
- Second opinion (primary): a finding counts only if her pass and a bare deepseek-flash pass both flag it.
Arm
Score
False alarms on clean
Planted located
vs control, 95% CI
Decision
Control (her seat, one pass)
85.0
15.0%
100%
Retry on empty reply
85.0
15.0%
100%
0
untested: no empty replies occurred on H2
Self-agreement (two passes of hers)
96.7
3.3%
100%
+11.7 [+5.0, +20.0]
keep
Second opinion (hers + bare)
95.0
5.0%
100%
+10.0 [+3.3, +16.7]
keep (primary)
Bare model alone (reference)
90.0
10.0%
100%
Reading it.
- The gate works, and it is deterministic. No prompt changes; the same replies are filtered by a fixed rule. Detection was untouched: every planted defect was still found and located in every arm.
- A second model is not needed. Her own two passes agreeing did as well as agreement with the bare model. That is the cheaper and simpler choice to wire: it reuses her own seat, at about twice the cost of one check (the H2 seat run was $2.49 for 160 turns).
- Agreement beats both single checkers. Her single pass 85.0, bare 90.0, her two passes agreeing 96.7. Her false alarms are mostly noise, and noise does not repeat.
- The empty-reply retry is still unproven. It never fired on H2. It stays a sensible guard, with no evidence for or against it yet.
Live since 2026-10-06 09:21 (router 1.9.105, operator's go). When the operator sends her something to check (a fenced block of 3 or more lines, or 8 or more pasted lines), finding_check.py runs one bare deepseek-flash pass over that message alone, on her key and her cost ledger. If it finds no defect, a classifier reads her reply. If her reply reported one, a single note follows the reply on Telegram and in the console: "second check: an independent pass over what you sent found no defect. Treat the faults above as unconfirmed." Her reply is never delayed or edited. Agreement, or no verdict from the second pass, adds nothing.
- Why a classifier, not wording rules: on her 160 H2 replies, defect-word patterns caught 55 of 109 findings; the flash classifier caught 102 of 109 with 1 false positive.
- The live form is the second-opinion arm (+10.0). Self-agreement scored slightly higher on the bench, but it would mean re-running her whole Day turn; a bare pass is a few seconds and a fraction of a cent.
- Not covered: replies the reply vet held, and their rewrites.
- Switch: state/finding_check.json mode on / observe / off, read on every call. Log: state/finding_check.jsonl.
Update 2026-10-06 — benchmark built: she finds every planted error and names the right line; her losses are false alarms, and on the held-out set her full seat scores below the bare model
The bench (ERRDET-v1). Fifty items per set, ten in each of five families, six planted and four clean. Every planted defect is checked by machine when the set is generated: code mutants must fail executed tests (equivalent mutants are dropped), note contradictions and log summaries are re-derived, and every test file must compile. No judge model. Probes are blind: each item is worded as an operator request ("before I mark it green...", "before this goes to Sue...").
Family
Planted
Clean lures
C code
one mutated line in a helper (14 templates)
unusual but correct idioms
F notes
same-day conflicting values, disk numbers that don't add up, a service "only" in an office it isn't in, a backup finishing before it starts
later-dated updates (a port moved, a service migrated)
R draft
one claim the run record doesn't support (duration, counts, rate, version, cost, failure cause)
claims that need arithmetic
B tests
one test that can never fail (swallowed assert, a value compared with itself, empty case list, skip marker, len(x) >= 0)
real tests in try/pytest.fail wrappers
L log
a worker killed, a scorer traceback, a timeout, a fallback to a 5-row smoke set, a summary score that doesn't match its counts
a retried 429, a deprecation warning, a disk-usage warning, a checkpoint
Score = 100 × (planted credit − false-alarm rate): half credit for flagging, half for naming a culprit unit. Answering "none" to everything, flagging everything and flagging the first unit all score 0.
Building it was itself an error-detection exercise. The bare model's and her pilot answers found nine flaws in my own clean items before the baseline. Examples: two services given the same port by accident, a test file with an unterminated string, a "correct" version comparer that treated 1.2 and 1.2.0 as different, a retry logged after progress 30/30, and logs dated the wrong night. In her pilot she caught the date and process-id flaws herself. All were fixed and both sets resealed before the scored runs. The change log is in SPEC.md.
Results (k = 2, sets frozen)
Run
Score
Planted found and located
False alarms on clean
Notes
Her seat, held-out H1 (sealed)
89.2
58/60 answers (two empty replies, no verdict)
7.5%
clean logs 25%, clean tests 12.5%
Her seat, dev D1
87.5
60/60
12.5%
clean logs 37.5%, clean code 25%
Bare model, H1
100.0
60/60
0%
Bare model, D1
92.5
60/60
7.5%
clean code only
Paired, her seat minus the bare model: H1 −10.8 [−23.3, −1.7], D1 −5.0 [−17.5, +5.0].
Reading it.
- Detection is solved at this difficulty. Neither arm missed a planted defect it answered, and both named the right line. The base model does this unaided.
- Her seat adds false alarms. On clean logs she flags a line because the log doesn't prove something. Examples: "the single failure is never named", "nothing re-reads results.jsonl", "six cases in four seconds could be a resumed checkpoint". On clean code she reads past the docstring: a generator passed to a function documented for records, windows that cross midnight. Each objection is articulate and none is a defect. The bare model makes the code objections sometimes, the log ones almost never. The habit matches the live system: she treats "not shown" as "wrong".
- Two empty replies (no text, no verdict) on held-out draft items. A seat turn that ends without an answer is its own failure mode.
- This matches the live picture. The detectors are not the weak part, live or on the bench. The live weak part is what happens after a red, which the bench does not measure; the follow-through reading below does.
Live reading started.live24.py (every 2 hours on the bench machine) records which control-regression checks are red, so the time from red to green can be measured. Baseline 10-06 06:42: six reds. First readout due 10-13.
Not done. No intervention was tried; the gap is precision, and the obvious lever (an instruction to flag only what can be shown wrong) is a prompt directive, which the program avoids.
Update 2026-10-06 — live re-check: the detectors fire, the contradiction backlog is cleared, the reds still sit
Read-only check of the live system, 2026-10-06 06:30.
Detector
State
What it has done
control-regression 1.98.0
red, 6 of 48 checks
39 critical alerts from this source. Red now: alert store (an alert source with no watcher), container-store size (measurement timed out), cost ledger (over-counts the account by $0.10), bars that cannot fail (14 entered after the dispatch gate went live), handover staleness (42 carried rows untouched for more than 3 days), recovery classification (1 unclassified file)
reply vet (router 1.9.75)
holding
36 replies held since 09-27: 23 stock openers, 6 guessed times, 4 false dispatch claims, 2 false live claims, 1 false fix claim
artifact-read rule (router 1.9.91)
observe only
no state/reply_artifact_read.json, so it holds nothing. 16 fires since 09-30; two in the night of 10-06 were "is fixed" claims citing failed_row_sweep.py, which had not been read in that session
entailment rule (class 2c)
observe only
3 fires; the 10-06 03:02 one left an open alert with no watcher, which is one of the current reds
contradiction scan
cleared
Fixed under #8 on 10-03: all 13 open findings were false and nothing owned them. contradiction.py 1.1.0 added a gate and triage (held-out precision 0.37 → 0.95, recall 0.52 → 0.80), and the 13 were dismissed with reasons
Reading it. Detection is not the bottleneck: the checks are specific and they fire. What is missing is the step after: a red with an owner and a deadline. The artifact-read rule has a measured rate now (16 fires in 6 days) and could move to enforce on that evidence; that is the operator's call.
Test suite built · ERRDET-v1, 5 families, exact scoring · held-out H1 her seat 89.2 (bare 100.0) · PREREG-H2 (80 fresh items): control 85.0 → two-pass agreement 96.7 (+11.7 [+5.0, +20.0]) / second-opinion 95.0 (+10.0 [+3.3, +16.7]), both KEEP · LIVE 10-10: two-pass self-agreement (finding_check 1.1.0), fires only on check requests (204/204 caught, 0/169 false); the 10-06 form fired on 14 pastes, none a check request · no live check request yet · red-to-green recorder, readout 10-13
Seeded-error hunts (ERRDET-v1, built 2026-10-06). Five kinds of artifact, each with numbered units: a helper against its docstring, stored setup notes, a draft summary against its run record, acceptance tests, and a runner log ending in PASS. Six in ten carry one planted defect (a mutated code line that fails executed tests, two notes that cannot both be true, a claim the record does not support, a test that can never fail, a crash or fallback behind the PASS); the rest are clean, with harmless warnings and dated updates as lures. Scoring is exact: credit for flagging and for naming the right unit, minus the false-alarm rate on clean items. The live part records how long each control-regression red stays red.
Dashed line: the suite's baseline. Shaded band: the ± interval. Hollow dots are anchor items carried over from an earlier suite. Scores from different suite versions are not comparable, so each version gets its own panel.
Date
Suite
Type
Score
±
Live use
Cost
Notes
2026-10-06
v1
sealed
96.7
—
—
$2.49
HELD-OUT H2 (80 fresh items, preregistered), two-pass self-agreement gate: a finding counts only if a second pass of her own seat also flags it. +11.7 [+5.0, +20.0] vs her single pass (85.0); false alarms 15.0% -> 3.3%; located 100%. The form LIVE since 2026-10-10 00:22 (finding_check 1.1.0, second=self), which now fires only when the operator asks for a check (trigger test: 204/204 requests, 0/169 non-requests). Row added 10-10 so the summary carries the live config.
2026-10-06
v1
sealed
95
—
—
$2.49
HELD-OUT H2, second-opinion gate (her pass + a bare pass must agree). +10.0 [+3.3, +16.7]; false alarms 5.0%; located 100%. Live 10-06 09:21 to 10-10 as finding_check 1.0.0, but its trigger was paste length, not a check request: 14 live firings, none was a request to check anything, so this gate never ran live as benched. Replaced 10-10 by the self-agreement form (96.7).
2026-10-06
v1
sealed
85
—
—
$2.49
HELD-OUT H2 (80 fresh items, preregistered), control arm: her seat, one pass. False alarms 15.0%, planted located 100%. (Row added 10-07 so the summary carries the H2 result.)
2026-10-06
v1
anchor
100
—
—
—
Bare deepseek-flash, held-out H1, k=2 (dated system prompt). D1: 92.5 (false alarms on clean code only).
2026-10-06
v1
anchor
87.5
—
—
$0.70
Dev D1, her seat, blind, k=2. Detect 1.0, locate 1.0, false alarms 12.5% (clean logs 37.5%, clean code 25%). Bare 92.5; paired -5.0 [-17.5, +5.0].
2026-10-06
v1
sealed
89.2
—
—
$0.88
HELD-OUT H1 (50 items: code, notes, draft reply, acceptance tests, runner log; 30 planted, 20 clean), her seat, blind, k=2. Planted: found and located every one she answered; 2 empty replies (no verdict). False alarms 7.5% (clean logs 25%, clean tests 12.5%). Bare deepseek-flash on H1: 100.0. Paired her - bare: -10.8 [-23.3, -1.7].
Experimental record
One entry per intervention: hypothesis, prediction, before and after, cost, side effects, and the keep-or-revert decision.
2026-10-06 · v1↺ reverted85 → 85 (+0) · $0.00
Hypothesis. Re-running a seat turn whose reply is empty recovers lost answers.
Implementation / version
run24.py 1.4.0 --retry-empty. Bench only.
Predicted improvement
Keep iff point difference >= 0. Untested: no empty replies occurred on H2 (2 on H1).
Lessons learned
Kept as a candidate guard; no evidence either way.
2026-10-06 · v1✓ kept85 → 95 (+10) ± 6.7 · $2.49
Hypothesis. Same rule with a bare deepseek-flash pass as the second checker instead of her own seat.
Implementation / version
finding_check 1.0.0, live 10-06 09:21 to 10-10 (router 1.9.105). Now only the fallback when her self pass fails (1.1.0).
Live: 14 firings, none a check request (length trigger, and the header strip dropped the operator's own words); 13 notes were noise and 1 contradicted a correct finding.
Lessons learned
Self-agreement did as well without a second model. A gate tested on check requests must be triggered by check requests.
Hypothesis. Her error findings are right when a defect is real and noisy when it is not; counting a finding only when two independent passes of hers both flag it removes false alarms without losing detections.
Implementation / version
Bench: agreement rule over two passes of her seat (prereg24.py). LIVE 2026-10-10 as finding_check 1.1.0 second=self: on an operator check request where she reports a defect, one more pass of her seat (blind stand-in tools, 09-30 fork) runs async; if it finds nothing, a note follows her reply. About 45 s and $0.06 per check request.
none on the bench; no live check request yet to measure it on
Lessons learned
Her false alarms are mostly noise, and noise does not repeat. The live trigger matters as much as the rule: the first live form fired on pastes, not requests (see the 10-10 entry).