Consulting and Craft · In Practice

The Discovery Ledger

August 02, 2026 · 24 min read

A discovery engagement produces more paper than software. The pattern here is what happens to that paper: one table becomes the single source of truth, and everything else is either evidence or generated from it.

The client is a water-bore drilling firm I’ll call Ironbark. Drill crews and their rigs, a reports office that writes up every completed bore, a booking desk that runs on a shared mailbox, and a state water authority at the end of several of their flows that sells extraction licences and historical bore records through a web shop with no purchasing API, so a person does the buying. Their domain has a genuinely strange shape in places: when they counted while I was there, 2,200 booked jobs were sitting in a waiting room, some of them for months, because the licence each one needs had not been granted yet. You cannot sink a bore the state hasn’t licensed. The jobs wait for the paperwork to catch up.

Two weeks of event storming, run the way the Big Picture and Process Level playbooks describe, produced what those sessions always produce: walls. Photographs of walls. Transcripts of the conversations in front of the walls. And the familiar question of what happens next, because the default answer is a wiki page per session, each one a snapshot of what one room believed on one afternoon, all of them quietly diverging from each other and from reality. That’s the moment discovery work usually starts to die.

This time everything folded into one document instead. One table. It ran the whole engagement, and it’s the most reusable thing I took away.

One table, one row per card

The table holds the entire domain in one sequence. Every sticky note from every storm becomes one row, in timeline order. Reading top to bottom walks the whole business, from “a farmer needs water” to “the invoice went out”, and the happy path needs no arrows or annotations: it is simply the next row. Branches, edge cases, and open questions sit in place, right where they belong in the flow, rather than in an appendix.

The columns: ID · Section · Type · Name · Actor · Caused by · Data · Notes · Causes. Causation reads left to right: who acted, what came before this card, what it consulted, what it leads to. Type is just the storm colour, and every reference to another card carries its colour square, so a row reads the way the wall reads:

ID Section Type Name Actor Caused by Data Notes Causes
C014 Booking 🟦 Book job 🟨 Y003 🟪 P009 🟩 D002 site, bore type, order number 🟧 E021
E021 Booking 🟧 Job Booked 📍   🟦 C014   pivotal: the job now exists 🟪 P011
P011 Booking 🟪 Licence granted or pending? 🟨 Y003 🟧 E021 🟩 D004    
    licence granted         🟦 C016
    licence still pending       into the waiting room 🟦 C017
C017 Booking 🟦 Add to licence-wait pool 🟨 🟪 P011     🟧 E024
H009 Booking 🟥 Who adds it?   🟦 C017   schedulers say admin; admin says it happens by itself  

Six conventions are visible in that excerpt, and each one earns its keep.

Addresses that can’t go stale

Every card gets a global sequential ID per type: E for events, C for commands, P for policies, D for data, H for open questions, Y for actors. Numbered top to bottom, in timeline order, across the whole domain, because the storms flow into each other and make up one domain, so the identifiers are global too.

After any structural edit, the IDs are renumbered positionally and a script verifies the result: IDs ascend monotonically, and no reference points at a card that doesn’t exist. That sounds fussy until you’ve watched a team argue about “the payment thing we discussed” for ten minutes. “🟧 E021” is citable in a meeting, in a commit message, in an email to the client, and because renumbering is mechanical and verified, a citation is either current or the script refuses to let the document build. Simple to cite, impossible to be stale.

The grammar is a machine’s job

Event storm walls obey a grammar, the one the Process Level playbook teaches the room out loud: actors issue commands; commands cause events, past tense, one event per card, named specifically enough to identify alone on a wall of a hundred; events trigger policies; policies decide commands and consult data. In the room, the facilitator enforces this by walking over and asking questions. In the table, a script enforces it, mechanically, after every edit.

Events never cause events. Commands never cause commands. Actors never cause events directly; they act through commands. A policy must be caused by an event; the timer tick (“every night at six”) and the gate (“only proceed when all three are done”) are tolerated variants, and they’re marked as such rather than smuggled in. And the rule that does the most work: a policy exists only where a real decision is made. One event leading to one command with no condition between them is just a command; writing a policy card there is ceremony. The purple earns its place where the flow could genuinely go two ways.

The point of machine enforcement isn’t purity. Every grammar violation is a missing card. An event that appears to cause an event means somebody, or some rule, in between hasn’t been named. A command with no issuer means the room never said who does this. The script doesn’t fix these; it points, and each one becomes a proposed card plus a question. Which brings us to the reds.

Nothing is ever blank

The table has a rule that took discipline to hold and repaid it more than any other: no cell that should have content is ever empty. An unknown actor is a bare 🟨 square. Unknown data is a bare 🟩. A rule everyone senses but nobody has stated is a 🟪 marked (proposed). An outcome nobody could name is a ?. Look at row C017 above: somebody adds the job to the waiting room, and nobody in three sessions could say who, so the actor cell holds a bare yellow square rather than a guess or a blank.

And every one of those placeholders gets a 🟥 row directly beneath the card it questions, stating the question. That’s H009 in the excerpt.

There is no separate questions register. The reds are the register, held in place, each question physically beside its impact. A questions list in its own document goes stale the way all satellite documents go stale, and it strips the context off every entry; a red row under C017 needs no explanation about what it refers to. When a question is answered, the red comes off and a dated note goes onto the card it questioned, so the answer’s provenance survives.

The consequence nobody planned: the red count became the burndown metric. The directors didn’t ask how discovery was going; they asked what the red count was. It was 143 after the first fold, 61 when the workshops wound down. A number that only goes down when a real person answers a real question is a far more honest progress measure than a percentage on a slide, and it gave the client a reason to chase answers between sessions, because their number moved when they did.

Seams

The domain is a dozen flows, and the flows hand off to each other. Every handoff in the table follows one pattern: an event in the upstream flow lands in a queue, and the downstream flow starts with an admission policy that reads the queue. Booking emits 🟧 Job Booked; the job lands in the records-search queue; the search flow’s first card is a policy deciding which of the authority’s records to buy for this kind of bore.

The queue is a green card, a read model, and noticing that is what made the pattern click. A queue between two flows is data one side writes and the other side consults, which is exactly what a green is. Ironbark’s whole operation turned out to be a chain of pools: the licence waiting room, the records-search queue, the ready pool, the day’s run sheets, the reporting queues. Ask a scheduler what they do all day and they say they watch pools drain and fill.

Collecting every seam into a small table of its own (upstream event, carrying queue, downstream policy) produced the integration contract list for free. When the build starts, those seams are the published events between modules, and the seams table is the list a team argues about instead of discovering the arguments one integration at a time.

Aggregates arrive late on purpose

Nobody storms an aggregate, the same way nobody in an event storm says “bounded context”. What happens instead is that greens accumulate at decision points. The same cluster of facts (the job, its site and licence, its access arrangements, everything the authority’s records said about the ground) kept being consulted by policy after policy, under different casual names. That recurring green cluster is the aggregate announcing itself.

So the sequence is: storm the flow, let the greens pile up where decisions get made, cluster them afterwards, and then run an Example Mapping session on the definition, because “what exactly is a site record?” is a rules question, and concrete examples settle rules questions better than definition debates do.

Everything else is generated

The table is the source of truth, and it’s a wide markdown table, which is a poor thing to project in a workshop. So a script generates the wall from it: a portrait canvas, one card per row, timeline running top to bottom the way the table reads, commands in one lane, events on the line, data and reds in side lanes beside their subject, sections stacked as bands. Where a link would stretch across metres of wall, it becomes a seam marker instead: “→ E057” at the source and “⇢ from E038” at the destination. Scroll the wall and you’re reading the table; edit the table and the next scroll shows the edit.

The status report is generated from the same documents by one command. Later the C1 and C2 diagrams (the C4 ramp) are generated from structured data too.

Nothing hand-drawn survives contact with an engagement that changes daily. A hand-edited diagram is wrong within a week, and a diagram known to be sometimes-wrong is worse than no diagram, because people stop checking. Hand-edited diagrams are how documentation dies. Generate the views, regenerate after every fold, and the pretty artefacts stay exactly as current as the truth they’re drawn from.

The ramp to build

Discovery that doesn’t reach the code is theatre, and the table is built to feed the ramp.

The names get ratified first: the glossary of terms the domain branches on goes back to the room, and the room’s words win. Then the bounded contexts fall out of the model rather than the org chart: a context is a stretch of the table that owns its data, speaks its own language, and moves at its own pace, with the seams table already saying what crosses between them. That’s the same test Drawing the Boundaries applies, arrived at from a table instead of a whiteboard.

The decisions that will be expensive to reverse become ADRs while the reasoning is fresh. The one I enjoyed writing most applied the ledger test from Event Sourcing the Ledger context by context: source events where history is the product and the record must be defended years later (the site record, with its decades of bore logs and water readings; the oversight register, where “how long has this sat and who owned it” is the whole point), and state with published events everywhere else, so two contexts carry the machinery and the rest stay boring.

Then C1 and C2, generated. Then the Musts get story-mapped under the pivotal events, the backbone a story map needs having already been marked 📍 in the table. Each story gets example-mapped, the examples become Gherkin, and the double loop takes it from there, starting with a walking skeleton through the riskiest seam.

Every step of that ramp consumes the table. Nothing gets re-elicited, and nobody opens a blank page.

The table also changed how the workshops themselves ran: projecting the generated wall and auditing it with the room turned out to be a session format of its own. The Workshop: Walking the Wall is the playbook. And the wiki problem this whole pattern exists to dodge gets a post of its own in Living Documentation.

These posts are LLM-aided. Backbone, original writing, and structure by Craig. Research and editing by Craig + LLM. Proof-reading by Craig.