Backlog
IN FLIGHT right now — read this first (11 October 2026, 16:30 UTC)
Written so this session is resumable. Delete this section when all three numbered items below are done.
0. The schedule as applied, 9 October 2026
Three blog posts a week: story Tuesday 06:00, a catalogue Wednesday 12:15, an explainer or In Practice Thursday 06:00. Exam Room keeps Mon/Wed/Fri/Sat at 06:00 and 20:25; Cooking keeps Sunday. A run that ends gets a blank slot before the next starts, in every queue.
Runway from what is written: narrative 2032-10 (6.0y), explainer 2033-01 (6.3y), In Practice 2032-08 (5.9y), catalogues 2030-02 (3.3y), exam 2029-06 (2.7y, set by AWS).
The catalogue slot is one a week. It was two for part of the day, which
drained the catalogues by May 2028; one a week reaches Feb 2030 now and 5 to 6
years once runs 12 to 14 and the theory streams land. Blackout days are
_data/blackout.yml: 24 Dec to 1 Jan inclusive, and 20 May, 16 Jun, 21 Jul,
12 Aug.
1. Runs 12, 13 and 14 are DONE. Run 15 is next, and needs new machinery
python3 scripts/run-status.py reports 305 of 305 written, 305 clean over
all thirteen runs. Runs written: 9 (Applied Patterns, 10), 10 (Algorithms, 28),
11 (Concurrency, 26), 12 (Distributed Systems, 30), 13 (Refactorings, 16),
14 (Resilience, 16).
Run 15 does not fit this harness, and that is the work before it can start.
It is 33 posts across two different structures (15 for The Workshop, 18 for
Consulting and Craft), keyed on series: rather than on an Under-the-Hood
group:, and with no Go and no measured figures in any of them. The write
side is a day’s work on write-reference-run.js. The check side is the real
problem: every defect worth catching in runs 10 to 14 came from building the
Go or re-running a figure, and a post with neither gives the check agent
nothing to compile and nothing to re-measure. Decide what a check pass
checks on a prose post before launching 33 of them, or the pass is a
read-through with extra steps.
Cost, measured over four waves of eight: 2.95M, 3.08M, 3.09M and 3.01M subagent tokens, so about 380,000 a post including the check pass, steady across runs 13 and 14 and matching run 12’s first six. Sixteen posts cost roughly 6M.
Eight posts at a time, sixteen agents, two waves in parallel. 32 agents
over one checkout, twice, with no disk exhaustion and no lost work. Run 10
filled the session’s whole disk allowance with eight; the cause was one
unbounded benchmark writing 28.7 GB to stdout, and the brief now caps every Go
command with timeout and -benchtime 200x. Watch df -h /.
Before launching any new catalogue run, three things have to exist. Two of them were missed for run 13:
- The run’s entry in
RUNSinscripts/workflows/write-reference-run.js, with its posts and its owndiagramsinstruction. - The group in the
caseinscripts/check-reference-post.sh. A group missing from that case gets no shape check at all, so a half-written post with one heading and one diagram came backALL CLEANand was committed. writing/<group>/index.html._data/modes.ymlneeds the group too, and/writing/the-catalogues/is data-driven so it needs nothing.
Then python3 scripts/workflows/dry-render.py write-reference-run '{"run":
"...","indices":[0]}' and read the brief before sixteen agents act on it.
scripts/land-post.sh "subject" _posts/....md is the only way to commit a
post. It stamps the reading time, stages by path, gates the STAGED content,
and commits only if the gate passes. Two bugs are designed out of it: a clean
working tree is not a clean commit while an agent is still rewriting the file,
and gate | grep FAIL && git commit succeeds because grep exits 0 when it
matches, which is how a post with an unwrapped Liquid brace reached a push.
Commit a post as soon as it has the group’s headings and diagrams, before its check agent reports: the write is done by then, and a finished post is better banked than lost to a restart, which cost four Concurrency posts. Commit the check pass only once the workflow reports done, or say plainly that it is partial. Re-gate the whole run after its last check agent lands: run 12’s check pass reintroduced three call-graph defects in posts those agents had just reported clean.
GitHub will not rebase-merge a branch much over 100 commits. A 1-commit PR
merged, a 100-commit PR reports rebaseable: true, and 114 and 227 both
returned 405 This branch can't be rebased with nothing further in the API
response and no merge commit, empty commit or second author to explain it. The
run-12 branch went in as four PRs (#1905, #1904, #1906, #1901). Merge each run
as its own PR, which is what runs 13 and 14 did.
node --check does not check these workflow scripts
It exits 0 on scripts/workflows/*.js whatever is in them. An unescaped
backtick pair inside the CHECK brief’s template literal, from the “check a
figure against the others around it” rule, left
write-reference-run.js syntactically invalid for a whole session, and three
node --check runs reported it fine. Only launching sixteen agents would have
found out.
python3 scripts/workflows/dry-render.py write-reference-run '{"run":"...",
"indices":[0]}' wraps the body in an async function, stubs agent,
pipeline, log and args, and prints every brief. A parse error is a parse
error, and the brief can be read before the agents act on it. It immediately
found a second fault: the two reference posts the brief names by path,
union-find and Fenwick, had both been re-stamped by a reschedule, so three
runs of agents were told to read files that did not exist. Name a post by slug
in a brief, never by path.
The Go call graph is in the gate now, and it found three more
./scripts/check-reference-post.sh PATH runs it; land-post.sh runs that.
Its CALL GRAPH line is advisory and has to be read, because a called symbol the
post defines nowhere means the test suite does not build, and eighty posts
across six runs shipped that way before anyone extracted the Go.
Run 12’s check pass reintroduced three: nodes and sampleKeys in fixed
partitions, six names in two-phase commit, and play with a three-argument
fold in CRDTs. All three were posts the check agents had just finished and
reported clean, so re-run the gate over a whole run after its last check
agent lands, not only per post as it is written.
The CRDT one is worth keeping for a different reason. Its check agent reported
re-measuring “five propagation schedules left 24, 26, 31, 35 and 39 of the 40
elements alive” — but play, the function that produces those schedules, was
never printed in the post, so there was nothing to re-run. With the helper
written and the schedules named, the figures are 15, 22, 27, 34 and 38. A
check agent can report a figure as measured when what it measured was its own
reconstruction, and the missing-helper check is what exposes it.
Run 13 produced a defect the checker had to learn to tell apart: a family of
state variants, each described in prose as a delta against one that IS printed.
Row0 and Row3 against a printed Row5, NewMeter0 to NewMeter5P against
a printed NewMeter, Price2 to Price5, rateStep1 to rateStep6. Printing
six near-identical bodies in a post about readability argues against itself,
and two check agents said so unprompted, so the gate now excuses such a name
when BOTH hold: it is a printed name plus a digit suffix, and the post names it
in the prose outside any fence, which is where the delta is stated. A range
names its interior too, since “named NewMeter0 through NewMeter5” writes
only the endpoints. Across all 174 catalogue posts the filter removed exactly
the two reports it was written for.
The gate gives a group it has never heard of no shape check at all. The
heading and diagram counts come from a case on the group, and a group missing
from that case is simply not checked, so a half-written post with one heading
and one diagram came back ALL CLEAN and was committed. Add a new catalogue
group to that case in the same commit that adds it to RUNS.
A check agent caught a figure set that no run could produce
The sharpest catch of run 12. The hybrid logical clock’s contamination simulation reported 528 of 543 timestamps in the future, 45 messages rejected, and 0 of 360 on the healthy node, and its check agent showed that no single iteration count yields those three together: at 412 iterations the total is 543 but only 538 are in the future and 36 messages are rejected, and at 371 the healthy count is 360 with 30 rejections. The numbers were not stale, they were impossible. Re-run at the 2,000 iterations the rest of the post uses, they are 2,680 of 2,685, 227 rejections and 0 of 1,804, and the prose, a test-section log line and a diagram’s labels all carried the old set.
That post also had a wrong division repeated into its frontmatter: 76.4 against 22.0 described as “three and a half times”, where the read is 46 ns against 22 ns, so about twice.
A stale figure is a re-measurement. An impossible figure set means nobody ran the thing, and only re-running it finds that out. Six check passes in this run have now corrected figures; this is the one that proves the pass is not optional.
Never run two workflows over the same post: I destroyed verified work
The version-vector helper agent wrote CompareMaps, Prune and toMap,
and verified them hard: its Prune reproduced the post’s own published
line, Before 229, Equal 455, Concurrent 316, After 0, which is strong
evidence it had reconstructed the exact function the post was measured
with. While it was finishing, run 12’s check agent rewrote the whole
post, and I committed that rewrite as 87174298 under the message “The
version vector: print CompareMaps, Prune and toMap”. So the commit says
one thing and carries another, and
git log -S'func CompareMaps' --all finds the helpers in no commit
anywhere. They are gone.
My fault, twice over: I had a helper workflow and a write-and-check workflow running over the same post, and I wrote a commit subject from the task I had launched rather than from the diff in front of me.
The rule: a post is in exactly one workflow at a time. Regenerate the inventory after a run’s check pass lands, not before, and never feed a path to the helper workflow while its own run still has an agent on it. The agent that lost its work worked this out unprompted and stopped touching the file: “editing a draft mid-rewrite is what destroyed my first pass.”
The current draft owes a different and larger set: randVec, toDense,
vec, (v Vector) check and (d dense) merge. The gate reports two of
the five, because three are method calls and vec is missed for a reason
I have not run down. A checker-clean line on that post would still be a
test file that does not compile, which is the sharpest statement yet of
what the gate is worth on its own.
The replicated log measures with code it does not show
_posts/2030-05-29-the-replicated-log.md rests on a 400-generation
election simulation: three of its five columns moved when its check agent
re-measured them, which makes it the load-bearing measurement of the whole
post. The simulation’s code is not on the page, so a reader cannot re-run
it or see what it assumes.
Same class as Raft’s successors before the helper workflow printed one,
and the same remedy: show the simulation. Its agent said this was the one
thing it would change with another pass. Unpublished, dated May 2030.
The messaging gateway’s contract suite has a failing case as printed
_posts/2028-03-22-the-messaging-gateway.md prints a contract suite whose
rejects a window that closes before it opens case asserts
permits.ErrRejected, and neither the printed RequestInspection nor
RecordingNotifier validates the window. The case fails against both
implementations exactly as published. Fixing it means either adding the
validation or changing the test, both outside the helper workflow’s brief,
so its agent reported it and left it.
Validation is probably the right half to add, since the contract is asserting a rule the gateway ought to hold. Unpublished, dated March 2028.
Raft’s state counts: resolved, and the shape to watch for
_posts/2030-06-05-raft-the-readable-one.md asserted 385,629 states
under the real rules, measured against a successors the post never
printed. Once the helper workflow printed one, the post contradicted code a
reader could run. Two check agents then re-measured it independently, one
reporting 228,434 and one 279,174, and the post has settled on 279,174
states with 1,368 elections losing a committed entry under a length-only
comparison, body and introduction frontmatter agreeing. It is
self-consistent against its own code again.
Those figures are valid only for the successors currently printed. If
that block changes, they need re-measuring, and the agent that wrote them
said so.
Three measurements, three numbers, all the same order and all reproducing the qualitative claims. That is what a figure measured against unprinted code is worth, and it is the argument for printing the code rather than for trusting the figure.
The same shape, smaller, survives in quicksort: random 352481, ratio 3.74
against the printed instrumentation’s 358,342 and 3.68, 1.7% out. Its agent
left the author’s figure, which was right at that size, and it is still
there if anyone wants it exact.
Gate the staged content, not the working tree:
git add <path> && scripts/check-reference-post.sh --staged <path>. CI went
red on an unwrapped Go nested composite literal in a post the gate had passed
minutes earlier, because the check agent rewrote the file in between. Both
readings were true. --staged extracts each path with git show :path, so
the result describes what the commit will contain. This removes the race that
“a file going quiet is not an agent finishing” only asks you to remember.
Gate every post with scripts/check-reference-post.sh <path> before
committing it. One command for the whole per-file gate: six h3 headings,
two diagrams, Liquid safety, diagram text inside its viewBox, currency
markers, class-prefix uniqueness, and the Go call graph. It is the call graph
and the prefix check that CI cannot do, and both have found real defects.
The call graph filters Go builtins, func-typed parameters and fields, and named func types, so what it reports is worth reading. Remaining known false positives: a reference implementation the post describes but does not print.
It found a fifth undefined symbol after the four below, outcome in the
request waiting list, already merged to main when it was found. Sweeping the
four catalogue runs with it turned up more, untriaged, in the Algorithms and
Data Structures runs: countedBM and synth (string search), build,
ceil, collect, refLCP, refMatch, refSuffixArray (suffix arrays),
ceil, log2, sqrt (sketches), distCode, firstDiff, prose
(Huffman), and a dozen in Data Structures. Some will be reference
implementations described rather than printed; triage before fixing.
Class-prefix collisions are now a CI ratchet,
scripts/check-diagram-class-prefixes.py --strict. 27 prefixes are shared
across the corpus and listed in the script’s KNOWN; a new one fails the
build. The bridge post moved off bf-, which it shared with the bloom
filter. Clearing a KNOWN entry means renaming one side and deleting the
entry.
1b. Run 10, Algorithms: WRITTEN, and what the check pass measured
28 posts, four workflows, 48 agents, zero errors, 9.94M subagent tokens over about 2.5 hours. Reading times 43 to 60 minutes, median 49. Nothing left to do on the run.
The check pass is where the value was, and it reported in enough detail to change how runs 11 to 14 should be written. Four findings, all measured:
1. A write agent that says it ran the code often has not run the code it
published. At least five posts shipped Go that did not compile: missing
imports (slices.Reverse used, slices not imported), and helper functions
named in the prose and called by the shown tests but defined in no block
(EditTableColumnMajor, GreedyCoins, edgeList, Probe.String,
randomKeys). Every one was caught by a check agent extracting the published
blocks into a fresh module, which is a different act from the write agent
running its working copy. Keep the independent re-extraction; it is the only
thing that tests what the reader will copy.
2. Counts reproduce exactly. Timings do not. Across every post, the
deterministic figures came back right to the digit: comparison counts,
allocation counts, inversion counts, fuzz corpus sizes, node counts, stack
frame sizes, the exact strings a test prints. The nanosecond and millisecond
figures were wrong by 20 to 40% almost everywhere, and several ratios built
on them were not merely off but backwards: a cache penalty claimed in the wrong
direction, [][]int32 against CSR stated as 4% when it measures 16%, a
standard-library shuffle called faster when it is consistently slower,
int32-versus-int sold as “nearly a third” of the time when it is a memory
saving with no measurable time change. Agents repeatedly said the same thing
about why: this is a shared 4-core container, and one agent watched a 1M
shuffle swing between 27 and 67 ns a swap across identical runs, while another
measured a 15% spread on a difference the post asked the reader to believe to
two decimal places.
So the next brief should say: prefer a deterministic figure to a timing. Comparisons, allocations, bytes, operations and exact outputs are reproducible and were right. Where a timing is genuinely the point, quote a median with its spread and say the machine was shared, or quote the ratio only when it is larger than the noise.
3. False claims about Go itself survive a write agent and need the check.
Corrected in this run: that the compiler elides an unobserved for b.Loop()
body (Go 1.24 special-cases it and does no inlining inside it); that slices
grow by doubling (1.25x above the threshold, and a 26-allocation count proves
it); that goroutine stack depth is bounded only by memory (maxstacksize is
1,000,000,000 bytes, so about four million frames at 256 bytes); that
slices.SortStableFunc reaches stableOrdered (dead code); and the ordering
of NaNs under cmp.Less. Also a plausible-sounding GC claim: a [256]int32
table was said to give the collector 407 MiB to scan, when a pointer-free span
is noscan and the measured pause did not move.
4. The gap the check pass could not close. Five agents independently
flagged the same remaining fault as the one thing they would fix with another
pass: a post quotes figures from inputs it never shows. The Aho-Corasick
dictionary is never described, so its node counts cannot be reproduced; the
knapsack and LIS iteration counts depend on generators the post omits; the
chain-depth step rests on a chainCall that appears nowhere. A figure a reader
cannot reproduce is only marginally better than one nobody measured. The next
brief should require that any input a figure depends on is either shown or
described.
Two operational faults, both already fixed in the brief and worth re-checking:
an agent left a Go program writing 28.7 GB to stdout and spent the whole
session’s disk allowance, destroying three finished posts; and a check agent
wrote a go.mod into the repository root. Watch df -h / and git status.
2. Information architecture: agreed, partly built
INFORMATION-ARCHITECTURE.md is the design, and the decisions at the bottom
of it are taken, not proposed: three front doors (the blog, The Exam
Room, Cooking), The Catalogues as the new reference surface, archive kept
separate, practice merged into In Practice, Week Notes with Cooking. The
schedule section sets the week: four blog posts (story Tue, catalogue Wed,
explainer-or-In-Practice Thu, catalogue Fri), eight Exam Room on Mon/Wed/Fri/
Sat, one Cooking Sunday.
Build steps, in order. 1, 2 and 4 are done. 3, 5, 6 and 7 remain.
— written, parses, zero post edits._data/modes.yml— written, in CI, all 1,990 posts resolve, and tested by deletingscripts/check-modes.py --strictThe Floorfrom the data file and watching it fail.CLAUDE.mdandscripts/reschedule.pyread_data/modes.ymlrather than carrying their own narrative lists.— both written. The page reads its group list from/writing/the-catalogues/landing page; the group banner names the catalogue, not the series._data/modes.yml; the banner asks it whether a group is a catalogue. Not yet verified by a Jekyll build (bundler is absent in this container), so CI is the test: check the Build step on the PR.- A separate Exam Room feed, and the main feed filtered to the blog’s four publishing modes.
- The standing line on all 293 fiction posts.
- Re-lay the three queues onto the new week. Unpublished dates only;
python3 scripts/check-published.pyis the guard and nothing already out may move.
3b. Run 15, the organisational craft: specified, 13 of 33 agreed
PLANNED-REFERENCE-RUNS.md carries the spec. Craig’s four asks on 11 October
2026 (Six Thinking Hats and Minto, decision recording and team health, team
documentation and ways of working, project scoping and getting a backlog out
of a vague one-pager) are one subject: the written and facilitated craft of
working in an organisation.
The number is Craig’s to set. He agreed 13 against the first proposal; the three later asks took the list to 33, and the spec names which 13 to keep if that is still the number.
Two things the spec records that are worth knowing without reading it:
- The Workshop is the queue that needs this. 325 days of runway against Under the Hood’s 2,286, Consulting and Craft’s 2,146 and High Performance Teams’ 1,754. It runs dry 2027-09-01. Fifteen posts take it to about 430 days, so it still wants a restocking run of its own.
- ADRs are already taught as narrative and name no format, so the reference post is which format and which fields, linking back rather than repeating. If that reads thin, fold it into The Decision Log.
3. Next runs: all laid out post by post
PLANNED-REFERENCE-RUNS.md now carries a post list, a structure and a
per-post prompt for everything specified. Runs 11 and 12 are written; 13, 14
and 15 are ready to launch.
| Run | Posts | Shape |
|---|---|---|
| 13 Refactorings | 16 | Six-heading catalogue, Go. Diagrams are before/after with the mechanical steps numbered between. |
| 14 Resilience | 16 | Same, but every post’s diagram shows the failure rather than the success. |
| 15 Organisational craft | 33 | 15 Workshop (nine-heading canonical) plus 18 Consulting and Craft. |
Two corrections the layout turned up, both from probing rather than from the original paragraphs:
- Run 13 must not write a strangler-fig post. It already has three
titles, one of them
Technical Migration: The Strangler Fig in Go. The slot is now Branch by Abstraction, which does the same job at the same scale and has one mention and no title. - Run 14’s gap is sharper than “not covered”. Every one of its sixteen is
named in Exam Room prose and taught nowhere:
timeout229 mentions,jitter42,circuit breaker27,steady state27, all at zero titles. The exam posts say “put a circuit breaker here” and no post says what one is.
Budget about 258,000 subagent tokens a post. The theory streams (~90) are named and sized but not laid out; the wider shelf (224) is catalogued and unchosen.
What is outstanding on this repo, and what has been decided. Kept here because the alternative is a session task list, which dies with its container: when one restarted on 6 October 2026 the whole list went with it, and every “what’s left?” after that was answered by re-deriving it from the repo.
Every number here is measured, and every number here goes stale. The command that produced it sits beside it, so check rather than quote. Update this file in the PR that changes what it describes.
Decided
Exam order for the person who sits them: AIP-C01, then AIB-C01. This sets priority for anything reader-facing. AIB-C01 is publishing now and ends 16 Dec 2026; AIP-C01 has already published in full.
ANS-C01 is not being swept, and not being written off. Its exam retired on 31 December 2026, the track is parked, and its 114 posts are dated Mar to Jun
- A sweep is a point in time, so verifying AWS facts now for posts no reader sees for over two years means verifying them again nearer the date. The open question is the track’s future: revive it, reframe it as a standalone networking series, or fold parts into SAP-C03. Folding wholesale loses depth that is hard to rebuild, 35 posts touching BGP against SAP-C03’s 4 and 54 on Direct Connect against 18.
The daily re-sweep Routine is disabled. Three firings, nothing produced each
time. CLAUDE.md carries the diagnosis and what to try next if it is revived.
It had no work left to do once ANS-C01 became the only outstanding track.
Open, and the person’s call
A fifth evidence rule for a non-AWS primary source. Recorded at
EXAM-VERIFICATION.md, deliberately not written during a sweep because what
counts as a primary source is an editorial judgement. It is live: a WebFetch
returned an ISO/IEC 42001 control list the page does not support, inventing an
A.8.6 that does not exist. The agents reattributed rather than sourced it, which
is correct by instinct and not by rule.
Work
90 cooking images outstanding on 45 recipe cards
for f in _cooking/*.md; do img=$(grep -o 'src="/images/posts/[^"]*"' "$f" | head -1 | sed 's|src="/||; s|"||'); [ -n "$img" ] && [ ! -f "$img" ] && echo "$f"; done
grep -rl "TODO: generate the two required diffusion images" _cooking/ | wc -l
Every cooking card wants two images in images/posts/: <slug>.png in woodcut
for the card watermark and <slug>-stand-in.png in watercolour for the inline
figure. scripts/generate-image renders them through mflux and a local FLUX
checkpoint, so it only runs on Apple Silicon and cannot run in a cloud session.
Forty-five cards are waiting on it, 20 of them written for the 18 Oct to 2 Jan
meal weeks and the rest accumulated from earlier cloud sessions. Each carries a
TODO comment in place of its <figure> block, with the image subject recorded
in it, so the card reads correctly until the images land and the figure goes
back in. The backlog grows by a card every time one gets written in the cloud,
so it wants a batch run on the laptop rather than a card at a time.
scripts/generate-image _cooking/<file>.md --subject "<the dish>"
scripts/generate-image _cooking/<file>.md --subject "<the dish>" --style watercolour --output <slug>-stand-in.png
Prose length, 490 of 537 scenario posts over budget
python3 scripts/check-exam-prose.py --strict # the table
python3 scripts/check-exam-prose.py --over # worst first
_data/prose_budget.yml has an enforced: list of certs, and CI gates every
track on it. Nothing is in that list, which is why the overruns grew
unnoticed. The job is per track: trim, add the cert to enforced, repeat, so
each track that comes right stays right.
| cert | over / posts | median words (budget 2000) |
|---|---|---|
| AIP-C01 | 136 / 137 | 2,928 |
| SAA-C03 | 65 / 66 | 2,836 |
| SAP-C03 | 48 / 48 | 3,164 |
| SCS-C03 | 44 / 45 | 2,798 |
| DOP-C02 | 43 / 44 | 2,622 |
| AIF-C01 | 41 / 41 | 2,987 |
| ANS-C01 | 76 / 76 | 2,960 |
| AIB-C01 | 29 / 66 | 2,268 |
| CLF-C02 | 8 / 14 | 2,165 |
The overruns concentrate in ### The solution, running +92% to +240% against a
400-word budget, which is the section a revising reader re-reads. AIP-C01
first, then AIB-C01, per the exam order above.
What is open here is one question, and it is not a measurement. Measured 8 October 2026 across all 537 scenario posts:
- Exam level does not predict length. Foundational 2,839 words, Associate 2,836, Professional 2,921, Specialty 2,898: every group within 3% of the others. Business is the only one that differs, at 2,268, and that is because AIB-C01 is the one track that has been through the pass. So there is no level signal to build per-level budgets on, which is what an earlier session recommended off the medians alone.
- The number of options weighed does not explain it either. Options
correlate with
### The landscapeat r = 0.25, which is nothing at the post level. The bin medians look tidy (3 options 560 words, 9 options 882) only because binning averages the noise out. - At matched option count, the longer tracks are a flat 1.27x the
known-good length. Against the 50 purpose-written AIB-C01 posts, the
professional tracks run 1.27, 1.26, 1.27 and 1.28 at 4, 5, 6 and 7 options.
A constant multiplier is not more options; it is more words per option,
uniformly, which is either 27% padding or the extra mechanism
CLAUDE.mdsays professional posts carry.
The flatness is what makes it a judgement rather than a defect. Padding varies post to post; this does not. So the open question is whether ~2,850 words is right for a non-business scenario post, or whether every track should be cut to AIB’s ~2,200. The second reading is a 120,700-word cut across the corpus, and nobody should take that decision from a ratio.
Reproduce any of the three with scripts/check-exam-prose.py plus the option
counts from the Evaluation tables; the script’s docstring carries the
calibration history.
Where the in-flight runs got to
python3 scripts/run-status.py # the table, the faults, the next action
python3 scripts/run-status.py --write # regenerate RUN-STATUS.md
Run this first when picking the work back up. Seven workflows were commissioned to write 179 Thursday posts and a workflow’s journal lives under the session directory, so it dies with the container and is no use for resuming. The script derives everything from the posts in git and recomputes every check rather than trusting a record of one, so it works in a fresh session after a token limit or a week away.
_data/run_manifest.yml is the durable expected post list, and its
titles-from-slug entry names the three runs the title repair applies to.
scripts/fix-run-titles.py is that repair, idempotent because a check-pass
agent that rewrites a post restores the description that was in the title
slot. Runs 4 to 7 pass an explicit title and are not in the list.
The script works out each run’s phase from the manifest rather than from a
flag, so a missing reading_time is a fault only once every post in that run
is on disk. That matters while several runs are draining at different rates.
Reference runs 4 to 7, 98 posts, DONE 8 October 2026
All four finished the same day they launched. 98 posts, 196 agents, zero errors, zero skips, zero empty results, 25.30M subagent tokens, which is about 258,000 a post against the 230,000 the October runs measured.
| run | posts | agents | tokens | duration |
|---|---|---|---|---|
| 4, Under the Hood | 18 | 36 | 4.02M | 1h34m |
| 5, Data Structures | 22 | 44 | 6.78M | 4h08m |
| 6, Patterns | 28 | 56 | 7.10M | 3h08m |
| 7, Integration | 30 | 60 | 7.40M | 3h01m |
Run 5 took longest per post because its reviewers compiled the Go: several
extracted every snippet into a module and ran go build, go vet, gofmt,
go test -race and go test -fuzz, and one reported a benchmark confirming
the post’s own claim about allocations.
The four workflows wrote into disjoint slot ranges from
2032-04-29 to 2034-03-23: 18 Under the Hood posts across Air, The Archive,
Trades, a second Assessment block and Elections; Data Structures in Go
(22, each answering the same six questions, including how to test it); the
Gang of Four patterns (24, with an introduction that says plainly which of
them are workarounds for a missing language feature) plus the four hexagonal
architecture posts; and the Enterprise Integration Patterns (30 of the 65,
extensible). PLANNED-REFERENCE-RUNS.md holds the reasoning and the rules
each run inherits; _data/run_manifest.yml holds the post list;
scripts/run-status.py says where they got to.
Seven group pages were created by hand before launch, because no writing agent
may touch a shared file: air, the-archive, trades, elections,
data-structures, patterns, integration.
Why four workflows and not one. A workflow’s concurrency cap is
min(16, nproc - 2) and this box reports nproc 4, so one workflow runs two
agents however large the token budget is. 196 agents at two concurrent is
about nine hours; across four workflows it is about three.
The arithmetic the queue now carries: 81 plus 98 is 179 posts, and the Thursday queue takes one a week, so it runs from 2030-09-19 to March 2034. Shortening that means either a second Thursday slot at 20:25, which the mechanism already supports and which halves the runway at the cost of changing a documented cadence, or displacing another weekday. Still the person’s call, and until it is made the queue is laid one a week.
Four tracks unwritten, about 370 posts
SOA-C03, DVA-C03, DEA-C01 and MLA-C02 have no posts. They publish from Feb 2028
at the current spacing, MLA-C02 last in Feb 2029. Plan each one before writing
it (scripts/workflows/plan-exam-track.js), and size the post count by
objectives per post rather than by the domain’s exam weight.
What the October 2026 re-sweep says about where the effort goes: mechanism defects concentrate in the long formats, 92 in 93 SAA-C03 posts and 63 in 92 AIF-C01 posts, almost none in the quizzes. That is the defect class no read-through catches, because nothing on the page contradicts anything else.
Queue runway, and two shelves that were spent
python3 scripts/queue-runway.py --series # the table, measured
python3 scripts/queue-runway.py --below 365 # exit 1 if a queue is thinner
The numbers are not reproduced here, because a runway table in markdown
is wrong as soon as anyone schedules or re-lays a post. The script
classifies queues by importing reschedule.py’s own constants, so it
cannot drift from the way the laying code sees them.
As at 8 October 2026 the exam queue is the nearest cliff at 2029-06-20, ahead of Thursday at 2030-07-04 and Tuesday at 2031-12-09. A first pass at this section claimed Thursday was nearest, which was wrong and is exactly what the script exists to stop. Note that the exam figure is flattered by parked ANS-C01 sitting at the end of it, and that the four unwritten tracks fill slots inside that span rather than extending it.
Thursday is the queue with the shape problem rather than the shortest runway. Consulting and Craft runs out on 2029-01-04, The Workshop on 2029-03-22 and High Performance Teams on 2028-04-13, while Under the Hood runs to 2030-07-04, so the slot CLAUDE.md describes as mixed between four series is single-series for its last eighteen months: 73 Under the Hood posts from 2029 against 5 of everything else. Nobody decided to stop writing craft posts; only one of the four series had a shelf to restock from.
All five Under the Hood candidates from the first shelf were written (2030-06-06 to 2030-07-04), and both narrative candidates were written as The Shortlist (11 posts) and The Safety Case (9 posts). All three shelves now carry fresh candidates, marked as things to mull over rather than commitments. High Performance Teams has five posts and no shelf; whether it continues or folds into Consulting and Craft is an open editorial call.
Writing the current shelves
Commissioned 8 October 2026: write every live candidate on the three shelves, to the house style, and keep this table current as each one lands. A shelf entry is an outline; the post is the deliverable. Tick a row in the PR that adds the post, not before.
The Thursday stream is free from 2030-07-11, which satisfies the two candidates whose outlines ask to be scheduled after March and June 2030 (they want backlinks that publish then).
python3 scripts/queue-runway.py --series # has the slot moved?
grep -c '^## ' NEXT-*-IDEAS.md # what is still on a shelf
| # | post | series | slot | state |
|---|---|---|---|---|
| 1 | Reading Code You Didn’t Write | Consulting and Craft | 2030-07-11 | written |
| 2 | The Restore You Haven’t Tried | Consulting and Craft (Hands On) | 2030-07-18 | written |
| 3 | Test Data That Isn’t Production | Consulting and Craft (Hands On) | 2030-07-25 | written |
| 4 | When They Want a Date | Consulting and Craft | 2030-08-01 | written |
| 5 | Leaving Well | Consulting and Craft | 2030-08-08 | written |
| 6 | How Is a Forecast Made? | Under the Hood | 2030-08-15 | written |
| 7 | How Does a Battery Actually Work? | Under the Hood | 2030-08-22 | written |
| 8 | Where Does It All Go? | Under the Hood | 2030-08-29 | written |
| 9 | How Does a Vote Get Counted? | Under the Hood | 2030-09-05 | written |
| 10 | How Does a Drug Get Approved? | Under the Hood | 2030-09-12 | written |
All ten written, 8 October 2026, totalling 24,110 words: the five Craft
posts run 22 to 25 minutes and the five Under the Hood 28 to 35. Every one
comes back within budget on measure-post.py, with no real em-dashes, none
of the banned tics, and backlinks only to posts dated earlier than its slot.
The Craft and Under the Hood shelves now hold no live candidates.
The three narrative candidates are now written too, commissioned
8 October 2026: The Floor (9 posts, 2031-12-16 to 2032-02-17), The
Reproducible Result (9, 2032-02-24 to 2032-04-20) and The Standard of
Proof (9, 2032-04-27 to 2032-06-22). 27 posts, about 43,000 words, each
arc with a CANON file written before post one. The Tuesday queue now runs
to 2032-06-22, a 5.7-year runway, and NEXT-STORY-IDEAS.md holds no live
candidates: what is left on that shelf is the “kept warm” three (The
Inquiry, The Twenty-Year System, The Device).
Decisions taken on the way, since the shelf said to take them before post one. The Standard of Proof is standalone with a one-name seam: Relay is the customer whose supplier contract requires the report, and has no scene, no line and no named person, which gets the provenance subject matter without needing a finished narrative to have been sloppy. The Floor is its own arc rather than three posts bolted onto The Shortlist, and Sinead acquires agency through the super-user arc, which is what makes nine posts carryable. The Reproducible Result is standalone in Perth with an invented reef, programme and agency.
The fault worth remembering from this run: narrator-at-altitude leaks.
Eight sentences across the first two arcs commented on the shape of the
narrative (“this is the moment the story actually starts”, “the reason
this is the post it is”), which is the worst class in
NARRATIVE-CRAFT.md, and style-census.py --narrative does not grep for
“story” or “post” so it caught none of them. The sweep that found them was
grep -noiE '(the|this) (story|post|arc|shelf|narrative)' over the drafts,
and it is worth running on any narrative before it lands.
A note on reading these shelves: they are layered, newest section last, so an entry near the top may already be written. Tentpeg, The Shortlist and The Safety Case all read as open candidates and are all done. Check the series counts before treating a shelf entry as work.
Dependencies
Three open Dependabot PRs (#1838 actions, #1852 cdk, #1853 everything-else) and
9 advisories on main, 6 high and 3 moderate, as of 8 October 2026. The npm
advisories were last cleared on 1 October, so these are newer than that.
gh api 'repos/barkingiguana/barkingiguana.github.com/pulls?state=open'
Two of the three landed on 8 October; #1853 cannot.
- #1838 (
actions/checkout,setup-go,setup-nodev6 to v7) merged. - #1862, split out of #1853, added
"types": ["node"]to the threetsconfig.jsonfiles. None of them had atypesfield, so each relied on TypeScript including everything undernode_modules/@typesby itself. Merged, all three stacks deployed clean. - #1852 (
aws-cdk2.1143.0 to 2.1144.0) merged, aftertsc --noEmitandcdk synthcame back clean on all three apps. All three deploys succeeded.
#1853 is blocked on ts-node, not on TypeScript. Rebased onto the
tsconfig fix, all three apps typecheck clean at typescript@7.0.2. cdk synth
still fails: cdk.json runs the app through npx ts-node bin/app.ts, and
ts-node@10.9.2 reads ts.sys.fileExists off the TypeScript API, which
TypeScript 7 does not have, so it throws TypeError: Cannot read properties of
undefined before reading a line of the app. Reproduced with signup’s own
locked ts-node, not a hoisted copy. Merging it would leave the three deploy
workflows red on every push; infrastructure would be untouched, since a failed
synth deploys nothing, but the apps would have no working deploy path.
ts-node@10.9.2 is the current published release, so there is nothing to bump
it to. TypeScript 7 means changing how cdk.json runs the app, to tsx or
Node’s --experimental-strip-types, and that is its own change to verify.
@types/node 22 to 26 would land on its own; typescript is the stuck half.
A clean typecheck is the floor and cdk synth is the check that decides.
TS 7 passed tsc --noEmit on all three apps and failed synth on all three.
Anything touching these apps gets synthed before it is merged.
Merging anything under analytics/, contact/ or signup/ deploys it.
deploy-analytics.yml, deploy-contact.yml and deploy-signup.yml trigger on
a push to main touching their directory, so even a one-line tsconfig change
is three live CloudFormation updates. Batch changes to these apps rather than
landing them one at a time.
One agent’s Go program can spend the whole session’s disk
Run 10’s first attempt died this way on 9 October 2026, and it is worth knowing before the next run, because nothing in the output said “disk”.
The brief told each agent to build its Go in a scratch module and measure
rather than estimate, which is right. One agent left a program running, in a
directory it had called trace, writing to stdout. The Bash tool captures a
child’s stdout to a file under the session’s temp directory, so that stream
went to disk: 28.7 GB in one file, from one process, in about twenty
minutes.
The session’s writable disk is a fixed allowance, not the 252G df reports,
and once it was spent every agent’s writes failed. The damage was not a clean
stop: of seven finished posts, two were truncated at a 4 KiB boundary
mid-function and one was left at zero bytes. The four that survived are intact
and on-subject.
What made it hard to diagnose is that the symptom hides the cause. Every Bash
call returned the temp filesystem ... is full instead of its output, so df,
du and ls could not be read either. Freeing space did nothing, because the
process refilled it in seconds. The sequence that worked:
- Delete something blind, with output to
/dev/null, to buy one working command (rm -rf /root/.cache/go-build/*). - Stop every workflow, so nothing is competing.
- Sum the deleted-but-still-open files, which is where the space actually was:
lsof -nP | awk '/deleted/ {s+=$7} END {print s/1048576" MB"}'reported 56 GB. - Find the holder by walking
/proc/*/fdfor links marked(deleted), withstat -L -c %sfor the size. That named one PID and one 28.7 GB file. kill -9that PID.killalone did not stop it. Space returned at once.
The brief now wraps every Go command in timeout with its output piped
through tail, caps benchmarks at -benchtime 200x rather than a duration,
forbids go run of anything unbounded, forbids runtime/trace and the
profile flags, forbids printing inside a hot loop, and tells the agent to
remove its scratch directory. The check agent carries the same bound.
The general rule for any future run that lets an agent execute what it wrote: an agent’s subprocess output lands on the session’s disk allowance, so every command it runs needs a timeout and a capped output. A run that measures is better than a run that estimates, and this is what measuring costs to do safely.
Done, where the result is worth keeping
Every Patterns, Data Structures and Integration post carries a diagram.
for g in "Data Structures" Patterns Integration; do t=0; m=0; for f in $(grep -l "^group: \"$g\"" _posts/*.md); do t=$((t+1)); grep -q '<svg' "$f" || m=$((m+1)); done; echo "$g: $t posts, $m without"; done
28, 22 and 30, none missing. 36 were added at once, 26 of them in Patterns, because the Integration brief asked each post for a diagram and the Patterns brief did not: a spec fault, not a drafting one, and worth checking for in the next reference run’s brief.
Conventions came from the-aggregator: inline SVG, 1000-wide viewBox, a
per-post scoped class prefix, hard-coded hex rather than currentColor (510 of
546 SVG posts do this, and the site has no dark mode), and an aria-label that
describes what the diagram shows rather than naming it.
scripts/measure-diagram-text.py exists because of this run and now runs in
CI. Diagram text is placed by hand, so a line one clause too long is clipped at
the right edge and nobody notices until it is published; the script estimates
rendered width from Helvetica advance widths. It found six such lines that four
rounds of reading had not.
python3 scripts/measure-diagram-text.py _posts/*.md
Two thresholds, because the width is an estimate. clipped is past the
viewBox edge and fails --strict; tight is inside the 18px margin and is
only reported, since at that distance the overflow is as likely to be the
estimator’s arithmetic as the author’s. Four lines in the corpus sit in that
band, all 1 to 4px in, and none is clipped. The edge comes from each diagram’s
own viewBox: an earlier version hardcoded 990 and so false-positived all 342
of the 1100-wide diagrams while under-reporting the 1000-wide ones by 8px, and
an earlier one still matched the scoped class prefix with [a-z]+, so a post
whose prefix carried a digit matched zero text elements and passed silently.
The Applied Patterns structure is checked, not remembered
(python3 scripts/check-applied-patterns.py). The nine headings verbatim and
in order with no tenth, 3 to 6 properties each a handle then a question, a
#### Side by side table with one column per property, 4 to 6 diagrams that
are 1000 wide and aria-labelled with one each in The system as it stands,
How they line up and Building it, and backward-only series-link targets.
Every rule was tested by planting the fault and watching --strict fail. The
ten posts were verified by hand when they were written, in a script thrown
away afterwards, which is the failure CLAUDE.md names in its own words.
Column wording against the properties is not checked, because the headers are deliberate abbreviations so a row fits; column count is.
The takeaway spec now covers Applied Patterns. check-takeaways.py keyed
on cert_code, so it saw the Exam Room and nothing else and reported zero
faults across 553 posts while nine of the ten Applied Patterns posts carried an
over-long handle. 32 handles were shortened. Coverage is the SPEC_GROUPS set
in the script, and a post carrying the heading outside it is now listed at the
end of the report rather than passing by omission; one is,
gig-posters-from-the-listings-database, which predates the spec.
Eight of nine written tracks are at the 2026-10 verification standard
(python3 scripts/resweep.py --status). The outstanding 109 are all ANS-C01,
deliberately held.
What that sweep measured, over 7,338 AWS claims in 185 posts already recorded verified: 492 wrong facts, 155 mechanism defects, 51 unpublished figures, 20 availability defects, 85 arithmetic errors, and 15 posts clean. A four-post sample sizes a track to within about 25%, and only if it is stratified by format: AIF-C01’s two halves split by format and came in at 1.57 and 3.46 wrong facts a post.
The exam queue has a two-week gap between runs and SAA-C03 sits entirely
after Christmas (python3 scripts/relay-exam-queue.py --summary). The anchors
in EXAM_RUNS are derived from the gap rather than chosen, so changing the gap
means recomputing all of them.