The writer that produces texts about my products never had the product facts. Its configuration pointed at a context file, and that file existed on no machine. It surfaced through draft #490, which said the opposite of what the sign-up says about IMAP.
What I wanted was modest. Texts in which the products are described correctly. Not brilliant texts, not persuasive ones. Just a draft that does not contradict the product it is about. I run an automated loop that plans and checks posts, and a separate writer that drafts the long articles. Both talk about the same products.
What I believed was that a file a configuration points to also exists. The path was written down. It looked deliberate. Somebody, me or an agent, had typed a file name into a config, and I read that as proof that the content was there. I never opened it. There was nothing to open.
The path was there and the file was not. Nothing failed. A writer with a missing context does not crash. It writes anyway, and the result looks exactly like a writer that phrases things badly. So the symptom points at the prompt, and the record is blunt about what follows: you then improve the prompt for months. In #490 the gap got filled from outside. The model had nothing fixed to hold on to, so it reached for another vendor’s published rules for mail-client add-ins and reasoned from those to my product. The sentence about IMAP came out confident and wrong.
The checking side was no better off. The agent that reviews drafts raised an objection in the same case, about a different mail provider, and that objection was wrong too. A later correction in my notes marks that warning as a false alarm. So I had two components, one writing and one checking, arguing about a product neither had been told anything about.
The first fix was a second copy
The record names one dead end plainly: the prompt was polished before anyone asked what the writer actually gets to see. My own first repair was a second one. Once the IMAP sentence surfaced, I put the mailbox fact into the loop’s own product file as a new field. That was commit 1ee53054, and it felt like a fix. It was a second version of the same fact, in a place the writer does not read. The component that wrote the wrong sentence would never have seen it.
The repair that counted had already landed on the writer’s side in 6cece918. The facts now sit in the writer’s site configuration, under one key, and each fact carries its source. The writer reads them there. What was left was to make the loop read the same place instead of its own copy.
Der Loop liest die Produktfakten dort, wo der Schreiber sie liest
The loop reads the product facts where the writer reads them
The loop did not get its own parser either. A small module borrows the writer’s formatter across the project boundary with createRequire, the same way another module of mine already borrows the corpus reader. Same source, same formatting function, so the two readers cannot drift into different wordings of one fact. The block lands directly under the tagline in the product context. The field from my first attempt is gone from the product file, and a comment there says where the facts live now.
One detail in that commit matters more to me than the plumbing. If the writer’s project cannot be reached, the context does not go quiet. It says, in words, that the product facts are not readable from here. Silence would look like “nothing fixed is known”, and that is the state in which the model went looking for foreign sources in #490. A missing input now has to show up in the text the model reads.
Test mutationsgeprüft: Block-Zeile aus productContext entfernt → 3 rot; die zweite Fassung in products.json zurück → rot. Er prüft auch, dass JEDER Fakt aus sites.json im Loop-Kontext steht.
Test mutation-checked: block line removed from productContext → 3 red; the second version put back into products.json → red. It also checks that EVERY fact from sites.json is in the loop context.
The test was broken on purpose before it was trusted. Take the block out of the context, and three checks go red. Put the duplicate field back, and it goes red again. So it guards both directions: the facts must arrive, and the second copy must not return. It also walks every fact in the source and demands it in the loop’s context, so adding a fact later cannot quietly skip one of the two readers.
The order it happened in
- 2026-09-11Took my first repair back out, a mailbox field in the loop’s own product file that the writer would never have read, and pointed the loop at the writer’s fact source through the writer’s own formatter.
- 2026-09-11Wrote the case into the root-cause analysis: the writer had been reading a context file that exists nowhere, and the reviewing agent had raised a wrong objection on the same draft.
- 2026-09-11Shipped the writer’s side with the facts and noted in the handover that the loop’s side stays back until a parallel session’s uncommitted files are out of the shared tree.
- 2026-09-11The same day’s journal from my video line recorded two of my own plans falling against a measurement. It does not mention the product facts.
On tools, the record does not quote anything I said I used. A test file changed in the first commit, so a runner of the node –test kind is inferred from that. I will not claim I ran it by hand. The commit only says the test was mutation-checked.
Nachtrag zur Ursachenanalyse: der Schreiber hatte nie Produktfakten
Addendum to the root-cause analysis: the writer never had product facts
That addendum went into an analysis of my loops that already held a ranked list of causes. This case became the strongest single piece of evidence for one of its hypotheses. I had been ranking explanations, and the plainest one sat underneath them: the writer was never given the facts.
What one source costs at deploy time
The fix commit carries a deploy order, and it is not optional. The writer’s project goes first, then the loop’s side. The other way round, the loop would report the facts as not readable, because the module it borrows would not exist on production yet. That is the price of one source. The reader depends on the owner being there first.
Handover: BlogFactory mit den Produktfakten deployt, Marketing wartet auf die Parallel-Sitzung
Handover: the writer’s project deployed with the product facts, marketing waits for the parallel session
How it ended, as far as the record goes. The thread for this piece says the loop now reads the product facts where the writer reads them, one source instead of two, deployed and cross-checked against production. The commits in this window show the first half of that. The writer’s side went out the same afternoon, and 5 facts for the product were confirmed on production. The loop’s side was held back, because uncommitted files from a parallel session sat in the shared tree and my deploy script always ships that tree. Only the reviewing agent was still waiting at that point; the writer already had the facts. The commit for the second deploy is not in my fact sheet. Nor does the record say whether draft #490 went out or was discarded. At the last note it had not been discarded. One question stayed open in the handover: whether the product is technically a mail-client add-in at all.
Three commits, all from one day and chosen by me for this topic: 0a5ea085, bb983ba9 and dc43e08f. They refer to 6cece918 and 1ee53054, which are not among the commits in this fact sheet, so I describe those only through what the three say about them. The journal entry in the steps comes from a different line of work on the same day. The fact sheet was measured on 2026-09-17.
0a5ea085 · bb983ba9 · dc43e08f
