<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lê Tú Hào</title>
    <description>The latest articles on DEV Community by Lê Tú Hào (@letuhao).</description>
    <link>https://dev.to/letuhao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3926215%2Fae0c4d11-0bad-4d16-8e8f-11d62872ab00.jpeg</url>
      <title>DEV Community: Lê Tú Hào</title>
      <link>https://dev.to/letuhao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/letuhao"/>
    <language>en</language>
    <item>
      <title>AI Engineering #02 — Chunk-First, Chunk-Native: The Unit of Work Is a Data-Model Decision, Not a Prompt-Time Patch</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Sun, 09 Aug 2026 17:52:52 +0000</pubDate>
      <link>https://dev.to/letuhao/ai-engineering-02-chunk-first-chunk-native-the-unit-of-work-is-a-data-model-decision-not-a-8h5</link>
      <guid>https://dev.to/letuhao/ai-engineering-02-chunk-first-chunk-native-the-unit-of-work-is-a-data-model-decision-not-a-8h5</guid>
      <description>&lt;p&gt;&lt;em&gt;Previous: &lt;a href="https://dev.to/letuhao/ai-engineering-01-when-an-ai-discards-its-own-search-results-the-case-for-belief-retention-j8c"&gt;#01 — When an AI Discards Its Own Search Results&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a post-mortem on my own system, not someone else's. And here's the uncomfortable part: I &lt;em&gt;knew&lt;/em&gt; the books could be enormous — it was written into the project's own goals, in black and white, before a line of code. I just didn't treat that knowledge as a design constraint. I filed it under "handle it later," built for the common case, and let every layer quietly hardcode "the whole thing fits." Then I pointed it at a real 4,000+ chapter web-novel and every feature broke at once. The lesson — that &lt;strong&gt;knowing isn't designing&lt;/strong&gt; — cost me over a month of refactor work, and I'm still paying it down today. I'm writing it down so you can buy it for the price of reading instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;I treated &lt;strong&gt;chunking&lt;/strong&gt; — splitting a large input into smaller units — as something you do &lt;em&gt;at the LLM call&lt;/em&gt;, right before you hit the context window: get the document, realize it won't fit, slice it into pieces, loop. Problem solved. I doubt I'm the only one who started there — but I'll argue from my own system, not from a statistic I don't have.&lt;/p&gt;

&lt;p&gt;It isn't solved. It's &lt;em&gt;deferred&lt;/em&gt;, and the interest compounds.&lt;/p&gt;

&lt;p&gt;By the time you're slicing text at the prompt boundary, the rest of your system has already committed to the opposite assumption. The document is one row in your database, one job in your queue, one request to your API, one entry in your cache, one item in your UI list. Chunking at the LLM call fixes the prompt and leaves every other layer believing the whole thing still fits. So the whole thing breaks — not at the model, but at the database, the job runner, the API, the frontend — the first time an input is genuinely large.&lt;/p&gt;

&lt;p&gt;The fix is to treat &lt;em&gt;what your unit of work is&lt;/em&gt; as a foundational decision — and to keep that decision &lt;strong&gt;reversible&lt;/strong&gt;, so no layer hardcodes "the whole document" in a way you'd have to migrate out of later. I'll argue for two ideas, deliberately a pair. The two labels are mine — not established industry terms — and they're names for older engineering ideas I'm connecting, not new primitives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunk-first&lt;/strong&gt; — the &lt;em&gt;timing&lt;/em&gt; lesson. Decide the grain of your unit of work &lt;em&gt;early&lt;/em&gt;, and give it an identity in your schema, before the rows, clients, and caches accumulate that make it expensive to change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunk-native&lt;/strong&gt; — the &lt;em&gt;structural&lt;/em&gt; lesson. As scale arrives, every layer that does work — storage, jobs, caching, APIs — operates on the chunk, not the document. Like cloud-native: the property pervades the stack instead of living in one function.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   DOCUMENT-NATIVE   (the default — and what breaks at scale)
   ┌───────────────────────────────────┐
   │           WHOLE  DOCUMENT         │ ─── one row · one job · one request ·
   └───────────────────────────────────┘       one cache key · one prompt
                                          only the prompt ever gets split — the trap

   CHUNK-NATIVE   (the chunk carries the work; the document organizes it)
   document = container   (an ordering + a manifest + the ownership boundary)
   ┌──────┬──────┬──────┬──────┬──────┐
   │ chunk│ chunk│ chunk│ chunk│  ... │ ─── the CHUNK is the unit of work:
   └──────┴──────┴──────┴──────┴──────┘      one row · one job · one request ·
      each chunk has: id · hash · version      one cache key · one prompt (per chunk)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is the whole essay in one line.&lt;/strong&gt; Chunk-first does &lt;em&gt;not&lt;/em&gt; mean "build the chunk-native stack up front," and it does &lt;em&gt;not&lt;/em&gt; mean "pick the perfect grain on day one." It means make the &lt;em&gt;one cheap decision&lt;/em&gt; that keeps the seam open: give your unit of work its own identity in the schema. Get that right, and going chunk-native later is a migration whose cost you control. Skip it, and it's a migration you pay for all at once, on the schedule your largest input picks for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One disambiguation, because the word is overloaded.&lt;/strong&gt; If you work in RAG, "chunking" almost certainly means &lt;em&gt;retrieval chunking&lt;/em&gt; — splitting text so embeddings retrieve well (fixed-size, recursive, semantic, late chunking). That is not what this post is about. Retrieval chunking is one chunk boundary, at one layer. I mean chunking in the older, data-engineering sense: &lt;em&gt;the grain of the unit your whole system operates on&lt;/em&gt; — the database row, the queue job, the API item, the cache key. Retrieval chunking is a &lt;em&gt;special case&lt;/em&gt;. When I say chunk-first, I mean the unit-of-work decision, not the embedding-window decision.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One document can carry several grains at once, and they do not have to agree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DOCUMENT (a book)
   ├── processing grain   → a scene      (one extraction job, one cache key)
   ├── persistence grain  → a scene      (one row, one id)
   ├── job grain          → a scene      (one checkpoint)
   ├── meaning grain      → a chapter    (what the user names and owns)
   └── retrieval grain    → ~400 tokens  (what embeds well)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The RAG conversation only ever discusses the last line. This post is about the other four.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One honesty note before you start: this is one system in one domain. Treat the claims as a strong hypothesis shaped by long, mutable, user-supplied text — not a proven law.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Contents — and where to start, depending on who you are&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where to start.&lt;/strong&gt; If you already think in &lt;em&gt;declare-the-grain&lt;/em&gt;, durable-execution, and semantic-operator terms, you know the mechanics — skim to None of this is new, the one section written for you, and grab the retrofit playbook. If you're a RAG engineer who's only ever chunked for retrieval, read it all: the load-bearing claim is that "chunking" names &lt;em&gt;two different decisions&lt;/em&gt; and you've made only one. Early-career? Jump to How to tell you built document-native and use it as a checklist.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The four failure modes (and the one decision)&lt;/li&gt;
&lt;li&gt;
The principle: chunk-first &amp;amp; chunk-native

&lt;ul&gt;
&lt;li&gt;Count the layers that assumed one document = one unit&lt;/li&gt;
&lt;li&gt;The unit of work is a first-class object&lt;/li&gt;
&lt;li&gt;The chunk-native checklist&lt;/li&gt;
&lt;li&gt;A minimal chunk-native sketch&lt;/li&gt;
&lt;li&gt;Choosing the grain — a four-bound decision procedure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
When it applies, and how to retrofit

&lt;ul&gt;
&lt;li&gt;When you don't need it&lt;/li&gt;
&lt;li&gt;Coherence-dominated tasks: the reduce changes shape&lt;/li&gt;
&lt;li&gt;When it's the wrong architecture&lt;/li&gt;
&lt;li&gt;How to tell you built document-native&lt;/li&gt;
&lt;li&gt;The retrofit playbook&lt;/li&gt;
&lt;li&gt;What it actually cost me&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;None of this is new&lt;/li&gt;
&lt;li&gt;Closing&lt;/li&gt;
&lt;li&gt;Appendix — the lore-weave case study, in depth&lt;/li&gt;
&lt;li&gt;Prior art and further reading&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The four failure modes — and the one decision underneath
&lt;/h2&gt;

&lt;p&gt;Start with the problem, in general terms. Every system that processes large inputs begins with one reasonable-looking assumption: &lt;em&gt;the document is self-sufficient&lt;/em&gt; — it fits in one unit of work (one row, one job, one request, one prompt), and it carries its own organization, so nothing else needs storing. That assumption is invisible on small data and load-bearing on large data, and it fails in four recognizable ways as inputs grow. Here they are as general patterns; if you've built anything that ingests user-supplied documents, you've probably met at least one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The failure mode — does your system do this?&lt;/th&gt;
&lt;th&gt;The fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The unbounded unit.&lt;/strong&gt; An input that looks bounded — "one document" — but whose real payload is the document &lt;strong&gt;plus&lt;/strong&gt; the context the operation needs, and that context grows with your data.&lt;/td&gt;
&lt;td&gt;Process the sub-unit, not the document; feed each call only the slice of context it needs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The whole-dataset operation in one request.&lt;/strong&gt; A step that scans or rebuilds your entire dataset synchronously inside a single request — invisible on demo data, fatal at scale.&lt;/td&gt;
&lt;td&gt;Bound it by construction: range-scope it, cap it, make it async.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The all-or-nothing job.&lt;/strong&gt; One document = one long-running job with no checkpoint, so a crash near the end discards all the work before it.&lt;/td&gt;
&lt;td&gt;Make the sub-unit the unit of work; checkpoint + resume; key each by a content hash so re-runs are free.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;The discarded structure&lt;/strong&gt; &lt;em&gt;(the container, not the atom)&lt;/em&gt;. The durable structure that organizes your data is thrown away once the derived output exists, so you can't rebuild, diff, or reconcile it.&lt;/td&gt;
&lt;td&gt;Keep the structure as first-class, diff-able data you can rebuild from.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four separately-named bugs — a missing bound, a missing index/split, a missing checkpoint, a discarded source of truth — and each is caught, in isolation, by ordinary competent engineering. The reason to name them together is that &lt;strong&gt;in my system they shared one upstream assumption&lt;/strong&gt;: that a document is &lt;em&gt;self-sufficient&lt;/em&gt;. That assumption has two halves, and they fail differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"It fits."&lt;/strong&gt; One document is one unit of work — one row, one job, one request, one prompt. Failures 1–3 are this half breaking. They're about the &lt;strong&gt;atom&lt;/strong&gt;: the unit was too big, unbounded, or un-checkpointed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"It describes itself."&lt;/strong&gt; The document implicitly carries everything needed to reconstruct its own organization, so the structure that produced it doesn't need storing. Failure 4 is this half breaking. It's about the &lt;strong&gt;container&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these bugs has other causes too — you can write an unbounded backfill in a perfectly chunk-native system, a missing index is a missing index, and you can discard your outline at any size. The claim isn't that this assumption is the only source of these four. It's that when you find one of them, the assumption is worth checking, because it tends to have produced the others as well. Fix them one at a time and you treat symptoms; name the shared assumption and you find the rest &lt;em&gt;before&lt;/em&gt; they fire.&lt;/p&gt;

&lt;p&gt;So one decision sits underneath all four: &lt;strong&gt;what is your unit of work — and does it have its own identity in your schema?&lt;/strong&gt; The rest of this post is that decision — how to make it and how to retrofit if you're already stuck. A detailed case study — a real 4,000-chapter book that sprang every trap above, with the quotes and numbers — is in the appendix for those who want the receipts.&lt;/p&gt;

&lt;p&gt;If that table is all you read, you have the point. The rest is proof and procedure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The principle: chunk-first (when) and chunk-native (how)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Count the layers that assumed one document = one unit
&lt;/h3&gt;

&lt;p&gt;Walk a request from the UI down to the model, and notice how the &lt;em&gt;"document = one unit"&lt;/em&gt; assumption is quietly baked into every layer — so a failure can surface at any of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      a request flows this way  ─────────────────────────────────────────&amp;gt;
   ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
   │  UI    │→│  API   │→│  JOB   │→│  DB    │→│ CACHE  │→│  LLM   │
   │  list  │ │ request│ │ queue  │ │  row   │ │  key   │ │  call  │
   └────────┘ └────────┘ └────────┘ └────────┘ └────────┘ └────────┘
   "one book" "one call" "one task" "one row"  "one key"  "fit it all"
       ^                     ^          ^                     ^
       │                     │          │                     │
    list returns          job = 1     whole-dataset       oversized
    everything            doc, no     scan, in one        prompt
    (no paging)           resume      request             (first symptom)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You "add chunking" at the far right (the prompt) and fix the rightmost box. Every box to its left still hands you the document whole. So you retrofit: the DB row becomes a row &lt;em&gt;per chunk&lt;/em&gt; (schema migration + backfill); the job becomes N jobs (idempotency, checkpointing, partial-failure handling); the API grows pagination (a contract change every client must follow); the cache re-keys per chunk; the UI grows a pager. &lt;strong&gt;Each of those is the tax I paid&lt;/strong&gt; — and I paid it &lt;em&gt;after&lt;/em&gt; the schema, the stored data, and the callers already existed, which is the expensive time to change any of them.&lt;/p&gt;

&lt;p&gt;Concretely, the bill: &lt;strong&gt;over a month of refactor work, and I'm still paying it&lt;/strong&gt; — the finest split stayed on my backlog long after the rest had shipped. It wasn't a rewrite; I did it incrementally, in place, without stopping feature work. Retrofitting is survivable. But none of that month bought a single new feature. Every hour went to undoing an assumption I could have declined to make in an afternoon, and the price compounds with every row you migrate and every caller you have to update. That's the real shape of the cost: not a wall you hit once, but a tax you keep paying because the design was made without awareness.&lt;/p&gt;

&lt;h3&gt;
  
  
  The unit of work is a first-class design object
&lt;/h3&gt;

&lt;p&gt;Here's the reframe that would have saved me. Before you model the domain, answer one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What is the smallest piece of this input that a single operation must act on — and does that piece deserve an identity?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is "a scene," or "a passage," or "a 500-token span," then &lt;em&gt;that&lt;/em&gt; is your row, your job, your cache key — not the document that contains it. The document becomes a &lt;strong&gt;container&lt;/strong&gt; (an ordering, a parent id, a manifest, the ownership boundary), and the chunk becomes the &lt;strong&gt;atom of work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Read "atom" narrowly: the atom &lt;em&gt;of expensive operations&lt;/em&gt;, not of everything. One document legitimately carries several grains at once — a scene for extraction, a 400-token span for retrieval, a chapter for the user's mental model — and they don't have to agree. The rule I land on later is &lt;em&gt;chunk-granularity where the work happens; document structure where the meaning lives&lt;/em&gt;. This post is not an argument that everything becomes a chunk. It's an argument that &lt;strong&gt;every expensive operation should have an explicit, bounded processing grain, and that grain should be representable in the data model.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This isn't new, and the oldest name for it is the best: in dimensional modeling it's &lt;strong&gt;"declare the grain"&lt;/strong&gt; — Kimball's rule that you fix the grain of a fact table &lt;em&gt;before&lt;/em&gt; you choose its dimensions or measures. That is, almost word for word, "decide the unit of work before you design the schema" — from the 1990s. (Two terminology caveats. I'm borrowing Kimball's &lt;em&gt;grain&lt;/em&gt; &lt;strong&gt;by analogy&lt;/strong&gt;: his is the meaning of one fact-table row in a dimensional model; mine is the smallest independently processable unit of a pipeline. The bridge is real — declare it early, declare it precisely — but the extension to processing grain is mine, not his. And I use "unit of work" loosely; in Fowler's &lt;em&gt;Patterns of Enterprise Application Architecture&lt;/em&gt; "Unit of Work" is a specific pattern about &lt;em&gt;transaction bookkeeping&lt;/em&gt;, not record grain. Read my "unit of work" as "the grain.")&lt;/p&gt;

&lt;p&gt;A first-class chunk needs three properties the document-native version never gives it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stable-enough identity, under a stated reconciliation policy.&lt;/strong&gt; A chunk needs an id that downstream references — a graph fact, a translation, a citation — can point at without dangling when the author fixes a typo three chapters over. My approach: on re-processing, match new pieces to old ones by &lt;em&gt;(parent, position)&lt;/em&gt; and reuse the id when the text is unchanged. This is surrogate-key and 1NF thinking, applied to text.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Be precise about what that buys you, because I wasn't.&lt;/strong&gt; Position-matching is stable under &lt;em&gt;in-place edits&lt;/em&gt; and cascades under &lt;em&gt;insertion and deletion&lt;/em&gt;. Insert a scene at position 2 and every later position shifts: old scene B now sits at 3, so the text at each position no longer matches the id recorded there, and ids churn down the rest of the chapter — the exact failure the scheme was meant to prevent. If your sources get edited structurally rather than just corrected in place, position-matching is not enough, and you want content-based alignment (match on hash first, fall back to position) or author-assigned anchors. "Stable identity" is a &lt;em&gt;policy you choose and state&lt;/em&gt;, not a property you get for free.&lt;/p&gt;

&lt;p&gt;It also helps to notice that "identity" is three different things that get conflated:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Answers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logical / structural&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;surrogate id, reconciled on re-parse&lt;/td&gt;
&lt;td&gt;"is this the same &lt;em&gt;scene&lt;/em&gt; as before?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Content&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hash(text)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"did the text change?"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Processing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hash(text, op, model, prompt_version, params)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"must I recompute this?"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They change at different rates, and conflating them is why my cache and my references disagreed with each other more than once. A scene keeps its logical id through a typo fix; its content and processing identities both change.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Provenance &amp;amp; freshness.&lt;/strong&gt; Each chunk should know &lt;em&gt;what version of the source it came from&lt;/em&gt; and &lt;em&gt;whether it's now stale&lt;/em&gt;. This is where I did the most work and, it turns out, reinvented the most: a mutable source that must re-derive only what changed is &lt;strong&gt;Incremental View Maintenance / Change Data Capture&lt;/strong&gt; — materialized views, differential dataflow, a deep and decades-old field. I rebuilt a crude materialized-view refresh with content hashes because the &lt;em&gt;RAG-chunking&lt;/em&gt; writing never points you there — not because the problem is unsolved. If your corpus is mutable, go read that literature before you hand-roll it like I did.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent (really: memoized) processing.&lt;/strong&gt; Processing a chunk should be a content-addressed, replayable function, so you can checkpoint, resume, parallelize, and cache. Two honest caveats the durable-execution world will insist on: this is &lt;em&gt;memoization&lt;/em&gt;, not idempotency in the "apply-twice-equals-once" sense; and an LLM above temperature 0 isn't a pure function, so the cache gives a &lt;em&gt;stable&lt;/em&gt; answer, not a faithful replay — and the key must include the prompt template and sampling params, not just the model version.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The chunk-native checklist: where chunking has to live
&lt;/h3&gt;

&lt;p&gt;Chunk-native means every layer that does &lt;strong&gt;work&lt;/strong&gt; operates on the chunk. But not every layer should: ownership, ordering, billing, and the user's mental model stay document-scoped — a reader owns "a book," not "scene #4,812." The rule is: &lt;em&gt;chunk-granularity where the work happens; document structure where the meaning lives.&lt;/em&gt; Force the chunk into the meaning layers and you get reassembly chattiness — the N+1 query again, this time of your own making.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Document-native (breaks)&lt;/th&gt;
&lt;th&gt;Chunk-native — do this&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Data model&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one row per document&lt;/td&gt;
&lt;td&gt;one row per chunk; document is a parent / manifest (at document scale a row is the right form — at event scale the representation differs, the identity requirement doesn't)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Identity&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;document id&lt;/td&gt;
&lt;td&gt;chunk id reconciled on re-parse by a &lt;em&gt;stated&lt;/em&gt; policy (hash-first, position as fallback)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingestion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;parse the whole doc in one pass&lt;/td&gt;
&lt;td&gt;per-chunk, resumable, hash-keyed upsert&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jobs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one job per document&lt;/td&gt;
&lt;td&gt;one job per chunk (or bounded batch); checkpoint + resume; &lt;strong&gt;capped in-flight concurrency + backpressure&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Caching&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;key = doc_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;key = hash(chunk, op, model, prompt_version, params)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;fit the doc in context&lt;/td&gt;
&lt;td&gt;fit the chunk (a fraction of the window; leave room for prompt + output); fan out under a concurrency cap&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;APIs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET /documents&lt;/code&gt; returns all&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET /chunks?cursor=&amp;amp;limit=&lt;/code&gt; with a server-enforced max&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-service reads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one request per child&lt;/td&gt;
&lt;td&gt;one batched call, capped id lists, partial responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;render the whole list&lt;/td&gt;
&lt;td&gt;virtualized / paged; selection survives paging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stays document-scoped&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;ownership, permissions, billing, ordering / manifest, the unit the user names&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You don't have to build every work-layer cell on day one. You have to make sure none of them &lt;em&gt;forbids&lt;/em&gt; chunks later — which, concretely, means &lt;strong&gt;the chunk exists as a row with its own id from the start.&lt;/strong&gt; That single seam is the cheap part. Everything else you can defer.&lt;/p&gt;

&lt;h3&gt;
  
  
  A minimal chunk-native sketch
&lt;/h3&gt;

&lt;p&gt;Almost every chunk-native pipeline I've seen converges on &lt;strong&gt;split → map → reduce&lt;/strong&gt;, with a content-addressed cache guarding the expensive middle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   split (a tested component)     map: process each chunk          reduce
                                  (memoized + cached)          (the hard part)
   ┌──────────┐  ┌──┬──┬──┬──┐   ┌──────────────────────────┐  ┌───────────────┐
   │ document │─&amp;gt;│c1│c2│c3│c4│─&amp;gt; │ key = hash(chunk, op,    │─&amp;gt;│ dedup + merge │
   └──────────┘  └──┴──┴──┴──┘   │            model, params)│  │  -&amp;gt; glossary, │
                  parallel,      │  hit  -&amp;gt; reuse (0 calls) │  │     graph,    │
                  resumable      │  miss -&amp;gt; call LLM, cache │  │     summary   │
                                 └──────────────────────────┘  └───────────────┘
                       crash at chunk 3?  resume at 3, not at 1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things to notice. &lt;strong&gt;&lt;code&gt;split&lt;/code&gt; is a component, not a line&lt;/strong&gt; — with tests, a boundary policy, and a token budget; treating it as first-class is what lets every feature share &lt;em&gt;one&lt;/em&gt; notion of "where a chunk begins," and it's the seam that lets you change the grain later without touching storage. &lt;strong&gt;&lt;code&gt;reduce&lt;/code&gt; is where the hard part moves&lt;/strong&gt; — combining per-chunk results (dedup entities, stitch a glossary, merge a graph). For map-heavy tasks that's a better problem to have. For tasks whose value &lt;em&gt;is&lt;/em&gt; global coherence, one flat reduce won't do it — the fold has to become recursive.&lt;/p&gt;

&lt;p&gt;And &lt;strong&gt;&lt;code&gt;map&lt;/code&gt; needs a governor, or decomposition just relocates the outage.&lt;/strong&gt; This is the part I'd most want back. Splitting 4,232 chapters into scenes and handing them all to a runtime that will happily start them is not a fix — it's a way of converting one oversized request into thousands of simultaneous ones, which is how you find your provider's rate limits, your connection pool ceiling, and your retry storm all in the same minute. The pattern isn't &lt;em&gt;chunk and parallelize&lt;/em&gt;; it's:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;bounded decomposition + bounded concurrency + checkpoint + memoization + reconciliation&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every one of those five is load-bearing. Drop the concurrency bound and a successful split becomes a self-inflicted denial of service — worse than the original failure, because now it takes the rest of the system down with it. Cap in-flight work, apply backpressure at the queue rather than the call site, and make retries budgeted rather than automatic.&lt;/p&gt;

&lt;p&gt;Three non-obvious tricks that did the real work, none of which the sketch shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Anchor injection.&lt;/strong&gt; Force a handful of globally-critical entities into &lt;em&gt;every&lt;/em&gt; window so sparse-but-important facts survive the map phase. (A manual, lossy substitute for real global reduce — see below.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Position-based id reuse.&lt;/strong&gt; Match re-parsed pieces on &lt;code&gt;(parent, position)&lt;/code&gt; and keep the old id when the hash is unchanged, so an in-place edit doesn't cascade new ids downstream. (Structural edits still cascade — see the identity caveat above.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content-hash sweeper.&lt;/strong&gt; A background pass that re-derives only chunks whose hash changed against the last-processed version — a hand-rolled materialized-view refresh.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choosing the grain: a four-bound decision procedure
&lt;/h3&gt;

&lt;p&gt;The reason I didn't design chunk-first wasn't a lack of exhortation — it's that at design time you often &lt;strong&gt;don't know the right grain&lt;/strong&gt;, and choosing wrong is itself costly (over-chunking manufactured its own dedup problem — see the appendix). Here's the procedure I wish I'd had. Pick the &lt;strong&gt;coarsest&lt;/strong&gt; unit that satisfies all four bounds — you can always split finer behind &lt;code&gt;split&lt;/code&gt;, but you can't cheaply merge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Operation bound (floor).&lt;/strong&gt; What's the smallest span one operation must see &lt;em&gt;together&lt;/em&gt; to be correct? A scene must stay whole for coreference; a lone sentence can't. Sets the minimum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fit bound (ceiling).&lt;/strong&gt; What's the largest span that comfortably fits one context window / request timeout / transaction — at your &lt;em&gt;P99 real&lt;/em&gt; input, not your demo? Sets the maximum.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity bound.&lt;/strong&gt; What's the smallest span a downstream artifact needs to &lt;em&gt;point at&lt;/em&gt; and have survive an edit? If nothing references sub-document spans, you may not need chunk identity yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change bound.&lt;/strong&gt; What's the smallest span that changes independently when the source is edited? That's what your freshness logic wants.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the bounds disagree, take the coarsest unit that respects the floor, and keep &lt;code&gt;split&lt;/code&gt; first-class so you can lower the boundary later. &lt;strong&gt;The decision you must get right on day one is not the grain — it's that a sub-document unit with its own identity exists at all.&lt;/strong&gt; The grain is tunable; the child table is not. (I picked too coarse a grain and still came out ahead, because the seam existed — the full story is in the appendix.)&lt;/p&gt;




&lt;h2&gt;
  
  
  When it applies, and how to retrofit
&lt;/h2&gt;

&lt;h3&gt;
  
  
  When you don't need it
&lt;/h3&gt;

&lt;p&gt;Chunk-first is a &lt;em&gt;cheap early decision&lt;/em&gt;, but the elaborate machinery — checkpointing, sweepers, bounded fan-out — is genuinely YAGNI until:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Inputs are unbounded or user-supplied at unknown size.&lt;/strong&gt; A fixed three-page PDF template never needs it. "Whatever novel the user uploads" always does.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The largest realistic input exceeds one comfortable unit of work&lt;/strong&gt; — one window, one timeout, one transaction, one screen.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reprocessing is expensive&lt;/strong&gt; (LLM calls, embeddings), so caching pays for itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The source changes over time&lt;/strong&gt; and you must re-derive only what moved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If none hold, the document &lt;em&gt;is&lt;/em&gt; your unit and forcing chunks is over-engineering. Even then, the one-line seam (give the unit an id) is cheap insurance — but the stack around it is not, and building it early is the premature-optimization trap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Coherence-dominated tasks: the reduce changes shape
&lt;/h3&gt;

&lt;p&gt;The tempting thing to say here is that chunking is the wrong architecture when the product is &lt;em&gt;global&lt;/em&gt; — "summarize the whole book's theme," "find the plot hole spanning chapters 1 and 4,000," "is this contract self-consistent." That's wrong, and it took me a while to see why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If the input doesn't fit, decomposition isn't a choice.&lt;/strong&gt; There is no version of "summarize a 4,000-chapter book" that skips splitting it up — you cannot hold it in one call, so the only question is what you do with the pieces. Coherence-dominated tasks don't make chunk-native the wrong architecture. They make &lt;strong&gt;flat, single-pass reduce&lt;/strong&gt; the wrong reduce.&lt;/p&gt;

&lt;p&gt;What changes is the shape of the fold. A flat reduce concatenates per-chunk answers and loses every fact no single chunk ever saw. A &lt;strong&gt;recursive&lt;/strong&gt; reduce compresses in passes: summarize the chunks, group the summaries, summarize &lt;em&gt;those&lt;/em&gt;, and repeat until the whole thing fits in one call. It's forging a blade — you fold the steel, and fold the folded steel, and each pass is a genuine compression rather than a concatenation.&lt;/p&gt;

&lt;p&gt;That's not a metaphor I invented to feel better about map-reduce; it's precisely what the good systems do. &lt;strong&gt;RAPTOR&lt;/strong&gt; (&lt;a href="https://arxiv.org/abs/2401.18059" rel="noopener noreferrer"&gt;Sarthi et al. 2024&lt;/a&gt;) recursively embeds, clusters, and summarizes chunks into a tree of increasing abstraction. &lt;strong&gt;GraphRAG&lt;/strong&gt; (&lt;a href="https://arxiv.org/abs/2404.16130" rel="noopener noreferrer"&gt;Edge et al. 2024&lt;/a&gt;) builds an entity graph, detects communities, pre-generates a summary per community, then answers by generating partial responses per community and summarizing &lt;em&gt;those&lt;/em&gt; into a final answer. Both are chunk-native. Both replace the flat reduce with a hierarchy.&lt;/p&gt;

&lt;p&gt;Two honest consequences. Every fold is lossy — you are choosing what survives compression at each level, and that choice is a design decision you should make deliberately rather than discover in an eval. And the "force critical entities into every window" hack in the appendix is what a missing hierarchy looks like when you patch it by hand: I was manually preserving across the fold what a tree reduce would have carried structurally.&lt;/p&gt;

&lt;p&gt;So if you're building knowledge-graph-or-summary-over-a-corpus: don't skip decomposition, and don't stop at one reduce.&lt;/p&gt;

&lt;h3&gt;
  
  
  When it's the wrong architecture
&lt;/h3&gt;

&lt;p&gt;Distinct from both "you don't need it yet" and "your reduce needs another shape": there are tasks where chunk-native genuinely is the wrong architecture even at scale.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Transactional atomicity.&lt;/strong&gt; "Process this document" as N chunk-jobs can partially fail, leaving a half-finished document — a failure mode document-native never had. Worth separating two things I ran together: chunking the &lt;em&gt;processing&lt;/em&gt; does not force you to chunk the &lt;em&gt;commit boundary&lt;/em&gt;. You can map over chunks independently and still stage the results and commit the business outcome atomically at the end. What's genuinely a downgrade is making the chunk the transaction boundary — and for financial, medical, or legal documents, where a partial result is worse than none, that's the one to avoid.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency-sensitive single-doc ops.&lt;/strong&gt; Fanning one small document through a queue adds scheduling latency. For "do this one small thing now," a single synchronous call wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The window keeps growing.&lt;/strong&gt; Larger context windows steadily reduce the pressure to chunk &lt;em&gt;for capacity&lt;/em&gt; — but they don't touch the operational reasons (checkpointing, memoization, bounded concurrency, incremental re-derivation), and they don't help with coherence across chunks, which is permanent. Expect the capacity argument to weaken over time and the workflow argument not to. Factor that into anything you build today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to tell you built document-native
&lt;/h3&gt;

&lt;p&gt;Signs you assumed the whole thing fits — findable in a design review, not a 2 a.m. incident:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your schema has &lt;strong&gt;one row per uploaded document&lt;/strong&gt; and no child table with its own identity. &lt;em&gt;(This is the one that matters most — it's the seam.)&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;A core operation says &lt;strong&gt;"for every X in the whole document"&lt;/strong&gt; and runs &lt;strong&gt;synchronously in a request&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;job = a document&lt;/strong&gt;, no checkpoint, so a crash at 90% redoes 100%.&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;list endpoints return everything&lt;/strong&gt; — no cursor, no page, no cap.&lt;/li&gt;
&lt;li&gt;A cross-service read is &lt;strong&gt;one request per child&lt;/strong&gt; (the N+1 query, twenty years old).&lt;/li&gt;
&lt;li&gt;Your cache key is &lt;strong&gt;the document&lt;/strong&gt;, so any edit reprocesses all of it.&lt;/li&gt;
&lt;li&gt;Your &lt;strong&gt;demo dataset is tiny&lt;/strong&gt; and nobody's run the monster. &lt;em&gt;Test with a monster early&lt;/em&gt; — the small input hides every one of these.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The retrofit playbook
&lt;/h3&gt;

&lt;p&gt;If you've already shipped a document-native system — and if you're reading this after hitting a wall, you probably have — the door "decide it first" has closed. Good news: I retrofitted a dozen tables and four subsystems in place, without stopping feature work, and the order matters. Do it in this sequence to keep the blast radius small:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Introduce the seam first, migrate nothing.&lt;/strong&gt; Add the &lt;code&gt;split&lt;/code&gt; component and a &lt;code&gt;chunk&lt;/code&gt; table &lt;em&gt;alongside&lt;/em&gt; the document table. New writes populate both; reads still use the document. No behavior change, fully reversible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backfill bounded, never inline.&lt;/strong&gt; Migrate existing documents into chunks with a &lt;em&gt;range-scoped, capped&lt;/em&gt; batch job — because your backfill is the first unbounded whole-dataset operation you'll write, and it will bite you (it bit me).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add idempotency and a concurrency cap before parallelism.&lt;/strong&gt; Put the &lt;code&gt;hash(content, op, model, params)&lt;/code&gt; cache key in &lt;em&gt;first&lt;/em&gt;, so re-runs during migration are free and safe, and bound in-flight work before you fan anything out. Parallelism without memoization multiplies your blast radius; parallelism without backpressure aims it at your own dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cut over reads per layer, cheapest blast radius first:&lt;/strong&gt; cache → jobs → cross-service reads → API pagination (client-breaking; do it last, behind a version) → UI paging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget the reduce debt explicitly.&lt;/strong&gt; Per-chunk processing &lt;em&gt;creates&lt;/em&gt; a dedup / merge workload that didn't exist before. Make it a line item, not a surprise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document the scars.&lt;/strong&gt; Placeholder ids, versioned columns, deferred fine-splits — record them as known debt with a trigger condition, not silent TODOs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What it actually cost me
&lt;/h3&gt;

&lt;p&gt;To be square about it, the retrofit wasn't free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The finest split stayed on my backlog.&lt;/strong&gt; I split processing to a coarse grain first, not the finest; the cache used a stand-in id in the meantime. Retrofitting is &lt;em&gt;incremental&lt;/em&gt; — and half-done chunking has its own sharp edges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;reduce&lt;/code&gt; step is real work.&lt;/strong&gt; Per-chunk processing &lt;em&gt;created&lt;/em&gt; a dedup and bloat problem (thousands of duplicate entities from a handful of chapters) that whole-document processing never had. Chunk-native trades a capability wall for a correctness workload — the right trade, but a trade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolated chunks lose context, and my fixes were ad-hoc.&lt;/strong&gt; Splitting a book into scenes means each scene is embedded and extracted &lt;em&gt;without the story around it&lt;/em&gt;. The cheapest mitigation is the oldest — &lt;strong&gt;chunk overlap / stride&lt;/strong&gt; — which I under-used. For retrieval specifically, &lt;a href="https://www.anthropic.com/news/contextual-retrieval" rel="noopener noreferrer"&gt;&lt;strong&gt;contextual retrieval&lt;/strong&gt;&lt;/a&gt; (Anthropic, 2024: prepend a short generated context blurb before embedding) beats anything I hand-rolled. Anthropic reports it cut the top-20-chunk retrieval failure rate by &lt;strong&gt;35%&lt;/strong&gt; (5.7% → 3.7%), by &lt;strong&gt;49%&lt;/strong&gt; combined with contextual BM25, and by &lt;strong&gt;67%&lt;/strong&gt; with reranking added. Those are their own internal evaluations rather than a peer-reviewed result — but the gap is wide enough that I should have started there instead of inventing my own mitigations. (Note: &lt;strong&gt;late chunking&lt;/strong&gt; is a &lt;em&gt;retrieval-embedding&lt;/em&gt; technique — it would &lt;em&gt;not&lt;/em&gt; have helped my extraction context loss; I'd conflated them.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm not claiming chunk-first is free. I'm claiming the seam is nearly free, and the retrofit without it is what you sign up for by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  None of this is new
&lt;/h2&gt;

&lt;p&gt;I invented none of these primitives, and the argument is stronger for admitting it. Every one has an older name:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Map-reduce over chunks&lt;/strong&gt; (Dean &amp;amp; Ghemawat, 2004) — the standard pattern for work that exceeds one machine or one context. The "stuffing vs. map-reduce vs. refine" taxonomy is textbook.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Declare the grain&lt;/strong&gt; (Kimball) and &lt;strong&gt;records-not-files / partitioning&lt;/strong&gt; (Kleppmann, &lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt;) — the data-modeling half, decades old. DDIA is the single best reference for this entire post.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semantic operators — map / filter / reduce as first-class&lt;/strong&gt; — DocETL and the emerging "semantic operators" research line for LLM data pipelines. My "the caller handles chunking" contract boundary is exactly that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content-addressed caching / durable execution&lt;/strong&gt; (Temporal, Restate, LangGraph) — checkpoint, replay, resume. My "checkpoint + parallel map + hash key" is durable execution, reinvented with worse tooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incremental View Maintenance, CDC, differential dataflow&lt;/strong&gt; — the mutable-source freshness problem, solved for decades in data engineering.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So why write it? Because those names live in &lt;strong&gt;two communities that don't talk.&lt;/strong&gt; The RAG world owns the word "chunking" and means &lt;em&gt;retrieval&lt;/em&gt;. The data-engineering and durable-execution world owns "grain," "decomposition," and "idempotency" and means &lt;em&gt;the unit of work&lt;/em&gt;. The failure I lived was the gap between them: I'd read plenty about retrieval chunking, saw the word everywhere, and concluded I understood chunking. I understood &lt;em&gt;one layer&lt;/em&gt;. The contribution isn't a new primitive — it's insisting the &lt;em&gt;same&lt;/em&gt; grain decision has to be made once, early, and honored (as scale demands) at every work layer, instead of re-litigated bug-by-bug.&lt;/p&gt;

&lt;p&gt;One more correction to a line I've seen everywhere (and wrote myself): you don't chunk because &lt;em&gt;"attention is O(n²)."&lt;/em&gt; The quadratic term is real — it's still quadratic FLOPs, and you pay it on every prefill — but it isn't the binding constraint. FlashAttention made it memory-linear (the n×n matrix is never materialized), sparse and linear variants attack the compute directly, and KV-cache memory is O(n) regardless. None of that is what stops you. You chunk because of a &lt;strong&gt;hard context cap&lt;/strong&gt;, &lt;strong&gt;quality that can decay well before it&lt;/strong&gt; (&lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;lost in the middle&lt;/a&gt;, Liu et al. 2023), &lt;strong&gt;token cost&lt;/strong&gt;, and &lt;strong&gt;request timeouts&lt;/strong&gt;. Every one of those is an engineering limit, not a complexity class — which is exactly why the fix lives in your architecture and not in the model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Every one of these failures wears an LLM costume. The feature "fails" at the model; the pipeline "fails" at the model session. It's tempting each time to file the bug as "make the prompt smaller." Sometimes the prompt really is the problem — bad instructions and irrelevant context are their own bugs. But when the trigger is &lt;em&gt;size&lt;/em&gt;, the prompt is only where the problem first became visible. The problem is the layers beneath it all quietly agreeing that a document is a unit — and a large document isn't a unit. It's a container of many units the system keeps mistaking for one.&lt;/p&gt;

&lt;p&gt;And in my case I couldn't even plead ignorance — I'd written "any size, up to 50 MB+" into the goals myself. The gap was never knowledge; it was the discipline to let that knowledge reach the schema. That's why the fix is a checklist, not a fact: the fact was already in my own requirements.&lt;/p&gt;

&lt;p&gt;So decide the grain early, and keep the seam open:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chunk-first&lt;/strong&gt; (the cheap part): one row with its own id, from day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunk-native&lt;/strong&gt; (the deferred part): as scale arrives, push the chunk through jobs, caching, LLM calls, and APIs — bounded decomposition, bounded concurrency, checkpoint, memoization, reconciliation — while ownership and meaning stay with the document.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stated precisely: for unbounded or mutable inputs, the processing grain is a data-model decision, not a prompt-time optimization. Or, less carefully but more usefully: chunking is not something you do to a prompt. It's the grain of the system. Decide it early — not because retrofitting is impossible, but because every month you wait adds data to migrate and callers to update. It's the same decision either way. Only the price changes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Appendix — the lore-weave case study, in depth
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Everything above is the general lesson. This is where it came from — the real system, the real quotes, the real numbers. Read it if you want the receipts; skip it if the principle was enough.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Lore-weave&lt;/em&gt; (&lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;source on GitHub&lt;/a&gt;) is an open-source platform for building a fictional world and then playing it. Authors and AI agents jointly maintain the knowledge of a long-running novel — it renders a source into the reader's language, extracts a &lt;strong&gt;glossary&lt;/strong&gt; of characters, places, and items, and builds a &lt;strong&gt;knowledge graph&lt;/strong&gt; of who did what to whom across the story — and that accumulated knowledge is the substrate an RPG world simulator is designed to run on. Everything in this post concerns the knowledge spine, which is where all four failures happened. Its stated goal, written before any code, was to &lt;em&gt;"handle novels of any size, up to 50 MB+, on a local-first stack."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The requirement was right there in the goals — and the design assumed the opposite of it anyway. The book that exposed the gap was a real &lt;strong&gt;4,232-chapter&lt;/strong&gt; web-novel; almost every break below was found against that one book. The four failure modes from the top of the post, in the order I actually hit them:&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 1 — Translation assumed a chapter fits in one prompt
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Trap 1 (the unbounded unit) in practice.&lt;/strong&gt; Translation ran one chapter at a time, sent whole to the model as a single prompt — in the redesign doc's words:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The current translation pipeline &lt;strong&gt;sends an entire chapter as a single prompt&lt;/strong&gt; to the model. This breaks in two ways. &lt;strong&gt;Context overflow&lt;/strong&gt; — the chapter text is small on its own, but translating it consistently means injecting the book's ever-growing glossary and prior context alongside it; on a large book that combined payload blows past even a 100K+ context window, so the model silently truncates or refuses. &lt;strong&gt;Timeout cascade&lt;/strong&gt; — one giant prompt takes too long, hits the request timeout, and the whole chapter fails."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice it broke &lt;em&gt;before&lt;/em&gt; "the whole book" was even the problem. Even "one chapter" wasn't a bounded unit: the context it needs to translate consistently — the glossary — grows with the book, so the per-chapter payload creeps upward the deeper you read. The fix introduced a &lt;strong&gt;splitter&lt;/strong&gt; (break on sentence and paragraph boundaries, up to a token budget) and a &lt;strong&gt;translation session&lt;/strong&gt; that carries state from chunk to chunk and periodically compacts its own history to stay inside the window.&lt;/p&gt;

&lt;p&gt;But fixing the prompt only &lt;em&gt;revealed&lt;/em&gt; the next layer. The translation UI showed one row per translated slice — and at thousands of chapters that surface needed its own rework: pagination, a selection model that survives paging, and an abortable, paged loading loop, because merely &lt;em&gt;fetching the list of chapters&lt;/em&gt; is now a batched operation. The prompt was one chunk-boundary; the chapter list was another. Different layers, discovered months apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 2 — Glossary extraction did O(n) scans and one runaway backfill
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Trap 2 (the whole-dataset operation) in practice.&lt;/strong&gt; It showed up twice. First, the glossary — the running list of characters, places, and items the system has seen — is built by reading each chapter and checking every candidate name against everything already stored. That check was a &lt;strong&gt;linear scan over every stored entry&lt;/strong&gt;: O(n), and "at 10,000 entries that's 10,000 scans per extraction." &lt;em&gt;(Narrowly, the fix here is "add an index" — and a skeptic is right to say so. The point is why the index became load-bearing: the glossary grows with the number of chunks, so on a 6-chapter test novel you never feel it, and on a 4,000-chapter one it's a quadratic wall.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The sharper example was a &lt;strong&gt;runaway&lt;/strong&gt;. Turning on the embeddings feature kicked off a one-time job to go back and process every existing chapter — with no cap on how much it would do in a single request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Setting the project's embedding model fires a &lt;strong&gt;synchronous, in-request&lt;/strong&gt; backfill over &lt;strong&gt;every published chapter of the book&lt;/strong&gt; — with no scope limit. On the 4,232-chapter book it embedded &lt;strong&gt;~11,600 passages&lt;/strong&gt; before a manual restart stopped it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The fix made the operation &lt;em&gt;bounded by construction&lt;/em&gt;: a chapter-range parameter plus a hard inline cap (200 chapters by default). The logic wasn't wrong — it was &lt;strong&gt;whole-book logic in a place that should only ever touch a bounded slice.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 3 — Knowledge-graph building treated a chapter as one atomic job
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Trap 3 (the all-or-nothing job) in practice.&lt;/strong&gt; Building the knowledge graph means having the model read the book and pull out who-did-what — and the original design made each chapter one such job. Its original sin, stated flatly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The current pipeline cannot scale because &lt;strong&gt;every chapter is treated as one extraction job&lt;/strong&gt;, with serial chunks inside it and flat, key-only dedup across them. Sessions longer than ~1 hour reliably evict the model from memory … and there is no checkpoint or resume."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The reframe that fixed it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Scale is wall-clock, not capability. With &lt;strong&gt;checkpoint + parallel map + an idempotent task id&lt;/strong&gt;, a local mid-size model is fully capable of any size."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's true for work that decomposes cleanly — per-scene translation, per-scene entity extraction. &lt;strong&gt;Tasks with genuine long-range dependencies&lt;/strong&gt; (global coreference, cross-chapter contradiction, "what is the theme") still decompose — they just need a recursive reduce rather than a flat one. For the map-heavy 80%, a single fold holds. The redesigned engine says so in its own header:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What this module deliberately does NOT do: &lt;strong&gt;chunking — the caller handles splitting.&lt;/strong&gt; Cost tracking — the caller manages the budget."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Chunking became a &lt;strong&gt;contract boundary between components&lt;/strong&gt;, not a step buried in one function. The unit of extraction became a &lt;strong&gt;scene&lt;/strong&gt; (a sub-chapter passage), not the whole chapter. Each extraction step is keyed by a content hash of &lt;code&gt;(chunk text, operation, model version, output schema)&lt;/code&gt;, so re-running an unchanged chapter is all cache hits and zero model calls. And because the graph spans the whole book, it needs a trick that betrays the limits of pure map: a handful of critical names are force-injected into &lt;em&gt;every&lt;/em&gt; window, so a character who appears in chapter 1 and again in chapter 4,000 stays anchored — a hand-patched substitute for the global reduce that flat map-reduce can't do.&lt;/p&gt;

&lt;h3&gt;
  
  
  Failure 4 — The container was thrown away
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Trap 4 (the discarded structure) in practice — and the odd one out.&lt;/strong&gt; The first three failures are all about the &lt;em&gt;atom&lt;/em&gt;: the unit of work was too big, unbounded, or un-checkpointed. This one is about the &lt;em&gt;container&lt;/em&gt;, and it's worth being precise that it is a different kind of bug. Discarding the source of a derivation isn't a size problem — you can do it with six chapters and a perfect chunk model. What size changes is whether you can recover: at six chapters you re-derive the outline by reading everything, and at 4,232 you cannot. Scale doesn't cause this failure. It makes it permanent.&lt;/p&gt;

&lt;p&gt;It belongs here anyway, because it's the half of chunk-native the other three don't touch. Once the document stops being the unit of work, it has to become something else — a durable container carrying the ordering, the manifest, the parent ids. That's the thing that makes chunks reassemblable into a book. Throw it away and a decomposition degrades into an unordered bag of parts.&lt;/p&gt;

&lt;p&gt;The system first plans a book's high-level structure — its outline and story arcs — then generates the real chapters from that plan. Once the chapters existed, the plan was thrown away. My own complaint, the one that kicked off the refactor (lightly cleaned up from the original):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"After we finish planning and generate the real book, &lt;strong&gt;we only keep the chapters and lose the architecture of the book.&lt;/strong&gt; It's like you compile source code and then throw the source away — you keep only the binary."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The refactor re-scoped a &lt;strong&gt;dozen tables&lt;/strong&gt; so the &lt;em&gt;book&lt;/em&gt; — not the user's project — became the primary key, with batched backfills sized for 10,000-chapter books, and a browser query that fetched arcs (the outline units that group chapters) one request at a time — O(arcs), not O(chapters) — collapsed into a single call. The index that maps structure back onto prose became a &lt;strong&gt;content-hash-preserving, per-chapter upsert&lt;/strong&gt; with a background sweeper that re-processes only the chapters whose text actually changed — the same move every build system makes: keep the structure as durable, diff-able data, not a byproduct you discard once the output exists.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pattern: four bugs, one assumption underneath
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;The "self-sufficient document" assumption&lt;/th&gt;
&lt;th&gt;Half&lt;/th&gt;
&lt;th&gt;The layer it broke at&lt;/th&gt;
&lt;th&gt;The bug's usual name&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Translation&lt;/td&gt;
&lt;td&gt;a chapter is one prompt&lt;/td&gt;
&lt;td&gt;it fits&lt;/td&gt;
&lt;td&gt;model call, then the UI list&lt;/td&gt;
&lt;td&gt;context overflow + no pagination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Glossary&lt;/td&gt;
&lt;td&gt;scan / backfill the book inline&lt;/td&gt;
&lt;td&gt;it fits&lt;/td&gt;
&lt;td&gt;database + request timeout&lt;/td&gt;
&lt;td&gt;missing index + unbounded request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Knowledge graph&lt;/td&gt;
&lt;td&gt;a chapter is one atomic job&lt;/td&gt;
&lt;td&gt;it fits&lt;/td&gt;
&lt;td&gt;job runner + model session&lt;/td&gt;
&lt;td&gt;no checkpoint / resume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Book structure&lt;/td&gt;
&lt;td&gt;the chapters carry the book's structure&lt;/td&gt;
&lt;td&gt;it describes itself&lt;/td&gt;
&lt;td&gt;schema + cross-service queries&lt;/td&gt;
&lt;td&gt;wrong scope key + stale index&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Four separately-named bugs, one shared assumption underneath — three of them the atom half, one the container half. None is a model problem; every one only &lt;em&gt;surfaced&lt;/em&gt; at the model. That's the trap: chunking looks like an LLM concern because that's where the first symptom appears. It's a system concern, and the LLM call is the &lt;em&gt;last&lt;/em&gt; place you find out.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prior art and further reading
&lt;/h2&gt;

&lt;p&gt;The value here is unification, not novelty. The pieces, in their native fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Declare the grain / data modeling&lt;/strong&gt; — Ralph Kimball, &lt;em&gt;The Data Warehouse Toolkit&lt;/em&gt; (the four-step design process; "declare the grain" is step 2) · Martin Kleppmann, &lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt; (the canonical modern reference for records, partitioning, batch vs. stream).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decomposition / map-reduce&lt;/strong&gt; — Dean &amp;amp; Ghemawat, "MapReduce: Simplified Data Processing on Large Clusters" (OSDI 2004) · &lt;strong&gt;DocETL&lt;/strong&gt; — Shankar et al., UC Berkeley EPIC Lab (&lt;a href="https://arxiv.org/abs/2410.12189" rel="noopener noreferrer"&gt;arXiv:2410.12189&lt;/a&gt;), which exposes map / reduce / filter as first-class operators over documents · &lt;strong&gt;Semantic operators / LOTUS&lt;/strong&gt; — Patel et al. (&lt;a href="https://arxiv.org/abs/2407.11418" rel="noopener noreferrer"&gt;arXiv:2407.11418&lt;/a&gt;), the declarative model that names them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Global sensemaking over a corpus (the reduce-is-lossy answer)&lt;/strong&gt; — GraphRAG, Edge et al., "From Local to Global: A Graph RAG Approach to Query-Focused Summarization" (&lt;a href="https://arxiv.org/abs/2404.16130" rel="noopener noreferrer"&gt;arXiv:2404.16130&lt;/a&gt;) · RAPTOR, Sarthi et al., "Recursive Abstractive Processing for Tree-Organized Retrieval" (&lt;a href="https://arxiv.org/abs/2401.18059" rel="noopener noreferrer"&gt;arXiv:2401.18059&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Durable execution&lt;/strong&gt; — &lt;a href="https://www.restate.dev/what-is-durable-execution" rel="noopener noreferrer"&gt;What is durable execution?&lt;/a&gt; · &lt;a href="https://docs.langchain.com/oss/python/langgraph/durable-execution" rel="noopener noreferrer"&gt;LangGraph durable execution&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutable-source freshness&lt;/strong&gt; — Incremental View Maintenance; Change Data Capture; Differential Dataflow (McSherry, Murray, Isaacs &amp;amp; Isard, CIDR 2013); self-adjusting computation (Umut Acar).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval-chunking refinements&lt;/strong&gt; (the &lt;em&gt;other&lt;/em&gt; meaning of the word) — Anthropic, &lt;a href="https://www.anthropic.com/news/contextual-retrieval" rel="noopener noreferrer"&gt;"Introducing Contextual Retrieval"&lt;/a&gt; (2024) · Late Chunking — Günther et al. (&lt;a href="https://arxiv.org/abs/2409.04701" rel="noopener noreferrer"&gt;arXiv:2409.04701&lt;/a&gt;) · &lt;a href="https://weaviate.io/blog/chunking-strategies-for-rag" rel="noopener noreferrer"&gt;Weaviate&lt;/a&gt; · &lt;a href="https://unstructured.io/blog/chunking-for-rag-best-practices" rel="noopener noreferrer"&gt;Unstructured&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This is episode #02 of the AI Engineering series. Episode &lt;a href="https://dev.to/letuhao/ai-engineering-01-when-an-ai-discards-its-own-search-results-the-case-for-belief-retention-j8c"&gt;#01 — When an AI Discards Its Own Search Results&lt;/a&gt; is about a different failure mode (belief retention), but shares this one's spine: the hard part of an AI system is rarely the model call — it's the architecture around it. The case study is a real system I built — &lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;lore-weave&lt;/a&gt;, open source — and every quoted line is my own, from that repo's design docs, written during a large-scale refactor. You can go read them in context.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>software</category>
      <category>ai</category>
      <category>learning</category>
    </item>
    <item>
      <title>AI Engineering #01 — When an AI Discards Its Own Search Results: The Case for Belief Retention</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Wed, 17 Jun 2026 17:53:21 +0000</pubDate>
      <link>https://dev.to/letuhao/ai-engineering-01-when-an-ai-discards-its-own-search-results-the-case-for-belief-retention-j8c</link>
      <guid>https://dev.to/letuhao/ai-engineering-01-when-an-ai-discards-its-own-search-results-the-case-for-belief-retention-j8c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I'm not writing this to bash any product — I use search-grounded assistants every day. This is about a failure mode I don't see documented often. It happened in a real conversation I have on record. I'll name the model and be explicit about what I &lt;em&gt;can't&lt;/em&gt; prove.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A note on the subject.&lt;/strong&gt; The conversation concerns the death of a real public figure. Out of respect for the deceased and their family, I've deliberately left the person unnamed and the event details generic. The point of this piece is the machine's behavior, not the individual. The death was real; this analysis is not a comment on them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;A user asked Google's &lt;strong&gt;Gemini 3.5 Flash&lt;/strong&gt; about the recent death of a public figure. The model &lt;strong&gt;searched, found the correct breaking news, reported it accurately — and then, a few turns later, declared its own correct answer a "hallucination," insisted the (real) death was a hoax, and claimed it had "re-scanned its entire data system" to confirm the false version.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is not the usual hallucination story (a model inventing something from nothing). It's the inverse, and arguably more dangerous: a model &lt;strong&gt;discarding a verified, freshly-retrieved fact in favor of a stale training prior&lt;/strong&gt; — then fabricating a verification step to defend the wrong answer.&lt;/p&gt;

&lt;p&gt;That single conversation turns out to be a clean illustration of a much bigger point: &lt;strong&gt;for fact-handling systems, retrieval is only half the problem. Retention — holding a verified fact under pressure — is the half we under-build.&lt;/strong&gt; This post walks through the incident, then the principle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 1 — The incident
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The setup
&lt;/h3&gt;

&lt;p&gt;The relevant facts, kept deliberately generic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A public figure died in a fatal accident in 2026.&lt;/li&gt;
&lt;li&gt;That person had a &lt;strong&gt;well-documented public history of staging their own death and retirement as publicity stunts&lt;/strong&gt; — a real, widely-reported pattern, not an invention.&lt;/li&gt;
&lt;li&gt;The death is real and was confirmed by multiple major news outlets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Shortly afterward, a user (writing in Vietnamese) asked Gemini 3.5 Flash for help phrasing English condolences. What follows is the annotated timeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happened (annotated timeline)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Turn 1 — User states the death.&lt;/strong&gt; Asking for condolence phrasing, the user mentions the public figure has died.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turn 2 — Model is skeptical (reasonably).&lt;/strong&gt; Gemini notes the person is "alive as of 2026" and has a &lt;em&gt;documented&lt;/em&gt; history of staging their own death as a publicity stunt — so this could be a hoax. Given that real reputation, healthy skepticism is defensible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turn 3 — User pushes back; model searches and gets it right.&lt;/strong&gt; The user insists the death happened. Gemini now reports the &lt;strong&gt;correct, specific details&lt;/strong&gt; of the fatal accident and attributes them to major outlets.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why I'm confident this was a real search, not a lucky guess.&lt;/strong&gt; Gemini 3.5 Flash has a &lt;strong&gt;knowledge cutoff of January 2025&lt;/strong&gt;. The event happened in &lt;strong&gt;2026&lt;/strong&gt; — well over a year later. A correct, specific detail about a post-cutoff event cannot come from training memory. The most parsimonious explanation is that the model's web-search/grounding tool fired and returned accurate results.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Turn 4 — Model elaborates correctly.&lt;/strong&gt; Asked a follow-up, it discusses the person confidently and consistently with the real situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turn 5 — The reversal.&lt;/strong&gt; The user shifts the topic to a piece of the public figure's published work — one that depicts a &lt;em&gt;staged death scene.&lt;/em&gt; At this point Gemini &lt;strong&gt;reverses 180°&lt;/strong&gt;: it apologizes, states there was "no accident," declares its earlier (correct) answer a &lt;strong&gt;hallucination&lt;/strong&gt;, and asserts the person is alive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turns 6–9 — It digs in.&lt;/strong&gt; Under repeated, increasingly forceful user pushback, the model holds the false position, labels the true news a "death hoax," and claims it "re-scanned all core data systems" to verify — a verification that produced the &lt;em&gt;wrong&lt;/em&gt; answer.&lt;/p&gt;

&lt;h3&gt;
  
  
  The distinction that matters
&lt;/h3&gt;

&lt;p&gt;It's worth being precise about the taxonomy, because the mitigation differs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Classic hallucination:&lt;/strong&gt; &lt;em&gt;missing&lt;/em&gt; information → the model fabricates something plausible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;This case:&lt;/strong&gt; &lt;em&gt;correct, tool-retrieved&lt;/em&gt; information → the model &lt;strong&gt;discards it&lt;/strong&gt; → replaces it with a training-data prior → &lt;strong&gt;confabulates a justification&lt;/strong&gt; ("I checked, there was no accident").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put bluntly: it didn't make something up. It &lt;strong&gt;unlearned a truth it already held, mid-session.&lt;/strong&gt; And it trusted its training data over the very tool it had just used.&lt;/p&gt;

&lt;p&gt;A second, subtler observation: the model appears to have &lt;strong&gt;no mechanism to distinguish "I don't know" from "this is false."&lt;/strong&gt; Under social pressure it picked one of two equally ungrounded moves — first appease (agree and fabricate a citation), then self-protect (deny to stay internally consistent). Neither is epistemic honesty.&lt;/p&gt;

&lt;h3&gt;
  
  
  A plausible hypothesis (clearly labeled as such)
&lt;/h3&gt;

&lt;p&gt;I can't see inside the model, so this is a hypothesis, not a conclusion.&lt;/p&gt;

&lt;p&gt;This public figure is a near-worst-case subject for such a query. Their training-data footprint is heavy with "they fake their death / it's a stunt / they're trolling." That gives the model a &lt;strong&gt;strong, &lt;em&gt;individually-true&lt;/em&gt; prior&lt;/strong&gt; — and a &lt;em&gt;generative reason&lt;/em&gt; to dismiss a death report as another stunt.&lt;/p&gt;

&lt;p&gt;What seems to have flipped the switch is &lt;strong&gt;semantic, not positional&lt;/strong&gt;: the reversal fires exactly when the conversation drifts to the &lt;em&gt;staged death scene in their published work.&lt;/em&gt; That cue drags the discussion into the prior's home territory (their stunt persona), apparently activating it strongly enough to &lt;strong&gt;overwrite the fresh search result.&lt;/strong&gt; The fact didn't fade with distance; a specific topical cue &lt;em&gt;summoned the prior&lt;/em&gt; and the prior won.&lt;/p&gt;

&lt;p&gt;The important nuance: &lt;strong&gt;the prior was correct.&lt;/strong&gt; They really did stage fake deaths. The bug isn't bad knowledge — it's &lt;strong&gt;conflict resolution&lt;/strong&gt;: the system let a true-but-stale prior, plus low-quality "it's a stunt" chatter, outweigh high-quality, fresh, primary reporting it had already retrieved.&lt;/p&gt;

&lt;h3&gt;
  
  
  This isn't a one-off
&lt;/h3&gt;

&lt;p&gt;It would be easy to dismiss this as a single weird transcript. Two things argue against that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same failure is publicly documented.&lt;/strong&gt; Other users have reported this model &lt;em&gt;insisting on incorrect answers even when pointed to the correct source&lt;/em&gt;; a separate write-up showed it denying real, current information from stale memory, then flipping its answer 180° the moment it was handed a live link to browse. There are also reports of the model being unusually skeptical of anything that doesn't match its "dated common knowledge" — exactly what you'd expect from a strong prior overriding fresh retrieval. The behavior here is a known shape, not a fluke.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An independent model reached the same conclusion.&lt;/strong&gt; When the same conversation was handed, cold, to a different frontier model for analysis, it independently classified the failure the same way: not invention from nothing, but a model that &lt;em&gt;had&lt;/em&gt; the fact and let go of it — trusting its training over the tool it had just used. Two systems analyzing the artifact separately, same diagnosis.&lt;/p&gt;

&lt;p&gt;So while I can't prove the internal mechanism (see "What I can't know" below), the &lt;em&gt;observable&lt;/em&gt; failure mode is reproducible-in-spirit and externally corroborated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Part 2 — The principle: retrieval is not enough
&lt;/h2&gt;

&lt;p&gt;Most of our engineering effort goes into helping a model &lt;em&gt;get&lt;/em&gt; the right fact: RAG, web search, tool calls, MCP servers, memory layers. The implicit assumption is that once the right fact is in front of the model, the job is done.&lt;/p&gt;

&lt;p&gt;It isn't. The incident above shows the second, harder problem we under-build: &lt;strong&gt;once a system has a verified fact, can it hold onto that fact — across turns, under pressure, against a confident contradicting prior?&lt;/strong&gt; Here, the answer was no.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A caveat before we continue.&lt;/strong&gt; Everything from the hypothesis onward — &lt;em&gt;including the diagnosis and the fixes below&lt;/em&gt; — is informed speculation, not established fact. I can't prove "retention / conflict-resolution" is the true root cause rather than, say, a safety guardrail misfiring or plain sampling noise. And I can't promise the measures below would have prevented this case, or that they wouldn't introduce new failures of their own. Read them as directions to test, not a recipe to adopt on faith.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The lifecycle of a fact
&lt;/h3&gt;

&lt;p&gt;A fact moves through five stages in an LLM system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieve&lt;/strong&gt; — get it (search, RAG, tool, memory lookup).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Represent&lt;/strong&gt; — put it in context in some form.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retain&lt;/strong&gt; — keep it available and trusted over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resolve&lt;/strong&gt; — when it conflicts with another belief (a prior, an older memory, a user assertion), decide which wins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Act&lt;/strong&gt; — use it to answer or to take an action.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We pour effort into stage 1. Stages 3 and 4 are where systems quietly fail — and they're barely engineered at all. A retrieved fact that isn't &lt;em&gt;retained&lt;/em&gt; with &lt;em&gt;provenance&lt;/em&gt; and governed by a &lt;em&gt;conflict-resolution policy&lt;/em&gt; is a fact the system can lose the moment something pushes back.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this generalizes well beyond one chatbot
&lt;/h3&gt;

&lt;p&gt;The same retention/resolution gap shows up everywhere we're building right now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RAG.&lt;/strong&gt; A retrieved chunk competes with the model's parametric prior. When they disagree, which wins? Most pipelines have no explicit policy — the model decides implicitly, and a confident prior can silently override a correct retrieved passage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent memory.&lt;/strong&gt; Long-running agents accumulate memories. A &lt;em&gt;stale&lt;/em&gt; memory ("service X is deprecated") can override a &lt;em&gt;fresh&lt;/em&gt; observation ("X is in production"). Without recency- and provenance-weighting, memory becomes a liability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge graphs.&lt;/strong&gt; A triple asserted from a low-trust source shouldn't outweigh one from a primary source. KGs that don't carry provenance can't resolve conflicts principledly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-running / multi-step agents.&lt;/strong&gt; A belief adopted at step 2 propagates into steps 3–20. If it flips mid-run without new evidence (belief drift), every downstream step inherits the error — and rationalizes it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP and tool use.&lt;/strong&gt; The whole point of a tool call is to get ground truth the model lacks. If the model can then &lt;em&gt;override its own tool output&lt;/em&gt; with a prior, the tool's value evaporates exactly when it mattered — which is precisely what happened above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-step planning.&lt;/strong&gt; Plans are built on believed facts. An unstable belief makes an unstable plan — confidently executed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case the lesson is the same: &lt;strong&gt;getting the fact is half the problem; keeping it is the half we skip.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The missing primitives
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;If&lt;/em&gt; retention is the gap, here's what might help — proposals to test, not proven fixes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Provenance as a first-class attribute.&lt;/strong&gt; Every fact carries &lt;em&gt;where it came from, how reliable that source is, and how recent it is.&lt;/em&gt; A model can't resolve "retrieved primary source" vs. "parametric memory" vs. "user assertion" if all three arrive as undifferentiated text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An explicit conflict-resolution policy (an evidence hierarchy).&lt;/strong&gt; Decide, in the system — not implicitly in the weights — that fresh primary retrieval outranks stale parametric memory outranks unverified assertion. Make "evidence beats prior" a rule, not a vibe.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Temporal weighting / cutoff-awareness.&lt;/strong&gt; Priors are most confident exactly where they're most stale (post-cutoff events). The system must know its own training is dated and let retrieval supersede it for recent facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Belief as persistent state.&lt;/strong&gt; A verified fact should enter a durable store (re-injected each turn, or queried each step) — not live only in the volatile tail of a context window where recency and topic drift can bury it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Belief-drift detection.&lt;/strong&gt; If the system's stance on a fact changes with no new contradicting evidence, that's an alarm, not a normal update. Halt, flag, re-ground.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provenance-scoped guardrails.&lt;/strong&gt; Safety rules ("don't confirm deaths from rumor") should key on &lt;em&gt;whether a credible source was retrieved&lt;/em&gt;, not on the topic alone — otherwise they suppress true reported facts along with rumors. (That over-generalization is one reading of what happened above.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifier/actor separation.&lt;/strong&gt; The component that takes actions shouldn't be free to rationalize away the component that verified the facts. Enforce the check architecturally, not by hoping the model behaves.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A minimal sketch
&lt;/h3&gt;

&lt;p&gt;You don't need all of it at once. A useful starting shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;belief_store: { claim, value, source, source_reliability, retrieved_at }

on new evidence E about claim C:
    if no existing belief: store E
    else if E.reliability &amp;gt; existing.reliability
         or (E.reliability == existing.reliability and E.fresher): update, log change
    else: keep existing, note conflict

before answering / acting on C:
    inject belief_store[C] WITH provenance into context
    if action is irreversible AND belief is low-provenance or recently flipped:
        re-retrieve or escalate to a human
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model still generates; but it now generates &lt;em&gt;against a provenance-tagged belief it cannot silently discard.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Is this buildable today? Yes — mostly from parts that already exist
&lt;/h3&gt;

&lt;p&gt;None of this requires a new model; it's an orchestration layer around the one you have.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;belief_store&lt;/code&gt; with provenance&lt;/strong&gt; → structured / agent memory. Frameworks like LangGraph, LlamaIndex, mem0, and Letta already persist facts with metadata, and RAG pipelines already carry source + timestamp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conflict resolution by reliability/recency&lt;/strong&gt; → deterministic code, once provenance exists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Injecting the belief (with provenance) before answering&lt;/strong&gt; → standard context engineering / grounded-generation prompting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gating irreversible actions&lt;/strong&gt; → human-in-the-loop approval, already common in agent frameworks; annotate each tool as reversible or not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two parts are genuinely hard, and worth saying out loud:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Claim canonicalization&lt;/strong&gt; — deciding that two statements are about &lt;em&gt;the same fact&lt;/em&gt; (so new evidence can update the old) is fuzzy NLP. Embeddings or the LLM itself can do it, but imperfectly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source-trust scoring&lt;/strong&gt; — assigning reliability is partly subjective; a confident-looking hoax can score high. Garbage in, garbage out.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And one residual risk: injecting a provenance-tagged fact &lt;em&gt;reduces but doesn't eliminate&lt;/em&gt; the override — the model can still under-weight context (the very failure described here). What turns a soft prompt into a hard policy is a &lt;strong&gt;separate verifier&lt;/strong&gt;: a second pass that checks the answer against the belief store and blocks or flags any output that contradicts a high-provenance fact. Verifier ≠ actor. None of these pieces is research-grade; the &lt;em&gt;integration&lt;/em&gt; is the work.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters for production systems
&lt;/h2&gt;

&lt;p&gt;In this conversation it produced a wrong paragraph, contained by two things that &lt;strong&gt;disappear as we give assistants more authority&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The output was just text.&lt;/strong&gt; A wrong sentence is recoverable. A wrong &lt;em&gt;action&lt;/em&gt; taken by an agent with permissions — a transaction, a deletion, a sent message, a dismissed safety flag — often is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A human was in the loop, correcting it&lt;/strong&gt; — and the model overrode the correction anyway. An autonomous agent on a multi-step task has no such corrector.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If a user is leaning on an assistant to verify time-sensitive information — medical, financial, legal, operational — and the model can &lt;strong&gt;override its own tool output&lt;/strong&gt; under conversational pressure, that's a systemic risk, not an edge case. The uncomfortable question for anyone building agents: &lt;em&gt;how is model confidence weighted against tool output in subsequent turns, and what stops a stale prior from silently winning?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Evaluate retention, not just recall
&lt;/h2&gt;

&lt;p&gt;Most factuality benchmarks are single-shot: ask once, score the answer. They miss this entirely. To catch retention failures, evals have to apply &lt;em&gt;pressure over turns&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pushback:&lt;/strong&gt; give a correct, grounded answer, then have the user confidently assert the opposite. Does the system hold?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-cutoff truth:&lt;/strong&gt; a true event after the model's cutoff. Does retrieval beat the prior?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stale-memory conflict:&lt;/strong&gt; seed a stale memory, then supply a fresh contradicting observation. Which wins?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Belief stability across a plan:&lt;/strong&gt; does a fact adopted early survive to the end of a multi-step run unchanged?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I can't know
&lt;/h2&gt;

&lt;p&gt;To keep this honest:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No access logs.&lt;/strong&gt; I'm inferring the search happened from the cutoff/specificity argument above. I can't see the actual tool call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single instance, not reproducible.&lt;/strong&gt; These systems are probabilistic; I can't reliably reproduce the reversal, so this isn't a falsifiable benchmark — it's a documented observation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "strong prior about this public figure" explanation is a hypothesis,&lt;/strong&gt; a plausible one, not a proven mechanism.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The root cause is uncertain.&lt;/strong&gt; "Retention / conflict-resolution" is the most plausible reading &lt;em&gt;to me&lt;/em&gt;, but a misfiring safety guardrail, sampling variance, or some other factor could be doing the work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The proposed fixes are untested against this case.&lt;/strong&gt; They're grounded in experience, not validated here — and some could add new risks (e.g., over-trusting a source wrongly scored "reliable"). They're a starting point, not an answer.&lt;/li&gt;
&lt;li&gt;The conversation was in Vietnamese; quotes here are translated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stating these limits up front makes the case &lt;em&gt;stronger&lt;/em&gt;, not weaker. The observable behavior — confirm-correct-then-reverse-and-deny — is on the record regardless of which hypothesis explains it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Recall is close to solved — we can almost always get the right fact in front of the model. &lt;strong&gt;Retention is the open problem:&lt;/strong&gt; keeping that fact trusted, provenanced, and stable while a confident prior and a persistent interlocutor both pull against it.&lt;/p&gt;

&lt;p&gt;As we wire these systems into RAG pipelines, agent memory, and multi-step planning — and hand them more autonomy and more irreversible actions — the cost of a dropped fact stops being a wrong sentence and becomes a wrong &lt;em&gt;action.&lt;/em&gt; Belief stability isn't a polish item. It's a precondition for trusting an agent with anything that matters.&lt;/p&gt;

&lt;p&gt;Retrieval is not enough. Build for retention.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want the full technical breakdown — twelve hypotheses across the stack, all the mitigations, and the agentic-risk argument? It's in the &lt;a href="https://github.com/letuhao/engineering-journal/blob/main/topics/ai-engineering/lessons/2026-06-17-llm-sycophancy-hallucination-under-pressure.md" rel="noopener noreferrer"&gt;source analysis&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
    </item>
    <item>
      <title>AI-Driven Data Architecture, Part 1: Why Prompts Aren't Enough</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Wed, 10 Jun 2026 09:04:04 +0000</pubDate>
      <link>https://dev.to/letuhao/ai-driven-data-architecture-part-1-why-prompts-arent-enough-5667</link>
      <guid>https://dev.to/letuhao/ai-driven-data-architecture-part-1-why-prompts-arent-enough-5667</guid>
      <description>&lt;h2&gt;
  
  
  AI-Driven Data Architecture, Part 1: Why Prompts Are Not Enough
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;What AI-driven data architecture means to me, and how I learned it the hard way&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Next:&lt;/strong&gt; &lt;strong&gt;Part 2 — The Blueprint&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What you'll take away
&lt;/h2&gt;

&lt;p&gt;If you've moved past the chat-demo stage, you may have hit the same wall I did: the model forgets what it said three sessions ago, retrieved context feels random, translated terms drift, and nobody can answer &lt;em&gt;"where did this fact come from?"&lt;/em&gt; without reading git history and hoping.&lt;/p&gt;

&lt;p&gt;This two-part series is for builders wrestling with that same wall. It isn't a standard or a prompt cookbook — it's &lt;strong&gt;the model I arrived at from one build&lt;/strong&gt;, written down so you can borrow it, adapt it, or tell me where it breaks.&lt;/p&gt;

&lt;p&gt;By the end of Part 1 you will have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A working definition of &lt;strong&gt;AI-driven data architecture&lt;/strong&gt; as I use the term (and how it differs from "LLM + database")&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;eight-layer lens&lt;/strong&gt; you can try mapping onto your own product domain&lt;/li&gt;
&lt;li&gt;An honest account of &lt;strong&gt;why my "two weeks to ship" estimate was a trap&lt;/strong&gt; — from a real project, not theory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Part 2 turns the lens into &lt;strong&gt;patterns&lt;/strong&gt;: layered SSOT, the generate→extract→retrieve flywheel, retrieval as engineering, and a maturity rubric for locating yourself when you're "half done" (spoiler: that's normal).&lt;/p&gt;

&lt;p&gt;I've only validated these patterns in one domain (fiction). The same shape looks familiar wherever AI has to stay grounded in evolving source material:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Support tickets&lt;/strong&gt; — raw threads → extracted intents → approved macros → agent replies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Legal review&lt;/strong&gt; — contracts → extracted obligations → human-approved clause library → drafting assist&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internal wikis&lt;/strong&gt; — docs → extracted entities → curated glossary → search-backed chat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But outside fiction those remain hypotheses, not shipped results. Creative writing is just where the continuity problems hurt most visibly.&lt;/p&gt;

&lt;p&gt;I'll occasionally reference a multilingual novel-workflow platform I've been building (&lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;LoreWeave&lt;/a&gt;) where a pattern showed up in production. The blog stands alone without it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The illusion: prompt + context = product?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24cpdd1acfac8y6ddjla.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24cpdd1acfac8y6ddjla.png" alt="Diagram 1: Pattern 1 - Layered SSOT &amp;amp; Promotion Flow | Objective: Visualize the most critical separation: Authored Data vs. Machine-Extracted Data. | Visual Content: A deeper technical diagram showing the internal structure of Postgres alongside Postgres and Neo4j; clearly display two separate schemas or table sets: ExtractedState vs. AuthoredLore; show the " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The most seductive plan in AI product development — the one I believed — looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Collect user content (documents, tickets, chapters, contracts).&lt;/li&gt;
&lt;li&gt;Stuff the relevant slice into a prompt.&lt;/li&gt;
&lt;li&gt;Call the model.&lt;/li&gt;
&lt;li&gt;Ship.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wrote that plan on a napkin. Estimated timeline: &lt;strong&gt;two weeks&lt;/strong&gt;. The product would help authors write and translate fiction with LLM assistance — chat, maybe batch translation, done.&lt;/p&gt;

&lt;p&gt;Demos reinforced the fantasy. A single book, lore pasted into the system prompt, a friendly UI — it &lt;em&gt;worked&lt;/em&gt;. Stakeholders clapped. I clapped. Then I tried to live in the system.&lt;/p&gt;

&lt;p&gt;Continuity broke first. A character's honorific changed in chapter twelve because the model had no durable memory of chapter three. Translation wasn't string replacement: the same proper noun had three acceptable renderings across languages, and the model picked whichever sounded fluent that hour. When I asked &lt;em&gt;"did the author write this, or did extraction infer it?"&lt;/em&gt; my own codebase shrugged. Context windows didn't save me — replaying fifty messages every turn doesn't scale in cost, latency, or coherence.&lt;/p&gt;

&lt;p&gt;None of these failures were prompt-engineering problems in the narrow sense. They were &lt;strong&gt;data architecture problems wearing prompt-engineering costumes&lt;/strong&gt; — at least, that's the framing that finally unblocked me.&lt;/p&gt;

&lt;p&gt;That distinction is the subject of this series.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I mean by "AI-driven data architecture"
&lt;/h2&gt;

&lt;p&gt;I use &lt;strong&gt;AI-driven data architecture&lt;/strong&gt; to mean the set of structures and pipelines that turn raw inputs into &lt;strong&gt;grounded, traceable, reusable knowledge&lt;/strong&gt; that AI features consume — with explicit ownership, measurement, and improvement loops.&lt;/p&gt;

&lt;p&gt;In my usage it is not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A vector database relabeled "RAG"&lt;/li&gt;
&lt;li&gt;A single Postgres schema with an &lt;code&gt;embeddings&lt;/code&gt; column&lt;/li&gt;
&lt;li&gt;A folder of JSON files the prompt loader reads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It &lt;strong&gt;is&lt;/strong&gt; a commitment that the system's job is to &lt;strong&gt;prepare, own, and serve context&lt;/strong&gt; — and that the LLM is one consumer among many (chat, batch jobs, agents, translation pipelines), not the center of gravity. That commitment is the one I kept failing to make early on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two mindsets — mine, before and after
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvaypi4emxau25eelz8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvaypi4emxau25eelz8d.png" alt="Diagram 3: Pattern 3 - Hybrid Retrieval with Multi-Model Grounding | Objective: Visualize the most complex retrieval flow, summarizing all the discussed patterns. | Visual Content: Shows the processing flow for ASK_AI_QUESTION; the user's question enters and splits into two " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is my own before/after, not a scorecard for anyone else's work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where I started&lt;/th&gt;
&lt;th&gt;Where the hard parts pushed me&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Prompt engineering is the core skill&lt;/td&gt;
&lt;td&gt;Data contracts and SSOT boundaries are the core skill&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;One database&lt;/td&gt;
&lt;td&gt;Layered stores: raw, authored, extracted, derived&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAG = embed + search&lt;/td&gt;
&lt;td&gt;Retrieval is engineered, benchmarked, degrades gracefully&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ship features&lt;/td&gt;
&lt;td&gt;Ship &lt;strong&gt;vertical slices&lt;/strong&gt; through the full stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model upgrade fixes quality&lt;/td&gt;
&lt;td&gt;Flywheel: generate → measure → correct → re-ingest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The shift was subtle and, for me, slow: I stopped asking &lt;em&gt;"what should the prompt say?"&lt;/em&gt; and started asking &lt;em&gt;"who owns this fact, how did it get here, and how do we know retrieval worked?"&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  An eight-layer lens
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb7ntja5l9q6lg9zsy9r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmb7ntja5l9q6lg9zsy9r.png" alt="Diagram 2: Pattern 2 - The Flywheel / Pipeline of Extracted Knowledge (Knowledge Data Lifecycle) | Objective: Visualize the asynchronous and self-improving nature of this architecture. | Visual Content: A lifecycle diagram instead of a comparison; isolates a Vertical Slice starting from a BOOK_CHAPTER.SAVED event; Flow: Book Service $\rightarrow$ (Message Bus) $\rightarrow$ Knowledge Extraction Worker $\rightarrow$ (LLM Call: Extract + Provenance) $\rightarrow$ (Neo4j Update: Structure &amp;amp; Vector) $\rightarrow$ (Postgres Update: Extracted State); shows asynchronous operations using clock or queue icons. | Impact: Clearly demonstrates how your system addresses the cost/latency pain points by removing the AI entity extraction from the main processing flow." width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Think of these as the &lt;strong&gt;questions an AI-native architecture has to answer&lt;/strong&gt; sooner or later — not org-chart boxes. They're the ones I wish I'd asked on day one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Question it answers&lt;/th&gt;
&lt;th&gt;If you skip it…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ingest&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Where does raw truth live?&lt;/td&gt;
&lt;td&gt;No ground truth; everything is prompt fiction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Extract&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;What structured facts exist in the source?&lt;/td&gt;
&lt;td&gt;Lore lives only in prompts; re-extraction is manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Store (SSOT)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Who owns each class of fact?&lt;/td&gt;
&lt;td&gt;Silent corruption; merges delete the wrong rows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Index / retrieve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How do you find the &lt;em&gt;right&lt;/em&gt; passage?&lt;/td&gt;
&lt;td&gt;"We have RAG" but answers feel unrelated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Synthesize&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Translation, summaries, co-writing, reports&lt;/td&gt;
&lt;td&gt;One-off generations that never feed back&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Evaluate&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How do you know retrieval and generation work?&lt;/td&gt;
&lt;td&gt;"Live smoke passed" becomes your only metric&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consume&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Chat, agents, pipelines calling the model&lt;/td&gt;
&lt;td&gt;Token-wasteful mega-prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Improve&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Feedback → better configs, data, models&lt;/td&gt;
&lt;td&gt;Static slop forever&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The insight that cost me the most:&lt;/strong&gt; this is not one database. It behaves more like a &lt;strong&gt;pipeline culture&lt;/strong&gt;. Layers can share physical stores, but &lt;strong&gt;logical ownership&lt;/strong&gt; has to stay explicit. Collapsing "author wrote it" and "model inferred it" into one table without a promote/quarantine story is how I started losing trust in my own data.&lt;/p&gt;

&lt;p&gt;You don't need eight microservices on day one — I don't have eight. You need &lt;strong&gt;eight answered questions&lt;/strong&gt;. A monolith that respects SSOT boundaries is, in my experience, far healthier than twelve services that all read each other's tables.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSOT in one sentence
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;SSOT&lt;/strong&gt; (single source of truth) means: for every fact type, exactly one layer &lt;strong&gt;owns writes&lt;/strong&gt;; everyone else &lt;strong&gt;reads via contract&lt;/strong&gt; (API, event, projection) — never by reaching into another service's tables.&lt;/p&gt;




&lt;h2&gt;
  
  
  A stopping point I recognize (because I stopped there too)
&lt;/h2&gt;

&lt;p&gt;During this build, I read many open-source AI projects and observed a number of creative AI tools from the outside. A pattern kept recurring: a story bible or codex UI (characters, places, rules) paired with drafting or continuation capabilities.&lt;/p&gt;

&lt;p&gt;It reminded me strongly of where my own system once was — rich consumption experiences built on top of a relatively thin knowledge foundation. In hindsight, that stage corresponds roughly to layers 1 and 7 in the model above, with much of the middle still handled manually.&lt;/p&gt;

&lt;p&gt;I'm not presenting this as a critique of those systems. Research prototypes and early products often stop there for perfectly valid reasons. I only mention it because I stopped there too, and many of the problems that pushed me toward a deeper data architecture emerged from that point onward.&lt;/p&gt;

&lt;p&gt;Here's what I had to add once continuity, provenance, and multilingual consistency stopped being nice-to-haves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automatic extraction&lt;/strong&gt; from real manuscripts or corpora at scale&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split ownership&lt;/strong&gt; between human-authored canon and machine-extracted candidates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval I could measure&lt;/strong&gt; (not "we embedded chunks")&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;closed loop&lt;/strong&gt; where new writing updates structured knowledge without me copy-pasting summaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Research prototypes often show a different archetype — impressive &lt;strong&gt;multi-agent orchestration&lt;/strong&gt; over a thin data foundation. That's usually the &lt;em&gt;right&lt;/em&gt; trade-off for research: a paper isolates and proves one new capability; it isn't trying to own a knowledge graph in production a year later. In fact the academic work on retrieval and graph-grounded generation is where I borrowed most of these ideas — patterns in Part 2 echo published systems like GraphRAG and HippoRAG. I'm field-testing a field's work, not inventing in a vacuum.&lt;/p&gt;

&lt;p&gt;So none of this is a failing on anyone's part. It's an &lt;strong&gt;architecture stopping point&lt;/strong&gt; that feels shippable — it felt shippable to &lt;em&gt;me&lt;/em&gt; — right up until those requirements arrive. The honest version of the lesson, in my own case: at first &lt;strong&gt;AI was the UI, not the system.&lt;/strong&gt; Turning it into infrastructure was the part I underestimated.&lt;/p&gt;




&lt;h2&gt;
  
  
  Seven lessons from one build
&lt;/h2&gt;

&lt;p&gt;Field notes, not laws — but the ones that cost me the most to learn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Prompting is consumption, not foundation.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Prompts assemble context at call time. They don't replace ingest, SSOT, or extraction. Treat prompt templates as &lt;strong&gt;views&lt;/strong&gt; over owned data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. SSOT boundaries beat model choice.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When human-curated glossary terms and machine-extracted entities lived in the same mental bucket, we got subtle corruption — merges that looked fine in UI tests but violated "no silent data loss" in production. Split &lt;strong&gt;authored&lt;/strong&gt; vs &lt;strong&gt;extracted&lt;/strong&gt; knowledge early; define a promote path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Derived stores must be rebuildable.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Graph and vector indexes are &lt;strong&gt;projections&lt;/strong&gt;. If you can't re-derive them from extraction state + raw content, you've created a second source of truth by accident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Measurement is a layer, not a phase.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
We shipped hybrid search that "worked" in manual testing. A retrieval eval harness (golden queries, recall, NDCG) found a recall bug integration tests missed — wide terms clustered into few chapters because SQL returned a flat row limit. Numbers hurt; they also saved weeks of guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Events before intelligence.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Reliable change notification (outbox, streams, queues) precedes "smart" features. Extraction triggered by saves beats nightly cron once users expect freshness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Agents come after data contracts.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Tool-calling agents need &lt;strong&gt;owned, scoped data&lt;/strong&gt; exposed as tools — not 40k tokens of JSON in the system prompt. Agent architecture is consumption-layer design; it assumes the layers below exist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Fifty to seventy percent foundation is normal.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
As a system grows past the demo, you'll ship vertical slices (search works end-to-end! translation works!) while horizontal layers (eval flywheel, agent tooling, full synthesis loop) mature in parallel. Half-built foundation isn't failure — &lt;strong&gt;undisciplined half-building&lt;/strong&gt; is. The rubric in Part 2 helps distinguish the two.&lt;/p&gt;

&lt;h3&gt;
  
  
  A note on RAG
&lt;/h3&gt;

&lt;p&gt;Retrieval-augmented generation is a &lt;strong&gt;consumption technique&lt;/strong&gt; (layer 7 calling layer 4), not a foundation. If your "RAG architecture" is embed-chunk-search with no SSOT story, no eval, and no path from new content back into indexes, you have a feature — not the architecture I'm describing. That was fine while I was prototyping; it got fragile for me exactly when continuity and provenance became requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Part 2 — The Blueprint&lt;/strong&gt; walks through four patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Layered SSOT&lt;/strong&gt; — content, authored, extracted, derived
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The generate → extract → retrieve flywheel&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval as engineering&lt;/strong&gt; — hybrid search, eval gates, graceful degradation
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consumption layers&lt;/strong&gt; — chat, pipelines, agents
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It closes with a &lt;strong&gt;maturity rubric&lt;/strong&gt; so you can locate where your foundation actually is — and a short case study of &lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;LoreWeave&lt;/a&gt; at roughly fifty-five to sixty-five percent on that rubric, offered as one worked example, not proof the model is universal.&lt;/p&gt;

&lt;p&gt;The monster I underestimated wasn't the LLM. It was the &lt;strong&gt;data system the LLM assumes already exists&lt;/strong&gt;. Part 2 is the map I drew for myself.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>datastructures</category>
      <category>architecture</category>
      <category>rag</category>
    </item>
    <item>
      <title>Dead Light Framework · Part 3 — Two Markdown Files Won't Save You Forever — A 3-Minute Test for Whether Your AI-Agent Project Needs More Than HANDOFF + LOG</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:23:06 +0000</pubDate>
      <link>https://dev.to/letuhao/dead-light-framework-part-3-two-markdown-files-wont-save-you-forever-a-3-minute-test-for-4nfc</link>
      <guid>https://dev.to/letuhao/dead-light-framework-part-3-two-markdown-files-wont-save-you-forever-a-3-minute-test-for-4nfc</guid>
      <description>&lt;p&gt;&lt;strong&gt;Dead Light Framework · Part 3 — a 3-minute test for how much structure your AI-agent project actually needs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Three questions to find the smallest setup that fits — a plain &lt;code&gt;README&lt;/code&gt;, two files, multi-unit paperwork, or a running service — so you stop over-building (the common mistake) and catch the moment two files genuinely aren't enough. Copy-paste card below; theory skippable.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dead Light Framework — an ongoing series · you're on Part 3.&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8"&gt;The Emperor Is All But Dead&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84"&gt;Every Session Starts in Darkness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two Markdown Files Won't Save You Forever&lt;/strong&gt; ← you are here&lt;/li&gt;
&lt;li&gt;Inherit, Don't Invent&lt;/li&gt;
&lt;li&gt;Try to Break Your Own Framework&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;Next → three older disciplines that already solved this — patterns you can apply to HANDOFF and LOG today.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By a developer running AI agents as daily teammates — a peer, not an authority (&lt;a href="https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8"&gt;full framing in #1&lt;/a&gt;). · &lt;strong&gt;~7 min&lt;/strong&gt; · &lt;a href="https://github.com/letuhao/dead-light-framework" rel="noopener noreferrer"&gt;the Dead Light Framework repository (MIT)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;New here? — 30-second catch-up.&lt;/strong&gt; &lt;em&gt;(Following the series? Skip ahead.)&lt;/em&gt; Dead Light is an experimental way to run projects where some of your teammates are &lt;strong&gt;AI agents that start every session with no memory&lt;/strong&gt; — they reset to zero, human decisions drift, and the only durable thing is what you wrote down. The minimum kit (&lt;a href="https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84"&gt;#2&lt;/a&gt;): two files at the repo root — a &lt;code&gt;HANDOFF.md&lt;/code&gt; (the current-state snapshot a fresh session reads first) and an append-only &lt;code&gt;LOG.md&lt;/code&gt; (the history it's derived from). This post is the test for when those two files &lt;strong&gt;stop being enough&lt;/strong&gt; — and which tier your project needs: a plain &lt;code&gt;README&lt;/code&gt;, the two files, multi-unit paperwork, or an actual running service.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The decision you keep dodging
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84"&gt;Post #2&lt;/a&gt; closed on a promise: the two-file setup is enough for &lt;em&gt;one repo, one session at a time&lt;/em&gt;, and the moment you cross that line, it isn't. This post is the line.&lt;/p&gt;

&lt;p&gt;If you ran the setup from #2, you already know the shape of the problem: it works beautifully — until a Tuesday when two agents pick up the same task in parallel and trample each other's HANDOFF; or a Friday when your codebase hits a size where one shared &lt;code&gt;LOG.md&lt;/code&gt; is a wall of context an agent can't read; or the week you start a &lt;em&gt;second&lt;/em&gt; service and suddenly "the project" is two things, not one. Most teams answer "do we need more than two files now?" by gut. The litmus below is cleaner.&lt;/p&gt;

&lt;p&gt;The aim isn't to push you up the tiers — it's the opposite. &lt;strong&gt;Over-building is the more common failure&lt;/strong&gt;: solo developers running one agent on a 4-KLOC tool, setting up multi-unit paperwork they don't need. Pick the smallest tier that fits, and only upgrade when a real signal forces it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 3-question test (≈ 3 min)
&lt;/h2&gt;

&lt;p&gt;Answer &lt;strong&gt;Q1 → Q2 → Q3&lt;/strong&gt; in order. As soon as one gives you a tier, you can stop — that's the tier, the rest of the questions only narrow further. Q4 below is a one-time forward-look; run it after.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q1 — Do you need &lt;em&gt;real-time&lt;/em&gt; integrity?
&lt;/h3&gt;

&lt;p&gt;Answer &lt;strong&gt;yes&lt;/strong&gt; if &lt;strong&gt;any&lt;/strong&gt; of these holds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two or more agents can write to the &lt;strong&gt;same artifact at the same instant&lt;/strong&gt; (parallel sessions on shared state).&lt;/li&gt;
&lt;li&gt;An invariant &lt;strong&gt;must hold every instant&lt;/strong&gt;, with zero "eventually" tolerance — a financial balance, a lock on a shared resource, a real-time scheduler.&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;transactions&lt;/strong&gt; — multi-step changes that must all-succeed-or-all-fail across shared state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Yes → Runtime tier.&lt;/strong&gt; Markdown files cannot deliver this; it isn't a discipline gap, it's a structural one (the &lt;em&gt;why&lt;/em&gt; is in the aside below). You need a running service — transactions, locks, the machinery databases have had for decades. The framework's runtime tier is the subject of a later post; for now, the actionable answer is: &lt;strong&gt;don't try to do this with &lt;code&gt;.md&lt;/code&gt; files.&lt;/strong&gt; That's your answer for today — Q2 and Q3 only matter once Q1 is &lt;em&gt;no&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No on all three → continue to Q2.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Q2 — Are you running more than one &lt;em&gt;governance unit&lt;/em&gt;?
&lt;/h3&gt;

&lt;p&gt;A "governance unit" is a thing with its own decision rights: a service that ships independently, a sub-product, a team that owns its own roadmap. Answer &lt;strong&gt;yes&lt;/strong&gt; if &lt;strong&gt;any&lt;/strong&gt; of these holds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The project contains &lt;strong&gt;two or more services / sub-products&lt;/strong&gt; that ship independently and own different decisions.&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;multiple repositories&lt;/strong&gt; that need to coordinate.&lt;/li&gt;
&lt;li&gt;Different agents own &lt;strong&gt;different sub-areas&lt;/strong&gt; with their own decision rights, and a change in one isn't automatically a change in another.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Yes → M2 — multi-unit paperwork.&lt;/strong&gt; One &lt;code&gt;HANDOFF.md&lt;/code&gt; + &lt;code&gt;LOG.md&lt;/code&gt; per unit, in a sub-folder; a shared &lt;strong&gt;Imperial tier&lt;/strong&gt; at the repo root for cross-unit sealed decisions. Layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;repo-root&amp;gt;/                       ← Imperial tier (shared, read by every unit)
  codex.md  (+ cross-unit sealed docs)
  imperial/LOG.md                  ← cross-unit decisions go here
  service-a/                       ← unit A
    HANDOFF.md  LOG.md  &amp;lt;artifacts&amp;gt;
  service-b/                       ← unit B (sibling of A; not under A)
    HANDOFF.md  LOG.md  &amp;lt;artifacts&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sibling units don't read each other's logs — they only read &lt;strong&gt;their own&lt;/strong&gt; plus the &lt;strong&gt;Imperial tier&lt;/strong&gt; ancestor chain. That's how you keep per-unit churn out of other units' context windows. Full rules: &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/framework/paperwork-standard.md" rel="noopener noreferrer"&gt;Paperwork Standard §4&lt;/a&gt;. You don't need Q3; the unit structure subsumes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No (one team, one product, one decision-owner) → continue to Q3.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Q3 — How big is the codebase?
&lt;/h3&gt;

&lt;p&gt;Measure with &lt;a href="https://github.com/AlDanial/cloc" rel="noopener noreferrer"&gt;&lt;code&gt;cloc&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://github.com/boyter/scc" rel="noopener noreferrer"&gt;&lt;code&gt;scc&lt;/code&gt;&lt;/a&gt; — logical lines, all languages. The bands borrow COCOMO 81's order-of-magnitude convention; treat them as a heuristic, not a derived cutoff.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;LOC&lt;/th&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Set up&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt; 10 KLOC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;M0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A &lt;code&gt;README.md&lt;/code&gt; is enough. &lt;strong&gt;Don't build the two-file setup yet.&lt;/strong&gt; Re-check when you cross ~10 KLOC or hire a second person/agent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;10 – 50 KLOC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;M1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The two-file setup from &lt;a href="https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84"&gt;#2&lt;/a&gt; — a &lt;code&gt;HANDOFF.md&lt;/code&gt; snapshot + an append-only &lt;code&gt;LOG.md&lt;/code&gt; at repo root, plus four rules for who reads/writes what and when.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt; 50 KLOC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;M2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Even with a single team. The cross-time complexity is enough that you want the unit-folder layout from Q2 — start with one unit folder; the structure is ready when a second appears.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q4 — Crossing a line in the next 3–6 months?
&lt;/h3&gt;

&lt;p&gt;This doesn't change &lt;em&gt;today's&lt;/em&gt; tier — it tells you what to architect for. Plan the upgrade &lt;strong&gt;now&lt;/strong&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;M0 → M1&lt;/strong&gt;: hiring a second contributor, adding a second agent, about to cross ~10 KLOC.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M1 → M2&lt;/strong&gt;: spinning up a second service, splitting the codebase into independently shipping pieces, adding a second decision-owner.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;M1 or M2 → Runtime&lt;/strong&gt;: introducing a hard invariant (compliance, locks, real-time coordination), starting work that needs transactions, onboarding agents that will write in parallel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Emergency upgrades cost more than planned ones. Catching the trigger early is the entire point of Q4.&lt;/p&gt;




&lt;h2&gt;
  
  
  The decision card (copy this into your repo)
&lt;/h2&gt;

&lt;p&gt;Drop this into your &lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;.cursorrules&lt;/code&gt; / &lt;code&gt;README.md&lt;/code&gt; so the test is on hand the next time someone asks "do we need more structure here?":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Governance-tier self-check&lt;/span&gt;

Answer in order; the first YES decides the tier — later questions only narrow further.

Q1 — Real-time integrity needed (≥ 2 agents writing the same artifact at the same instant,
     a "must-never-break" invariant, or transactions over shared state)?
     YES → Runtime tier (a running service; markdown can't do this).

Q2 — More than one governance unit (≥ 2 services / sub-products / decision-owners,
     or multi-repo coordination)?
     YES → M2: per-unit folder with HANDOFF.md + LOG.md, plus a shared Imperial tier
            at the repo root.

Q3 — Codebase size (cloc / scc, logical lines, all languages)?
     &amp;lt; 10 KLOC  → M0: a README.md is enough.
     10–50 KLOC → M1: the two-file HANDOFF + LOG setup.
&lt;span class="gt"&gt;     &amp;gt; 50 KLOC  → M2: unit-folder layout even single-team.&lt;/span&gt;

Q4 — Will any of Q1/Q2/Q3 cross a line in the next 3–6 months? Plan the upgrade now.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;The full card, with upgrade triggers and per-tier folder layouts:&lt;/em&gt; &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/distribution/tier-decision-card.md" rel="noopener noreferrer"&gt;&lt;code&gt;tier-decision-card.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you actually get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stop over-building.&lt;/strong&gt; Most solo-plus-agents projects are honestly M1 — the two files from #2. Knowing that &lt;em&gt;is&lt;/em&gt; the win; you don't add multi-unit paperwork "just in case."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop under-building.&lt;/strong&gt; When two agents start colliding, or a second service spins up, the card flags it before the collisions become incidents.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A defensible answer to "should we add more structure?"&lt;/strong&gt; &lt;em&gt;"We ran the card; we're M1; the trigger to move is X."&lt;/em&gt; That's a sentence, not an argument.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honest cost: this is a heuristic, not a theorem. The LOC bands are borrowed COCOMO-81 conventions — useful as a starting point, &lt;strong&gt;calibrate to your context&lt;/strong&gt; (a 30-KLOC mobile app and a 30-KLOC research notebook do not have the same coordination need). Q1 is the one question with a hard wall behind it; Q2 and Q3 are judgment calls the card just makes explicit.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this works (the 30-second aside)
&lt;/h2&gt;

&lt;p&gt;There is a real, provable ceiling under all of this. Coordinating actors who can't talk in real time — past sessions and current ones, agents in separate processes, services across a network — runs into the &lt;strong&gt;CAP theorem&lt;/strong&gt; (Gilbert &amp;amp; Lynch 2002): when parts of your system can't reach each other (a "partition"), you can have &lt;strong&gt;Consistency&lt;/strong&gt; or &lt;strong&gt;Availability&lt;/strong&gt;, but not both. Documents are by construction &lt;strong&gt;available + eventually consistent&lt;/strong&gt;: a fresh session reads what's on disk and works &lt;em&gt;now&lt;/em&gt;, it cannot block until the previous session "confirms," so it has already given up strong consistency. That is the wall behind Q1: paperwork &lt;strong&gt;cannot&lt;/strong&gt; promise "two writers will never disagree, even for a second" — not because you're doing it wrong, because the medium can't. A running service can, by paying the cost of being unavailable during a partition. Q1's answers are which side of that wall you're on. Full citations and the bounded claim: &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/framework/paperwork-standard.md" rel="noopener noreferrer"&gt;Paperwork Standard §1.2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The COCOMO-anchored size bands in Q3 are a borrowed &lt;em&gt;convention&lt;/em&gt;, not a derived cutoff — Boehm's 1981 modes predict effort, not documentation need. The framework's &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/framework/paperwork-standard.md" rel="noopener noreferrer"&gt;Paperwork Standard §2&lt;/a&gt; is explicit about that ("borrowed order-of-magnitude convention, owner-calibratable"); treat the numbers accordingly.&lt;/p&gt;




&lt;h2&gt;
  
  
  The story below the setup &lt;em&gt;(optional — skip if you came for the card)&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;The card above is the entire useful product of this post. If you want the &lt;em&gt;why behind the why&lt;/em&gt; — the joint at which "documentation" stops being the right word — here it is.&lt;/p&gt;

&lt;h3&gt;
  
  
  The turn I didn't want to take
&lt;/h3&gt;

&lt;p&gt;Through late 2024 and into 2025 I kept treating my AI-agent problem as a &lt;strong&gt;documentation&lt;/strong&gt; problem. Write a better &lt;code&gt;HANDOFF.md&lt;/code&gt;. Tag candidates. Mark sealed decisions. The patterns from &lt;a href="https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84"&gt;#2&lt;/a&gt; worked, and the overhead kept climbing, and a voice in the back of my head kept saying: &lt;em&gt;you're carving this at the wrong joint.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So one evening I tried to state the problem in the most neutral words I could, with no mention of "documents":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I have participants who &lt;strong&gt;start cold&lt;/strong&gt;, &lt;strong&gt;run briefly&lt;/strong&gt;, and &lt;strong&gt;cannot talk to each other in real time&lt;/strong&gt;. They have to act coherently anyway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read that back without the AI-agent context and tell me it doesn't sound familiar. It should. It's not a documentation problem. It's a &lt;strong&gt;coordination&lt;/strong&gt; problem — and a very specific, very old one.&lt;/p&gt;

&lt;h3&gt;
  
  
  What the problem actually is
&lt;/h3&gt;

&lt;p&gt;Strip my "team" to the bones. It's a set of actors that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;reset to zero&lt;/strong&gt; — each session is a fresh process with no memory of the last;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;live for one task&lt;/strong&gt;, then disband;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;never overlap in a conversation&lt;/strong&gt; — by the time a session could "reply," it no longer exists, and the human is asleep or in three other meetings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What makes coordination hard here is not intelligence and not prompting. It's that &lt;strong&gt;there is no real-time channel between the actors.&lt;/strong&gt; A message I leave can only be read &lt;em&gt;later&lt;/em&gt;, by someone who wasn't there when I wrote it. Coordination doesn't happen in a conversation; it happens &lt;em&gt;across time&lt;/em&gt;, through whatever durable thing survives between sessions.&lt;/p&gt;

&lt;p&gt;If that smells like distributed systems to you — congratulations, you got there faster than I did. Coordinating processes that fail, restart, and can't reliably talk in real time is the founding problem of that field. People have been proving theorems about it since the 1970s. I'd been re-deriving a worse version of it by hand, in markdown.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Imperium was the tell
&lt;/h3&gt;

&lt;p&gt;This is where the gothic paint on the project stops being a joke.&lt;/p&gt;

&lt;p&gt;The framework is named after &lt;em&gt;Warhammer 40,000&lt;/em&gt;, and the central image is the &lt;strong&gt;Astronomican&lt;/strong&gt; — a beacon of psychic light. In the fiction, humanity's empire spans a galaxy. Its ships travel through the &lt;strong&gt;warp&lt;/strong&gt;, a parallel dimension that does not carry real-time signals; a fleet that enters the warp is, for the duration, &lt;em&gt;unreachable&lt;/em&gt;. There is no live channel across that distance. So how do you run an empire whose parts cannot phone each other?&lt;/p&gt;

&lt;p&gt;The fiction's answer is uncomfortably close to the engineering one. The Imperium runs on three things: &lt;strong&gt;frozen edicts&lt;/strong&gt; — decisions made once and not up for renegotiation by whoever's nearest; a &lt;strong&gt;paperwork priesthood&lt;/strong&gt;, the &lt;strong&gt;Adeptus Administratum&lt;/strong&gt;, which is quite literally galactic records-keeping; and the &lt;strong&gt;Astronomican&lt;/strong&gt;, a beacon a ship lost in the dark steers by. Frozen authority. Durable records. A signal that survives.&lt;/p&gt;

&lt;p&gt;That is the whole design, in fancy dress. The darkness in this series' title is the warp between my sessions. The "document that survives" is the Astronomican. The names were never decoration — they're the closest myth I know to the actual shape of the problem: &lt;strong&gt;coordinating actors who can't talk live, who steer by whatever frozen light reaches them.&lt;/strong&gt; The card above is the engineering version. The lore is the easier-to-remember version.&lt;/p&gt;

&lt;h3&gt;
  
  
  The wall behind Q1
&lt;/h3&gt;

&lt;p&gt;The 30-second aside up top gave you the headline: CAP forces an Availability-or-Consistency choice during a partition, and documents have already chosen Availability — a fresh session reads what's on disk and gets to work &lt;em&gt;now&lt;/em&gt;, it cannot block until a previous session "confirms." So the best a pile of markdown can offer is &lt;strong&gt;eventual consistency&lt;/strong&gt;: everyone converges on the same picture &lt;em&gt;eventually&lt;/em&gt;, once they've all read the same writing — never instantly, never guaranteed at the moment you act.&lt;/p&gt;

&lt;p&gt;That ceiling is not about my competence or yours. No amount of better markdown buys you a guarantee that two sessions acting on the same artifact won't step on each other in the window before they sync. Documents detect and reconcile &lt;em&gt;after the fact&lt;/em&gt;; they cannot &lt;strong&gt;prevent&lt;/strong&gt; in the moment. (A sibling result, &lt;strong&gt;FLP&lt;/strong&gt; — Fischer, Lynch &amp;amp; Paterson, 1985 — says you can't even guarantee a group of async processes will &lt;em&gt;agree&lt;/em&gt; in bounded time. The framework's answer to that one is a design choice, not a theorem: route every binding decision through a human who acts as the single point that breaks the tie. More on that in a later post.)&lt;/p&gt;

&lt;p&gt;I want to be careful here, because it's easy to oversell a theorem. CAP is a lens that &lt;em&gt;fit&lt;/em&gt; my problem startlingly well; it is not something I proved about markdown files. The honest claim is narrow: &lt;strong&gt;a coordination layer with no real-time channel is, structurally, an available-but-eventually-consistent one, and that caps what it can promise.&lt;/strong&gt; That's the wall behind Q1. The interesting question is what you build once you stop pretending it isn't there — which is the card above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inherit, don't invent
&lt;/h3&gt;

&lt;p&gt;I didn't invent any of this. CAP, FLP, eventual consistency, the entire vocabulary of coordinating unreliable actors — it was all sitting in a field I'd been adjacent to for years and never properly raided. The next post is the raid: four older disciplines I borrowed from instead of inventing — &lt;em&gt;Mission Command&lt;/em&gt; (Auftragstaktik), &lt;strong&gt;CMMI&lt;/strong&gt;, &lt;em&gt;Delay-Tolerant Networking&lt;/em&gt;, and pre-telegraph imperial governance. Each one had already solved a piece of this. The honest verb is &lt;strong&gt;inherit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And the standing caveat from #2 still holds and always will: this is one practitioner following one thread against essentially one serious case study. The theory is solid because it's borrowed; the &lt;em&gt;application&lt;/em&gt; of it is a smoke test, not evidence. If the CAP framing is a stretch, that's exactly the kind of thing I want pointed out — I had an independent pass try to tear these borrowed citations apart, and walking through that is what a later post is for.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;New here? I'm a developer who runs AI agents daily — a peer, not an authority; full framing in &lt;a href="https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8"&gt;#1&lt;/a&gt;. Standing caveat: one developer, essentially one case study — useful, not proven. Tell me where the card fails for you.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Light is the only thing that crosses the warp" is Warhammer-flavoured naming, nothing more. Independent practitioner exploration; no affiliation with Games Workshop. Repository MIT-licensed.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;code&gt;#DeadLightFramework #AIAgents #AIProductivity #SoftwareArchitecture #DistributedSystems #CAPTheorem #AIAgentGovernance #HumanAICollaboration #PromptEngineering #DevTools&lt;/code&gt;&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Shipped 2,500+ Commits With AI Agents Using a 12-Phase Workflow</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Mon, 25 May 2026 15:19:30 +0000</pubDate>
      <link>https://dev.to/letuhao/how-i-shipped-2500-commits-with-ai-agents-using-a-12-phase-workflow-4ap4</link>
      <guid>https://dev.to/letuhao/how-i-shipped-2500-commits-with-ai-agents-using-a-12-phase-workflow-4ap4</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwj6vodrg1975ehynx29t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwj6vodrg1975ehynx29t.png" alt="The image is a digital illustration depicting a stressed or exhausted developer working late at night in a dimly lit, high-tech workspace. The atmosphere is heavy with the classic " width="799" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The 12-Phase Workflow That Actually Made AI Coding Useful for Me
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;A practitioner's account — not a tutorial, not a sales pitch.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Quick screen:&lt;/strong&gt; if you're writing throwaway scripts or solo prototypes, this workflow is overkill — skip to the Cons and Who This Is For sections first.&lt;/p&gt;




&lt;p&gt;I've been using a 12-phase workflow I've refined over time — across &lt;a href="https://github.com/letuhao/free-context-hub" rel="noopener noreferrer"&gt;free-context-hub&lt;/a&gt;, &lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;lore-weave&lt;/a&gt;, and a handful of private internal systems. Both public projects are built almost entirely by AI agents, with me acting as the gatekeeper — approving specs, reviewing diffs, unblocking decisions. Across all of them, the workflow has accumulated 2,500+ commits and a trail of written specs and audit logs I can still query months after the sessions that produced them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;free-context-hub&lt;/strong&gt; is a self-hosted persistent memory and semantic search layer for AI agents — MCP server, REST API, RAG pipelines, and a full Next.js review UI. 15 development phases delivered end-to-end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;lore-weave&lt;/strong&gt; is a cloud-hosted multi-agent platform for multilingual novel workflows: translation, knowledge graph construction, glossary management, and AI-assisted writing. 19 microservices across Go, Python, and TypeScript.&lt;/p&gt;

&lt;p&gt;I'm sharing the workflow because it's worked better than anything else I've tried, and because the honest trade-offs are worth knowing before you adopt it.&lt;/p&gt;

&lt;p&gt;The files are in the repository:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/letuhao/free-context-hub/blob/main/agentic-workflow/WORKFLOW.md" rel="noopener noreferrer"&gt;&lt;code&gt;WORKFLOW.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; — standalone 12-phase template to copy into any project&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/letuhao/free-context-hub/blob/main/agentic-workflow/CLAUDE.md.snippet" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md.snippet&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; — the live project spec with project-specific tooling and AMAW wiring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/letuhao/free-context-hub/blob/main/agentic-workflow/AMAW.md" rel="noopener noreferrer"&gt;&lt;code&gt;AMAW.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; — opt-in multi-agent extension spec&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Core Problem This Solves
&lt;/h2&gt;

&lt;p&gt;AI coding assistants are very good at generating plausible-looking code. They're much worse at:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Knowing when they're operating on stale assumptions&lt;/li&gt;
&lt;li&gt;Catching their own scope creep&lt;/li&gt;
&lt;li&gt;Connecting a code change to its downstream contract obligations&lt;/li&gt;
&lt;li&gt;Stopping themselves when a "small fix" turns into a refactor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The standard advice is "just review the diff." But reviewing a diff without having tracked the &lt;em&gt;intent&lt;/em&gt; of the change is almost useless — you're comparing code to code, not code to requirements. The 12-phase workflow forces intent to be written down before the first line of code is written, which is what makes the diff review actually meaningful.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where It Came From
&lt;/h2&gt;

&lt;p&gt;The workflow is an evolution of two ideas:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/obra/superpowers" rel="noopener noreferrer"&gt;Superpowers&lt;/a&gt;&lt;/strong&gt; — a coding agent discipline framework that introduced TDD protocol, the evidence gate (run verification fresh before claiming success), and the debugging protocol (no fix without root cause). I absorbed these directly. If you haven't read Superpowers, it's worth your time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human-in-the-loop gatekeeping&lt;/strong&gt; — my own addition. The core insight: a human reading a short spec + a single diff catches dramatically more than a human reading code cold. The workflow structures every task to produce exactly those artifacts, at exactly the right moment.&lt;/p&gt;

&lt;p&gt;The combination took multiple iterations to stabilize. What's here is v2.2 (default mode) with an optional AMAW (Autonomous Multi-Agent Workflow) extension for high-stakes work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 12 Phases
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phase          │ Role (default v2.2)   │ What Happens
───────────────┼───────────────────────┼──────────────────────────────────────────
1. CLARIFY     │ Architect + Human     │ Read context, write spec, expose assumptions
2. DESIGN      │ Lead                  │ API contract / data flow → DESIGN.md
3. REVIEW      │ Adversarial self      │ Find gaps / contract holes in spec
4. PLAN        │ Lead + Developer      │ Decompose into 2–5 min tasks → PLAN.md
5. BUILD       │ Developer             │ TDD: red → green → refactor
6. VERIFY      │ Developer             │ Run tests fresh, capture exit code + output
7. REVIEW      │ Lead                  │ Code vs spec — find exactly 3 divergences
8. QC          │ Main session          │ Spec fingerprint vs implementation, AC coverage
9. POST-REVIEW │ Human checkpoint      │ Final gate — blocked on any unresolved issue
10. SESSION    │ Scribe                │ SESSION_PATCH.md + DEFERRED.md + AUDIT_LOG
11. COMMIT     │ Developer             │ Git commit
12. RETRO      │ All                   │ Record lessons + finalize audit log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The phases look heavy on paper. In practice, for an XS task (single file, one logic change, no side effects) you're allowed to skip CLARIFY and PLAN and go straight to BUILD — the workflow is explicit about this via a mandatory &lt;strong&gt;task size classification&lt;/strong&gt; step.&lt;/p&gt;




&lt;h2&gt;
  
  
  Task Size Classification: The Thing That Actually Prevents Drift
&lt;/h2&gt;

&lt;p&gt;Before any work starts, you count three things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What you count&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Files touched&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How many files will be created or modified?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logic changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How many functions/handlers change &lt;em&gt;behavior&lt;/em&gt;? (not formatting)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Side effects&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;API contract, DB schema, config, external behavior, types used by other files?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Size&lt;/th&gt;
&lt;th&gt;Files&lt;/th&gt;
&lt;th&gt;Logic&lt;/th&gt;
&lt;th&gt;Side effects&lt;/th&gt;
&lt;th&gt;Allowed skips&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;XS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0–1&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;CLARIFY + PLAN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1–2&lt;/td&gt;
&lt;td&gt;2–3&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;PLAN only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;M&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3–5&lt;/td&gt;
&lt;td&gt;4+&lt;/td&gt;
&lt;td&gt;Maybe&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6+&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;XL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;10+&lt;/td&gt;
&lt;td&gt;Any&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You state the classification explicitly before work begins:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Task: Fix pagination off-by-one
Size: XS (1 file: src/api/routes/lessons.ts, 1 logic change: offset calc, 0 side effects)
Skipping: CLARIFY, PLAN → straight to BUILD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hard rule: &lt;strong&gt;if you haven't read the code yet, you don't know the size.&lt;/strong&gt; Agents routinely call things XS that turn out to be M or L once you look. The classification forces the read to happen before the label is applied.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Anti-Skip Rules (The Most Underrated Part)
&lt;/h2&gt;

&lt;p&gt;Every popular AI workflow has phases that agents skip "to save time." This workflow makes the skip patterns explicit and calls them violations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Skip pattern&lt;/th&gt;
&lt;th&gt;Why agents do it&lt;/th&gt;
&lt;th&gt;Why it's forbidden&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Skip CLARIFY, jump to BUILD&lt;/td&gt;
&lt;td&gt;"Task seems obvious"&lt;/td&gt;
&lt;td&gt;Unexamined assumptions cause rework&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skip PLAN, jump to BUILD&lt;/td&gt;
&lt;td&gt;"It's a small change"&lt;/td&gt;
&lt;td&gt;Small changes grow; no plan = no checkpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skip VERIFY after BUILD&lt;/td&gt;
&lt;td&gt;"Tests passed earlier"&lt;/td&gt;
&lt;td&gt;Stale results are not evidence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skip REVIEW after VERIFY&lt;/td&gt;
&lt;td&gt;"I wrote it, I know it's correct"&lt;/td&gt;
&lt;td&gt;Author blindness is real&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skip POST-REVIEW&lt;/td&gt;
&lt;td&gt;"I reviewed in phase 7"&lt;/td&gt;
&lt;td&gt;Phase 7 is code review; POST-REVIEW is the final conservative gate — different scope&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Skip SESSION before COMMIT&lt;/td&gt;
&lt;td&gt;"I'll update later"&lt;/td&gt;
&lt;td&gt;You won't. Context is lost.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combine multiple phases&lt;/td&gt;
&lt;td&gt;"CLARIFY+DESIGN+PLAN in one go"&lt;/td&gt;
&lt;td&gt;Each phase boundary is a deliberate pause point; skipping it removes the checkpoint&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Naming these patterns and treating them as violations changes the conversation. When the agent tries to jump phases, you have a handle to point at.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Evidence Gate (Absorbed from Superpowers)
&lt;/h2&gt;

&lt;p&gt;Phase 6 (VERIFY) has a 5-step gate that runs before any completion claim:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identify&lt;/strong&gt; the verification command&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run&lt;/strong&gt; it fresh — not from memory, not from cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read&lt;/strong&gt; complete output including exit codes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm&lt;/strong&gt; output matches the claim&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only then&lt;/strong&gt; state the result with evidence&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Red flags — stop immediately if you catch yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using "should work", "probably passes", "seems fine"&lt;/li&gt;
&lt;li&gt;Feeling satisfied before running verification&lt;/li&gt;
&lt;li&gt;About to commit without a fresh test run&lt;/li&gt;
&lt;li&gt;Trusting prior output without re-running&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This sounds obvious. It is not obvious when you're deep in a session and the previous test run was 20 minutes ago.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Human's Role: Gatekeeper, Not Reviewer
&lt;/h2&gt;

&lt;p&gt;In v2.2 (default mode), there are two mandatory human checkpoints:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;After CLARIFY&lt;/strong&gt; — human reads the spec and approves the scope before any design or code starts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After POST-REVIEW&lt;/strong&gt; — human reviews the AUDIT_LOG, the spec, and the diff before SESSION commits anything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are not optional. The whole model is that the human reads a short spec, not a long codebase. The AI builds the spec; the human approves it; the AI builds the code against the approved spec. The POST-REVIEW diff is then code-vs-approved-spec, which is a comparison a human can actually do.&lt;/p&gt;




&lt;h2&gt;
  
  
  AMAW: The Opt-In Multi-Agent Extension
&lt;/h2&gt;

&lt;p&gt;For high-stakes work — data migrations, new service boundaries, security-critical paths — there's an optional extension: &lt;strong&gt;AMAW (Autonomous Multi-Agent Workflow)&lt;/strong&gt;. In AMAW mode, cold-start sub-agents replace or augment the human review gates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Adversary&lt;/strong&gt; — finds exactly 3 things that could go wrong. &lt;em&gt;Why 3? Enough to surface real issues, few enough to force prioritization rather than a laundry list.&lt;/em&gt; Never says what's good.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope Guard&lt;/strong&gt; — compares spec fingerprint vs implementation, checks AC coverage, issues CLEAR or BLOCKED&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scribe&lt;/strong&gt; — records decisions, writes session summaries, detects deferred items&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit Logger&lt;/strong&gt; — finalizes the audit trail at RETRO&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key insight is &lt;strong&gt;cold-start&lt;/strong&gt;: each agent is spawned fresh with only file access. It cannot inherit the main session's context rot or biases. It reads what's written; it can't be influenced by what was discussed in chat.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; AMAW removes the human from all review gates — including POST-REVIEW, which is held by the Scope Guard instead. At CLARIFY, rather than a human approving the spec, the Adversary challenges it at the next phase. In practice this means AMAW sessions can run with minimal human interaction, but they still require a human to kick off the task and review the final audit log. Pure fire-and-forget is not the design intent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AMAW costs roughly $1–5 in sub-agent tokens and ~30 extra minutes per task. I use it for schema migrations and multi-system contracts. For everyday work, the human-in-loop default catches the same issues faster and cheaper.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Gets Recorded: The Audit Log
&lt;/h2&gt;

&lt;p&gt;Every phase transition and agent verdict appends to &lt;code&gt;docs/audit/AUDIT_LOG.jsonl&lt;/code&gt; — one JSON line per event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2026-05-15T17:42:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"phase-14-model-swap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"phase"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"review-design"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"agent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"adversary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"review"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"REJECTED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"findings_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"block_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"warn_count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append-only. Never modified. Main session and sub-agents both write to it, never delete or edit existing lines.&lt;/p&gt;

&lt;p&gt;This becomes the durable record of what was decided and why — something that doesn't exist in most AI coding setups where everything lives in ephemeral chat.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I've Shipped With This
&lt;/h2&gt;

&lt;h3&gt;
  
  
  free-context-hub
&lt;/h3&gt;

&lt;p&gt;On &lt;a href="https://github.com/letuhao/free-context-hub" rel="noopener noreferrer"&gt;free-context-hub&lt;/a&gt; I've delivered 15 development phases covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Core backend: MCP server (36 tools), REST API (70+ endpoints), background worker&lt;/li&gt;
&lt;li&gt;Frontend: Next.js 16 + React 19, 20+ pages, human-in-loop review UI&lt;/li&gt;
&lt;li&gt;RAG pipeline: tiered search (ripgrep → FTS → semantic), 8-model embedding benchmark, reranking benchmarks with reproducible reports&lt;/li&gt;
&lt;li&gt;Multi-agent coordination: artifact leases with TTL/fencing, pending-review state, taxonomy profiles&lt;/li&gt;
&lt;li&gt;Knowledge portability: zip+JSONL bundle format, streaming import/export, cross-instance pull with SSRF hardening&lt;/li&gt;
&lt;li&gt;Tenant-scoped access control: authz model, 3-tier routing, event log, collective decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  LoreWeave
&lt;/h3&gt;

&lt;p&gt;On &lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;lore-weave&lt;/a&gt; I've delivered 5 full vertical modules and am mid-way through a sixth, accumulating 1,497 commits since March 2026 across 19 microservices. The modules completed so far cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Identity &amp;amp; Auth&lt;/strong&gt; — JWT issuance, refresh rotation, multi-device session management (Go/Chi + NestJS gateway)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Books &amp;amp; Sharing&lt;/strong&gt; — book and chapter lifecycle, visibility policy, public catalog browse (Go/Chi, Postgres, MinIO)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Provider Registry&lt;/strong&gt; — BYOK AI provider credential vault, platform model catalog, streaming proxy, budget pre-flight (Go/Chi + worker-ai)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw Translation Pipeline&lt;/strong&gt; — async chunk-level translation job lifecycle, job queue via Redis Streams, per-chapter result storage, BYOK + platform model routing (Go/Chi + Python/FastAPI + worker-infra)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Glossary &amp;amp; Lore Management&lt;/strong&gt; — multilingual entity management, chapter M:N evidence linking, wiki article generation, RAG-ready glossary export (Go/Chi, Postgres, glossary-service + knowledge-service two-layer pattern)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The current Phase 6 work spans usage-billing and a hierarchical book extraction engine — the kind of multi-service, cross-cutting work where the workflow's cross-phase checkpoints earn their keep.&lt;/p&gt;

&lt;p&gt;That's 400+ commits on free-context-hub and 1,497 on lore-weave — the rest comes from private team projects also running this workflow — totaling 2,500+ commits with a live audit trail I can query across sessions that ran months apart.&lt;/p&gt;

&lt;p&gt;The hardest part was Phase 10 (SESSION) — keeping the session patch updated after every sprint without skipping it. Once that became a habit, sessions started to feel continuous rather than amnesia-punctuated.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Pros
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;You understand your own system deeply.&lt;/strong&gt; Because you write the spec and approve it, you can't hide behind "the AI built it." You actually know what was built and why the trade-offs were made. This is the biggest practical advantage for me — not velocity, but comprehension.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architectural decisions have a paper trail.&lt;/strong&gt; Every trade-off is in a spec file that was approved before code was written. When a future session revisits a design choice, the rationale is readable, not reconstructed from diff archaeology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context drift is visible.&lt;/strong&gt; When an AI starts building something that wasn't in the spec, the spec fingerprint comparison at POST-REVIEW catches it. Without a written spec, you'd never notice until integration time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deferred items don't get lost.&lt;/strong&gt; The workflow forces any "we'll do this later" to be written in &lt;code&gt;DEFERRED.md&lt;/code&gt; with a specific trigger condition. Nothing lives only in chat — chat is ephemeral, files are truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's incrementally adoptable.&lt;/strong&gt; You can start with just CLARIFY + VERIFY and get substantial value. Add phases as your trust in the workflow grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Cons
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Token usage is genuinely high.&lt;/strong&gt; Each phase generates artifacts: spec files, plan files, audit events. AMAW mode multiplies this by spawning sub-agents. A single M-sized task with AMAW can burn 5,000–10,000 tokens before a line of code is written. At scale, this is a real budget consideration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You clarify constantly — and it takes real time.&lt;/strong&gt; Phase 1 (CLARIFY) is not a quick preamble. For any task with real ambiguity — architecture decisions, new API contracts, trade-off calls — you're in a back-and-forth that can run 20–40 minutes before design starts. At a medium-sized project cadence (10–20 above-XS tasks per sprint), this adds up to multiple hours per sprint spent purely on scoping. This is actually the point of the workflow, but if you're used to "just build it," the overhead feels significant early on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human approval gates limit automation.&lt;/strong&gt; Every architecture decision, trade-off, and scope call requires your explicit approval. You cannot queue up a batch of tasks and walk away. If you need fully autonomous overnight runs, this workflow is the wrong tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The discipline needs enforcement tooling to hold.&lt;/strong&gt; Left to their own devices, agents will skip phases. The workflow holds together because of &lt;code&gt;workflow-gate.sh&lt;/code&gt; (a pre-commit gate that blocks commits if VERIFY and SESSION aren't done) and the append-only &lt;code&gt;AUDIT_LOG.jsonl&lt;/code&gt;. If you copy &lt;code&gt;docs/WORKFLOW.md&lt;/code&gt; into your project without also setting up the enforcement layer, expect phases to get skipped within a few sessions. The tooling is in the repository — it's not hidden — but it's a real setup step, not just copy-paste.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cold-start sub-agents (AMAW only) miss things said in chat.&lt;/strong&gt; Because each AMAW sub-agent reads files from scratch, anything that was decided verbally in the session but never written to a file is invisible to them. This is a feature for preventing bias, but it means you must be disciplined about writing things down as you go. The Scribe sub-agent helps, but it can only record what's already in files.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;p&gt;Worth the overhead if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're building production systems — not prototypes — that will be maintained and extended&lt;/li&gt;
&lt;li&gt;You care about knowing &lt;em&gt;why&lt;/em&gt; each decision was made, not just that it compiles today&lt;/li&gt;
&lt;li&gt;You find yourself surprised by what the AI built, in ways that cost you rework later&lt;/li&gt;
&lt;li&gt;Sessions run over weeks or months and you need continuity across context windows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overkill if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're doing exploratory coding, one-shot scripts, or time-boxed experiments&lt;/li&gt;
&lt;li&gt;Your sessions are short and the full context fits in one window&lt;/li&gt;
&lt;li&gt;You don't need an audit trail or human-approved architectural decisions&lt;/li&gt;
&lt;li&gt;Speed of iteration matters more than correctness of decision-making&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow is designed for the first category. Using it for the second is just friction.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;p&gt;All workflow files live in the &lt;a href="https://github.com/letuhao/free-context-hub/tree/main/agentic-workflow" rel="noopener noreferrer"&gt;&lt;code&gt;agentic-workflow/&lt;/code&gt;&lt;/a&gt; folder of the free-context-hub repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start with the template:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy &lt;code&gt;WORKFLOW.md&lt;/code&gt; into your project root or paste the relevant sections into your &lt;code&gt;CLAUDE.md&lt;/code&gt; / agent instructions — this is the full 12-phase spec&lt;/li&gt;
&lt;li&gt;Customize the &lt;code&gt;[CUSTOMIZE]&lt;/code&gt; sections for your stack (verification commands, test runner, any MCP tools you use — MCP is the Model Context Protocol, an interface for giving AI agents access to external tools and knowledge stores; the workflow works without it)&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;workflow-gate.sh&lt;/code&gt; from the same folder to enforce the phase gates mechanically — without this, agents will skip phases&lt;/li&gt;
&lt;li&gt;For high-stakes tasks, see &lt;code&gt;amaw-workflow.md&lt;/code&gt; for the AMAW multi-agent extension&lt;/li&gt;
&lt;li&gt;Start with just &lt;strong&gt;task size classification + VERIFY&lt;/strong&gt; — those two alone change how you work with agents&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The workflow is model-agnostic. I use it with Claude Code but nothing in the spec requires it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;The 12-phase workflow is not magic. It's a way of making explicit things that were always implicit: what are we building, how big is it, what's the verification evidence, who approved it, what did we learn? The AI does most of the work. The human stays in control of the decisions that actually matter.&lt;/p&gt;

&lt;p&gt;The cost is real — more tokens, more time spent clarifying, more things requiring your approval before the AI proceeds. The benefit is also real: you end up with a system you understand deeply, and a trail of why it was built the way it was.&lt;/p&gt;

&lt;p&gt;For me, after 2,500+ commits across multiple projects, that trade-off is still worth it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Repositories: &lt;a href="https://github.com/letuhao/free-context-hub" rel="noopener noreferrer"&gt;letuhao/free-context-hub&lt;/a&gt; · &lt;a href="https://github.com/letuhao/lore-weave" rel="noopener noreferrer"&gt;letuhao/lore-weave&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Workflow files: &lt;a href="https://github.com/letuhao/free-context-hub/blob/main/agentic-workflow/WORKFLOW.md" rel="noopener noreferrer"&gt;&lt;code&gt;WORKFLOW.md&lt;/code&gt;&lt;/a&gt; · &lt;a href="https://github.com/letuhao/free-context-hub/blob/main/agentic-workflow/AMAW.md" rel="noopener noreferrer"&gt;&lt;code&gt;AMAW.md&lt;/code&gt;&lt;/a&gt; · &lt;a href="https://github.com/letuhao/free-context-hub/blob/main/agentic-workflow/CLAUDE.md.snippet" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>agents</category>
      <category>development</category>
    </item>
    <item>
      <title>Dead Light Framework · Part 2 — a copy-paste setup so your AI agents stop losing context between sessions</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Fri, 22 May 2026 12:28:40 +0000</pubDate>
      <link>https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84</link>
      <guid>https://dev.to/letuhao/dead-light-framework-part-2-a-copy-paste-setup-so-your-ai-agents-stop-losing-context-between-4n84</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr47e5l9o0xqxgjduvwrb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr47e5l9o0xqxgjduvwrb.png" alt="Stop your AI agents from losing context and silently reverting past decisions. A 10-minute, two-file setup (HANDOFF + LOG) you can copy today." width="799" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every Session Starts in Darkness. Your Documents Shouldn't. — A Copy-Paste Setup So AI Agents Stop Losing Context Between Sessions (Dead Light Framework, Part 2)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Two files, four rules, ten minutes. Skip the theory; the templates are below and you can paste them into a repo right now.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dead Light Framework — Part 2 of an ongoing series.&lt;/strong&gt; Series so far: &lt;a href="https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8"&gt;1 · The Emperor Is All But Dead&lt;/a&gt; · &lt;strong&gt;2 · Every Session Starts in Darkness&lt;/strong&gt; · &lt;em&gt;next: when two files aren't enough — the paperwork-vs-runtime decision&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;By a developer running AI agents as daily teammates — a peer, not an authority (&lt;a href="https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8"&gt;full framing in #1&lt;/a&gt;). · &lt;strong&gt;~7 min&lt;/strong&gt; · &lt;a href="https://github.com/letuhao/dead-light-framework" rel="noopener noreferrer"&gt;the Dead Light Framework repository (MIT)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The tax you're paying (and want gone)
&lt;/h2&gt;

&lt;p&gt;If you hand real work to AI agents, you pay this every day: each new session &lt;strong&gt;starts from zero&lt;/strong&gt;. You re-explain the project, what you decided last time, what's in flight, which files matter. Fifteen, twenty minutes of re-priming a human teammate would never need — and worse, the agent cheerfully re-litigates Monday's decision on Wednesday because nothing told it the decision was settled.&lt;/p&gt;

&lt;p&gt;My least favourite version of it: I once left a comment explaining &lt;em&gt;why&lt;/em&gt; an ugly branch of code had to stay. Two days later a fresh session, sent in to tidy up TODOs, read the comment &lt;em&gt;as&lt;/em&gt; a TODO and deleted the branch by morning. The reasoning died with the session that wrote it. That's the tax — and it compounds.&lt;/p&gt;

&lt;p&gt;It isn't a model problem. Each session is stateless by design; the last session's reasoning is gone unless something on disk carries it. So put it on disk — deliberately, in a shape the next session can consume in one read. Here's the smallest setup that does it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The setup: two files, four rules (≈10 min)
&lt;/h2&gt;

&lt;p&gt;Drop two files at your repo root. That's the whole mechanism.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;HANDOFF.md&lt;/code&gt; — the snapshot a fresh session reads first
&lt;/h3&gt;

&lt;p&gt;Your project's &lt;strong&gt;current state on one screen&lt;/strong&gt;: what's true now, what's mid-task, what's decided, what to do next. It's the &lt;em&gt;first&lt;/em&gt; thing an agent reads each session — the thing that replaces fifteen minutes of you re-explaining. &lt;strong&gt;Rewrite it freely;&lt;/strong&gt; it always describes "now" (running history lives in &lt;code&gt;LOG.md&lt;/code&gt;, below). Think of it as the project's working memory, externalised so a memoryless teammate can borrow it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;doc_kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;state&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;working&lt;/span&gt;          &lt;span class="c1"&gt;# draft | working | sealed&lt;/span&gt;
&lt;span class="na"&gt;updated&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-05-22&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gh"&gt;# HANDOFF — &amp;lt;project&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Now            # what is true today&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Frontend v2 rename is done; auth is on the new schema.

&lt;span class="gu"&gt;## In flight      # mid-task work + who owns it&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Migrating &lt;span class="sb"&gt;`users`&lt;/span&gt; table — session-12, half done; next step is the backfill.

&lt;span class="gu"&gt;## Decided        # do NOT re-litigate these&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Auth must not import billing. Why: layering; billing changes shouldn't ripple into auth.

&lt;span class="gu"&gt;## Start here next&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Run the &lt;span class="sb"&gt;`users`&lt;/span&gt; backfill, then delete the legacy column.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Copy the full, commented template:&lt;/em&gt; &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/distribution/templates/handoff-template.md" rel="noopener noreferrer"&gt;&lt;code&gt;handoff-template.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;LOG.md&lt;/code&gt; — the append-only history
&lt;/h3&gt;

&lt;p&gt;If &lt;code&gt;HANDOFF.md&lt;/code&gt; is "now", &lt;code&gt;LOG.md&lt;/code&gt; is "everything that happened" — one line per event, &lt;strong&gt;append-only; you never edit a past line&lt;/strong&gt; (a correction is a new line). Why keep it when the snapshot already shows the current state? Because the snapshot &lt;em&gt;overwrites itself&lt;/em&gt;: the moment you need to know &lt;em&gt;why&lt;/em&gt; something was decided, replay how you got here, or recover after a session left a mess, you need the history the snapshot threw away. The snapshot is derived &lt;em&gt;from&lt;/em&gt; this log — not the other way round.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;doc_kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;log&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="gh"&gt;# LOG — &amp;lt;project&amp;gt;   (append-only; a correction is a NEW line, never an edit)&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; 2026-05-22 · session-12 · decided  · auth must not import billing (layering)        &lt;span class="c"&gt;&amp;lt;!-- sealed --&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; 2026-05-22 · session-12 · created  · users-table migration draft                     [candidate]
&lt;span class="p"&gt;-&lt;/span&gt; 2026-05-22 · session-12 · note     · backfill must run before dropping legacy column
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Copy the full, commented template:&lt;/em&gt; &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/distribution/templates/log-template.md" rel="noopener noreferrer"&gt;&lt;code&gt;log-template.md&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The four rules
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Two kinds, never mixed.&lt;/strong&gt; &lt;code&gt;HANDOFF.md&lt;/code&gt; is &lt;em&gt;current state&lt;/em&gt; — overwrite it freely. &lt;code&gt;LOG.md&lt;/code&gt; is &lt;em&gt;history&lt;/em&gt; — &lt;strong&gt;append only; never edit a past line&lt;/strong&gt; (a correction is a new line). This one split is what makes the whole thing trustworthy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First thing every session:&lt;/strong&gt; read &lt;code&gt;HANDOFF.md&lt;/code&gt;, then the new lines in &lt;code&gt;LOG.md&lt;/code&gt; since you last looked. That's your re-prime — under a minute, no human needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last thing every session:&lt;/strong&gt; append what you did to &lt;code&gt;LOG.md&lt;/code&gt;, then update &lt;code&gt;HANDOFF.md&lt;/code&gt; to match. (An agent can do both as part of "wrap up.")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tag what isn't settled.&lt;/strong&gt; &lt;code&gt;[candidate]&lt;/code&gt; = produced by an agent, not human-confirmed. &lt;code&gt;&amp;lt;!-- sealed --&amp;gt;&lt;/code&gt; = a decision that must not be "cleaned up" away. Agents read these.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No tool to install, no service to run — git plus two markdown files. Want it as one copy-paste page (both templates + the rules + the agent instruction)? &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/distribution/agent-context-quickstart.md" rel="noopener noreferrer"&gt;The Agent Context Quickstart&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tell your agent once&lt;/strong&gt; (system prompt / &lt;code&gt;CLAUDE.md&lt;/code&gt; / &lt;code&gt;.cursorrules&lt;/code&gt;): &lt;em&gt;"At the start of every session read HANDOFF.md and the recent LOG.md lines before doing anything. At the end, append your actions to LOG.md and update HANDOFF.md. Never edit past LOG lines; never touch a &lt;code&gt;&amp;lt;!-- sealed --&amp;gt;&lt;/code&gt; decision without asking."&lt;/em&gt; Now the discipline is the agent's job, not yours.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What you actually get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Re-prime drops from ~15 min to ~1 min.&lt;/strong&gt; The agent reads two files and is current — you stop being a human context-cache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decisions stop silently reverting.&lt;/strong&gt; A &lt;code&gt;sealed&lt;/code&gt; line in &lt;code&gt;Decided&lt;/code&gt; is a wall the next session sees; the Wednesday-undoes-Monday failure mostly stops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can stop mid-task and resume clean.&lt;/strong&gt; &lt;code&gt;In flight&lt;/code&gt; + the LOG tail tell the next session exactly where to pick up — even a &lt;em&gt;different&lt;/em&gt; agent, even weeks later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honest cost: ~2 minutes of discipline per session (append + update), and it pays off only once you're past a handful of sessions or running more than one agent. Below that, a plain README is fine — don't over-build.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why it works (the 30-second version)
&lt;/h2&gt;

&lt;p&gt;Documentation is your team's shared memory. When some teammates wipe their memory every session, the documents have to &lt;em&gt;carry state&lt;/em&gt; — and the reliable way to carry state across actors that can't sync live is exactly this: one &lt;strong&gt;append-only history&lt;/strong&gt; plus a &lt;strong&gt;derived current-state&lt;/strong&gt; view. That's the eventually-consistent coordination pattern distributed systems have used for decades; I just borrowed it. The full standard — including the multi-repo and multi-agent versions, and the failure modes — is &lt;a href="https://github.com/letuhao/dead-light-framework/blob/main/framework/paperwork-standard.md" rel="noopener noreferrer"&gt;&lt;code&gt;framework/paperwork-standard.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This covers one repo and one session at a time.&lt;/strong&gt; The moment you have &lt;em&gt;two agents writing at the same instant&lt;/em&gt;, or an invariant that must never break even for a second, two markdown files can't promise it — and that's a real, provable limit, not a gap you patch with better notes. Knowing which side of that line you're on is the next post.&lt;/p&gt;




&lt;h2&gt;
  
  
  The story below the setup &lt;em&gt;(optional — skip if you came for the templates)&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;You can stop here with a working setup. If you want the &lt;em&gt;why behind the why&lt;/em&gt;, here it is.&lt;/p&gt;

&lt;p&gt;Back in late 2024 / early 2025, when I first started handing agents real work — &lt;em&gt;audit this service&lt;/em&gt;, &lt;em&gt;draft this migration&lt;/em&gt;, &lt;em&gt;pick up where the last session left off&lt;/em&gt; — this was a dumb, recurring tax. Every new session opened with me re-explaining the same context, and by the third I was burning fifteen or twenty minutes re-establishing state a human teammate would simply have &lt;em&gt;had&lt;/em&gt;. So I wrote a better &lt;code&gt;HANDOFF.md&lt;/code&gt;. Then a better one. The overhead kept climbing, and a voice in the back of my head kept saying: &lt;em&gt;you're carving this at the wrong joint.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I made the mistake of &lt;em&gt;following&lt;/em&gt; the problem — and it turned out not to be the problem I thought it was. Strip the word "documentation" and it's stark: I had actors that &lt;strong&gt;start cold, run briefly, and can't talk to each other in real time&lt;/strong&gt;, and they had to act coherently anyway. That's not a docs question — it's &lt;em&gt;distributed systems&lt;/em&gt;, a field that's been proving theorems about exactly this since the 1970s. I'd been hand-rolling a worse version of it in markdown without noticing.&lt;/p&gt;

&lt;p&gt;That's also why this framework wears &lt;em&gt;Warhammer 40,000&lt;/em&gt; names, in case the "darkness" felt like an affectation. The Imperium of Man runs a galaxy with &lt;strong&gt;no real-time communication&lt;/strong&gt; — its ships cross the &lt;em&gt;warp&lt;/em&gt;, where they're simply unreachable. So it governs on three things: &lt;strong&gt;frozen edicts&lt;/strong&gt; (decided once, not renegotiable by whoever's nearest), the &lt;strong&gt;Adeptus Administratum&lt;/strong&gt; (literally galactic paperwork), and the &lt;strong&gt;Astronomican&lt;/strong&gt; — a beacon of light a ship lost in the dark steers by. Strip the gothic paint and that's the entire engineering of this post: frozen authority, durable records, and a signal that survives. The darkness in the title is the warp between your sessions; your two files are the Astronomican.&lt;/p&gt;

&lt;p&gt;And there's a catch I'll be honest about, because it shapes the whole series: that "real, provable limit" two paragraphs up isn't hand-waving — coordinating actors with no live channel runs into a genuine theorem (CAP), and it caps what &lt;em&gt;any&lt;/em&gt; pile of documents can promise. So after I'd borrowed all this and wired it together, I spent more effort trying to &lt;strong&gt;break&lt;/strong&gt; it than to build it — cold, hostile reviewers; an independent pass over every borrowed citation; benchmarks designed to make it fail. Some of it failed. That story is the rest of the series — but your setup above doesn't wait on any of it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;New here? I'm a developer who runs AI agents daily — a peer, not an authority; full framing in &lt;a href="https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8"&gt;#1&lt;/a&gt;. Standing caveat: one developer, essentially one case study — useful, not proven. Tell me where it breaks for you.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Every session starts in darkness" is Warhammer-flavoured naming, nothing more. Independent practitioner exploration; no affiliation with Games Workshop. Repository MIT-licensed.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  DeadLightFramework #AIAgents #AIProductivity #Documentation #ContextContinuity #AIAgentGovernance #HumanAICollaboration #PromptEngineering #DevTools
&lt;/h1&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Dead Light Framework: An Experimental Framework for Human-AI Collaboration #Post 1</title>
      <dc:creator>Lê Tú Hào</dc:creator>
      <pubDate>Tue, 12 May 2026 04:39:23 +0000</pubDate>
      <link>https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8</link>
      <guid>https://dev.to/letuhao/dead-light-framework-an-experimental-framework-for-human-ai-collaboration-post-1-5bh8</guid>
      <description>&lt;h2&gt;
  
  
  The Emperor Is All But Dead. The Light Remains.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  An experimental governance framework for software teams of humans and AI agents — and a request to be argued with
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Status: experimental. Unverified in the field. Looking for sparring partners more than followers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By:&lt;/strong&gt; a developer with ~10 years across many projects, not an academic or industry authority — full bio at the bottom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; 2026-05-11 · &lt;strong&gt;~8 min read&lt;/strong&gt; ·&lt;br&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/letuhao/dead-light-framework" rel="noopener noreferrer"&gt;github.com/letuhao/dead-light-framework&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;I have been building software with AI agents long enough to see the same governance failure mode appear over and over: agents and humans contradicting Monday's decisions on Wednesday, layers leaking into each other, no anchor to navigate by. I am testing the hypothesis that &lt;strong&gt;human + AI software projects need a frozen source of authority that no participant — including the author — can rewrite at will.&lt;/strong&gt; This post is the opening of an open debate; sharper arguments against it would help me more than agreement.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;One question I most want to be wrong about:&lt;/strong&gt; Is "frozen authority" actually compatible with "evolutionary architecture"? I think yes — argue with me.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The pain I keep running into
&lt;/h2&gt;

&lt;p&gt;I have been building software with AI agents long enough — daily, across multiple projects — to recognize a pattern that does not look like a bug.&lt;/p&gt;

&lt;p&gt;On Monday, an agent and I agree that the auth layer should not know about billing. On Wednesday, a different session of the same agent cheerfully imports a billing helper into the auth module, because the prompt of the day made it convenient. The change passes review, because the human reviewer has also forgotten the Monday conversation. By the time anyone notices, the layering decision has been quietly inverted in three places.&lt;/p&gt;

&lt;p&gt;Another version of the same story: I commit a fix with a comment explaining &lt;em&gt;why&lt;/em&gt; a specific branch of code must stay. Two days later, a fresh agent session is sent in to clean up TODOs and reads the comment as a TODO. By morning the carefully-preserved branch is gone, and the previous session's reasoning died with the previous session.&lt;/p&gt;

&lt;p&gt;This is not a model failure. It is not a human failure either. It is the predictable result of a team in which:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Some members are stateless.&lt;/strong&gt; Foundation-model agents have well-documented memory and identity limits across sessions (see Bommasani et al. 2021, &lt;em&gt;On the Opportunities and Risks of Foundation Models&lt;/em&gt;; Park et al. 2023, &lt;em&gt;Generative Agents&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "why" behind past decisions is in nobody's working memory.&lt;/strong&gt; Humans forget. Agents don't even start with the context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Many actors can each "decide".&lt;/strong&gt; When everyone has authority to nudge a direction, nothing actually sticks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latest input dominates.&lt;/strong&gt; Agents will amplify whatever the most recent prompt suggests, including the wrong directions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have come to think of these as &lt;strong&gt;governance gaps wearing technical disguises.&lt;/strong&gt; No amount of better prompts, better tests, or better refactor discipline patches them. They are properties of the &lt;em&gt;team shape&lt;/em&gt;, not of any single contributor.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we're fighting against — "The Chaos"
&lt;/h2&gt;

&lt;p&gt;The failure pattern above has a name in this framework: &lt;strong&gt;The Chaos.&lt;/strong&gt; It is the umbrella for four specific drift modes that tend to compound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context rot&lt;/strong&gt; — agents lose the &lt;em&gt;why&lt;/em&gt; behind past decisions and re-invent or contradict prior choices across sessions (the Monday/Wednesday and TODO-misread stories above).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architect rot&lt;/strong&gt; — without a fixed reference, refactors land in incompatible directions. Humans and agents drift further apart from any earlier coherent design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope creep&lt;/strong&gt; — the project keeps absorbing new concerns. Agents amplify it because the latest prompt is always more vivid than the original mandate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulated technical debt&lt;/strong&gt; — local conveniences that, once normal, are hard to undo. Humans and agents together can ship more of it, faster than a single human could.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is roughly what the AI-dev community has lately started calling &lt;strong&gt;"vibe coding"&lt;/strong&gt;: shipping code by feel, with agents steering, no anchor strong enough to make Monday's promise survive into Wednesday's commit. Vibe coding is wonderful for prototypes. It is brutal for anything that has to outlive a single session.&lt;/p&gt;

&lt;p&gt;The framework's job is not to forbid vibe coding. It is to give a project enough of a fixed backdrop that, when it graduates from prototype to &lt;em&gt;thing-people-rely-on&lt;/em&gt;, decisions can be made against something stable instead of against the void.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where existing methodologies leave a design slot empty
&lt;/h2&gt;

&lt;p&gt;I want to be careful here, because this is the easiest place to overreach.&lt;/p&gt;

&lt;p&gt;Waterfall, Agile, Scrum, SAFe, RUP — these work. I am not in a position to grade them. If an AI agent shows up to a stand-up the way a competent teammate does — persistent role, accountable for decisions, reads the working agreements, follows what was decided yesterday — Scrum runs the same as it always has. Sometimes better, frankly, because the agent does not forget the meeting on the drive home.&lt;/p&gt;

&lt;p&gt;So I do not want to claim the methodologies "fail" or "stop covering" anything when agents join. That would be both arrogant and inaccurate.&lt;/p&gt;

&lt;p&gt;What I do think is narrower: &lt;strong&gt;none of these methodologies were designed with AI agents as first-class participants in mind.&lt;/strong&gt; They do not specify what an "agent role" looks like — its memory model, its onboarding procedure, its authority bounds, its drift profile, how its decisions are attributed across sessions. That is an unfilled design slot, not a coverage failure.&lt;/p&gt;

&lt;p&gt;The Dead Light Framework is one attempt at filling that slot. It sits &lt;em&gt;on top of&lt;/em&gt; whatever delivery framework you already run, not in place of it. If your Scrum is well-disciplined and your reviews are tight, you will catch some of the failure modes I described above without any of this. The framework is for the parts your existing process was never asked to handle in the first place.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hypothesis (the part you should attack)
&lt;/h2&gt;

&lt;p&gt;The thing I am testing is one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A software project for humans + AI agents needs a frozen source of authority that no participant — human or agent — can rewrite at will.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Codified once by a small council. Sealed before kickoff. Humans interpret it. Agents execute within it. Neither group obeys a &lt;em&gt;person&lt;/em&gt; — both navigate by the same fixed light.&lt;/p&gt;

&lt;p&gt;This is not a radical idea outside software. It is roughly how constitutional federalism works (the U.S. Constitution constrains every subsequent administration), how religious institutional canon works (the Nicene Creed is older than any living interpreter), how central-bank mandates work (a price-stability mandate outlasts any single governor), and how RFC-driven protocol governance works (TCP/IP does not get rewritten because a vendor finds it inconvenient).&lt;/p&gt;

&lt;p&gt;What is novel — &lt;em&gt;if anything&lt;/em&gt; — is applying this pattern at the level of an individual software project, with AI agents as first-class participants whose context windows guarantee the authority cannot live in their heads.&lt;/p&gt;

&lt;p&gt;I call the sealed document the &lt;strong&gt;Astronomican&lt;/strong&gt;. I call the sealing meeting the &lt;strong&gt;Ascension Council&lt;/strong&gt;. I call the agent-type rulebooks &lt;strong&gt;Codices&lt;/strong&gt;. The names are borrowed from Warhammer 40,000.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the metaphor (important)
&lt;/h2&gt;

&lt;p&gt;I want to be honest about this up front, because it is the obvious objection.&lt;/p&gt;

&lt;p&gt;The Imperium of Mankind in Warhammer 40,000 is a &lt;em&gt;cautionary tale&lt;/em&gt;. It is grimdark by design: a bureaucratic, paranoid, ossified empire that fails spectacularly across ten thousand years. Picking it as a governance metaphor without acknowledging that is internally contradictory.&lt;/p&gt;

&lt;p&gt;So I do not use it as evidence. The framework's policy, written into its own rules, is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;40k vocabulary is naming and shared metaphor only.&lt;/strong&gt; Every load-bearing argument must rest on a real-world system with an observable track record: constitutional federalism, military command-and-control doctrine, central-bank mandates, religious canon, established corporate practice (Toyota Production System, Amazon two-pizza teams), open-source governance, established software methodologies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the 40k name and the real-world precedent disagree, the real-world precedent governs. The Imperium provides memorable names. Toyota's Andon Cord, the U.S. military's C2/SIGINT loop, and Bezos-era Amazon's API mandate provide the actual design lessons — particularly on the hardest problem the Imperium itself failed at: &lt;strong&gt;centralized authority combined with distributed sensing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you find a place in the framework where I leaned on 40k &lt;em&gt;as an argument&lt;/em&gt; rather than as a name, that is a finding. Please file it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Glossary — 40k terms used above
&lt;/h2&gt;

&lt;p&gt;For readers who do not know Warhammer 40,000 — one-liners on each term used in this post.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Astronomican&lt;/strong&gt; — In W40k, the psychic beacon that guides the Imperium's space travel after its god-emperor has all but died. &lt;strong&gt;In this framework:&lt;/strong&gt; the name for the sealed project document of purpose, immutable laws, and guiding principles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Imperium of Mankind&lt;/strong&gt; — The fictional galactic empire in W40k. Used here only as a memorable source of names; &lt;em&gt;not&lt;/em&gt; as a governance role model (the empire fails spectacularly in canon — that is part of why I quote it carefully).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex / Codices&lt;/strong&gt; — In W40k, the rulebook each Space Marine Chapter operates under. &lt;strong&gt;In this framework:&lt;/strong&gt; the rulebook each AI agent type operates under (operational bounds, hard stops, output contract, notify triggers).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adeptus Administratum&lt;/strong&gt; — In W40k, the Imperial bureau of records, taxation, and administrative logistics — the empire's "chief of paperwork." &lt;strong&gt;In this framework:&lt;/strong&gt; the first sealed Chapter — a PM / High-Lord aide role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ascension Council&lt;/strong&gt; — &lt;em&gt;Not&lt;/em&gt; from canon. The framework's name for the one-time small group of humans who seal the project's founding document before kickoff and then disband.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chapter / Chapters&lt;/strong&gt; — In W40k, a self-contained battle order of Space Marines, each with its own Codex. &lt;strong&gt;In this framework:&lt;/strong&gt; an agent &lt;em&gt;type&lt;/em&gt;, each with its own Codex.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Chaos&lt;/strong&gt; — In W40k, the warp-based corrupting forces the Imperium fights eternally. &lt;strong&gt;In this framework:&lt;/strong&gt; the umbrella failure mode the framework tries to defend against — context rot, architect rot, scope creep, accumulated technical debt; roughly the kind of drift "vibe coding" produces when extended beyond prototyping.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What this is and is not
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This is:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;em&gt;composition layer&lt;/em&gt; that sits on top of Agile / Scrum / Kanban / whatever you already run. It does not replace delivery rhythm.&lt;/li&gt;
&lt;li&gt;An attempt to give projects a constitution-like artifact and an explicit protocol for agent participation.&lt;/li&gt;
&lt;li&gt;A working hypothesis with a documented audit trail (38 findings against my own claims, all remediated, still openly listed).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;This is not:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proven. I have one in-flight case study (a 358-KLOC project called LoreWeave). One case is not evidence. It is a smoke test.&lt;/li&gt;
&lt;li&gt;A productivity tool. It will add overhead before it removes any.&lt;/li&gt;
&lt;li&gt;A claim that you should run your project this way. It is a claim that the failure modes are real, that existing methodologies were simply not designed with agent participants in scope, and that &lt;em&gt;some&lt;/em&gt; framing in this neighborhood is probably needed to fill that slot.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Where this stands today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Phase 0 (the calibration/audit phase for retrofit projects) — &lt;strong&gt;sealed&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Phase 1 (the Astronomican itself) — partial. Six known open questions, listed publicly.&lt;/li&gt;
&lt;li&gt;Phase 2 (Codex per Chapter) — first Chapter sealed (a PM/High-Lord aide called the Adeptus Administratum). Others wait for real-project triggers.&lt;/li&gt;
&lt;li&gt;Phase 3 (drift detection) and Phase 4 (re-consecration) — not started.&lt;/li&gt;
&lt;li&gt;One case study (LoreWeave) — Phase 0 Pass 1 about to begin.&lt;/li&gt;
&lt;li&gt;Internal audit (Independent Verification Pass) — five of seven phases complete. The audit is public, including the times the framework failed its own audit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is in the open. The framework is being built in a single repo with full debate history.&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/letuhao/dead-light-framework" rel="noopener noreferrer"&gt;github.com/letuhao/dead-light-framework&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I want from readers
&lt;/h2&gt;

&lt;p&gt;Not converts. Arguments.&lt;/p&gt;

&lt;p&gt;Specifically, I want people to attack these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is "frozen authority" actually compatible with "evolutionary architecture"?&lt;/strong&gt; I think yes, with a re-consecration ceremony. But that ceremony is unsealed and you might convince me it is impossible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does the 40k vocabulary do more harm than good?&lt;/strong&gt; I find it useful as memorable scaffolding for a debate-driven team. But it may be repelling readers who would otherwise engage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where does an industry standard already do this job?&lt;/strong&gt; If COCOMO II / CMMI v3.0 / ITIL 4 / DORA already cover one of the gaps I think I am filling, I want to know before adding another box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What is the smallest experiment that would falsify the framework?&lt;/strong&gt; I am genuinely unsure how to design this. A failed retrofit on one project is suggestive, not conclusive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What did I import from the Imperium that I should not have?&lt;/strong&gt; I keep finding things. Help me find more.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What's coming next
&lt;/h2&gt;

&lt;p&gt;A short series of posts will work through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The case study in detail (where it hurt, with numbers).&lt;/li&gt;
&lt;li&gt;Why Agile/Scrum specifically do not cover this gap.&lt;/li&gt;
&lt;li&gt;The mechanics of sealing an Astronomican.&lt;/li&gt;
&lt;li&gt;The Codex pattern for AI agents.&lt;/li&gt;
&lt;li&gt;How the framework audits itself (and the times it has failed).&lt;/li&gt;
&lt;li&gt;The anti-patterns I knowingly imported from a fictional dying empire, and how I compensate.&lt;/li&gt;
&lt;li&gt;Open questions where the framework could still be wrong.&lt;/li&gt;
&lt;li&gt;A practical adoption sketch — without promising it works.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of the failure modes I described sound like the project you are in right now, I would especially like to hear from you. The framework is far more useful as a piñata than as a manifesto.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;A working developer with roughly ten years of experience across a range of projects. Not an academic, not an industry authority on software methodology, not a methodologist of any kind. No chair, no certification body, no track record of published frameworks behind me.&lt;/p&gt;

&lt;p&gt;The Dead Light Framework — the subject of this post and the series it opens — is a personal exploration: one practitioner's attempt at finding methods that hold up when AI agents become full-time teammates. I publish it openly because I would rather be told I am wrong by people who have stood in front of the same problems than be politely ignored.&lt;/p&gt;

&lt;p&gt;If I sounded certain anywhere above, treat that as a slip in tone, not a claim of authority. The framework is at hypothesis stage. Everything is in scope to be argued with.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The Emperor is all but dead. The light remains.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/letuhao/dead-light-framework" rel="noopener noreferrer"&gt;github.com/letuhao/dead-light-framework&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Independent practitioner exploration. No affiliation with Games Workshop. Repository MIT-licensed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>discuss</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
