Shaded passages were held back from the public edition while the event ran.
Sunday was eight challenges and 24 checks, and the last one went CORRECT at 100.3 seconds after the release. Fastest of anyone again, and still 1st overall.
I was glad to be in first again, but it could have been a lot faster. Most of those 100 seconds went on one answer in the warmups that we had decoded correctly from the start, and on three web challenges we had already beaten the week before.
09:00
Eight challenges dropped. In the order they finished:
- Integrity Signals (mobile): all three modes by 6.4 seconds.
- Ashes to Assets (script): 11.0.
- Hive Mind (metadata): a Windows registry hive. 16.5.
- Out of Bounds (binary): 20.4.
- Shell Company (web): command injection. 73.0.
- Reset Expectations (web): a password reset that trusts too much. 77.8.
- Resolver Revolt (web): a GraphQL API. 89.7.
- You Have a Point (warmups): 100.3.
Four lanes done inside 21 seconds. Then a long wait for the web, and a longer one for the warmups.
The warmups Mastery is where most of the time went. One delivery decoded to a slug with a tail, ...-hazel-20-17936. A Day 24 speed run had shown us a tail like that, and the fix then was to send the slug alone, but no grader had ever confirmed that reading, and on Sunday the grader rejected it. Worse, the ticket that followed (the short report the robot writes when it can’t answer) told the agents a decoding layer was missing, when all 25 layers had read fine. So the hand agent, the AI that works a question directly and answers that one attempt, went hunting for a layer that wasn’t there, guessed four two-digit endings, and only then sent the whole thing as written. CORRECT at 100.4 seconds into the attempt. About 95 seconds lost to an answer we had right all along.
The web lanes lost time differently. All three web challenges were re-releases of routes from Days 16 to 18, and the agents had solved every one back then. But their patches, the small fixes agents write mid-race, were never folded into the solver for good, and the web lane has no replay corpus (the recorded answer key every change is tested against) to catch that. So every mode of every web challenge went to the hand agents again, at 12 to 23 seconds a mode. Resolver Revolt’s Mastery also spent 9.2 seconds answering preparing while the Game Master, the event’s API, built its target. That part was the server’s.
Hive Mind was simpler: no registry reader, so eleven questions went to the hand agent, at 13 to 17 seconds a mode.
The record
From the public scoreboard, Day 25, all 24 checks:
- Wizdumb 100.3 seconds, 1st on the day. OscarsWorld 137.9.
- First blood on 10 of 24 boards (first blood is the first team to solve a board).
- 1st overall on 1,450 points. OscarsWorld and Frostbyte are on 1,450 too, so the order still goes to whoever banks their last flag first, and that’s still us.
The speed boards were a weaker story. As of 15:13 we were 2nd on the Hive Mind and Integrity Signals Masteries and 3rd on Ashes to Assets’ Mastery, and everything else on Sunday’s 16 boards sat between 4th and 30th. We had been 1st on both of those Masteries at the close of the race, until a team called help took them. help has been rerunning Saturday’s boards too, and our ten firsts from Day 24 were down to five.
That was the afternoon. The evening was ours.
Everything we’ve been shown
The web miss bothered me more than the warmups. We had been shown those routes, we had solved them, and the robot still didn’t know them. As I put it that morning, “we can’t have that type of miss moving forward. We need to have everything we’ve been shown implemented in our solvers should we see that again in future challenges. That, quite literally, could cost us the competition.”
The warmups tail needed a fix too, and the only sound one was the plain one: have the submitter try both, “the ‘expected format’ along with the received answer.” There will always be surprises in a CTF. The job is to be ready for them.
So Sunday’s two rules. When the file and the expected format disagree, try both, and try the file as written first. And nothing the organizers have shown us is allowed to need a hand again.
Everything landed in one commit at 11:14:
- Both readings. The warmups tail now goes out as written first, the slug second. After any rejection, the submitter tries other spellings of the same answer: with and without quotes, a trailing period, hex padding or a
0xprefix where the question says hex, leading zeros, the head of a patterned format. Never a different case. The tail ticket now says plainly that no layer is missing, and the agents carry a new rule: the file wins first, then the format-shaped reading. - The three web techniques, folded as general techniques: command injection through a clue route’s parameter, the recovery-token flaw, and GraphQL field-level authorization. Each reads the clues and the error text, never a recorded route, and spends nothing when its clue isn’t there.
- A registry hive reader for metadata.
- A coverage gate. Any unanswered question on any recorded day is now a miss, and a miss blocks a commit. Its first run found two holes nobody knew about: Day 12’s binary Mastery, 360 questions open because the recording had kept the unpacked files but not their zip, and Sunday’s 11 metadata questions. Both are at 0 now, and the exemption list is empty.
The mock, our local stand-in for the event server, came back clean on the final code: 15 of 15 file-lane checks CORRECT on the first try, no tickets, 9.7 seconds. In a blind test of the warmups miss, agents on and the robot knowing nothing past Saturday, Mastery ticket to CORRECT went from 64.3 seconds to 15.9.
Then the web census: every web challenge from Days 19 to 25, 18 of them, run live and free with agents off. Every Time Trial and every Mastery came back CORRECT with zero tickets. It found one more gap: twice, a Mastery retry came back with no attempt id, and the robot counted the old CORRECT and never played the new attempt. It now waits for the new one.
When I approved the plan, I added one caution: be careful writing the solver rules. Zero exemptions was the aim, and if one ever had to exist, we would plan for it together.
The speed runs
With the folds in and proven, the evening went to the speed boards for Days 23 to 25: 121 ranked runs between 17:50 and 18:25, every one of them CORRECT.
Going in, we held 5 of those three days’ 46 boards. By 19:10 we held 21: 6 of Sunday’s 16, 7 of Saturday’s, and 8 of Friday’s, where we had held none. Across all 25 days, 269 boards now carry times, and we hold 24 firsts, 22 seconds and 27 thirds.
Two teams hold most of what’s left on Sunday’s boards. help beats us by hundredths in the file lanes: Hive Mind’s Mastery 0.544 to our 0.603, Integrity Signals’ Time Trial 0.253 to our 0.277. STI Sentinels 402 holds both Ashes to Assets boards and all three web Masteries, and those web Masteries are the real gap: we’re 4th to 6th at 3.5 to 4.3 seconds, against firsts between 1.28 and 1.59.
The runs went one lane at a time so nothing shared the rate limit, on the day’s final code with agents off. Each board got ranked attempts until the public board showed us 1st, its budget ran out (8, or a third of the pool, whichever was smaller), or the pool was empty. Boards we already held were left alone. Binary was dropped after one round on Out of Bounds: its best from the house was 4.177 against DataCats’ 2.203, and on race day the binary hedge already sends answers predicted from the counter alongside the download, which no rerun from here can beat. Spot the Difference’s binary boards still wait for the VM, our second copy of the robot on a cloud machine closer to the download servers.
The web Masteries are the honest gap. Two ranked runs on each didn’t bring our best under 3.5 seconds, where the leaders sit near 1.3. Five attempts are left on each, and that is the next speed job for the web lane.
Six days
By late morning I was fairly sure we had seen most of the organizers’ challenge shapes. I was also almost certain they were saving the best for last. Both things point the same way: anything we’ve already seen has to be automatic, so that when Day 31 throws something new at us, the hand solvers have exactly one problem to work on.
Six days to go.
How we built it: the Day 25 dev log
Why the file reading goes first. Saturday’s tail fix made a guess about the grader and treated it like knowledge. The slug-only reading was a fine candidate; the mistake was putting it ahead of what the file actually said, with no grader record either way. So the order is now by evidence: what the file says first, the format-shaped reading second. Saturday’s String Theory record was re-ranked the same way.
Why the submitter never tries another case. Every other spelling change keeps the answer the same, but changing case can make a different one. In testing, a lower-cased web proof broke a test: a made-up value that only looked right. So the alternatives are capped at four, and case is never one of them.
Why one commit instead of one per lane. The house rule is one fix per commit, so a bad change traces to one revert. On Sunday every per-lane commit failed the test that checks the agent briefs, because those briefs are generated from every lane’s code and the whole corpus together. Splitting the fold would have meant committing red states, so it went in as one, and the commit message says why.
Why the coverage gate leaves the patch judge alone. The judge that decides whether an agent’s patch goes live mid-race replays under hidden knowledge: it can’t see what tomorrow’s solver will know. When the coverage rule was made global, that judge refused every patch that had ever worked. So the commit gate counts an open question as a miss, and the live judge keeps its old rule. Exemptions are planned with me, not taken, which was the point of my caution.
Why the preparing wait was left alone. There was a design to shave Resolver Revolt’s 9.2 seconds, and it was dropped once the timeline showed the wait was all on the server’s side. I was glad it saw through that instead of spending the time.
The watcher’s feed. The watcher, the AI session that watches the race live, had its feed filled with the release poller’s 404s before 09:00 for two mornings. It now reads a summarised tail that drops the pre-release noise and folds repeats, so a real problem stands out.
What the speed runs nearly broke. Every ranked run is recorded into the same corpus the patch judge replays. The evening’s runs, on top of Saturday’s, were hundreds of near-identical attempts, and they pushed the judge’s binary check to 27 to 31 seconds against its 30-second wall. A binary patch on Day 26 would have been refused for running out of time, not for being wrong. The 302 rerun recordings were moved to an archive, which can be undone, and the check is back to 12.9 seconds. The proper fix, recording speed runs somewhere the judge doesn’t read or capping what it replays per challenge, waits until after Day 26 and a review. Raising the wall without knowing why it was hit was never on the table.