There is a moment in every personal framework where the problem stops being only architectural.
At first, Lubinik was mostly about separation: parent theme, core plugin, addons, child themes, support plugins, and tools that each had a clearer job than the messy first version. That part mattered. It still matters. But once the system started to live across several real sites, another problem became impossible to ignore.
How do you keep all of this updated without turning maintenance into a full-time job?
By that point, Lubinik was no longer one codebase. It was a small constellation. The parent theme. The core plugin. SEO. Structured data. Translation. The API plugin. Addons for shelters, comics, freelance portfolios, and the Lubinik showcase itself. Child themes for each site. Local versions. Live versions. Private GitHub repositories. Real production sites that should not be treated like experiments.
Manually pulling every local folder, checking every plugin, uploading ZIPs by hand, remembering which child theme belonged to which site, and hoping I had not forgotten one small shared package was not a system. It was a future headache politely waiting its turn.
The local problem came first
My local setup had several WordPress sites, and many of them reused the same Lubinik pieces. When the parent theme changed, or the core plugin changed, or one shared support plugin changed, I had to update those clones in multiple places.
So the first small tool was not glamorous. It was a local sync helper: a script that reads a config file, walks through the sites I care about, finds the Lubinik packages declared for each one, and runs a safe git pull --ff-only when the repo is clean.
It does not copy code between sites. It does not try to be clever. It simply asks each local Git clone to update itself, and it refuses to touch anything dirty, locked, missing, or not actually controlled by Git.
The goal was not automation for the sake of automation. The goal was to stop making maintenance depend on my memory.
Then came the live sites
Local updates were only half the story. The live sites needed something different. I did not want production sites to be Git checkouts. I wanted them to behave like normal WordPress installations: installed themes and plugins, updated through WordPress, with private code still staying private.
That meant building a private updater.
The idea is simple: each Lubinik package has a version, each GitHub repository has tags, and the updater checks the latest tag it can access. If the live site has an older version, it can download the tagged ZIP from GitHub and replace the installed plugin or theme.
Because the repositories are private, each live site gets its own GitHub token with read-only access to only the repositories that site is allowed to update. The Lubinik site can read the showcase packages. The comics site can read the comics packages. The shelter sites can read the shelter packages. No one site needs a master key to everything.
Release hygiene suddenly mattered
Once updates come from GitHub tags, releases become real. Versions cannot just be vibes. A plugin header saying 1.0.0 and a Git tag called v1.0.0 need to mean the same thing.
That also exposed another tiny but important detail: GitHub ZIP archives include the repository contents. Which means development folders, documentation folders, and internal notes can end up on production unless the repository says otherwise.
So the next little ritual appeared: add .gitattributes, exclude doc and docs from export archives, bump the version, commit, push, tag, push the tag.
Doing that once is fine. Doing that across fourteen packages is the moment you start questioning your life choices. So I made another helper. Not a big product. Just a small release script to handle the repetitive part while still keeping the process explicit.
What changed
After this, Lubinik felt different.
It was not only a framework made of reusable pieces anymore. It had a maintenance path. Local sites could be updated from their Git clones. Live sites could update from private tagged releases. Each site could have its own limited token. Release ZIPs could avoid internal documentation. The updater could understand that a child theme folder might be named differently from its repository.
None of that is especially flashy, but it changes the emotional weight of the project. A system that is hard to update slowly becomes a system you avoid touching. A system that is easy to update stays alive.
Architecture is not only how the code is arranged. It is also how calmly you can keep using it six months later.
This is the kind of milestone that does not make a beautiful screenshot. There is no new homepage section, no dramatic visual change, no shiny feature to point at.
But it matters. Lubinik is now easier to maintain across real sites. That means it is easier to trust. And for a personal framework built from real projects, that might be one of the most important features so far.