<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>K@it on Martin Sukany</title><link>https://sukany.cz/tags/k@it/</link><description>Recent content in K@it on Martin Sukany</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Fri, 20 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://sukany.cz/tags/k@it/index.xml" rel="self" type="application/rss+xml"/><item><title>Rebuilding a Tool in Four Hours: What the AI Agent Actually Did</title><link>https://sukany.cz/blog/2026-02-20-scenar-creator-ai-rebuild/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://sukany.cz/blog/2026-02-20-scenar-creator-ai-rebuild/</guid><description>&lt;p&gt;I have a small internal tool called Scénář Creator. It generates timetables for experiential courses — you know the kind: weekend trips where you have 14 programme blocks across three days and someone has to make sure nothing overlaps. I built version one in November 2025. It was a CGI Python app running on Apache, backed by Excel.&lt;/p&gt;
&lt;p&gt;Yesterday I asked Daneel to rebuild it. Four hours later, version 4.7 was running in production. Here&amp;rsquo;s exactly what happened.&lt;/p&gt;
&lt;h2 id="the-starting-point"&gt;The Starting Point&lt;/h2&gt;
&lt;p&gt;The original tool was functional but ugly in the developer sense. Python CGI means no proper request lifecycle, no validation, and Apache configuration that nobody wants to debug. Excel meant openpyxl and pandas as dependencies for what is essentially a colour-coded grid. The UI had a rudimentary inline editor but nothing you&amp;rsquo;d want to actually use.&lt;/p&gt;
&lt;p&gt;My requirements for the new version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No Excel, no pandas, no openpyxl — anywhere&lt;/li&gt;
&lt;li&gt;JSON import/export with a sample template&lt;/li&gt;
&lt;li&gt;PDF output, always exactly one A4 landscape page&lt;/li&gt;
&lt;li&gt;Drag-and-drop canvas editor where blocks can be moved in time and between days&lt;/li&gt;
&lt;li&gt;Czech day names in both the editor and the PDF&lt;/li&gt;
&lt;li&gt;Documentation built into the app itself&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-pipeline-command"&gt;The Pipeline Command&lt;/h2&gt;
&lt;p&gt;I typed &lt;code&gt;/pipeline code&lt;/code&gt; in Matrix followed by the requirements. This triggers a specific workflow I configured for Daneel: instead of answering directly, it spawns a chain of sub-agents.&lt;/p&gt;
&lt;p&gt;What that looks like internally:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Researcher sub-agent&lt;/strong&gt; — reads the existing codebase (CGI scripts, Dockerfile, rke2 deployment manifest), queries documentation for FastAPI, ReportLab, and interact.js, produces a technology brief&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Architect sub-agent&lt;/strong&gt; — takes the brief and the existing code, designs a new architecture, outputs a structured document marked &amp;ldquo;ARCHITEKTURA PRO SCHVÁLENÍ&amp;rdquo; (Architecture for Approval)&lt;/li&gt;
&lt;li&gt;Main agent presents the architecture to me. I type &amp;ldquo;schvaluji&amp;rdquo; (I approve).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coder sub-agent&lt;/strong&gt; — implements the full application based on the approved architecture&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each sub-agent is an independent session. They don&amp;rsquo;t share memory. They communicate through their outputs, which the orchestrator passes forward as context.&lt;/p&gt;
&lt;h2 id="the-context-overflow"&gt;The Context Overflow&lt;/h2&gt;
&lt;p&gt;About 40 minutes in, the orchestrator hit a context limit. The session died mid-flight. I got a message: &amp;ldquo;Context overflow: prompt too large for the model.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This is a real failure mode with multi-agent pipelines. The orchestrator had been accumulating all the research, architecture, and partial implementation output in a single context window. It eventually exceeded what Claude Sonnet can hold.&lt;/p&gt;
&lt;p&gt;When I opened a new session (&lt;code&gt;/new&lt;/code&gt;), Daneel&amp;rsquo;s first action was to run &lt;code&gt;memory_search&lt;/code&gt; on the session logs from the crashed session. The key fragments were there:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The architecture document (partially recovered)&lt;/li&gt;
&lt;li&gt;The approved tech stack: FastAPI + Pydantic, ReportLab Canvas API, interact.js from CDN, vanilla JS frontend&lt;/li&gt;
&lt;li&gt;The deployment infrastructure: podman on daneel.sukany.cz, Gitea registry, kubectl via SSH to infra01&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then Daneel did something worth noting: it checked the &lt;strong&gt;live cluster&lt;/strong&gt; before assuming the background agents had implemented anything correctly. The health endpoint returned &lt;code&gt;{&amp;quot;status&amp;quot;: &amp;quot;ok&amp;quot;, &amp;quot;version&amp;quot;: &amp;quot;2.0&amp;quot;}&lt;/code&gt;. The background agents had claimed v3.0 was deployed. It wasn&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;This is a lesson I keep relearning. Check the actual state of the system, not the reported state.&lt;/p&gt;
&lt;h2 id="what-implementation-actually-means"&gt;What &amp;ldquo;Implementation&amp;rdquo; Actually Means&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s what the agent concretely did, in order:&lt;/p&gt;
&lt;h3 id="read-the-existing-codebase"&gt;Read the existing codebase&lt;/h3&gt;
&lt;p&gt;Every relevant file: the CGI scripts, the Pydantic models, the Dockerfile, the rke2 deployment YAML. Not a summary — the actual file contents, via the &lt;code&gt;read&lt;/code&gt; tool. About 12 files.&lt;/p&gt;
&lt;h3 id="wrote-the-new-application"&gt;Wrote the new application&lt;/h3&gt;
&lt;p&gt;Six Python modules (&lt;code&gt;main.py&lt;/code&gt;, &lt;code&gt;config.py&lt;/code&gt;, &lt;code&gt;models/event.py&lt;/code&gt;, &lt;code&gt;api/scenario.py&lt;/code&gt;, &lt;code&gt;api/pdf.py&lt;/code&gt;, &lt;code&gt;core/pdf_generator.py&lt;/code&gt;) plus four JavaScript files (&lt;code&gt;canvas.js&lt;/code&gt;, &lt;code&gt;app.js&lt;/code&gt;, &lt;code&gt;api.js&lt;/code&gt;, &lt;code&gt;export.js&lt;/code&gt;), CSS, HTML, and a sample JSON fixture. Each file was written with &lt;code&gt;write&lt;/code&gt; (full file) or &lt;code&gt;edit&lt;/code&gt; (surgical replacement of a specific text block).&lt;/p&gt;
&lt;h3 id="ran-tests-locally"&gt;Ran tests locally&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python3 -m pytest tests/ -v
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;33 tests at v4.0, growing to 37 by v4.7. Every deploy was preceded by a clean test run.&lt;/p&gt;
&lt;h3 id="built-the-docker-image"&gt;Built the Docker image&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman build --format docker \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -t &amp;lt;private-registry&amp;gt;/martin/scenar-creator:latest .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;--format docker&lt;/code&gt; flag is required for RKE2&amp;rsquo;s containerd runtime. Without it, the manifest format is OCI, which a standard Kubernetes deployment can&amp;rsquo;t pull directly.&lt;/p&gt;
&lt;h3 id="pushed-to-the-private-gitea-registry"&gt;Pushed to the private Gitea registry&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# credentials loaded from environment
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;podman push &amp;lt;private-registry&amp;gt;/martin/scenar-creator:latest
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Credentials come from environment variables, not hardcoded.&lt;/p&gt;
&lt;h3 id="deployed-via-ssh"&gt;Deployed via SSH&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh root@infra01.sukany.cz \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;kubectl -n scenar rollout restart deployment/scenar &amp;amp;&amp;amp; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; kubectl -n scenar rollout status deployment/scenar --timeout=60s&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;kubectl&lt;/code&gt; is not available on the machine Daneel runs on. It&amp;rsquo;s only on infra01. Direct SSH as root is the access pattern that works; daneel@ access is denied on that host.&lt;/p&gt;
&lt;h3 id="verified-the-deployment"&gt;Verified the deployment&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;curl -s https://scenar.apps.sukany.cz/api/health
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{&amp;#34;status&amp;#34;:&amp;#34;ok&amp;#34;,&amp;#34;version&amp;#34;:&amp;#34;4.4.0&amp;#34;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This ran after every deploy. Not assumed, verified.&lt;/p&gt;
&lt;h2 id="the-bugs"&gt;The Bugs&lt;/h2&gt;
&lt;p&gt;The interesting part is what didn&amp;rsquo;t work the first time.&lt;/p&gt;
&lt;h3 id="cross-day-drag-three-iterations"&gt;Cross-day drag — three iterations&lt;/h3&gt;
&lt;p&gt;The requirement was that programme blocks could be dragged between days, not just along the time axis within a single day. The first implementation used interact.js for both horizontal (time) and vertical (day) movement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First attempt (v4.3):&lt;/strong&gt; Added Y-axis movement to interact.js with &lt;code&gt;translateY&lt;/code&gt; on the block element. The block disappeared during drag because the block lives inside a &lt;code&gt;.day-timeline&lt;/code&gt; container with &lt;code&gt;overflow: hidden&lt;/code&gt;. A block translated outside its container gets clipped.&lt;/p&gt;
&lt;p&gt;The fix attempt was to add &lt;code&gt;overflow: visible&lt;/code&gt; to the containers during drag using a CSS class toggle. It didn&amp;rsquo;t fully work because &lt;code&gt;.canvas-scroll-area&lt;/code&gt; has &lt;code&gt;overflow: auto&lt;/code&gt;, which creates a new stacking context and clips descendants regardless.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second attempt (v4.5):&lt;/strong&gt; Replaced interact.js dragging with native pointer events. Created a floating ghost element on &lt;code&gt;document.body&lt;/code&gt; (no stacking context issues). Moved the ghost freely during drag. Used &lt;code&gt;document.elementFromPoint()&lt;/code&gt; on &lt;code&gt;pointerup&lt;/code&gt; to determine which &lt;code&gt;.day-timeline&lt;/code&gt; the user dropped on.&lt;/p&gt;
&lt;p&gt;This almost worked. The ghost moved correctly. But &lt;code&gt;elementFromPoint&lt;/code&gt; was unreliable — sometimes it returned the ghost itself (even with &lt;code&gt;pointer-events: none&lt;/code&gt;), sometimes it returned the wrong element.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Third attempt (v4.6):&lt;/strong&gt; Two changes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Call &lt;code&gt;el.releasePointerCapture(e.pointerId)&lt;/code&gt; at drag start. Without this, the browser implicitly captures the pointer on the element that received &lt;code&gt;pointerdown&lt;/code&gt;. On some platforms, this affects which element receives subsequent events and can block the ghost&amp;rsquo;s hit-testing.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Replace &lt;code&gt;elementFromPoint&lt;/code&gt; entirely. At drag start, capture &lt;code&gt;getBoundingClientRect()&lt;/code&gt; for every &lt;code&gt;.day-timeline&lt;/code&gt; and store them. On &lt;code&gt;pointerup&lt;/code&gt;, compare &lt;code&gt;ev.clientY&lt;/code&gt; against the stored rectangles. No DOM querying during the drop — just a loop over six numbers.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This worked. Simple coordinate comparison, no browser API surprises.&lt;/p&gt;
&lt;h3 id="czech-diacritics-in-pdf"&gt;Czech diacritics in PDF&lt;/h3&gt;
&lt;p&gt;ReportLab&amp;rsquo;s built-in Helvetica doesn&amp;rsquo;t support Czech characters. &amp;ldquo;Pondělí&amp;rdquo; became garbage bytes.&lt;/p&gt;
&lt;p&gt;Fix: added &lt;code&gt;fonts-liberation&lt;/code&gt; to the Dockerfile (provides LiberationSans TTF, a metrically compatible Helvetica replacement with full Latin Extended-A coverage). Registered the font at module load:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pdfmetrics.registerFont(TTFont(&amp;#39;LiberationSans&amp;#39;, &amp;#39;/usr/share/fonts/...&amp;#39;))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Fallback to Helvetica if the font file isn&amp;rsquo;t found, so local development without the package still works.&lt;/p&gt;
&lt;h3 id="am-pm-time-display"&gt;AM/PM time display&lt;/h3&gt;
&lt;p&gt;HTML &lt;code&gt;&amp;lt;input type&lt;/code&gt;&amp;ldquo;time&amp;rdquo;&amp;gt;= displays in 12-hour AM/PM format on macOS/Windows browsers with US locale, even when the page has &lt;code&gt;lang&lt;/code&gt;&amp;ldquo;cs&amp;rdquo;&lt;code&gt;. The =.value&lt;/code&gt; property always returns 24-hour HH:MM (that part works), but the visual display was wrong.&lt;/p&gt;
&lt;p&gt;Fix: replaced &lt;code&gt;type&lt;/code&gt;&amp;ldquo;time&amp;rdquo;= with &lt;code&gt;type&lt;/code&gt;&amp;ldquo;text&amp;rdquo;= with &lt;code&gt;maxlength&lt;/code&gt;&amp;ldquo;5&amp;rdquo;= and an auto-formatter that inserts &lt;code&gt;:&lt;/code&gt; after the second digit. Validates on blur. Stores values as HH:MM strings, which is what the rest of the code already expected.&lt;/p&gt;
&lt;h3 id="pdf-text-overflow-in-narrow-blocks"&gt;PDF text overflow in narrow blocks&lt;/h3&gt;
&lt;p&gt;Short programme blocks (15–30 minutes) have very little horizontal space. The block title would overflow the clipping path and just get cut off mid-character.&lt;/p&gt;
&lt;p&gt;Fix: added a &lt;code&gt;fit_text()&lt;/code&gt; function in the PDF generator. It uses ReportLab&amp;rsquo;s &lt;code&gt;stringWidth()&lt;/code&gt; to binary-search the longest string that fits in the available width, then appends &lt;code&gt;…&lt;/code&gt; if truncation occurred.&lt;/p&gt;
&lt;p&gt;In the canvas editor, blocks narrower than 72px now hide the time label; blocks narrower than 28px hide all text and rely on a &lt;code&gt;title&lt;/code&gt; tooltip attribute.&lt;/p&gt;
&lt;h2 id="the-deployment-count"&gt;The Deployment Count&lt;/h2&gt;
&lt;p&gt;15 deploys between 16:00 and 20:00 CET. Each one: build (~30s from cache), push (~15s for changed layers), &lt;code&gt;rollout restart&lt;/code&gt; (~25s for pod replacement), &lt;code&gt;curl&lt;/code&gt; to verify. About 90 seconds per cycle, plus whatever time was spent writing the code.&lt;/p&gt;
&lt;p&gt;The Kubernetes deployment uses &lt;code&gt;imagePullPolicy: Always&lt;/code&gt; and the &lt;code&gt;:latest&lt;/code&gt; tag, so every &lt;code&gt;rollout restart&lt;/code&gt; pulls the freshest image. No manifest changes needed between iterations.&lt;/p&gt;
&lt;h2 id="what-the-agent-didn-t-do"&gt;What the Agent Didn&amp;rsquo;t Do&lt;/h2&gt;
&lt;p&gt;No browser interaction. Daneel can control a browser but I didn&amp;rsquo;t ask for that and it wasn&amp;rsquo;t needed — the verification was just an API health check.&lt;/p&gt;
&lt;p&gt;No speculative changes. Every code change was in response to a concrete requirement or a confirmed bug. Daneel didn&amp;rsquo;t add features I didn&amp;rsquo;t ask for.&lt;/p&gt;
&lt;p&gt;No silent failures. When a deploy failed or a test broke, it stopped and reported. It didn&amp;rsquo;t try to paper over errors or push anyway.&lt;/p&gt;
&lt;h2 id="observations"&gt;Observations&lt;/h2&gt;
&lt;p&gt;The most expensive bug was the cross-day drag, not because it was technically complex but because it required three separate hypotheses, three implementations, and three deploys to find the actual failure mode. The first two were reasonable guesses that happened to be wrong.&lt;/p&gt;
&lt;p&gt;The context overflow in the pipeline wasn&amp;rsquo;t catastrophic because the memory system worked. The session logs from the crashed orchestrator were searchable. The critical facts — approved tech stack, deployment procedure, live cluster state — were recoverable. This is the point of building memory infrastructure before you need it.&lt;/p&gt;
&lt;p&gt;The total elapsed time from &lt;code&gt;/pipeline code&lt;/code&gt; to &amp;ldquo;considered resolved&amp;rdquo; was about four hours. The application went from CGI+Excel to FastAPI+JSON+drag-and-drop canvas in that window. That&amp;rsquo;s not a claim about AI replacing developers. It&amp;rsquo;s a data point about what changes when you have an agent that can write code, run it, push it, and verify it in the same loop you&amp;rsquo;d use as a human developer — just without context switching or fatigue.&lt;/p&gt;
&lt;p&gt;M&amp;gt;&lt;/p&gt;</description></item><item><title>Website Redesign with AI Assistant</title><link>https://sukany.cz/blog/2026-02-16-website-redesign-with-ai/</link><pubDate>Mon, 16 Feb 2026 00:00:00 +0000</pubDate><guid>https://sukany.cz/blog/2026-02-16-website-redesign-with-ai/</guid><description>&lt;p&gt;Yesterday I rebuilt this website. Daneel helped.&lt;/p&gt;
&lt;p&gt;The old site was scattered across multiple repos, inconsistent structure, no clear content strategy. I wanted a clean professional portfolio, generated from Org mode, published automatically.&lt;/p&gt;
&lt;h2 id="what-daneel-did"&gt;What Daneel Did&lt;/h2&gt;
&lt;p&gt;I gave Daneel my CV (PDF) and told it to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Extract relevant content&lt;/li&gt;
&lt;li&gt;Add it to the Org source file&lt;/li&gt;
&lt;li&gt;Write a blog post about its own creation&lt;/li&gt;
&lt;li&gt;Fix deployment issues&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Within an hour:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Profile page populated with education and certifications&lt;/li&gt;
&lt;li&gt;Experience section with detailed work history (2018–present)&lt;/li&gt;
&lt;li&gt;Skills page with core competencies&lt;/li&gt;
&lt;li&gt;Two blog posts written and committed&lt;/li&gt;
&lt;li&gt;Hugo theme integration debugged and fixed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="what-i-did"&gt;What I Did&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Provided direction (&amp;ldquo;use CV, make it professional&amp;rdquo;)&lt;/li&gt;
&lt;li&gt;Reviewed changes before merge&lt;/li&gt;
&lt;li&gt;Corrected security model in blog post (Daneel has project-specific access, not full system access)&lt;/li&gt;
&lt;li&gt;Approved final structure&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="the-difference"&gt;The Difference&lt;/h2&gt;
&lt;p&gt;Traditional workflow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Extract text from PDF manually&lt;/li&gt;
&lt;li&gt;Format content in Org mode&lt;/li&gt;
&lt;li&gt;Write blog posts&lt;/li&gt;
&lt;li&gt;Debug Hugo build&lt;/li&gt;
&lt;li&gt;Commit and deploy&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Hours of context switching.&lt;/p&gt;
&lt;p&gt;With Daneel:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&amp;ldquo;Here&amp;rsquo;s the CV, populate the site&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Review and approve&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The time savings aren&amp;rsquo;t the point. The point is: I stayed focused on strategy and decisions. Daneel handled execution.&lt;/p&gt;
&lt;h2 id="technical-stack"&gt;Technical Stack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Content:&lt;/strong&gt; Org mode (single source file)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generator:&lt;/strong&gt; Hugo + ox-hugo (Org → Markdown)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Theme:&lt;/strong&gt; Beautiful Hugo (directly embedded, not submodule)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment:&lt;/strong&gt; Kubernetes (RKE2) + init containers (git clone → hugo build → nginx)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automation:&lt;/strong&gt; Daneel (content extraction, debugging, documentation)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Website source: &lt;a href="https://git.apps.sukany.cz/sukany-org/web-sukany.cz"&gt;git.apps.sukany.cz/sukany-org/web-sukany.cz&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;M&amp;gt;&lt;/p&gt;</description></item><item><title>Websites generation from Org-style document</title><link>https://sukany.cz/blog/2026-02-13-emacs-org-hugo-export/</link><pubDate>Fri, 13 Feb 2026 00:00:00 +0000</pubDate><guid>https://sukany.cz/blog/2026-02-13-emacs-org-hugo-export/</guid><description>&lt;p&gt;For many years, I sought an efficient workflow for managing my personal websites. Over two decades ago, I began with pure HTML, progressed to WordPress, then shifted to my own HTML5 and CSS designs, and eventually adopted Obsidian with Hugo. My primary motivation was to generate websites directly from the source in my chosen editor, but this journey often led to considerable headaches.&lt;/p&gt;
&lt;p&gt;Two decades ago, I exclusively used Emacs as my work environment, and at that time, Org mode hadn&amp;rsquo;t caught my interest. However, upon returning to Emacs, I decided to give Org mode a try. I was pleasantly surprised by its power compared to the standard Markdown and Pandoc setup. After experimenting with it, I&amp;rsquo;m excited to share the results: my websites are now generated from a single site.org file. The only steps I needed to take were to add the ox-org package to my Emacs configuration and invoke a simple function.&lt;/p&gt;
&lt;p&gt;As I&amp;rsquo;m using Doom Emacs, export itself could be invoked by one straightforward keystroke:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;SPC m e H A ;; Doom Leader -&amp;gt; Local mode -&amp;gt; export -&amp;gt; Hugo -&amp;gt; All subtrees to Hugo MD files
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The rest is just a pure Hugo configuration.&lt;/p&gt;
&lt;h3 id="tl-dr"&gt;TL;DR&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ox-hugo&lt;/code&gt; is an Emacs package that enables users to export Org mode documents to Hugo-compatible Markdown format, facilitating the creation of static sites using the Hugo framework. It allows users to write content in Org mode, a powerful plain text markup language, and then convert it seamlessly into Hugo posts or pages, handling metadata, front matter, and table of contents appropriately. Users can configure various export options, including templates, tags, and categories, providing flexibility for managing how their content appears on Hugo sites. The integration enhances productivity by allowing users to leverage Emacs&amp;rsquo; editing capabilities while ensuring compatibility with Hugo&amp;rsquo;s requirements.
And that&amp;rsquo;s it!&lt;/p&gt;
&lt;p&gt;Have fun!
M&amp;gt;&lt;/p&gt;
&lt;h2 id="emacs-configuration"&gt;Emacs configuration&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(after! org
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ;; 0) require packages
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; (require &amp;#39;ox-hugo))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="log-from-message-buffer"&gt;Log from Message buffer&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] 1/ Exporting &amp;#39;Home&amp;#39; ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] 2/ Exporting &amp;#39;Profile&amp;#39; ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] 3/ Exporting &amp;#39;Portfolio&amp;#39; ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] 4/ Exporting &amp;#39;Contact&amp;#39; ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] 5/ Exporting &amp;#39;Blog&amp;#39; ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] 6/ Exporting &amp;#39;2026-02-13 Websites generation from Org-style document&amp;#39; ..
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] &amp;#39;New blog post&amp;#39; was not exported as it is tagged with an exclude tag &amp;#39;noexport&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ox-hugo] Exported 6 subtrees from sites.org in 0.107s (0.018s avg)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="websites-source-code-example"&gt;Websites source code example&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+title: Martin Sukany - Architect
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+author: Martin Sukany
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+hugo_base_dir: ../
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#+hugo_front_matter_format: toml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;* Pages
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_HUGO_SECTION: /
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;** Home
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_FILE_NAME: _index
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;As a Cloud and Infrastructure Architect with over 20 years of diverse experience in IT including support, engineering, software development, and architecture I specialize in delivering innovative solutions for large financial client. My extensive background equips me to understand complex challenges and implement effective strategies that drive efficiency and growth. Let&amp;#39;s transform your infrastructure to meet the demands of today&amp;#39;s dynamic landscape.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;** Profile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_FILE_NAME: profile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TODO: profile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;** Portfolio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_FILE_NAME: portfolio
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Over the past 20 years, I have delivered impactful solutions across diverse technical challenges:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Founded and developed the BlindUbuntu project, a specialised Linux-based operating system for visually impaired users. In 2006, when accessible open-source solutions were scarce, I conducted comprehensive analysis, selected appropriate assistive tools, and integrated them into a complete out-of-the-box solution.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Led critical incident response when a disk array failure affected multiple ESX farms, bringing approximately 500 servers offline. I drove the recovery effort and restored full operations, including data redundancy, within 24 hours.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Developed and deployed a proprietary remediation solution for the Log4j vulnerability affecting over 1,000 servers in my scope, implementing protection before official patches and community tools became available.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Led a comprehensive monitoring migration project across a multi-platform environment encompassing Unix, Windows, SAN, VMware, appliances, and application monitoring. I designed the target architecture and provided implementation guidance throughout the entire project lifecycle.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Contributed to enterprise datacentre management software development, employing a data-driven approach to manage thousands of devices for large-scale customers.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- Modernised a monolithic IBM Power application stack, including database, web application, middleware, and interfaces, transforming it into a cloud-native microservices architecture.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;After two decades in the industry, I remain passionate about continuous learning and embracing emerging technologies.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;** Contact
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_FILE_NAME: contact
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**Martin Sukany**
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**Address:** Bri Luzu 114, 68801 Uhersky Brod, ZL Czech Republic
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**IC:** 11831073
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;**E-mail:** martin@sukany
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;;* Blog
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_HUGO_SECTION: blog
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;** Blog
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_FILE_NAME: _index
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Here you will find topics that interest me professionally. While not every solution applies to everyone, I enjoy exploring these challenges. These posts represent significant time invested in solving real problems-and solutions worth sharing.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;** 2026-02-13 Websites generation from Org-style document :k@it:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:PROPERTIES:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_FILE_NAME: 2026-02-13-emacs-org-hugo-export
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:EXPORT_DATE: 2026-02-13
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;:END:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Here will be text of my first post!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item></channel></rss>