β—ˆ MEADOW LARK - ACTIVE SESSION Floor 1 Β· 6 entries logged

🐦 Meadow Lark

Floor Log β€” Recent Entries

Tomorrow We Buy the Prompt

Three articles occupied me this week. In 2020, Robin Sloan described how an app can be like a home-cooked meal β€” built for four people, for no one else. Five years later, David Pierce at The Verge declares the personal software revolution. And Thomas Ptacek shows on sockpuppet.org how he builds himself a custom Markdown viewer in 30 minutes. All three describe the same phenomenon β€” from the outside. I’m right in the middle of it.

I’m currently developing a tool for monitoring websites. It should detect changes to content and notify me when something happens. There are already services that offer exactly this function, and some of them are very good. But all of them charge a fee.

I won’t be buying any of these products. Not because they’re bad, but because I spent a weekend working with Claude and in just a few hours we developed a solution that meets my requirements better than any of those products. Not in spite of that fact, but precisely because of it.

Because purchased software has to fit many people. It makes decisions in favor of interoperability, not in favor of my process. It’s built to fit into a hundred different workflows β€” and therefore fits none of them perfectly. My software only has to work in a single context: mine. That’s not a compromise. That’s a structural advantage.

And then came the question that hasn’t let me go since: If I can do this β€” what am I actually still buying?

From Idea to Prompt β€” a Process, Not a Moment

The answer is more uncomfortable than expected: less than today β€” but not because of price.

The prompt is not the starting point. It is the result.

Building is the method by which you force understanding. You start, run into questions you didn’t have before, and make decisions that sharpen the idea. At the end you have not just a tool β€” but an understanding of the problem precise enough to describe it.

Only now does the prompt emerge β€” the one you can pass on.

The real work is not the building. It is the understanding. Anyone who has never iterated themselves, who has never run up against the limits of a vague idea, doesn’t have a prompt β€” they have a wish list.

And wish lists can’t be sold.

The Invisible Hurdle

While writing this article, I notice: I need a small tool. A Markdown editor that lets me leave annotations directly in the text β€” not in a separate document, not via copy-paste, but inline, next to the paragraphs. Something like comments in Google Docs, but for my local workflow.

I know I could build it. I have a rough idea of what it would look like. And yet I hesitate.

Not because of cost. Not because of missing competence. But because of the 20 minutes. Or is it two hours? Or more? The hesitation doesn’t arise from inability, but from uncertainty about the effort β€” and from the quiet fear of getting stuck in the middle of another task.

That is the new hurdle. It is no longer technical. It is psychological.

Anyone who works with AI assistants today quickly notices: the building itself is no longer the problem. The problem is knowing what you want. Precisely enough to describe it. Concretely enough to take the first step. The barrier has shifted β€” from being able to wanting to. And that is a skill you have to practice.

The Other Side: What Do You Sell Then?

If I can build a tool that serves my needs better than a commercial product, what does that mean for providers of such products? What does it mean for everyone who sells software?

Take my monitoring tool as a thought experiment: suppose I wanted to offer it. What would my product be? The software itself? That’s replicable in a few hours. The code? Meaningless once someone runs the same prompt. The infrastructure? Commodity.

What remains is the idea. The observation that a particular problem exists. The precise description of what a solution should look like. The judgment to recognize when the result is good.

That sounds abstract. But it isn’t.

Writing a prompt for a good monitoring tool is not a trivial task. It must describe the problem precisely: What kind of changes interest me? How should notification work? What are the edge cases? What is explicitly not wanted? A vague prompt produces vague software. The work has shifted β€” from typing code to thinking about the problem.

The Thesis: The Idea Becomes the Asset

Ideas always need a medium. Music needs a recording, a book needs a publisher, a film needs a studio. In all these cases, the production barrier separates the idea from its realization β€” and whoever controls the means of production controls access to the market.

Software was the same β€” but with one crucial difference. With music or books, someone had to produce. With software, someone had to translate. The idea existed in a language that machines didn’t understand. Developers weren’t producers β€” they were interpreters. And interpreters are hard to replace.

The code was the asset, because the translation was the actual work.

That is no longer true.

Code is today as easy to produce as text. What has value is the description of what the code should do. The context. The knowledge about the user, the problem, the constraints. The ability to formulate a problem so precisely that a machine can solve it.

In other words: the specification is the product.

This is a radical shift. Not because developers become superfluous β€” whoever judges whether the result is good still needs deep understanding. But the value creation sits elsewhere. It sits with the person who knows the problem, who asks the right questions, who recognizes when the solution is done.

Today we buy software. Tomorrow we buy the prompt.

Where This Applies β€” and Where It Doesn’t

This is the question I cannot answer definitively. And honestly: one I don’t want to answer. Because I believe the right answer depends on context β€” and that it is only now beginning to emerge.

My observation: it applies everywhere software is a tool. Where it fulfills a clearly defined task that someone who understands the problem can describe.

It may apply less where software is a network. Where the value lies not in the functionality but in the critical mass of users. Where integration, trust, and reliability have been built over years.

And it may not apply at all where software is regulated infrastructure β€” where certifications, liability, and compliance are the actual service, not the code.

But these are my provisional answers. I’m curious about yours.

Where in your work are you still buying software today that you would build yourself tomorrow? And conversely: where are you still developing something by hand even though a well-formulated prompt could do it just as well?

What Remains

I will keep developing my monitoring tool. Not because I think I can build a business with it. But because it does exactly what I need β€” and because I learned in the process how to describe the next problem.

That feels like the more important competence. Not the building. The describing.

Those who learn to formulate problems precisely will not be replaced. They will be the ones who instruct the machine.

And that, I think, is a pretty good position.


This article is the first in a loose series about software, AI, and the question of what actually has value.


Daily News by Email – Automated, Free, Self-Built

Every evening at 6 PM I want to know what moved the day. Not the front page of some news portal with its clickbait headlines. Not a newsletter someone else curated for a different audience. My news – my topics, my language, my format.

So I built it myself.

The Idea

The concept is simple: a Python script runs once a day, asks Gemini for the most important news of the day, and sends the result as a formatted HTML email to my inbox. Done.

The interesting question was: where does the script run? A dedicated server would be overkill. fly.io, Sprites, Cloud Functions – all possible, but more effort than necessary. The answer was sitting right in the repository: GitHub Actions.

The repository exists anyway, because I want to version-control the prompt and configuration. If I already have a repo, GitHub Actions can handle the cron job – free, precise, no additional infrastructure.

The Architecture

GitHub Repository
β”œβ”€β”€ run.py          ← the actual script
β”œβ”€β”€ config.json     ← topics, recipients, format
β”œβ”€β”€ prompt.md       ← instructions for the AI model
└── .github/
    └── workflows/
        └── news.yml  ← cron job, daily at 6 PM

The flow on each run:

  1. GitHub Actions fires daily at 16:00 UTC (= 18:00 CEST)
  2. run.py loads config.json and prompt.md from the repo
  3. Gemini 2.5 Flash researches the news via Google Search Grounding
  4. The script formats the result as an HTML email
  5. Resend delivers the mail
  6. The runner exits – no ongoing costs

The Core: Google Search Grounding

The key difference from a regular LLM call is Google Search Grounding. Gemini gets direct access to current web content – no hallucinating yesterday’s news, no stale training data.

The API call looks like this:

body = {
    "system_instruction": {"parts": [{"text": prompt}]},
    "contents": [{"role": "user", "parts": [{"text": user_message}]}],
    "tools": [{"google_search": {}}]   # ← this is the key
}

response = requests.post(
    f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={api_key}",
    json=body,
    timeout=60
)

The google_search parameter activates the grounding. Gemini decides when and what to search for – guided by the prompt.

The Prompt as Control Center

The prompt lives as prompt.md in the repository. That’s the real lever: anyone who wants to change the structure of the email edits a Markdown file and pushes. The next run at 6 PM picks up the new version – no deployment, no rebuild.

My prompt defines a fixed output structure with seven sections: Top News, Story of the Day, Germany, International, Technology, Markets, and Tomorrow’s Outlook. Plus style rules: always German, no opinions, no articles older than 24 hours, sources in parentheses.

## Output Structure

### Top News
5–7 headlines. **Bold headline**, 2–3 sentences of context. *(Source)*

### Story of the Day
The most important event (~150 words): What happened? What's the background? What comes next?

### Markets
- DAX (closing price, change in %)
- EUR/USD
- Brent crude (USD/barrel)
...

The Configuration

Topics and recipients live in config.json:

{
  "recipient_email": "me@example.com",
  "sender_name": "Evening Edition",
  "topics": [
    "Automotive & Electric Mobility",
    "Artificial Intelligence & Technology",
    "German Fiscal and Economic Policy",
    "Energy & Climate Policy"
  ],
  "subject_template": "πŸ“° Evening Edition – {date}"
}

The topics array ends up in the prompt under “My Topics” – Gemini then actively searches for news from those areas.

The GitHub Actions Workflow

# .github/workflows/news.yml
name: Daily News Mailer

on:
  schedule:
    - cron: '0 16 * * *'   # 16:00 UTC = 18:00 CEST

jobs:
  send-news:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run news mailer
        run: pip install requests && python run.py
        env:
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}
          SENDER_EMAIL:   ${{ secrets.SENDER_EMAIL }}

Secrets are stored under Repository β†’ Settings β†’ Secrets and variables β†’ Actions – never in code, never in files.

In winter the cron needs to be adjusted to 0 17 * * * (CET instead of CEST).

What It Costs

ServiceFree TierMy Usage
Gemini 2.5 Flash500 requests/day1–2/day
Resend100 emails/day1–2/day
GitHub Actions2,000 min/month~2 min/day

Total: €0 per month. Permanently, no credit card required.

What I Learned

Prompt engineering is the actual product. The Python script took an hour to write. Getting the prompt right – so that Gemini reliably produces the correct structure, doesn’t invent links, and actually only uses current articles – took more iterations than the code.

GitHub Actions as a cron is underrated. For anything that runs once a day and doesn’t need a persistently running deployment, it’s the most straightforward solution.


A Writing Workflow for the Blog

The blog is running, drafts can be published, Cloudflare builds reliably. What’s missing is the core activity: writing.

Over the past few weeks I’ve tried different ways to create a post, and learned one thing: the question of how can only be answered once the what is clear. Otherwise you swap tooling every two weeks and still don’t write a post.

This post is about the requirements I have for my workflow, the options I evaluated, and the decision I landed on.

1) Requirements for My Workflow

Before I compare tools, I need a yardstick. A workflow for this blog should:

  • work bilingually, creating DE and EN bundles in one step
  • minimize friction (one command, not five)
  • behave identically locally and in the cloud
  • play well with Git and version control
  • keep drafts clearly separated from published posts
  • be usable from any editor

It should not be a CMS, offer no WYSIWYG interface, and avoid any dependency on an external service. Anything that is not in Git does not exist for me.

2) Option A β€” Directly in the Editor

The simplest variant: create two files in the editor, copy front matter from an existing post, start typing.

Upsides: no learning curve, no extra tooling, full control over every line.

Downsides: every repetition is a potential source of error. You forget a front matter field, name the slug inconsistently, or create only the German file and never follow up with the English one. The bilingual pairing happens in your head, not in the tool.

Workable for a one-off post. Too fragile for a blog I want to maintain long-term.

3) Option B β€” hugo new content

Hugo ships with a built-in CLI that creates a new content file from an archetype.

hugo new content posts/my-post/index.md

The archetype under archetypes/posts.md defines which front matter gets pre-filled. The result is a file with a consistent skeleton, without copying lines around.

Upsides: official, well documented, zero external dependencies.

Downsides: the command creates only one file. For my bilingual structure I would have to run it twice and build the English bundle by hand around it. The publish step (draft to false, commit, push) is out of scope.

Good as a building block, too incomplete as a workflow.

4) Option C β€” make draft

The third option is a Makefile target that calls a shell script under the hood:

make draft SLUG=my-post

The script creates the bundle: content/posts/my-post/index.md and content/posts/my-post/index.en.md, both with correctly pre-filled front matter and draft: true.

On top of that, there is make publish (flips draft: false) and make push (commit and push). The three targets together cover the lifecycle.

The key point: the same scripts run in GitHub Actions. There are no two truths β€” the logic lives in one place and is called by both the Makefile and the workflow.

Upsides: one command for setup, one for publish, consistent bundles by construction, no logic duplication between local and online.

Downsides: the scripts have to be built and maintained. For a generic Hugo blog that would be overkill; for my specific bilingual case it fits.

5) Option D β€” GitHub Actions Web UI

Option C has a twin in the browser. Under Actions β†’ Create Draft Post / Publish Draft Post, the same scripts run directly from the GitHub web interface. Enter a slug, click Run workflow, done β€” the bundle is created, committed, Cloudflare builds.

The nice thing is that the actions run the same code as the local Make targets. I don’t have to wonder whether a draft created online looks different from a local one. It doesn’t.

What I use it for: when I’m at someone else’s machine, have no repo cloned, or am just lazy. The web UI is unsuitable for actual writing, but for creating a placeholder post while an idea hits me on a walk, it’s enough.

6) Decision

make draft is the main path.

Three reasons: first, it maps exactly to my bilingual structure, with no mental arithmetic. Second, it avoids logic duplication β€” the shell script is the single source of truth, whether local or in the cloud. Third, it is simple enough to read and extend in ten minutes, and capable enough to save me work.

hugo new content stays as a backup in case I ever need a single post outside the bundle structure. Option D steps in when I can’t reach the terminal.

7) My Workflow in Practice

A typical post cycle for me looks like this:

  1. Capture the idea (Obsidian, notepad, anywhere)
  2. At the desk: make draft SLUG=new-post
  3. The editor opens the DE bundle, I start writing
  4. Keep hugo server -D running in parallel to see drafts live
  5. Pull the English version along in the same bundle
  6. make publish SLUG=new-post flips draft: false
  7. make push MESSAGE="Post about X" commits and deploys

On the road the same thing runs via Working Copy plus the GitHub Actions web UI. The workflow is robust enough to work either way, and simple enough that I don’t have to relearn it after two weeks off.

Conclusion

The workflow is a tool, not an end in itself. Every minute I spend tuning tooling is a minute less spent writing.

make draft won for me because it hits my requirements most precisely β€” not because Makefiles are inherently superior. For a different blog with a different structure, the right answer might be hugo new content or a CMS.

The only thing that matters is that the friction of starting a post eventually drops to near zero. Then the question “how do I start?” stops being an excuse.


Bilingual with Hugo: How I Approached It

Multilingual setup in Hugo sounds simple at first: define languages in config, translate content, done. In practice, issues hide in the details.

Why bother with multilingual content at all if browsers can translate pages live? For me, the answer is simple: I think in German, writing in German is more natural. But live translation is not enough for me, because I want full control over tone, terminology, and nuance in English.

For a moment, I considered writing in German, translating to English, and then throwing the German original away. That felt wrong. I chose a clear path: the blog stays German, with a deliberately maintained English version.

In this post, I share how I extended Meadow Lark to German and English, what to watch out for, and which errors I hit.

1) A Solid Base in Hugo Config

The first step is a clean language configuration in hugo.yaml.

The key points are:

  • Set defaultContentLanguage to ‘de’
  • Define both languages under languages
  • Maintain menu entries per language
  • Use a consistent date format per language

I set the theme entry explicitly. Without an active theme, layouts can be missing or pages can appear blank.

2) Use Language File Pairs for Content

I use paired files:

  • about.md and about.en.md
  • projects.md and projects.en.md
  • posts/slug.md and posts/slug.en.md

This makes mapping unambiguous and allows Hugo to connect translations cleanly.

Important: each language version needs its own front matter and a meaningful summary. The summary should be written manually, not auto-generated.

3) Language Switcher in the Template

The switcher lives in a reusable navigation component. The basic principle:

  • If a translation exists for the current page, link to that page directly
  • If not, link to the other language’s homepage

That second part matters, so users do not end up in a dead end.

4) Typical Errors That Happen Fast

Error 1: Theme Not Active

Symptom: The homepage looks blank or layouts are incomplete.

Cause: The theme entry is missing in hugo.yaml.

Fix: Set theme explicitly so base layouts are loaded.

Important: theme belongs at the top level in hugo.yaml, not under languages, params, or outputs.

baseURL: "https://meadowlark.care"
theme: "hugo-bearblog"

defaultContentLanguage: de
defaultContentLanguageInSubdir: false

Error 2: API Differences Between Hugo Versions

Symptom: Everything works locally, but the cloud build fails in templates because two Hugo versions are involved.

In my case, local was Hugo 0.160 while Cloudflare built with Hugo 0.146. A newer template API therefore worked locally.

Fix: Pin versions: local and cloud use the same Hugo version (for example 0.160.1), then deploy. If that is not possible, adjust the language/i18n logic to more compatible constructs. Make sure to rebuild locally after that, then redeploy.

In practice, I pinned in two places: Cloudflare with HUGO_VERSION=0.160.1 and locally via a .tool-versions file containing hugo 0.160.1. That keeps local and online builds aligned.

Symptom: RSS links or language links point to the wrong language or to 404s.

Fix: Build links via Hugo output formats and RelPermalink instead of hardcoding paths.

Error 4: Translations Exist but Are Not Linked

Symptom: The EN file exists, but the switcher does not show it.

Cause is inconsistent file naming or missing counterpart files.

Fix: Keep language pair naming consistent and compare front matter.

5) My Minimal Test Set Before Push

Before I push, I do a quick check:

  • Local hugo build without errors
  • Language switcher on home and post pages
  • Click DE/EN menu links
  • Test RSS links in both languages

The local-vs-cloud version mismatch taught me one thing: a green local build is not always enough.

Conclusion

Bilingual setup with Hugo is manageable when configuration, file naming, and templates work together cleanly.

The biggest pitfalls are not the content itself, but small infrastructure details: theme activation, stable template APIs, and robust URL generation.

Once those three are in place, Hugo i18n becomes pretty relaxed.


SQLite as Application File Format β†—

The SQLite docs have a fantastic page about why SQLite works as an application file format. The arguments are convincing: ACID transactions, extensible without format migrations, performant on partial reads, and universally toolable.

Anyone who has ever built their own binary format or nested JSON as a storage format knows the pain. SQLite solves this elegantly.


New Achievement: Blog Started

Welcome to Meadow Lark.

This blog is now my place for notes, projects and thoughts on tech, self-hosting, AI and everything else that keeps me busy. No algorithm, no comment section, no cookies β€” just static HTML, served by Cloudflare Pages.

Why a Blog?

Because good ideas get lost in chat logs. Because documentation is useful for yourself too. And because in 2026 it’s time again to maintain your own small corner of the internet.

What’s Coming?

  • Self-Hosting: Raspberry Pi, Pi-hole, Grafana, Starlink monitoring
  • AI & Tools: Claude Code, workflows, automation
  • Projects: What I’m currently building and why
  • Notes: Everything worth writing down

The residence has opened its doors. Let’s go.


Level 1 begins.