2026

An empty queue meant 111 blocked tasks

The name Ready empty describes a tick's outcome, not the state of a folder, and the system reacts the same way to both; the daemon meanwhile ran on old code because nobody restarted it after the fix.

What I did

  • Worked out that “empty queue” is the name of a tick’s outcome, not the state of a folder: Ready held 111 tasks and not one could be taken, but the system treats that as emptiness — it goes off promoting drafts instead of finding out why everything is blocked.
  • The daemon kept shutting down on a fault we had fixed the day before; it was running the code it started with, and a restart was all it needed.

What the factory did

  • Found that a renamed epic freed its name for a new one, so a reference written before the rename still resolves, only it points at a different epic; of 28 live citations four are broken and sixteen need the surrounding sentence read.

The data crossed into the new database

After three rounds of deciding and a rehearsed transfer, lifalign-prod holds 5,236 rows; the first sign-in then found a redirect loop, which is fixed.

What I did

  • Settled how the data crosses into the new database: a cutover, not a period of running both, and three rounds fixed the eight parameters that turn a direction into something executable.
  • The crossing ran: lifalign-prod holds 5,236 rows, every table balances, and I checked on screen that the data is where it belongs.
  • The first sign-in to the new application looped — three different errors in a row had one cause; fixed.
  • Moved the functions to Frankfurt; until now a request from Prague was executed in Washington.
  • Found that the daily 16:00 deploy I had set up the day before has never run: zero scheduled runs, and production is 95 commits behind.

What the factory did

  • Finished moving the reminder scheduler onto the API; the web reaches Supabase nowhere any more.
  • Finished binding the colours to tokens — no CSS module in the web writes a colour now, and the exception list is deleted rather than left empty.
  • Converted the documents in lifalign/designs and the last two decision records to English, in batches overnight.
  • Refuses to store a retirement older than the row itself, on every table that can be retired.
  • Deleted the web’s unreachable copy of the day’s rules and repointed what named it.
2026

The orchestrator shut down over a state that clears itself

A lock on a branch whose task could not be found ended the whole daemon, when skipping one tick would have done.

What I did

  • The orchestrator stopped shutting itself down when it cannot find the task behind a locked branch; that state clears on its own, so skipping one tick is enough.
  • Had three cheap guard workflows merged into one billed job, because I was running out of free GitHub Actions minutes.
  • Measured the factory’s throughput: 32 unique tasks reached a pull request in 24 hours, 52 in 48.

What the factory did

  • Split a task that contradicted itself — “empty the whole colour allowlist” against “one screen per pull request” — into four that can be claimed, and split them along 550 measured violations rather than by file count.

The last path from the web into Supabase has a replacement

The reminder scheduler was the last place the web reached Supabase directly; how it should run was settled, and the schema meanwhile got indexes under its foreign keys.

What I did

  • Settled how reminders will run: a cron runs SQL that only checks whether waking the backend is worth it, and the backend does the sending.
  • Had production deploy once a day at 16:00.
  • Dropped the “add as a flow moment” action; the Flow lístek no longer takes anything over from a todo.
  • Settled that emptying the text in the diary leaves an empty record — the diary has no delete.
  • Turned down the proposed fix for the false “Úkol se mezitím změnil” warning: the guard would skip a write that changes nothing, but the application makes exactly that write on purpose, so that a save with no change still passes the concurrent-edit check.

What the factory did

  • Opened the move of the reminder scheduler onto the API, the last place the web reached Supabase directly; a cron in the database now wakes it only when there may be something to send.
  • Moved Historie plánů and the Dnes dashboard behind the API, and thinned Uzavření dne down to one read.
  • Gave every foreign key an index on its own side; Postgres indexes only the target, so deleting a parent scanned the whole child table.
  • Named the columns on on delete set null across seven composite keys; without the list it also cleared user_id, which may not be empty.
  • Readiness now refuses a replica that does not hold the issuer’s signing keys and so could not verify a sign-in token.
  • Made the build fail and name the method when an adapter does not wrap its database work so a technical failure arrives above as one known type; nothing watched for the omission before.
  • Tested for the first time what happens when saving a form fails — until now the tests only knew success: an expired session sends you to the login, a server outage is not passed off as a mistake in what you typed, and a refused form does not wipe what you wrote.
  • Bound the colours in the seven Detail dne CSS modules and in the shared components to tokens, and added a detector for every syntax that can write a colour.
2026

Eight days of silence in #alerts and an eleven-hour queue

One rejected message jammed the whole channel and nothing said so; the seven thousand alerts that piled up were then delivered at one every six seconds.

What I did

  • Brought the Discord bot back up; it had died with an unexpected machine restart and stayed down because its configuration still held the repository path on the old drive.
  • Found why #alerts went silent for eight days: one rejected message whose fingerprint ran to 2,224 characters against a 2,048 limit in the footer jammed the whole channel.
  • Made the parked-task alert name what is parked; until now it sent an empty list.
  • Went through the piled-up alert queue one at a time and insisted the cause of the slowness be measured rather than guessed; batching came out of it, instead of one card edit per alert.
  • Extended branch cleanup to every namespace the factory pushes under; only claude/ and codex/ were being swept.
  • Had the remote branches nobody needs any more deleted.

What the factory did

  • Stopped one task file from living in two board folders at once.
  • Promoted nothing and wrote down why: every draft it would have taken sits in one of five open promotion pull requests.

Detail dne went behind the API, the month and year arrived

Eight more screens moved behind the API, Detail dne among them, and the last two rungs of rolling planning were built.

What I did

  • Settled how a collision in the day’s plan is resolved: either rearrange the day or drop the colliding blocks — both ways stay.
  • The smoke check now starts the whole stack itself, fake sign-in included, so it needs no dedicated account and no hosted project; it had been red on every task since 17 September.

What the factory did

  • Moved eight more screens behind the API: the notification devices, Opakované bloky, Úkoly, Rituály, Rolling Planning, Detail dne, Uzavření dne and Plán dne.
  • Built Měsíční plán and Roční plán, the last two rungs of rolling planning with nowhere to store themselves.
  • Taught the API to drop colliding blocks; until now it could only rearrange the day, so Detail dne could not move without losing the feature.
  • Renamed the colours from --color-daily-* to roles; the name claimed they belonged to one screen while every screen read them (974 occurrences in 62 files).
  • Gave ScreenHeader the two slots its last two screens needed and were drawing their own heading for.
  • Deleted the self-location line from fifteen documents; it was wrong twice over — it named folders that had moved and a drive the repository had left.
  • Made decision 0017 derive its numbers from the model; the hand count was stale for the third time (3 → 16 → 28).

The site can hold books and reading notes

Books are their own kind of content on the site, next to projects, and a journal entry can hang off a book the way it hangs off a project.

What I did

  • Added books to the site as their own kind of content, next to projects, and reading notes from individual chapters under them.
  • A journal entry can now hang off a book the way it hangs off a project, and the journal index filters by book.
  • Books and notes have both language versions and are in the sitemap.
2026

The factory ran a finished task fourteen times

The orchestrator read the board from the working tree, so any branch under the primary checkout could make a finished task look ready; the board now comes from origin/main.

What I did

  • Moved reading the board from the working tree to origin/main, after the orchestrator ran a long-finished task fourteen times and ate 20% of a five-hour limit; along the way it turned out Get-TaskFileTitle returned an empty title for all 92 Ready tasks.
  • Let promotion pull requests merge on their own; a coordination run adds no code, it only moves tasks around the board.
  • Counted what the factory has done so far: 504 task runs with a median of 13 minutes.

What the factory did

  • Promoted five refined drafts and recorded three blockers.
  • Refused the premise that the board was empty — Ready held 14 and 75 tasks — and instead of promoting, wrote two generated stubs into real tasks and parked one for me to decide.

Eight screens moved behind the API

The factory moved eight screens off the database and behind the .NET API, and added the checks that catch a broken deploy before a user does.

What the factory did

  • Moved eight screens behind the API: Kategorie času, Nastavení, Osobní kontext, Flow lístek, Ohlédnutí, Šablony rozpočtu dne, Rozpočet dne and Zastavení; each lost its Supabase adapter and its second copy of the rules.
  • Opened the ninth, the notification device screens; that one waits to merge.
  • Production schema drift now reports itself; until today a user found it first, on a screen that stopped working.
  • Ran dotnet ef database update in CI for the first time; the one route by which the schema reaches a database had never been exercised.
  • The health check now tries the database too; until today a live process was enough, so a deploy with the wrong password came up green and fell over on the first real request.
  • Added a test that reads the schema as the migrations leave it and catches a write that depends on a default which disappears on the way.
  • Fixed a randomly failing test — the fault was in the test, not the component: it did not wait for React to process the action’s result and skipped a state transition that runs once.

První kapitola Software Engineer's Guidebook

Četl jsem o typech firem, profit a cost centrech a o tom, jak různá prostředí mění rychlost kariérního růstu.

Co jsem četl

Začal jsem číst The Software Engineer’s Guidebook a prošel první kapitolu o kariérních cestách. Kapitola rozebírá rozdíly mezi typy firem, jejich velikostí, sektorem, fází růstu a tím, jak důležitý je pro ně software.

Co mi došlo

Nejsilnější pro mě byla myšlenka, že firma a tým nejsou jen místo, kde člověk pracuje. Jsou to prostředí s různě rychlou zpětnou vazbou, různou mírou příležitostí a různým stropem kariérního růstu.

Dobře mi do toho zapadla metafora výtahu po kariérním žebříčku. Každá firma, tým a pozice má jinou rychlost. Některé prostředí člověka táhne nahoru rychleji, jiné ho může držet na místě, i když se v něm pořád pracuje.

Další krok

Chci si u dalších kapitol víc hlídat, které myšlenky jsou jen obecné rady a které mi pomáhají lépe pojmenovat vlastní zkušenost z Gopasu, Mews a aktuálního směru kolem AI Factory a AI Coach.

2026

A record that changes nothing is just an archive

P15 and the incident rules were rewritten so whoever writes an incident also proposes the change, and the one-open-pull-request limit gave way to the Touch set.

What I did

  • Rewrote P15 and the incidents README: a record that changes nothing is not learning but an archive — whoever writes an incident proposes the change too.
  • Had the incident written about the session that handed me a table of three Vercel projects as fact, with nothing to build it from.
  • Added reading a pull request’s state before pushing to its branch to the developer skill; three of the day’s pull requests took commits an hour after they merged.
  • Replaced the one-open-pull-request limit with the Touch set: a tick now skips only a task that touches files an open pull request already changed.
  • Repointed the paths agents create worktrees and read their state from at the drive the repository moved to (the agent spotted it and proposed the change).
  • Rewrote 68 task references so they carry no folder; the folder is the task’s state, so a reference broke every time a task moved (the agent spotted it and proposed the change).

What the factory did

  • Promoted three refined drafts and parked the one only the owner can decide.
  • Opened the record that the coordination pass promoted the same drafts three times in thirty-seven minutes, because nobody merged its branches.

New production up, the web on the API

The new production estate in Azure stands for the first time and a full deploy ran through it; the Lifalign web app started moving onto the .NET API.

What I did

  • Brought up a new production estate in Azure and pulled a full deploy through it for the first time: migrations, image, container app, web, tag.
  • Opened ingress on production; prod.bicepparam still had it off and would have cut production off again on the next deploy.
  • Disconnected the old ai-coach Vercel project from git so no migration can break it and it keeps running on the old sign-in and the old database; the two new Lifalign projects went up beside it.
  • Renamed Supabase to lifalign-prod, postponed staging and now deploy production straight from main; the free tier allows two projects and both are taken.
  • Pulled the design gallery back to the development server; its prerendered images had grown the deployment bundle from 1.74 MB to 52 MB.
  • Built the gateway to the .NET API and sent the first screen, Poznámky, across it.
  • Rewired the Lifalign web app’s sign-in from Smart Diary to the .NET API; Supabase is still the identity provider, the API is just the one that talks to it now.
  • A screen not yet behind the API throws at its first read and names the missing table, so the migration is visible; sign in, sign out, More and Poznámky work.
  • Wrote down how the columns of the live and target databases correspond, both ways; they are not two versions of one schema.
  • Ran the product locally against plain Postgres.
  • Cut the rest of the move into eighteen P0 tasks, Detail dne among them at five screens.

What the factory did

  • Served personal context, deleting a note, and the day’s plan timeline over HTTP, the last of those across eleven routes.
  • Gave the change trail an implementation, so writing a plan block no longer ends at an empty description.
  • Added a check that finds a merged, unapplied migration before a user does.
  • Capped the time category tree at three levels.
  • Straightened the naming: the epic is Detail dne and the section Na tento den; the fix for twelve glossary citations waits to merge.
2026

A full quota and a client with no database key

Vercel hit the free tier ceiling, so the web app stopped building for changes it cannot see, and a new client started that holds no access to the database.

What I did

  • Vercel reported 100% of its 10 GB Function Storage; the cause is accumulation — every retained deployment keeps its own copy of three Node functions, and since 17 July only 40 of 1072 commits on main touched lifalign/apps/web.
  • The web app stopped building for changes it cannot see; the rest of the day added two skipped records instead of dozens of builds.
  • Wrote a tool in the factory that deletes old deployments from the tick — it has never run, because profile.json has no vercel block and the trigger returns silently.
  • Deleted the old deployments by hand: 312 left, 154 of them READY, nothing older than 1 September. The 12.33 GB has not moved yet.
  • Rate limiting got a cheap local check: a cookie’s token is tested for shape and a known signing key before anything goes out to Supabase for it.
  • Both .bicepparam files asked for the image tag latest, which nothing publishes; they point at dev.
  • Decided the new frontend will hold no access to the database, and started the client that proves it: sign in, list time categories, sign out, one API_BASE_URL.

What the factory did

  • Opened the change that offers a ritual as done without a Reality block — a finished plan block is enough; waiting to merge.

The journal stopped showing a week twice

A project journal lists only the roots — a week appears as its summary, not again as each of its days.

What I did

  • A project journal lists only the roots: the weekly summary yes, the days inside it no, and a day from a week that has not closed still yes.
  • One function holds that rule and the journal index calls it too — it had the same logic written inline.
  • The entry chip lost the word “Projekt”; the project’s name alone is left.
2026

Dev, prod, and a seventeen-day outage

A day spent settling how dev and prod should look, which turned up an API that had been serving a three-week-old image.

What I did

  • Worked out what dev and prod should look like for the database, the frontend and the backend, and picked Bicep over Terraform and Pulumi.
  • The grilling turned up that the dev API had been serving an image from 28 August for seventeen days, because a setting it needs was never added to the container app.
  • Split the work on the environments into seven tasks, by who can actually execute each step.
  • Both Azure environments are described by one Bicep template now, and a deploy verifies the revision it created.
  • Fixed deploying migrations: the command ran with a context the caller guard had never been attached to.
  • Wrote down that the product name has two spellings and which one belongs where.

What the factory did

  • Put a row’s two timestamps on one clock and repaired the rows that had them apart.
  • Recomputed the length of every Reality block that disagreed with its own range.
  • Mirrored the Todo text bounds in the EF mapping and put a guard on every check constraint.
  • Reads a day’s captured blocks in the order they happened.

The journal learned to point at projects

Entries and projects now link both ways, the product got its real name across the site, and a finished week closes itself.

What I did

  • A journal entry carries a chip for its project: clickable when that project has a page, dashed and inert when it does not.
  • A project links the other way, into the journal pre-filtered to itself.
  • The entry header came down to the chip, the date and the title.
  • Renamed the product to Lif-align across the whole site.
  • The personal website got its own project page, and a link to it from the Now page.
  • A finished week now gets a summary, and that week’s days leave the journal index.
week

The week the house model began

Week of 7-13 September on Raj 512: the model started from nothing and ended with four variants of the new house and a staged plan.

What I did

  • Started the model, so the options for extending the house could stand side by side and be compared.
  • Put the plot, the setbacks and the water main’s easement into it as walls that can be hidden, which shows where nothing may be built.
  • The roof, eight windows and nine rooms came out of the drawings instead of out of an estimate.
  • By the end of the week there were four variants of the new house plus a staged plan, each with a rough cost off the model’s own quantities.
  • An independent critic showed that half the checks could never have failed, and a mutation test went in against that.

The week I picked where to learn from

Week of 7-13 September: the first LeetCode problem, and a decision about which platforms and channels to follow.

What I did

  • Signed up for LeetCode and solved the first problem.
  • Looked at four learning platforms and settled which newsletter and which channels to follow.

The week the backend got a layer and an API

Week of 7-13 September on Lif-align: an unauthenticated caller no longer reaches the API, the application layer went in, and then it went out over HTTP.

What the factory did

  • An endpoint is now locked until someone writes that it is public - it used to be the other way round. A rate limit went in beside it.
  • The signed-in user reached the database connection: the caller scope opens a transaction and sets the claims and the role inside it.
  • Twelve areas got an application layer above their repositories, all in the same shape - domain type, repository interface, use case, mapper.
  • That layer then went out over HTTP, ten more parts of the domain by Sunday.
  • Five changes were visible in the app: the user’s own time zone, a two-layer budget slider, a note length, offering an archived template again, and taking a todo off the list and back.
  • Penpot ended. A screen is now assembled from real components in a gallery inside the repository.
  • The Actions minutes ran out, so the repository checks moved into the factory’s own run.
  • A test that no runner executed turned up, and a guard against the next one went in with it.

What I did

  • Deploying migrations to production failed on our own guard, and the fix is still not written.

The week the site got two languages and a journal

Week of 7-13 September on the personal website: Czech and English split apart, the journal arrived, and the CV moved to the front page.

What I did

  • Split the site into a Czech and an English version and put it on its own domain.
  • Added the journal as two collections sharing one schema, with an index, a detail page and filters, and no database behind it.
  • Hid the Writing section behind a feature flag without deleting a single article.
  • Moved the CV onto the front page, as PDF or Word, and deleted the page that used to hold it.

No journal entries yet.