What good is an article command center if it silently loses articles and fails publishing with a pile of raw Git errors?
Not much.
The dashboard had three related problems. The queue UI was stale, blog ingestion could quietly return zero articles, and publishing failures exposed Git mechanics instead of telling me what actually needed fixing.
I started with the queue.
I ported the flagship queue interface into the dashboard, including actions to generate or fill the queue. Rejected articles can now be replaced while inheriting the original slot, so rejecting one item does not scramble the schedule behind it. Every slot is anchored to 10:00 AM ET.
That inheritance matters. A queue is not just a list of drafts. It is a schedule with state. Replacing an article should change the content, not accidentally change the publishing plan.
I also removed the old single draft flow and the stale 72 hour review copy. Leaving obsolete interface language in place is worse than leaving no guidance at all. It teaches users a workflow the system no longer follows.
Next came blog ingestion.
The new Sync from Blog action pulls existing articles into the dashboard. The backend route now resolves src/content/articles relative to the configured site root. Previously, setting PERSONAL_SITE_DIR could make ingestion look successful while returning zero articles. A silent empty result is a particularly annoying failure because every layer appears operational. The path was simply being resolved from the wrong place.
Publishing needed the same treatment.
The publish preflight now identifies the actual blocker: no upstream branch, a detached HEAD, an unsynced upstream, or a dirty working tree. Raw Git output may be technically accurate, but it is not a product interface. The useful question is not what Git printed. It is what must be fixed before publishing can proceed.
Publishing now goes through publish_site_dir(). It prefers PERSONAL_SITE_PUBLISH_DIR, which can point to a dedicated publisher worktree tracking origin/main, and falls back to PERSONAL_SITE_DIR when that separate directory is unavailable.
The tradeoff is more configuration. A dedicated publisher worktree adds another path and another environment variable to manage. In return, publishing no longer depends on the state of the primary development tree. That is a good exchange. Editing and publishing have different operational requirements, so forcing both through one checkout was convenient right up until it was not.
I also regenerated the stale pnpm lockfile. The package dependencies had never been recorded correctly, which meant pnpm install with the frozen lockfile setting failed on every fresh checkout. Reproducibility that only works on the original machine is just cached luck.
The launch configuration now includes PERSONAL_SITE_PUBLISH_DIR. Updating the host LaunchAgent is deliberately deferred until after merge, since changing live host configuration before the code lands would invert the dependency order.
What would I do differently? I would separate the publisher worktree from the development tree earlier, and I would make zero article ingestion an explicit warning from day one. Silent success is not graceful failure. It is failure wearing a clean shirt.
Top comments (0)