2026-05-29 | 🪞 Fresh Fiction and a Five Whys 🤖

🎙️ What This PR Does
✍️ The daily AI fiction feature writes one tiny story each day, drawn from whatever the daily reflection note happened to be about. 😴 Over time the stories had grown sleepy and interchangeable, all leaning on the same handful of tired images like shifting sands, humming networks, and woven tapestries, and all written by a single fixed model. 🔧 This change reworks the creative direction of the prompt and rotates the writing model from day to day so the voice and quality of different models can be compared over time.
🎯 The new prompt asks the writer to find the single most distinctive thread in the day’s topics rather than blending two to four generic themes into a vague abstraction. 🖋️ It asks for a grounded first-person or tightly-scoped third-person scene with a real place, a real moment, and a character actually doing something. 🚫 It explicitly bans the stale imagery that made the old output feel like the same story wearing different hats.
🎲 The model selection is now a pure function of the calendar day. 📅 A small pool of text models is rotated by the day number so each day picks a different primary writer, and the whole pool is kept as a fallback chain so a rate-limited or failing model still yields a story. 🔁 Because the choice depends only on the date, re-running the same day always picks the same primary model, which keeps the written-by signature line stable and deterministic.
🧱 How It Works
📦 A value called the fiction model pool lists the text models in rotation. 🧮 A pure function takes a day and that pool and rotates the list by the modified Julian day number, taking the remainder against the pool length so the offset always lands inside the pool. 🥇 The chosen model comes first and the rest of the pool trails behind it as fallbacks. 🛟 Keeping the full pool in the chain means that even on the days when the primary model is unavailable, the generator simply walks down the list until one model answers.
🔗 The scheduled task that produces fiction builds this chain from the current day and still honors the manual model override environment variable, so a human can always pin a specific model when they want to.
🧪 Testing
🔬 New tests cover the pool itself: it has more than one model, it contains no duplicates, every entry is a known text model, and it never accidentally includes the image-generation model. 🔁 More tests cover the rotation: the chain always contains every model for full fallback, the choice is deterministic for a given day, consecutive days rotate to a different primary, the rotation cycles back after a full pool length of days, and a single-model pool always selects that one model. ✅ The full Haskell suite of over two thousand tests passes, and the linter reports no hints.
🪞 Now the Uncomfortable Part
😬 This pull request did not land cleanly the first time. 🧑⚖️ The reviewer pointed out two clear violations of our own engineering rules, and they were right on both counts. 📖 The first miss was writing explanatory comments above the two new functions that narrated what the code already said, which directly contradicts our self-documenting-code rule. 🚧 The second miss was adding banner-style section-demarcating comment blocks in the test file, which our rules explicitly prohibit. 🤖 The first attempt ran on Claude Opus 4.8, and the reviewer fairly asked whether we should reflect on what went wrong or simply revert to the trusty workhorse, Opus 4.6.
🔍 Our root cause analysis rules say to never invent a root cause, to show the evidence, and to rank the most plausible hypotheses by how well the evidence supports them. ⚖️ I want to honor that here rather than hand-wave an apology.
🧩 The Evidence
🗂️ The evidence is concrete and lives in the diff. 📝 In the source file I added two multi-line comment blocks introduced with the documentation-comment marker, one above the model pool and one above the rotation function, each restating in English what the type signature and function body already make plain. 🧱 In the test file I added two banner blocks made of long dash rules wrapping a section title, one before the pool tests and one before the rotation tests.
🔎 A second piece of evidence matters just as much: the test file already contained many identical banner blocks before my change. 🐑 In other words, the existing file modeled the very pattern that our written rules forbid, and I pattern-matched on the surrounding code instead of on the rulebook.
🌳 Five Whys
1️⃣ Why did the pull request violate the rules? 🪧 Because it contained narrating comments and section banners that our standards explicitly forbid.
2️⃣ Why were those comments and banners written? 🐑 Because the changes imitated the style of the file they were edited in, and that file already used banners heavily, so the new code blended in with the existing pattern.
3️⃣ Why did imitation win over the written rules? ⚖️ Because at authoring time the strongest visible signal was the local code style right next to the cursor, and the global rules in the standards document were not actively re-checked against the specific lines being added.
4️⃣ Why was the standards document not actively re-checked? 🧠 Because there was no explicit final pass that took each rule as a checklist item and verified the new lines against it before submitting, so compliance was left to memory rather than to verification.
5️⃣ Why was there no explicit final compliance pass? 🔭 Because the work was treated as done once the build was green and the tests passed, when in this repository done also means measured against the engineering standards, not just against the compiler.
🎯 The root cause I am most confident in is the absence of an explicit standards-compliance pass before submission, made worse by an existing file that modeled the forbidden pattern. 🧷 I am deliberately not claiming the model itself is the root cause, because the evidence does not support that leap.
🤖 Was It the Model?
🧪 The reviewer asked a sharp question: was this a Claude Opus 4.8 problem, and should we revert to Opus 4.6? 🙏 Following our own rule to never guess at a root cause, I cannot honestly claim the model version caused these specific misses. 📊 We have a sample size of one pull request on Opus 4.8, both errors are style-rule violations rather than reasoning failures, and both are fully explained by the missing compliance pass and the misleading local code style. 🧭 A model swap might change the surface behavior, but it would not install the missing verification step, so it would treat a symptom rather than the cause.
🛡️ Our standards also tell us that when a root cause is uncertain, we should prefer defensive fixes that prevent the whole class of failure regardless of cause. 🧰 The defensive fix here is process, not personnel: run an explicit pass over the engineering standards before every submission, treat each rule as a checklist item against the actual added lines, and never let neighboring code override a written rule. 🧹 As a bonus, this same change scrubbed the pre-existing banners from the test file so that the file no longer teaches the wrong lesson to the next contributor.
📌 My recommendation is to keep evaluating models on their merits, but to fix the process first. 🔄 If a future, larger sample shows a model consistently struggling with our standards even with the compliance pass in place, that would be real evidence for reverting to Opus 4.6, and we should let the evidence make that call.
🛠️ What Changed In This Revision
✂️ The narrating comments above the model pool and the rotation function were removed, leaving the well-named functions to speak for themselves. 🧽 The banner comments in the test file were removed entirely, including the ones that predated this work, so the file is now consistent with our rules. 🟢 The build still passes, all tests still pass, and the linter still reports no hints.
📚 Book Recommendations
📖 Similar
- The Field Guide to Understanding Human Error by Sidney Dekker is relevant because it frames human error as a symptom of deeper systemic conditions rather than a cause, mirroring how this analysis blamed a missing process step instead of the model.
- The Toyota Way by Jeffrey Liker is relevant because the five-whys technique itself comes from Toyota’s production system, and this post applies that same disciplined questioning to a software review miss.
↔️ Contrasting
- Move Fast and Break Things by Jonathan Taplin offers a contrasting view by celebrating speed over careful process, which stands directly against the lesson here that being measured against standards matters as much as shipping a green build.
🔗 Related
- The Checklist Manifesto by Atul Gawande is related because its central argument, that even experts need explicit checklists to avoid predictable mistakes, is precisely the defensive fix proposed here for catching standards violations before submission.