The challenge
"That address bar problem? It happened twice."
| answer_id | Question | Format |
|---|---|---|
message |
"Decode the assigned blob completely. What exact answer slug does its final non-empty decoded line instruct you to submit?" | pattern: "three lowercase words separated by hyphens, followed by a hyphen and a two-digit number; submit the slug only" |
The Time Trial asked the same. The Mastery asked it once per blob: 148 sets (set1.message … set148.message).
Reading the prompt: what it said we'd need
| Clue | What it pointed to | What I did about it |
|---|---|---|
| "That address bar problem?" | URL encoding, the %xx form browsers show in the address bar: Day 7's layer |
The blob is nothing but %xx triplets |
| "It happened twice." | The same encoding applied a second time | %25 is an encoded %, so %25%36%63 → %6c → l. Decode, then decode the result again |
| "Percent Percent" (title) | A percent sign that is itself percent-encoded | Confirmed the reading |
| "its final non-empty decoded line" | In a multi-line blob, the answer sits on the last line | The Mastery blobs have 30 labelled lines, and line 30 is in today's layer. That is where the old chain balked |
What was in the files
- Single Solve:
blob.txt, 972 bytes, one line beginning%25%34%65%25%36%39…. One decode gives%4e%69…(inner hex lowercase); the second gives the sentence (it starts "Ni…"). Slugcalm-prairie-ridge-16. - Time Trial:
blob.txt, 963 bytes, the same template with a different slug,merry-storm-flint-76. - Mastery:
warmups-day-30-mastery.zip(333,205 bytes on the ranked attempt, 327,703 on the retry), 148 blobs (blob-01.txt…). Each blob has 30 tab-labelled lines,01to30, and line N is in Day N's layer. Line 30 is double percent-encoded (e.g.30, a tab, then%25%36%35%25%32%64…).
So the Single and Time Trial were the whole sentence in one layer; the Mastery was the event's history, one day per line.
Single Solve and Time Trial: the automated solver
Both CORRECT on the first submit, with no stop. The solver was still running the Day 27 Warmups chain. That chain already reads double percent-encoding on a whole blob as two Day 7 steps, so nothing new was needed.
Single Solve
started_at 09:00:19.3 ET deadline none
answers 1 calm-prairie-ridge-16
start 574 ms
artifact 352 ms (972 bytes)
solve 1 ms
submit 2131 ms
TOTAL 3058 ms (submit +2,618 ms after started_at)
result CORRECT flag db631ef5…8e7f (09:00:19.9 ET)
Time Trial (ranked)
started_at 09:00:27.5 ET deadline 09:00:57.5 (30 s clock)
answers 1 merry-storm-flint-76
start 604 ms
artifact 668 ms (963 bytes)
solve 1 ms
submit 896 ms
TOTAL 2169 ms (submit +1,676 ms after started_at)
result CORRECT flag a2297217…e173 (09:00:28.4 ET)
Mastery
The ranked run: stopped unsent, clock expired
ranked run, 148 sets
started_at 09:00:34.8 ET deadline 09:03:19.8 (165 s clock)
result none sent; the clock expired at 09:03:19.8
The solver's parallel solve failed on every set, and the whole-blob fallback stopped too. Two reasons appear in its output:
- Most sets: "new walk: ambiguous last line: '
Why: today's layer is a stack of two old layers (Day 7 twice). The last-line guard we pre-built on Day 17 exists to refuse exactly that: a newest line that an earlier day's layer can also read. The guard did its job, since nothing was guessed, but it left no answer. The solver doesn't retry on its own, so it stopped the lane there.
The fix (Claude helper agent)
Today's solver builds on the Day 27 chain and adds:
- The strict per-day reading first, for labelled blobs of 30 or more lines (the strict line endings built after Day 25). Every line is decoded with only its own day's layer. The joined text must start with the template and give exactly one slug in place. Otherwise the blob goes to the unchanged Day 27 chain, so every older blob takes the path it took before.
- Three new strict layers: 28 (hex written backwards, Back in Sixteen), 29 (ROT13 over Base64, Rotten Base), and 30 (double percent; both decodes must be strict %xx).
- An encoder so the regression rehearsal can build synthetic double-percent sets (the shared library wasn't edited), and per-set solving.
Its tests: 11 pass (the real Single and Time Trial blobs, the double decode and what it rejects, strict layers 28 and 29, saved Mastery sets, a trailer that moves the answer, an old layer on line 30 that is not taken as strict, a short blob that skips the strict path, a delivery-shaped Mastery, Ook! still read, and the per-set flag).
Checked offline before sending: - The saved ranked Mastery zip: 148/148 answered in 0.04 s, 0 ambiguous. Against the slug the Day 27 guard had named: 130 agree, 0 disagree, and 18 where the chain had nothing. - The Single and Time Trial blobs give the same answers the solver sent. - A regression rehearsal against earlier days' puzzles (60 synthetic double-percent sets added): passed. 183 saved attempts, slowest 4.43 s, per-set identical; 182 identical, 1 newly solved (today's Mastery), 0 changed; synthetic 60/60 in 0.40 s.
The untimed retry: CORRECT, not ranked
The ranked attempt had expired unsent, so the helper ran one untimed retry of the Mastery (the solver checks that the status says expired before it retries). It was a fresh variant, and every set was answered by the new solver in one submit.
untimed retry; deadline none; not ranked
started_at 09:14:04.9 ET
answers 148 sets
retry 1305 ms
artifact 2159 ms
solve 33 ms
submit 1168 ms
TOTAL 4.7 s (submit +3,431 ms after started_at)
result CORRECT flag e2a77b6c…7463 (09:14:07.7 ET)
Not ranked. We didn't run another ranked pass, so there is no ranked Mastery for this day. Today's solver then became our Warmups solver for later days.
Time to solve
The Single (09:00:19.9 ET) and ranked Time Trial (09:00:28.4) went through on the first submit within half a minute of release. The ranked Mastery's 165 s clock expired unsent at 09:03:19.8; the fix took about 10 min 40 s, and the untimed, unranked retry was CORRECT at 09:14:07.7, 14 min 7.7 s after release.
Takeaways
- A new layer made of old layers trips the guard by design. "Double percent" is Day 7 twice, so the last-line guard saw an old layer and refused. That refusal is right in general; the labelled Mastery needed a different reading.
- Labelled lines make a strict reading cheap. When line N is in Day N's layer, decoding each line with only its own layer gives an answer that doesn't depend on guessing which layer came last. The fix is to read it that way first and keep the old chain as the fallback.
- Keep the strict per-day layer list up to date every day. Days 28 and 29 had never been added. The new path needed them, and so does every later day (Day 31's was due to be added the next morning).
- Single and Time Trial passing says little about the Mastery. Both were a single-layer sentence; only the Mastery had the history shape, and a 165 s clock leaves no room to build a fix.
- Cross-check against what the guard named. 130 of 148 sets matched the slug the old chain had found but refused, with 0 disagreements. That gave confidence before the one retry.