The project switch was saved and invisible

8–12 minutes
measured on my own machine
The project switch was saved and invisible

On 2026-09-07 the history of my ticket cards showed the same empty line 20 times: “Note human:georg”, with no text after it. Each of those lines was a project switch I had made. Each one had been saved, and the production database held the value. The tool still told me 20 times that nothing had happened. Meanwhile 43 open cards were waiting on exactly that switch.

I wanted two things from a card: which product it belongs to, and what has happened to it. The board is where my agent loops park their output, one card per item. A rule I opened on 2026-09-07 says the channel follows the card. A card with no product never reaches an outbound gate. So giving 43 open cards a product was my job, done by hand in a dropdown. Nothing could go out until I had done it.

I believed saved and visible were the same thing. If I changed a value and the screen showed no trace of it, the change had not happened. So when card #307 would not show my switch to cross-product, I reported it as broken: I can’t submit it, I can’t save it. I went looking for a save button I must have missed. There wasn’t one, and there didn’t need to be.

The dropdown saves the moment you change it. There is no button. The submit button next to it belongs to the chat, which is why I went looking there. My value landed at 09:47. The route also wrote a comment event with two keys, the new project and the previous one. The problem was at the reading end. The drawer that renders the history knew exactly three comment keys: quote, reason and note. It dropped any other shape without a word. A project switch matched none of the three, so it rendered as a note with no body. You got an author, a timestamp and nothing else. When a save leaves no trace, it looks exactly like a save that failed.

I counted on production with sqlite3, the one tool the record names me using. My switches turned out not to be the only casualty. The renderer had been swallowing three kinds of events. Each one was recorded correctly and displayed as nothing.

Event shapeEvents shown as empty
Project switch (new, previous)20
Snooze9
File change10

I fixed it where the data is read, not where it is written. The writer had done its job, and the event was there. The history now names a project switch in plain words, “project: none → cross-product”, and takes the product names from the product config. Any shape it doesn’t recognise prints as key: value, so nothing renders silently any more. I checked the change against all 12 event shapes I had measured on production, and the test suite stayed green.

43063f4b · 2026-09-07

Die Historie sagt jetzt, was der Projektwechsel getan hat — vorher stand dort eine leere Notiz

The history now says what the project switch did. Before, an empty note stood there.

Once the history was readable, I could do the work the bug had been hiding. A one-off script assigned products to the open cards. It signed its events with its own actor name, so the history shows that a script made the change, not me. 37 cards got a product, and the number of open cards without one went from 43 to 5. Four of those five are genuinely ambiguous and wait for my decision. The fifth was simply missing from the list the script worked from.

e3f68cad · 2026-09-07

Handover: der Projektwechsel war gespeichert und unsichtbar, 37 Karten tragen jetzt ein Produkt

Handover: the project switch was saved and invisible, 37 cards now carry a product

The commit subjects in the original German, from the empty note to the single derivation.
The commit subjects in the original German, from the empty note to the single derivation.

On 2026-09-12 the same card shape came back one step further down the line. A card set to both products has to answer a second question: which site does it mean? A fix went in that morning. I asked a follow-up question: was it at the cause, and was it deterministic? When I measured it, the answer was half. The key format now comes from a data file and is computed rather than copied, and that half stays. The other half was a patch at the place where the bug showed up.

That patch gave one module a new local rule for “is this product value a site at all”. An existing function already answered that question at 13 call sites, with the word “both” written literally in its comment. Now there were two rules for one question, and the new one was weaker: when the site config could not be read, it let the raw value through. The patch was also incomplete. A sibling module committed the day before had the same raw fallback and printed a ‘both’ card as “Blog — both”.

08eebeeb · 2026-09-12

Eine Herleitung für „welche Site meint diese Karte” — der erste Fix saß halb am Fundort

One derivation for “which site does this card mean”. The first fix sat half at the place where the bug was found.

What stands now is one function that answers “which site does this card mean”. Both of its inputs are data, not code: the key format comes from one config file and the product vocabulary from another. It returns both intermediate results, so a caller can report a contradiction instead of swallowing it. The local rule is deleted. The product list used to be loaded in three places and is now loaded in one. A site that left the inventory on 07.08 now drops out for the same reason “both” does.

The steps, in the order they happened

  1. 2026-09-07I traced the empty history line to the renderer. It knew three comment keys and dropped every other shape. I fixed the read side: a project switch now reads in plain words, and unknown shapes print as key: value.
  2. 2026-09-07A one-off script gave 37 open cards a product, and I recorded the result. Cards without a product dropped from 43 to 5.
  3. 2026-09-07I wrote down what I had reported next to what was true. The value had been saved at 09:47, the dropdown saves without a button, and the submit button belongs to the chat.
  4. 2026-09-12I found that the morning’s site fix was only half at the cause. It added a weaker local rule next to an existing one and missed a sibling module with the same fallback. I replaced both with one derivation.
All 8 steps in order. Four of them are told here.
All 8 steps in order. Four of them are told here.

It ended mostly solved, with the open parts named. 37 cards carry a product, and the history says what every switch did. “Which site does this card mean” now has one derivation instead of several places that each guessed. What is not solved: 5 cards still have no product. I checked against all 19 live outbound cards on production. 17 are intact and 2 are defective. #501 points at an account that isn’t mine, and #504 has a site contradiction. That count is the same as it was that morning. The new function reports the contradiction instead of hiding it, but it doesn’t fix it. The record also found nine places in four files that split the card key by hand, and it describes a guard that counts them. The record is cut off at that point, so I can’t say what that guard enforces. Three test files changed with the fix, which points to node –test. The record doesn’t say I ran it.

What the record supports, and where it stops.
What the record supports, and where it stops.

This post is based on three commits from 2026-09-07 to 2026-09-12 and the handover journal of 2026-09-07. The record was measured on 2026-09-17.

43063f4b · e3f68cad · 08eebeeb

Georg Singer

Georg Singer

I automate my own projects in public and write down what each one cost. Physics degree, doctorate in computer science, nine systems in production on hardware I own, all of it built in my own time.

More from the log